@@ -7,10 +7,14 @@ package main
77import (
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