Skip to content

Commit 21b6ab5

Browse files
ianlancetaylorgopherbot
authored andcommitted
cmd/link: test that funcdata values are in gopclntab section
This is a test for CL 719440. For #76038 Change-Id: I8fc55118b3c7dea39a36e04ffb060fcb6150af54 Reviewed-on: https://go-review.googlesource.com/c/go/+/721460 Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Ian Lance Taylor <iant@golang.org>
1 parent c03e25a commit 21b6ab5

File tree

1 file changed

+337
-0
lines changed

1 file changed

+337
-0
lines changed

src/cmd/link/link_test.go

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ package main
77
import (
88
"bufio"
99
"bytes"
10+
"debug/elf"
1011
"debug/macho"
12+
"debug/pe"
1113
"errors"
14+
"internal/abi"
1215
"internal/platform"
1316
"internal/testenv"
17+
"internal/xcoff"
1418
"os"
1519
"os/exec"
1620
"path/filepath"
@@ -19,6 +23,7 @@ import (
1923
"strconv"
2024
"strings"
2125
"testing"
26+
"unsafe"
2227

2328
imacho "cmd/internal/macho"
2429
"cmd/internal/objfile"
@@ -1773,3 +1778,335 @@ func TestLinknameBSS(t *testing.T) {
17731778
t.Errorf("executable failed to run: %v\n%s", err, out)
17741779
}
17751780
}
1781+
1782+
// setValueFromBytes copies from a []byte to a variable.
1783+
// This is used to get correctly aligned values in TestFuncdataPlacement.
1784+
func setValueFromBytes[T any](p *T, s []byte) {
1785+
copy(unsafe.Slice((*byte)(unsafe.Pointer(p)), unsafe.Sizeof(*p)), s)
1786+
}
1787+
1788+
// Test that all funcdata values are stored in the .gopclntab section.
1789+
// This is pretty ugly as there is no API for accessing this data.
1790+
// This test will have to be updated when the data formats change.
1791+
func TestFuncdataPlacement(t *testing.T) {
1792+
testenv.MustHaveGoBuild(t)
1793+
t.Parallel()
1794+
1795+
tmpdir := t.TempDir()
1796+
src := filepath.Join(tmpdir, "x.go")
1797+
if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
1798+
t.Fatal(err)
1799+
}
1800+
1801+
exe := filepath.Join(tmpdir, "x.exe")
1802+
cmd := goCmd(t, "build", "-o", exe, src)
1803+
if out, err := cmd.CombinedOutput(); err != nil {
1804+
t.Fatalf("build failed; %v, output:\n%s", err, out)
1805+
}
1806+
1807+
// We want to find the funcdata in the executable.
1808+
// We look at the section table to find the .gopclntab section,
1809+
// which starts with the pcHeader.
1810+
// That will give us the table of functions,
1811+
// which we can use to find the funcdata.
1812+
1813+
ef, _ := elf.Open(exe)
1814+
mf, _ := macho.Open(exe)
1815+
pf, _ := pe.Open(exe)
1816+
xf, _ := xcoff.Open(exe)
1817+
// TODO: plan9
1818+
if ef == nil && mf == nil && pf == nil && xf == nil {
1819+
t.Skip("unrecognized executable file format")
1820+
}
1821+
1822+
const moddataSymName = "runtime.firstmoduledata"
1823+
const gofuncSymName = "go:func.*"
1824+
var (
1825+
pclntab []byte
1826+
pclntabAddr uint64
1827+
pclntabEnd uint64
1828+
moddataAddr uint64
1829+
moddataBytes []byte
1830+
gofuncAddr uint64
1831+
imageBase uint64
1832+
)
1833+
switch {
1834+
case ef != nil:
1835+
defer ef.Close()
1836+
1837+
syms, err := ef.Symbols()
1838+
if err != nil {
1839+
t.Fatal(err)
1840+
}
1841+
for _, sym := range syms {
1842+
switch sym.Name {
1843+
case moddataSymName:
1844+
moddataAddr = sym.Value
1845+
case gofuncSymName:
1846+
gofuncAddr = sym.Value
1847+
}
1848+
}
1849+
1850+
for _, sec := range ef.Sections {
1851+
if sec.Name == ".gopclntab" {
1852+
data, err := sec.Data()
1853+
if err != nil {
1854+
t.Fatal(err)
1855+
}
1856+
pclntab = data
1857+
pclntabAddr = sec.Addr
1858+
pclntabEnd = sec.Addr + sec.Size
1859+
}
1860+
if sec.Flags&elf.SHF_ALLOC != 0 && moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
1861+
data, err := sec.Data()
1862+
if err != nil {
1863+
t.Fatal(err)
1864+
}
1865+
moddataBytes = data[moddataAddr-sec.Addr:]
1866+
}
1867+
}
1868+
1869+
case mf != nil:
1870+
defer mf.Close()
1871+
1872+
for _, sym := range mf.Symtab.Syms {
1873+
switch sym.Name {
1874+
case moddataSymName:
1875+
moddataAddr = sym.Value
1876+
case gofuncSymName:
1877+
gofuncAddr = sym.Value
1878+
}
1879+
}
1880+
1881+
for _, sec := range mf.Sections {
1882+
if sec.Name == "__gopclntab" {
1883+
data, err := sec.Data()
1884+
if err != nil {
1885+
t.Fatal(err)
1886+
}
1887+
pclntab = data
1888+
pclntabAddr = sec.Addr
1889+
pclntabEnd = sec.Addr + sec.Size
1890+
}
1891+
if moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
1892+
data, err := sec.Data()
1893+
if err != nil {
1894+
t.Fatal(err)
1895+
}
1896+
moddataBytes = data[moddataAddr-sec.Addr:]
1897+
}
1898+
}
1899+
1900+
case pf != nil:
1901+
defer pf.Close()
1902+
1903+
switch ohdr := pf.OptionalHeader.(type) {
1904+
case *pe.OptionalHeader32:
1905+
imageBase = uint64(ohdr.ImageBase)
1906+
case *pe.OptionalHeader64:
1907+
imageBase = ohdr.ImageBase
1908+
}
1909+
1910+
var moddataSym, gofuncSym, pclntabSym, epclntabSym *pe.Symbol
1911+
for _, sym := range pf.Symbols {
1912+
switch sym.Name {
1913+
case moddataSymName:
1914+
moddataSym = sym
1915+
case gofuncSymName:
1916+
gofuncSym = sym
1917+
case "runtime.pclntab":
1918+
pclntabSym = sym
1919+
case "runtime.epclntab":
1920+
epclntabSym = sym
1921+
}
1922+
}
1923+
1924+
if moddataSym == nil {
1925+
t.Fatalf("could not find symbol %s", moddataSymName)
1926+
}
1927+
if gofuncSym == nil {
1928+
t.Fatalf("could not find symbol %s", gofuncSymName)
1929+
}
1930+
if pclntabSym == nil {
1931+
t.Fatal("could not find symbol runtime.pclntab")
1932+
}
1933+
if epclntabSym == nil {
1934+
t.Fatal("could not find symbol runtime.epclntab")
1935+
}
1936+
1937+
sec := pf.Sections[moddataSym.SectionNumber-1]
1938+
data, err := sec.Data()
1939+
if err != nil {
1940+
t.Fatal(err)
1941+
}
1942+
moddataBytes = data[moddataSym.Value:]
1943+
moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
1944+
1945+
sec = pf.Sections[gofuncSym.SectionNumber-1]
1946+
gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
1947+
1948+
if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
1949+
t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
1950+
}
1951+
sec = pf.Sections[pclntabSym.SectionNumber-1]
1952+
data, err = sec.Data()
1953+
if err != nil {
1954+
t.Fatal(err)
1955+
}
1956+
pclntab = data[pclntabSym.Value:epclntabSym.Value]
1957+
pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
1958+
pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
1959+
1960+
case xf != nil:
1961+
defer xf.Close()
1962+
1963+
for _, sym := range xf.Symbols {
1964+
switch sym.Name {
1965+
case moddataSymName:
1966+
moddataAddr = sym.Value
1967+
case gofuncSymName:
1968+
gofuncAddr = sym.Value
1969+
}
1970+
}
1971+
1972+
for _, sec := range xf.Sections {
1973+
if sec.Name == ".go.pclntab" {
1974+
data, err := sec.Data()
1975+
if err != nil {
1976+
t.Fatal(err)
1977+
}
1978+
pclntab = data
1979+
pclntabAddr = sec.VirtualAddress
1980+
pclntabEnd = sec.VirtualAddress + sec.Size
1981+
}
1982+
if moddataAddr >= sec.VirtualAddress && moddataAddr < sec.VirtualAddress+sec.Size {
1983+
data, err := sec.Data()
1984+
if err != nil {
1985+
t.Fatal(err)
1986+
}
1987+
moddataBytes = data[moddataAddr-sec.VirtualAddress:]
1988+
}
1989+
}
1990+
1991+
default:
1992+
panic("can't happen")
1993+
}
1994+
1995+
if len(pclntab) == 0 {
1996+
t.Fatal("could not find pclntab section")
1997+
}
1998+
if moddataAddr == 0 {
1999+
t.Fatalf("could not find %s symbol", moddataSymName)
2000+
}
2001+
if gofuncAddr == 0 {
2002+
t.Fatalf("could not find %s symbol", gofuncSymName)
2003+
}
2004+
if gofuncAddr < pclntabAddr || gofuncAddr >= pclntabEnd {
2005+
t.Fatalf("%s out of range: value %#x not between %#x and %#x", gofuncSymName, gofuncAddr, pclntabAddr, pclntabEnd)
2006+
}
2007+
if len(moddataBytes) == 0 {
2008+
t.Fatal("could not find module data")
2009+
}
2010+
2011+
// What a slice looks like in the object file.
2012+
type moddataSlice struct {
2013+
addr uintptr
2014+
len int
2015+
cap int
2016+
}
2017+
2018+
// This needs to match the struct defined in runtime/symtab.go,
2019+
// and written out by (*Link).symtab.
2020+
// This is not the complete moddata struct, only what we need here.
2021+
type moddataType struct {
2022+
pcHeader uintptr
2023+
funcnametab moddataSlice
2024+
cutab moddataSlice
2025+
filetab moddataSlice
2026+
pctab moddataSlice
2027+
pclntable moddataSlice
2028+
ftab moddataSlice
2029+
findfunctab uintptr
2030+
minpc, maxpc uintptr
2031+
2032+
text, etext uintptr
2033+
noptrdata, enoptrdata uintptr
2034+
data, edata uintptr
2035+
bss, ebss uintptr
2036+
noptrbss, enoptrbss uintptr
2037+
covctrs, ecovctrs uintptr
2038+
end, gcdata, gcbss uintptr
2039+
types, etypes uintptr
2040+
rodata uintptr
2041+
gofunc uintptr
2042+
}
2043+
2044+
// The executable is on the same system as we are running,
2045+
// so the sizes and alignments should match.
2046+
// But moddataBytes itself may not be aligned as needed.
2047+
// Copy to a variable to ensure alignment.
2048+
var moddata moddataType
2049+
setValueFromBytes(&moddata, moddataBytes)
2050+
2051+
ftabAddr := uint64(moddata.ftab.addr) - imageBase
2052+
if ftabAddr < pclntabAddr || ftabAddr >= pclntabEnd {
2053+
t.Fatalf("ftab address %#x not between %#x and %#x", ftabAddr, pclntabAddr, pclntabEnd)
2054+
}
2055+
2056+
// From runtime/symtab.go and the linker function writePCToFunc.
2057+
type functab struct {
2058+
entryoff uint32
2059+
funcoff uint32
2060+
}
2061+
// The ftab slice in moddata has one extra entry used to record
2062+
// the final PC.
2063+
ftabLen := moddata.ftab.len - 1
2064+
ftab := make([]functab, ftabLen)
2065+
copy(ftab, unsafe.Slice((*functab)(unsafe.Pointer(&pclntab[ftabAddr-pclntabAddr])), ftabLen))
2066+
2067+
ftabBase := uint64(moddata.pclntable.addr) - imageBase
2068+
2069+
// From runtime/runtime2.go _func and the linker function writeFuncs.
2070+
type funcEntry struct {
2071+
entryOff uint32
2072+
nameOff int32
2073+
2074+
args int32
2075+
deferreturn uint32
2076+
2077+
pcsp uint32
2078+
pcfile uint32
2079+
pcln uint32
2080+
npcdata uint32
2081+
cuOffset uint32
2082+
startLine int32
2083+
funcID abi.FuncID
2084+
flag abi.FuncFlag
2085+
_ [1]byte
2086+
nfuncdata uint8
2087+
}
2088+
2089+
for i, ftabEntry := range ftab {
2090+
funcAddr := ftabBase + uint64(ftabEntry.funcoff)
2091+
if funcAddr < pclntabAddr || funcAddr >= pclntabEnd {
2092+
t.Errorf("ftab entry %d address %#x not between %#x and %#x", i, funcAddr, pclntabAddr, pclntabEnd)
2093+
continue
2094+
}
2095+
2096+
var fe funcEntry
2097+
setValueFromBytes(&fe, pclntab[funcAddr-pclntabAddr:])
2098+
2099+
funcdataVals := funcAddr + uint64(unsafe.Sizeof(fe)) + uint64(fe.npcdata*4)
2100+
for j := range fe.nfuncdata {
2101+
var funcdataVal uint32
2102+
setValueFromBytes(&funcdataVal, pclntab[funcdataVals+uint64(j)*4-pclntabAddr:])
2103+
if funcdataVal == ^uint32(0) {
2104+
continue
2105+
}
2106+
funcdataAddr := gofuncAddr + uint64(funcdataVal)
2107+
if funcdataAddr < pclntabAddr || funcdataAddr >= pclntabEnd {
2108+
t.Errorf("ftab entry %d funcdata %d address %#x not between %#x and %#x", i, j, funcdataAddr, pclntabAddr, pclntabEnd)
2109+
}
2110+
}
2111+
}
2112+
}

0 commit comments

Comments
 (0)