已合并
Go Compiler changes for llvm-bolt support #95
Vasily Leonenko创建于 8月25日
Go Compiler changes for llvm-bolt support #95
已合并
Vasily Leonenko创建于 8月25日
共 47 个文件变更+9215-69
@@ -6,6 +6,7 @@
6package flags6package flags
7 7 
8import (8import (
9+ "cmd/internal/goobj"
9 "cmd/internal/obj"10 "cmd/internal/obj"
10 "cmd/internal/objabi"11 "cmd/internal/objabi"
11 "flag"12 "flag"
@@ -46,6 +47,8 @@ func init() {
46 flag.Var(&I, "I", "include directory; can be set multiple times")47 flag.Var(&I, "I", "include directory; can be set multiple times")
47 flag.BoolVar(&DebugV, "v", false, "print debug output")48 flag.BoolVar(&DebugV, "v", false, "print debug output")
48 flag.Var(objabi.NewDebugFlag(&DebugFlags, nil), "d", "enable debugging settings; try -d help")49 flag.Var(objabi.NewDebugFlag(&DebugFlags, nil), "d", "enable debugging settings; try -d help")
50+ flag.BoolVar(&goobj.EnableMappingSymbols, "mappingsymbol", false, "generate mapping symbols for arm64")
51+ 
49 objabi.AddVersionFlag() // -V52 objabi.AddVersionFlag() // -V
50 objabi.Flagcount("S", "print assembly and machine code", &PrintOut)53 objabi.Flagcount("S", "print assembly and machine code", &PrintOut)
51}54}
@@ -18,6 +18,7 @@ import (
18 "cmd/asm/internal/lex"18 "cmd/asm/internal/lex"
19 19 
20 "cmd/internal/bio"20 "cmd/internal/bio"
21+ "cmd/internal/goobj"
21 "cmd/internal/obj"22 "cmd/internal/obj"
22 "cmd/internal/objabi"23 "cmd/internal/objabi"
23 "cmd/internal/telemetry/counter"24 "cmd/internal/telemetry/counter"
@@ -39,6 +40,11 @@ func main() {
39 if architecture == nil {40 if architecture == nil {
40 log.Fatalf("unrecognized architecture %s", GOARCH)41 log.Fatalf("unrecognized architecture %s", GOARCH)
41 }42 }
43+ if goobj.EnableMappingSymbols && GOARCH != "arm64" {
44+ // mapping symbols are arm64-only; keep the goobj file format
45+ // identical to upstream on other architectures
46+ goobj.EnableMappingSymbols = false
47+ }
42 ctxt := obj.Linknew(architecture.LinkArch)48 ctxt := obj.Linknew(architecture.LinkArch)
43 ctxt.Debugasm = flags.PrintOut49 ctxt.Debugasm = flags.PrintOut
44 ctxt.Debugvlog = flags.DebugV50 ctxt.Debugvlog = flags.DebugV
@@ -76,6 +76,7 @@ type DebugFlags struct {
76 WrapGlobalMapDbg int `help:"debug trace output for global map init wrapping"`76 WrapGlobalMapDbg int `help:"debug trace output for global map init wrapping"`
77 WrapGlobalMapCtl int `help:"global map init wrap control (0 => default, 1 => off, 2 => stress mode, no size cutoff)"`77 WrapGlobalMapCtl int `help:"global map init wrap control (0 => default, 1 => off, 2 => stress mode, no size cutoff)"`
78 ZeroCopy int `help:"enable zero-copy string->[]byte conversions" concurrent:"ok"`78 ZeroCopy int `help:"enable zero-copy string->[]byte conversions" concurrent:"ok"`
79+ Go119UseJumpTables int `help:"allow to use jump tables"`
79 ForceInline int `help:"enable force inline"`80 ForceInline int `help:"enable force inline"`
80 ForceInlineLog int `help:"print force inline log"`81 ForceInlineLog int `help:"print force inline log"`
81 AArch64LdSt string `help:"allow to merge loads and stores using ldp/stp on AArch64\nOne of: none, off, load, store, all, on"`82 AArch64LdSt string `help:"allow to merge loads and stores using ldp/stp on AArch64\nOne of: none, off, load, store, all, on"`
@@ -6,6 +6,7 @@ package base
6 6 
7import (7import (
8 "cmd/internal/cov/covcmd"8 "cmd/internal/cov/covcmd"
9+ "cmd/internal/goobj"
9 "cmd/internal/telemetry/counter"10 "cmd/internal/telemetry/counter"
10 "encoding/json"11 "encoding/json"
11 "flag"12 "flag"
@@ -203,7 +204,10 @@ func ParseFlags() {
203 204 
204 Debug.AArch64LdSt = "none"205 Debug.AArch64LdSt = "none"
205 206 
207+ Debug.Go119UseJumpTables = 1 // allow to use jump tables by default
208+ 
206 Flag.Cfg.ImportMap = make(map[string]string)209 Flag.Cfg.ImportMap = make(map[string]string)
210+ flag.BoolVar(&goobj.EnableMappingSymbols, "mappingsymbol", false, "generate mapping symbols for arm64")
207 211 
208 objabi.AddVersionFlag() // -V212 objabi.AddVersionFlag() // -V
209 registerFlags()213 registerFlags()
@@ -284,6 +288,11 @@ func ParseFlags() {
284 MergeLocalsHash = NewHashDebug("mergelocals", Debug.MergeLocalsHash, nil)288 MergeLocalsHash = NewHashDebug("mergelocals", Debug.MergeLocalsHash, nil)
285 }289 }
286 290 
291+ if goobj.EnableMappingSymbols && buildcfg.GOARCH != "arm64" {
292+ // mapping symbols are arm64-only; keep the goobj file format
293+ // identical to upstream on other architectures
294+ goobj.EnableMappingSymbols = false
295+ }
287 if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {296 if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
288 log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)297 log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
289 }298 }
@@ -295,10 +295,14 @@ func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) {
295 295 
296// Try to implement the clauses with a jump table. Returns true if successful.296// Try to implement the clauses with a jump table. Returns true if successful.
297func (s *exprSwitch) tryJumpTable(cc []exprClause, out *ir.Nodes) bool {297func (s *exprSwitch) tryJumpTable(cc []exprClause, out *ir.Nodes) bool {
298+ var go119UseJumpTables = true
298 const minCases = 8 // have at least minCases cases in the switch299 const minCases = 8 // have at least minCases cases in the switch
299 const minDensity = 4 // use at least 1 out of every minDensity entries300 const minDensity = 4 // use at least 1 out of every minDensity entries
300 301 
301- if base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable || base.Ctxt.Retpoline {302+ if base.Debug.Go119UseJumpTables == 0 {
303+ go119UseJumpTables = false
304+ }
305+ if !go119UseJumpTables || base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable || base.Ctxt.Retpoline {
302 return false306 return false
303 }307 }
304 if len(cc) < minCases {308 if len(cc) < minCases {
@@ -774,8 +778,12 @@ func (s *typeSwitch) flush(cc []typeClause, compiled *ir.Nodes) {
774 778 
775// Try to implement the clauses with a jump table. Returns true if successful.779// Try to implement the clauses with a jump table. Returns true if successful.
776func (s *typeSwitch) tryJumpTable(cc []typeClause, out *ir.Nodes) bool {780func (s *typeSwitch) tryJumpTable(cc []typeClause, out *ir.Nodes) bool {
781+ var go119UseJumpTables = true
777 const minCases = 5 // have at least minCases cases in the switch782 const minCases = 5 // have at least minCases cases in the switch
778- if base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable || base.Ctxt.Retpoline {783+ if base.Debug.Go119UseJumpTables == 0 {
784+ go119UseJumpTables = false
785+ }
786+ if !go119UseJumpTables || base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable || base.Ctxt.Retpoline {
779 return false787 return false
780 }788 }
781 if len(cc) < minCases {789 if len(cc) < minCases {
@@ -110,6 +110,8 @@ var (
110 GoPathError string110 GoPathError string
111 GOPATHChanged bool111 GOPATHChanged bool
112 CGOChanged bool112 CGOChanged bool
113+ 
114+ MappingSymbol bool // -mappingsymbol flag
113)115)
114 116 
115func defaultContext() build.Context {117func defaultContext() build.Context {
@@ -355,6 +355,8 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
355 cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "")355 cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "")
356 cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "")356 cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "")
357 357 
358+ cmd.Flag.BoolVar(&cfg.MappingSymbol, "mappingsymbol", false, "")
359+ 
358 // Undocumented, unstable debugging flags.360 // Undocumented, unstable debugging flags.
359 cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")361 cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
360 cmd.Flag.StringVar(&cfg.DebugRuntimeTrace, "debug-runtime-trace", "", "")362 cmd.Flag.StringVar(&cfg.DebugRuntimeTrace, "debug-runtime-trace", "", "")
@@ -336,6 +336,11 @@ func buildModeInit() {
336 base.Fatalf("build flag -modfile only valid when using modules")336 base.Fatalf("build flag -modfile only valid when using modules")
337 }337 }
338 }338 }
339+ if cfg.MappingSymbol {
340+ forcedGcflags = append(forcedGcflags, "-mappingsymbol")
341+ forcedAsmflags = append(forcedAsmflags, "-mappingsymbol")
342+ forcedLdflags = append(forcedLdflags, "-mappingsymbol")
343+ }
339}344}
340 345 
341type version struct {346type version struct {
@@ -24,6 +24,7 @@ import (
24 24 
25 "cmd/internal/objfile"25 "cmd/internal/objfile"
26 "cmd/internal/src"26 "cmd/internal/src"
27+ "internal/abi"
27 28 
28 "golang.org/x/arch/arm/armasm"29 "golang.org/x/arch/arm/armasm"
29 "golang.org/x/arch/arm64/arm64asm"30 "golang.org/x/arch/arm64/arm64asm"
@@ -36,14 +37,15 @@ import (
36 37 
37// Disasm is a disassembler for a given File.38// Disasm is a disassembler for a given File.
38type Disasm struct {39type Disasm struct {
39- syms []objfile.Sym // symbols in file, sorted by address40+ syms []objfile.Sym // symbols in file, sorted by address
40- pcln objfile.Liner // pcln table41+ pcln objfile.Liner // pcln table
41- text []byte // bytes of text segment (actual instructions)42+ text []byte // bytes of text segment (actual instructions)
42- textStart uint64 // start PC of text43+ textStart uint64 // start PC of text
43- textEnd uint64 // end PC of text44+ textEnd uint64 // end PC of text
44- goarch string // GOARCH string45+ goarch string // GOARCH string
45- disasm disasmFunc // disassembler function for goarch46+ disasm disasmFunc // disassembler function for goarch
46- byteOrder binary.ByteOrder // byte order for goarch47+ byteOrder binary.ByteOrder // byte order for goarch
48+ md *objfile.Moduledata // moduledata for funcInfo lookup
47}49}
48 50 
49// DisasmForFile returns a disassembler for the file f.51// DisasmForFile returns a disassembler for the file f.
@@ -111,6 +113,13 @@ func (d *Disasm) lookup(addr uint64) (name string, base uint64) {
111 return "", 0113 return "", 0
112}114}
113 115 
116+func (d *Disasm) SetModuledata(md *objfile.Moduledata) {
117+ if md == nil {
118+ return
119+ }
120+ d.md = md
121+}
122+ 
114// base returns the final element in the path.123// base returns the final element in the path.
115// It works on both Windows and Unix paths,124// It works on both Windows and Unix paths,
116// regardless of host operating system.125// regardless of host operating system.
@@ -194,11 +203,28 @@ func (fc *FileCache) Line(filename string, line int) ([]byte, error) {
194 return cf.Lines[line-1], nil203 return cf.Lines[line-1], nil
195}204}
196 205 
206+// MDOptions contains moduledata display options (FUNCDATA, PCSP, PCDATA).
207+type MDOptions struct {
208+ PrintFuncData bool
209+ InlinePCSP bool
210+ InlineUnsafePoint bool
211+ InlineStackMap bool
212+ InlineInlTree bool
213+ InlineArgLive bool
214+}
215+ 
197// Print prints a disassembly of the file to w.216// Print prints a disassembly of the file to w.
198// If filter is non-nil, the disassembly only includes functions with names matching filter.217// If filter is non-nil, the disassembly only includes functions with names matching filter.
199// If printCode is true, the disassembly includes corresponding source lines.218// If printCode is true, the disassembly includes corresponding source lines.
219+// If md options are set, the disassembly includes moduledata annotations inline.
200// The disassembly only includes functions that overlap the range [start, end).220// The disassembly only includes functions that overlap the range [start, end).
201-func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, printCode bool, gnuAsm bool) {221+func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, printCode bool, gnuAsm bool, md MDOptions) {
222+ printFuncData := md.PrintFuncData
223+ inlinePCSP := md.InlinePCSP
224+ inlineUnsafePoint := md.InlineUnsafePoint
225+ inlineStackMap := md.InlineStackMap
226+ inlineInlTree := md.InlineInlTree
227+ inlineArgLive := md.InlineArgLive
202 if start < d.textStart {228 if start < d.textStart {
203 start = d.textStart229 start = d.textStart
204 }230 }
@@ -230,6 +256,17 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, pr
230 printed = true256 printed = true
231 257 
232 file, _, _ := d.pcln.PCToLine(sym.Addr)258 file, _, _ := d.pcln.PCToLine(sym.Addr)
259+ 
260+ var currentFuncInfo objfile.FuncInfo
261+ if printFuncData && d.md != nil {
262+ if fi, ok := d.md.FuncInfoByName(sym.Name); ok {
263+ fmt.Fprintf(bw, "%s", fi.String())
264+ if inlinePCSP || inlineUnsafePoint || inlineStackMap || inlineInlTree || inlineArgLive {
265+ currentFuncInfo = fi
266+ }
267+ }
268+ }
269+ 
233 fmt.Fprintf(bw, "TEXT %s(SB) %s\n", sym.Name, file)270 fmt.Fprintf(bw, "TEXT %s(SB) %s\n", sym.Name, file)
234 271 
235 if symEnd > end {272 if symEnd > end {
@@ -258,10 +295,8 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, pr
258 }295 }
259 296 
260 if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" {297 if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" {
261- // Print instruction as bytes.
262 fmt.Fprintf(tw, "%x", code[i:i+size])298 fmt.Fprintf(tw, "%x", code[i:i+size])
263 } else {299 } else {
264- // Print instruction as 32-bit words.
265 for j := uint64(0); j < size; j += 4 {300 for j := uint64(0); j < size; j += 4 {
266 if j > 0 {301 if j > 0 {
267 fmt.Fprintf(tw, " ")302 fmt.Fprintf(tw, " ")
@@ -269,7 +304,49 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, pr
269 fmt.Fprintf(tw, "%08x", d.byteOrder.Uint32(code[i+j:]))304 fmt.Fprintf(tw, "%08x", d.byteOrder.Uint32(code[i+j:]))
270 }305 }
271 }306 }
272- fmt.Fprintf(tw, "\t%s\t\n", text)307+ if currentFuncInfo.Valid() && (inlinePCSP || inlineUnsafePoint || inlineStackMap || inlineInlTree || inlineArgLive) {
308+ fmt.Fprintf(tw, "\t%s\t[", text)
309+ first := true
310+ if inlinePCSP {
311+ pcsp := currentFuncInfo.PCSPValue(pc)
312+ fmt.Fprintf(tw, "PCSP:%d", pcsp)
313+ first = false
314+ }
315+ if inlineUnsafePoint {
316+ if !first {
317+ fmt.Fprintf(tw, ",\t")
318+ }
319+ unsafepoint := currentFuncInfo.PCDataValue(int(abi.PCDATA_UnsafePoint), pc)
320+ fmt.Fprintf(tw, "PCDATA_UnsafePoint:%d", unsafepoint)
321+ first = false
322+ }
323+ if inlineStackMap {
324+ if !first {
325+ fmt.Fprintf(tw, ",\t")
326+ }
327+ stackmap := currentFuncInfo.PCDataValue(int(abi.PCDATA_StackMapIndex), pc)
328+ fmt.Fprintf(tw, "PCDATA_StackMapIndex:%d", stackmap)
329+ first = false
330+ }
331+ if inlineInlTree {
332+ if !first {
333+ fmt.Fprintf(tw, ",\t")
334+ }
335+ inltree := currentFuncInfo.PCDataValue(int(abi.PCDATA_InlTreeIndex), pc)
336+ fmt.Fprintf(tw, "PCDATA_InlTreeIndex:%d", inltree)
337+ first = false
338+ }
339+ if inlineArgLive {
340+ if !first {
341+ fmt.Fprintf(tw, ",\t")
342+ }
343+ arglive := currentFuncInfo.PCDataValue(int(abi.PCDATA_ArgLiveIndex), pc)
344+ fmt.Fprintf(tw, "PCDATA_ArgLiveIndex:%d", arglive)
345+ }
346+ fmt.Fprintf(tw, "]\t\n")
347+ } else {
348+ fmt.Fprintf(tw, "\t%s\t\n", text)
349+ }
273 })350 })
274 tw.Flush()351 tw.Flush()
275 }352 }
@@ -10,6 +10,8 @@ import (
10 "internal/abi"10 "internal/abi"
11)11)
12 12 
13+var EnableMappingSymbols bool
14+ 
13// CUFileIndex is used to index the filenames that are stored in the15// CUFileIndex is used to index the filenames that are stored in the
14// per-package/per-CU FileList.16// per-package/per-CU FileList.
15type CUFileIndex uint3217type CUFileIndex uint32
@@ -24,6 +26,7 @@ type FuncInfo struct {
24 StartLine int3226 StartLine int32
25 File []CUFileIndex27 File []CUFileIndex
26 InlTree []InlTreeNode28 InlTree []InlTreeNode
29+ PoolsInfo []PoolInfo
27}30}
28 31 
29func (a *FuncInfo) Write(w *bytes.Buffer) {32func (a *FuncInfo) Write(w *bytes.Buffer) {
@@ -52,6 +55,12 @@ func (a *FuncInfo) Write(w *bytes.Buffer) {
52 for i := range a.InlTree {55 for i := range a.InlTree {
53 a.InlTree[i].Write(w)56 a.InlTree[i].Write(w)
54 }57 }
58+ if EnableMappingSymbols {
59+ writeUint32(uint32(len(a.PoolsInfo)))
60+ for i := range a.PoolsInfo {
61+ a.PoolsInfo[i].Write(w)
62+ }
63+ }
55}64}
56 65 
57// FuncInfoLengths is a cache containing a roadmap of offsets and66// FuncInfoLengths is a cache containing a roadmap of offsets and
@@ -64,6 +73,8 @@ type FuncInfoLengths struct {
64 FileOff uint3273 FileOff uint32
65 NumInlTree uint3274 NumInlTree uint32
66 InlTreeOff uint3275 InlTreeOff uint32
76+ NumPoolInfo uint32
77+ PoolInfoOff uint32
67 Initialized bool78 Initialized bool
68}79}
69 80 
@@ -80,11 +91,59 @@ func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths {
80 result.NumInlTree = binary.LittleEndian.Uint32(b[numinltreeOff:])91 result.NumInlTree = binary.LittleEndian.Uint32(b[numinltreeOff:])
81 result.InlTreeOff = numinltreeOff + 492 result.InlTreeOff = numinltreeOff + 4
82 93 
94+ if EnableMappingSymbols {
95+ numpoolinfoOff := result.InlTreeOff + 24*result.NumInlTree
96+ result.NumPoolInfo = binary.LittleEndian.Uint32(b[numpoolinfoOff:])
97+ result.PoolInfoOff = numpoolinfoOff + 4
98+ }
99+ 
83 result.Initialized = true100 result.Initialized = true
84 101 
85 return result102 return result
86}103}
87 104 
105+func (*FuncInfo) ReadPoolInfo(b []byte, PoolInfoOff uint32, k uint32) PoolInfo {
106+ if !EnableMappingSymbols {
107+ panic("disabled mapping symbols, but (*FuncInfo).ReadPoolInfo is called, is there a bug?")
108+ }
109+ const PoolInfoSize = 4 * 2
110+ var result PoolInfo
111+ result.Read(b[PoolInfoOff+k*PoolInfoSize:])
112+ return result
113+}
114+ 
115+type PoolInfo struct {
116+ PoolOff uint32
117+ CodeOff uint32
118+}
119+ 
120+func (pinfo *PoolInfo) Write(w *bytes.Buffer) {
121+ if !EnableMappingSymbols {
122+ panic("disabled mapping symbols, but (*PoolInfo).Write is called, is there a bug?")
123+ }
124+ var b [4]byte
125+ writeUint32 := func(x uint32) {
126+ binary.LittleEndian.PutUint32(b[:], x)
127+ w.Write(b[:])
128+ }
129+ writeUint32(pinfo.PoolOff)
130+ writeUint32(pinfo.CodeOff)
131+}
132+ 
133+func (pinfo *PoolInfo) Read(b []byte) []byte {
134+ if !EnableMappingSymbols {
135+ panic("disabled mapping symbols, but (*PoolInfo).Read is called, is there a bug?")
136+ }
137+ readUint32 := func() uint32 {
138+ x := binary.LittleEndian.Uint32(b)
139+ b = b[4:]
140+ return x
141+ }
142+ pinfo.PoolOff = readUint32()
143+ pinfo.CodeOff = readUint32()
144+ return b
145+}
146+ 
88func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }147func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }
89 148 
90func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) }149func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) }
@@ -31,6 +31,7 @@
31package arm6431package arm64
32 32 
33import (33import (
34+ "cmd/internal/goobj"
34 "cmd/internal/obj"35 "cmd/internal/obj"
35 "cmd/internal/objabi"36 "cmd/internal/objabi"
36 "encoding/binary"37 "encoding/binary"
@@ -1447,11 +1448,18 @@ func (c *ctxt7) flushpool(p *obj.Prog) {
1447 // Needs to insert a branch before flushing the pool.1448 // Needs to insert a branch before flushing the pool.
1448 // We don't need the jump if following an unconditional branch.1449 // We don't need the jump if following an unconditional branch.
1449 // TODO: other unconditional operations.1450 // TODO: other unconditional operations.
1450- if !(p.As == AB || p.As == obj.ARET || p.As == AERET) {1451+ // Mark with $d mapping symbol if it is located past end of last instruction, but before next function
1452+ if !(p.As == AB || p.As == obj.ARET || p.As == AERET) || goobj.EnableMappingSymbols {
1451 if c.ctxt.Debugvlog {1453 if c.ctxt.Debugvlog {
1452 fmt.Printf("note: flush literal pool at %#x: len=%d ref=%x\n", uint64(p.Pc+4), c.pool.size, c.pool.start)1454 fmt.Printf("note: flush literal pool at %#x: len=%d ref=%x\n", uint64(p.Pc+4), c.pool.size, c.pool.start)
1453 }1455 }
1454- q := c.newprog()1456+ var q *obj.Prog
1457+ if !goobj.EnableMappingSymbols {
1458+ q = c.newprog()
1459+ } else {
1460+ q = new(obj.Prog)
1461+ q.Ctxt = c.ctxt
1462+ }
1455 if p.Link == nil {1463 if p.Link == nil {
1456 // If p is the last instruction of the function, insert an UNDEF instruction in case the1464 // If p is the last instruction of the function, insert an UNDEF instruction in case the
1457 // execution fall through to the pool.1465 // execution fall through to the pool.
@@ -1465,6 +1473,9 @@ func (c *ctxt7) flushpool(p *obj.Prog) {
1465 q.Link = c.blitrl1473 q.Link = c.blitrl
1466 q.Pos = p.Pos1474 q.Pos = p.Pos
1467 c.blitrl = q1475 c.blitrl = q
1476+ if goobj.EnableMappingSymbols {
1477+ c.cursym.Func().Pool = append(c.cursym.Func().Pool, q)
1478+ }
1468 }1479 }
1469 1480 
1470 // The line number for constant pool entries doesn't really matter.1481 // The line number for constant pool entries doesn't really matter.
@@ -8520,8 +8531,8 @@ func (c *ctxt7) asmout(p *obj.Prog, out []uint32) (count int) {
8520 o1 |= ((Q & 1) << 30) | (imm & 0x7f << 16) | (uint32(rf&31) << 5) | uint32(rt&31)8531 o1 |= ((Q & 1) << 30) | (imm & 0x7f << 16) | (uint32(rf&31) << 5) | uint32(rt&31)
8521 8532 
8522 case 109: /* rprfm (Rn), Rm, <rprfop | $imm6> */8533 case 109: /* rprfm (Rn), Rm, <rprfop | $imm6> */
8523- srcReg := p.From.Reg // Rn - register with base address8534+ srcReg := p.From.Reg // Rn - register with base address
8524- rangeReg := p.Reg // Rm - register with prefetch metadata8535+ rangeReg := p.Reg // Rm - register with prefetch metadata
8525 var operation uint328536 var operation uint32
8526 var ok bool8537 var ok bool
8527 8538 
@@ -8552,7 +8563,7 @@ func (c *ctxt7) asmout(p *obj.Prog, out []uint32) (count int) {
8552 rt := operation & 78563 rt := operation & 7
8553 8564 
8554 o1 = c.opirr(p, p.As)8565 o1 = c.opirr(p, p.As)
8555- o1 |= (uint32(rangeReg&31)<<16) | (uint32(srcReg&31)<<5) | option2 | option0 | s | rt8566+ o1 |= (uint32(rangeReg&31) << 16) | (uint32(srcReg&31) << 5) | option2 | option0 | s | rt
8556 }8567 }
8557 out[0] = o18568 out[0] = o1
8558 out[1] = o28569 out[1] = o2
@@ -511,6 +511,8 @@ type FuncInfo struct {
511 WasmExport *WasmExport511 WasmExport *WasmExport
512 512 
513 sehUnwindInfoSym *LSym513 sehUnwindInfoSym *LSym
514+ 
515+ Pool []*Prog
514}516}
515 517 
516// JumpTable represents a table used for implementing multi-way518// JumpTable represents a table used for implementing multi-way
@@ -773,6 +773,19 @@ func genFuncInfoSyms(ctxt *Link) {
773 FuncFlag: fn.FuncFlag,773 FuncFlag: fn.FuncFlag,
774 StartLine: fn.StartLine,774 StartLine: fn.StartLine,
775 }775 }
776+ 
777+ if goobj.EnableMappingSymbols {
778+ poolsInfo := make([]goobj.PoolInfo, len(fn.Pool))
779+ for i, p := range fn.Pool {
780+ poolsInfo[i].PoolOff = uint32(p.Pc)
781+ target := p.To.Target()
782+ if target != nil {
783+ poolsInfo[i].CodeOff = uint32(target.Pc)
784+ }
785+ }
786+ o.PoolsInfo = poolsInfo
787+ }
788+ 
776 pc := &fn.Pcln789 pc := &fn.Pcln
777 i := 0790 i := 0
778 o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles))791 o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles))
@@ -12,10 +12,12 @@ import (
12 "encoding/binary"12 "encoding/binary"
13 "fmt"13 "fmt"
14 "io"14 "io"
15+ "unsafe"
15)16)
16 17 
17type elfFile struct {18type elfFile struct {
18 elf *elf.File19 elf *elf.File
20+ r io.ReaderAt
19}21}
20 22 
21func openElf(r io.ReaderAt) (rawFile, error) {23func openElf(r io.ReaderAt) (rawFile, error) {
@@ -23,13 +25,17 @@ func openElf(r io.ReaderAt) (rawFile, error) {
23 if err != nil {25 if err != nil {
24 return nil, err26 return nil, err
25 }27 }
26- return &elfFile{f}, nil28+ return &elfFile{f, r}, nil
27}29}
28 30 
29func (f *elfFile) symbols() ([]Sym, error) {31func (f *elfFile) symbols() ([]Sym, error) {
30 elfSyms, err := f.elf.Symbols()32 elfSyms, err := f.elf.Symbols()
31 if err != nil {33 if err != nil {
32- return nil, err34+ elfDSyms, err2 := f.elf.DynamicSymbols()
35+ if err2 != nil {
36+ return nil, err2
37+ }
38+ elfSyms = elfDSyms
33 }39 }
34 40 
35 var syms []Sym41 var syms []Sym
@@ -64,6 +70,19 @@ func (f *elfFile) symbols() ([]Sym, error) {
64 return syms, nil70 return syms, nil
65}71}
66 72 
73+func (f *elfFile) symbolSize(name string) (uint64, error) {
74+ elfSyms, err := f.elf.Symbols()
75+ if err != nil {
76+ return 0, err
77+ }
78+ for _, s := range elfSyms {
79+ if s.Name == name {
80+ return s.Size, nil
81+ }
82+ }
83+ return 0, fmt.Errorf("symbol %q not found", name)
84+}
85+ 
67func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {86func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
68 if sect := f.elf.Section(".text"); sect != nil {87 if sect := f.elf.Section(".text"); sect != nil {
69 textStart = sect.Addr88 textStart = sect.Addr
@@ -100,6 +119,33 @@ func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
100 return textStart, symtab, pclntab, nil119 return textStart, symtab, pclntab, nil
101}120}
102 121 
122+func (f *elfFile) firstmoduledata() ([]byte, error) {
123+ size := uint64(unsafe.Sizeof(*(*moduledata)(nil)))
124+ buf := f.symbolDataSz("runtime.firstmoduledata", size)
125+ if buf == nil {
126+ return nil, fmt.Errorf("reading runtime.firstmoduledata symbol")
127+ }
128+ return buf, nil
129+}
130+ 
131+func (f *elfFile) buildVersion() (string, error) {
132+ buf := f.symbolDataSz("runtime.buildVersion.str", 6)
133+ if buf == nil {
134+ return "", fmt.Errorf("reading runtime.buildVersion symbol")
135+ }
136+ return fmt.Sprintf("%s", buf), nil
137+}
138+ 
139+func (f *elfFile) pcHeader(addr uint64) ([]byte, error) {
140+ var ph pcHeader
141+ size := uint64(unsafe.Sizeof(ph))
142+ return f.tableBufAt(addr, size)
143+}
144+ 
145+func (f *elfFile) tableBufAt(addr uint64, size uint64) ([]byte, error) {
146+ return f.segmentDataAt(addr, size)
147+}
148+ 
103func (f *elfFile) text() (textStart uint64, text []byte, err error) {149func (f *elfFile) text() (textStart uint64, text []byte, err error) {
104 sect := f.elf.Section(".text")150 sect := f.elf.Section(".text")
105 if sect == nil {151 if sect == nil {
@@ -158,7 +204,11 @@ func (f *elfFile) dwarf() (*dwarf.Data, error) {
158func (f *elfFile) symbolData(start, end string) []byte {204func (f *elfFile) symbolData(start, end string) []byte {
159 elfSyms, err := f.elf.Symbols()205 elfSyms, err := f.elf.Symbols()
160 if err != nil {206 if err != nil {
161- return nil207+ elfDSyms, err2 := f.elf.DynamicSymbols()
208+ if err2 != nil {
209+ return nil
210+ }
211+ elfSyms = elfDSyms
162 }212 }
163 var addr, eaddr uint64213 var addr, eaddr uint64
164 for _, s := range elfSyms {214 for _, s := range elfSyms {
@@ -175,14 +225,91 @@ func (f *elfFile) symbolData(start, end string) []byte {
175 return nil225 return nil
176 }226 }
177 size := eaddr - addr227 size := eaddr - addr
178- data := make([]byte, size)228+ return f.symbolDataAt(addr, size)
179- for _, prog := range f.elf.Progs {229+}
180- if prog.Vaddr <= addr && addr+size-1 <= prog.Vaddr+prog.Filesz-1 {230+ 
181- if _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)); err != nil {231+func (f *elfFile) symbolDataSz(start string, size uint64) []byte {
182- return nil232+ elfSyms, err := f.elf.Symbols()
183- }233+ if err != nil {
184- return data234+ elfDSyms, err2 := f.elf.DynamicSymbols()
235+ if err2 != nil {
236+ return nil
237+ }
238+ elfSyms = elfDSyms
239+ }
240+ var addr uint64
241+ for _, s := range elfSyms {
242+ if s.Name == start {
243+ addr = s.Value
244+ break
245+ }
246+ }
247+ if addr == 0 {
248+ return nil
249+ }
250+ return f.symbolDataAt(addr, size)
251+}
252+ 
253+func (f *elfFile) symbolDataAt(addr uint64, size uint64) []byte {
254+ if size == 0 {
255+ return make([]byte, 0)
256+ }
257+ buf, err := f.segmentDataAt(addr, size)
258+ if err != nil {
259+ return nil
260+ }
261+ return buf
262+}
263+ 
264+func (f *elfFile) findSectionForAddr(addr uint64) *elf.Section {
265+ for _, sect := range f.elf.Sections {
266+ if sect.Flags&elf.SHF_ALLOC == 0 {
267+ continue
268+ }
269+ if sect.Type == elf.SHT_NOBITS {
270+ continue
271+ }
272+ if sect.Addr <= addr && addr < sect.Addr+sect.Size {
273+ return sect
185 }274 }
186 }275 }
187 return nil276 return nil
188}277}
278+ 
279+func (f *elfFile) addrToFileOffset(addr uint64) (uint64, *elf.Prog, bool) {
280+ for _, prog := range f.elf.Progs {
281+ if prog.Type != elf.PT_LOAD {
282+ continue
283+ }
284+ vmaEnd := prog.Vaddr + prog.Filesz
285+ if prog.Vaddr <= addr && addr < vmaEnd {
286+ return prog.Off + (addr - prog.Vaddr), prog, true
287+ }
288+ }
289+ return 0, nil, false
290+}
291+ 
292+func (f *elfFile) segmentDataAt(addr uint64, size uint64) ([]byte, error) {
293+ if size == 0 {
294+ return []byte{}, nil
295+ }
296+ 
297+ fileOff, prog, found := f.addrToFileOffset(addr)
298+ if !found {
299+ return nil, fmt.Errorf("address 0x%x not in any PT_LOAD segment", addr)
300+ }
301+ 
302+ endOff := fileOff + size
303+ segEnd := prog.Off + prog.Filesz
304+ 
305+ if endOff > segEnd {
306+ return nil, fmt.Errorf("address 0x%x (size %d) extends past PT_LOAD segment boundary (segment end 0x%x)", addr, size, segEnd)
307+ }
308+ 
309+ data := make([]byte, size)
310+ _, err := f.r.ReadAt(data, int64(fileOff))
311+ if err != nil {
312+ return nil, fmt.Errorf("reading at file offset 0x%x: %v", fileOff, err)
313+ }
314+ return data, nil
315+}
@@ -0,0 +1,282 @@
1+// Copyright 2026 The Go Authors. All rights reserved.
2+// Use of this source code is governed by a BSD-style
3+// license that can be found in the LICENSE file.
4+ 
5+//go:build ignore
6+ 
7+// generate_types.go generates type definitions from runtime source files.
8+//
9+// This tool parses struct definitions from Go runtime and internal/abi packages
10+// and generates corresponding type definitions in moduledata_types.go. This
11+// avoids duplicating complex type definitions and ensures consistency between
12+// objfile's understanding of runtime data structures and the actual runtime.
13+//
14+// Usage:
15+// go run generate_types.go
16+//
17+// The generated file should NOT be edited manually - rerun this tool instead.
18+//
19+// Types are sourced from:
20+// - src/runtime/*.go (moduledata, pcHeader, etc.)
21+// - src/internal/abi/*.go (Type system types)
22+//
23+// The generator handles:
24+// - Embedded struct types (no field name)
25+// - sys.NotInHeap marker (output as comment)
26+// - struct{} anonymous fields
27+// - Field comments from original sources
28+ 
29+package main
30+ 
31+import (
32+ "bytes"
33+ "fmt"
34+ "go/ast"
35+ "go/parser"
36+ "go/token"
37+ "os"
38+ "path/filepath"
39+ "strings"
40+)
41+ 
42+const (
43+ goroot = "../../.."
44+ outputFile = "moduledata_types.go"
45+)
46+ 
47+var targetTypes = []struct {
48+ name string
49+ src string
50+}{
51+ {"pcHeader", "runtime/symtab.go"},
52+ {"modulehash", "runtime/symtab.go"},
53+ {"functab", "runtime/symtab.go"},
54+ {"textsect", "runtime/symtab.go"},
55+ {"findfuncbucket", "runtime/symtab.go"},
56+ {"stackmap", "runtime/symtab.go"},
57+ {"Func", "runtime/symtab.go"},
58+ {"moduledata", "runtime/symtab.go"},
59+ {"inlinedCall", "runtime/symtabinl.go"},
60+ {"initTask", "runtime/proc.go"},
61+ {"ptabEntry", "runtime/plugin.go"},
62+ {"bitvector", "runtime/stack.go"},
63+ {"stringStruct", "runtime/string.go"},
64+ {"stackObjectRecord", "runtime/stack.go"},
65+ {"_func", "runtime/runtime2.go"},
66+ {"itab", "runtime/runtime2.go"},
67+ {"UncommonType", "internal/abi/type.go"},
68+ {"Imethod", "internal/abi/type.go"},
69+ {"Method", "internal/abi/type.go"},
70+ {"InterfaceType", "internal/abi/type.go"},
71+ {"StructType", "internal/abi/type.go"},
72+ {"StructField", "internal/abi/type.go"},
73+ {"ArrayType", "internal/abi/type.go"},
74+ {"SliceType", "internal/abi/type.go"},
75+ {"ChanType", "internal/abi/type.go"},
76+ {"PtrType", "internal/abi/type.go"},
77+ {"FuncType", "internal/abi/type.go"},
78+ {"ITab", "internal/abi/iface.go"},
79+ {"EmptyInterface", "internal/abi/iface.go"},
80+}
81+ 
82+func main() {
83+ var genDecl bytes.Buffer
84+ 
85+ for _, t := range targetTypes {
86+ typeName := t.name
87+ srcFile := t.src
88+ inputFile := filepath.Join(goroot, srcFile)
89+ fset := token.NewFileSet()
90+ node, err := parser.ParseFile(fset, inputFile, nil, parser.ParseComments)
91+ if err != nil {
92+ fmt.Fprintf(os.Stderr, "failed to parse %s: %v\n", inputFile, err)
93+ os.Exit(1)
94+ }
95+ 
96+ for _, decl := range node.Decls {
97+ gd, ok := decl.(*ast.GenDecl)
98+ if !ok || gd.Tok != token.TYPE {
99+ continue
100+ }
101+ 
102+ for _, spec := range gd.Specs {
103+ ts, ok := spec.(*ast.TypeSpec)
104+ if !ok || ts.Name.Name != typeName {
105+ continue
106+ }
107+ 
108+ st, ok := ts.Type.(*ast.StructType)
109+ if !ok {
110+ continue
111+ }
112+ 
113+ fmt.Fprintf(&genDecl, "// Copy of structure from %s\n", srcFile)
114+ fmt.Fprintf(&genDecl, "type %s struct {\n", typeName)
115+ for _, field := range st.Fields.List {
116+ fieldType := typeString(field.Type)
117+ 
118+ if fieldType == "sys.NotInHeap" {
119+ fmt.Fprintf(&genDecl, "\t// sys.NotInHeap // Only in static data\n")
120+ continue
121+ }
122+ 
123+ if field.Comment != nil && len(field.Names) == 0 {
124+ fmt.Fprintf(&genDecl, "\t// %s\n", strings.TrimSpace(field.Comment.Text()))
125+ }
126+ 
127+ names := make([]string, len(field.Names))
128+ for i, n := range field.Names {
129+ names[i] = n.Name
130+ }
131+ 
132+ if len(names) > 1 {
133+ fmt.Fprintf(&genDecl, "\t%s %s\n", strings.Join(names, ", "), fieldType)
134+ } else if len(names) == 1 {
135+ fmt.Fprintf(&genDecl, "\t%s %s", names[0], fieldType)
136+ if field.Comment != nil {
137+ fmt.Fprintf(&genDecl, " // %s", strings.TrimSpace(field.Comment.Text()))
138+ }
139+ fmt.Fprintln(&genDecl)
140+ } else {
141+ fmt.Fprintf(&genDecl, "\t%s\n", fieldType)
142+ }
143+ }
144+ fmt.Fprintln(&genDecl, "}")
145+ fmt.Fprintln(&genDecl)
146+ }
147+ }
148+ }
149+ 
150+ var buf bytes.Buffer
151+ fmt.Fprintln(&buf, "// Code generated by generate_types.go; DO NOT EDIT.")
152+ fmt.Fprintln(&buf, "//go:generate go run generate_types.go")
153+ fmt.Fprintln(&buf)
154+ fmt.Fprintln(&buf, "package objfile")
155+ fmt.Fprintln(&buf)
156+ fmt.Fprintln(&buf, "import (")
157+ fmt.Fprintln(&buf, "\t\"internal/abi\"")
158+ fmt.Fprintln(&buf, "\t\"unsafe\"")
159+ fmt.Fprintln(&buf, ")")
160+ fmt.Fprintln(&buf)
161+ fmt.Fprintln(&buf, "// Type aliases for types used in generated struct definitions")
162+ fmt.Fprintln(&buf, "type Type = abi.Type")
163+ fmt.Fprintln(&buf, "type _type = abi.Type") // backward compatibility alias
164+ fmt.Fprintln(&buf, "type Name = abi.Name")
165+ fmt.Fprintln(&buf, "type Kind = abi.Kind")
166+ fmt.Fprintln(&buf, "type TFlag = abi.TFlag")
167+ fmt.Fprintln(&buf, "type ChanDir = abi.ChanDir")
168+ fmt.Fprintln(&buf, "type NameOff = abi.NameOff")
169+ fmt.Fprintln(&buf, "type TypeOff = abi.TypeOff")
170+ fmt.Fprintln(&buf, "type TextOff = abi.TextOff")
171+ fmt.Fprintln(&buf, "type typeOff = abi.TypeOff") // lowercase alias for ptabEntry
172+ fmt.Fprintln(&buf, "type nameOff = abi.NameOff") // lowercase alias for ptabEntry
173+ fmt.Fprintln(&buf, "type name = abi.Name") // lowercase alias for name() method return
174+ fmt.Fprintln(&buf, "type itab struct {")
175+ fmt.Fprintln(&buf, "\tInter *InterfaceType")
176+ fmt.Fprintln(&buf, "\tType *Type")
177+ fmt.Fprintln(&buf, "\tHash uint32")
178+ fmt.Fprintln(&buf, "\t_ uint32 // padding")
179+ fmt.Fprintln(&buf, "\tFun [1]uintptr")
180+ fmt.Fprintln(&buf, "}")
181+ fmt.Fprintln(&buf)
182+ fmt.Fprintln(&buf, "// Types copied from runtime source files")
183+ fmt.Fprintln(&buf)
184+ buf.WriteString(genDecl.String())
185+ 
186+ if err := os.WriteFile(outputFile, buf.Bytes(), 0644); err != nil {
187+ fmt.Fprintf(os.Stderr, "failed to write %s: %v\n", outputFile, err)
188+ os.Exit(1)
189+ }
190+ 
191+ fmt.Printf("Generated %s\n", outputFile)
192+}
193+ 
194+func typeString(expr ast.Expr) string {
195+ switch t := expr.(type) {
196+ case *ast.Ident:
197+ return t.Name
198+ case *ast.StructType:
199+ if t.Fields == nil || len(t.Fields.List) == 0 {
200+ return "struct{}"
201+ }
202+ var buf bytes.Buffer
203+ buf.WriteString("struct{")
204+ for i, f := range t.Fields.List {
205+ if i > 0 {
206+ buf.WriteString("; ")
207+ }
208+ for j, n := range f.Names {
209+ if j > 0 {
210+ buf.WriteString(", ")
211+ }
212+ buf.WriteString(n.Name)
213+ }
214+ buf.WriteString(" ")
215+ buf.WriteString(typeString(f.Type))
216+ }
217+ buf.WriteString("}")
218+ return buf.String()
219+ case *ast.SelectorExpr:
220+ return typeString(t.X) + "." + t.Sel.Name
221+ case *ast.ArrayType:
222+ if t.Len == nil {
223+ return "[]" + typeString(t.Elt)
224+ }
225+ return "[" + typeString(t.Len) + "]" + typeString(t.Elt)
226+ case *ast.StarExpr:
227+ return "*" + typeString(t.X)
228+ case *ast.MapType:
229+ return "map[" + typeString(t.Key) + "]" + typeString(t.Value)
230+ case *ast.BasicLit:
231+ return t.Value
232+ case *ast.FuncType:
233+ var buf bytes.Buffer
234+ buf.WriteString("func(")
235+ if t.Params != nil {
236+ for i, p := range t.Params.List {
237+ if i > 0 {
238+ buf.WriteString(", ")
239+ }
240+ if len(p.Names) > 0 {
241+ for j, n := range p.Names {
242+ if j > 0 {
243+ buf.WriteString(", ")
244+ }
245+ buf.WriteString(n.Name)
246+ }
247+ buf.WriteString(" ")
248+ }
249+ buf.WriteString(typeString(p.Type))
250+ }
251+ }
252+ buf.WriteString(")")
253+ if t.Results != nil && len(t.Results.List) > 0 {
254+ buf.WriteString(" ")
255+ if len(t.Results.List) == 1 && len(t.Results.List[0].Names) == 0 {
256+ buf.WriteString(typeString(t.Results.List[0].Type))
257+ } else {
258+ buf.WriteString("(")
259+ for i, r := range t.Results.List {
260+ if i > 0 {
261+ buf.WriteString(", ")
262+ }
263+ if len(r.Names) > 0 {
264+ for j, n := range r.Names {
265+ if j > 0 {
266+ buf.WriteString(", ")
267+ }
268+ buf.WriteString(n.Name)
269+ }
270+ buf.WriteString(" ")
271+ }
272+ buf.WriteString(typeString(r.Type))
273+ }
274+ buf.WriteString(")")
275+ }
276+ }
277+ return buf.String()
278+ default:
279+ return "interface{}"
280+ }
281+}
282+ 
@@ -26,6 +26,10 @@ type goobjFile struct {
26 arch *sys.Arch26 arch *sys.Arch
27}27}
28 28 
29+func (f *goobjFile) symbolSize(name string) (uint64, error) {
30+ return 0, fmt.Errorf("symbol size not available in go object files")
31+}
32+ 
29func openGoFile(f *os.File) (*File, error) {33func openGoFile(f *os.File) (*File, error) {
30 a, err := archive.Parse(f, false)34 a, err := archive.Parse(f, false)
31 if err != nil {35 if err != nil {
@@ -227,6 +231,22 @@ func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error)
227 return 0, nil, nil, fmt.Errorf("pcln not available in go object file")231 return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
228}232}
229 233 
234+func (f *goobjFile) firstmoduledata() ([]byte, error) {
235+ return nil, fmt.Errorf("not implemented")
236+}
237+ 
238+func (f *goobjFile) buildVersion() (string, error) {
239+ return "", fmt.Errorf("not implemented")
240+}
241+ 
242+func (f *goobjFile) pcHeader(addr uint64) ([]byte, error) {
243+ return nil, fmt.Errorf("not implemented")
244+}
245+ 
246+func (f *goobjFile) tableBufAt(addr uint64, size uint64) ([]byte, error) {
247+ return nil, fmt.Errorf("not implemented")
248+}
249+ 
230// Find returns the file name, line, and function data for the given pc.250// Find returns the file name, line, and function data for the given pc.
231// Returns "",0,nil if unknown.251// Returns "",0,nil if unknown.
232// This function implements the Liner interface in preference to pcln() above.252// This function implements the Liner interface in preference to pcln() above.
@@ -79,6 +79,24 @@ func (f *machoFile) symbols() ([]Sym, error) {
79 return syms, nil79 return syms, nil
80}80}
81 81 
82+func (f *machoFile) symbolSize(name string) (uint64, error) {
83+ if f.macho.Symtab == nil {
84+ return 0, fmt.Errorf("symbol table not available")
85+ }
86+ for _, s := range f.macho.Symtab.Syms {
87+ if s.Name == name {
88+ i := sort.Search(len(f.macho.Symtab.Syms), func(x int) bool {
89+ return f.macho.Symtab.Syms[x].Value > s.Value
90+ })
91+ if i < len(f.macho.Symtab.Syms) {
92+ return f.macho.Symtab.Syms[i].Value - s.Value, nil
93+ }
94+ return 0, nil
95+ }
96+ }
97+ return 0, fmt.Errorf("symbol %q not found", name)
98+}
99+ 
82func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {100func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
83 if sect := f.macho.Section("__text"); sect != nil {101 if sect := f.macho.Section("__text"); sect != nil {
84 textStart = sect.Addr102 textStart = sect.Addr
@@ -96,6 +114,22 @@ func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error)
96 return textStart, symtab, pclntab, nil114 return textStart, symtab, pclntab, nil
97}115}
98 116 
117+func (f *machoFile) firstmoduledata() ([]byte, error) {
118+ return nil, fmt.Errorf("not implemented")
119+}
120+ 
121+func (f *machoFile) buildVersion() (string, error) {
122+ return "", fmt.Errorf("not implemented")
123+}
124+ 
125+func (f *machoFile) pcHeader(addr uint64) ([]byte, error) {
126+ return nil, fmt.Errorf("not implemented")
127+}
128+ 
129+func (f *machoFile) tableBufAt(addr uint64, size uint64) ([]byte, error) {
130+ return nil, fmt.Errorf("not implemented")
131+}
132+ 
99func (f *machoFile) text() (textStart uint64, text []byte, err error) {133func (f *machoFile) text() (textStart uint64, text []byte, err error) {
100 sect := f.macho.Section("__text")134 sect := f.macho.Section("__text")
101 if sect == nil {135 if sect == nil {
@@ -0,0 +1,276 @@
1+// Code generated by generate_types.go; DO NOT EDIT.
2+//go:generate go run generate_types.go
3+ 
4+package objfile
5+ 
6+import (
7+ "internal/abi"
8+ "unsafe"
9+)
10+ 
11+// Type aliases for types used in generated struct definitions
12+type Type = abi.Type
13+type _type = abi.Type
14+type Name = abi.Name
15+type Kind = abi.Kind
16+type TFlag = abi.TFlag
17+type ChanDir = abi.ChanDir
18+type NameOff = abi.NameOff
19+type TypeOff = abi.TypeOff
20+type TextOff = abi.TextOff
21+type typeOff = abi.TypeOff
22+type nameOff = abi.NameOff
23+type name = abi.Name
24+type itab struct {
25+ Inter *InterfaceType
26+ Type *Type
27+ Hash uint32
28+ _ uint32 // padding
29+ Fun [1]uintptr
30+}
31+ 
32+// Types copied from runtime source files
33+ 
34+// Copy of structure from runtime/symtab.go
35+type pcHeader struct {
36+ magic uint32 // 0xFFFFFFF1
37+ pad1, pad2 uint8
38+ minLC uint8 // min instruction size
39+ ptrSize uint8 // size of a ptr in bytes
40+ nfunc int // number of functions in the module
41+ nfiles uint // number of entries in the file tab
42+ textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text
43+ funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
44+ cuOffset uintptr // offset to the cutab variable from pcHeader
45+ filetabOffset uintptr // offset to the filetab variable from pcHeader
46+ pctabOffset uintptr // offset to the pctab variable from pcHeader
47+ pclnOffset uintptr // offset to the pclntab variable from pcHeader
48+}
49+ 
50+// Copy of structure from runtime/symtab.go
51+type modulehash struct {
52+ modulename string
53+ linktimehash string
54+ runtimehash *string
55+}
56+ 
57+// Copy of structure from runtime/symtab.go
58+type functab struct {
59+ entryoff uint32 // relative to runtime.text
60+ funcoff uint32
61+}
62+ 
63+// Copy of structure from runtime/symtab.go
64+type textsect struct {
65+ vaddr uintptr // prelinked section vaddr
66+ end uintptr // vaddr + section length
67+ baseaddr uintptr // relocated section address
68+}
69+ 
70+// Copy of structure from runtime/symtab.go
71+type findfuncbucket struct {
72+ idx uint32
73+ subbuckets [16]byte
74+}
75+ 
76+// Copy of structure from runtime/symtab.go
77+type stackmap struct {
78+ n int32 // number of bitmaps
79+ nbit int32 // number of bits in each bitmap
80+ bytedata [1]byte // bitmaps, each starting on a byte boundary
81+}
82+ 
83+// Copy of structure from runtime/symtab.go
84+type Func struct {
85+ opaque struct{} // unexported field to disallow conversions
86+}
87+ 
88+// Copy of structure from runtime/symtab.go
89+type moduledata struct {
90+ // sys.NotInHeap // Only in static data
91+ pcHeader *pcHeader
92+ funcnametab []byte
93+ cutab []uint32
94+ filetab []byte
95+ pctab []byte
96+ pclntable []byte
97+ ftab []functab
98+ findfunctab uintptr
99+ minpc, maxpc uintptr
100+ text, etext uintptr
101+ noptrdata, enoptrdata uintptr
102+ data, edata uintptr
103+ bss, ebss uintptr
104+ noptrbss, enoptrbss uintptr
105+ covctrs, ecovctrs uintptr
106+ end, gcdata, gcbss uintptr
107+ types, etypes uintptr
108+ rodata uintptr
109+ gofunc uintptr // go.func.*
110+ textsectmap []textsect
111+ typelinks []int32 // offsets from types
112+ itablinks []*itab
113+ ptab []ptabEntry
114+ pluginpath string
115+ pkghashes []modulehash
116+ inittasks []*initTask
117+ modulename string
118+ modulehashes []modulehash
119+ hasmain uint8 // 1 if module contains the main function, 0 otherwise
120+ bad bool // module failed to load and should be ignored
121+ gcdatamask, gcbssmask bitvector
122+ typemap map[typeOff]*_type // offset to *_rtype in previous module
123+ next *moduledata
124+}
125+ 
126+// Copy of structure from runtime/symtabinl.go
127+type inlinedCall struct {
128+ funcID abi.FuncID // type of the called function
129+ _ [3]byte
130+ nameOff int32 // offset into pclntab for name of called function
131+ parentPc int32 // position of an instruction whose source position is the call site (offset from entry)
132+ startLine int32 // line number of start of function (func keyword/TEXT directive)
133+}
134+ 
135+// Copy of structure from runtime/proc.go
136+type initTask struct {
137+ state uint32 // 0 = uninitialized, 1 = in progress, 2 = done
138+ nfns uint32
139+}
140+ 
141+// Copy of structure from runtime/plugin.go
142+type ptabEntry struct {
143+ name nameOff
144+ typ typeOff
145+}
146+ 
147+// Copy of structure from runtime/stack.go
148+type bitvector struct {
149+ n int32 // # of bits
150+ bytedata *uint8
151+}
152+ 
153+// Copy of structure from runtime/string.go
154+type stringStruct struct {
155+ str unsafe.Pointer
156+ len int
157+}
158+ 
159+// Copy of structure from runtime/stack.go
160+type stackObjectRecord struct {
161+ off int32
162+ size int32
163+ ptrBytes int32
164+ gcdataoff uint32 // offset to gcdata from moduledata.rodata
165+}
166+ 
167+// Copy of structure from runtime/runtime2.go
168+type _func struct {
169+ // sys.NotInHeap // Only in static data
170+ entryOff uint32 // start pc, as offset from moduledata.text/pcHeader.textStart
171+ nameOff int32 // function name, as index into moduledata.funcnametab.
172+ args int32 // in/out args size
173+ deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
174+ pcsp uint32
175+ pcfile uint32
176+ pcln uint32
177+ npcdata uint32
178+ cuOffset uint32 // runtime.cutab offset of this function's CU
179+ startLine int32 // line number of start of function (func keyword/TEXT directive)
180+ funcID abi.FuncID // set for certain special runtime functions
181+ flag abi.FuncFlag
182+ _ [1]byte // pad
183+ nfuncdata uint8 // must be last, must end on a uint32-aligned boundary
184+}
185+ 
186+// Copy of structure from internal/abi/type.go
187+type UncommonType struct {
188+ PkgPath NameOff // import path; empty for built-in types like int, string
189+ Mcount uint16 // number of methods
190+ Xcount uint16 // number of exported methods
191+ Moff uint32 // offset from this uncommontype to [mcount]Method
192+ _ uint32 // unused
193+}
194+ 
195+// Copy of structure from internal/abi/type.go
196+type Imethod struct {
197+ Name NameOff // name of method
198+ Typ TypeOff // .(*FuncType) underneath
199+}
200+ 
201+// Copy of structure from internal/abi/type.go
202+type Method struct {
203+ Name NameOff // name of method
204+ Mtyp TypeOff // method type (without receiver)
205+ Ifn TextOff // fn used in interface call (one-word receiver)
206+ Tfn TextOff // fn used for normal method call
207+}
208+ 
209+// Copy of structure from internal/abi/type.go
210+type InterfaceType struct {
211+ Type
212+ PkgPath Name // import path
213+ Methods []Imethod // sorted by hash
214+}
215+ 
216+// Copy of structure from internal/abi/type.go
217+type StructType struct {
218+ Type
219+ PkgPath Name
220+ Fields []StructField
221+}
222+ 
223+// Copy of structure from internal/abi/type.go
224+type StructField struct {
225+ Name Name // name is always non-empty
226+ Typ *Type // type of field
227+ Offset uintptr // byte offset of field
228+}
229+ 
230+// Copy of structure from internal/abi/type.go
231+type ArrayType struct {
232+ Type
233+ Elem *Type // array element type
234+ Slice *Type // slice type
235+ Len uintptr
236+}
237+ 
238+// Copy of structure from internal/abi/type.go
239+type SliceType struct {
240+ Type
241+ Elem *Type // slice element type
242+}
243+ 
244+// Copy of structure from internal/abi/type.go
245+type ChanType struct {
246+ Type
247+ Elem *Type
248+ Dir ChanDir
249+}
250+ 
251+// Copy of structure from internal/abi/type.go
252+type PtrType struct {
253+ Type
254+ Elem *Type // pointer element (pointed at) type
255+}
256+ 
257+// Copy of structure from internal/abi/type.go
258+type FuncType struct {
259+ Type
260+ InCount uint16
261+ OutCount uint16 // top bit is set if last input parameter is ...
262+}
263+ 
264+// Copy of structure from internal/abi/iface.go
265+type ITab struct {
266+ Inter *InterfaceType
267+ Type *Type
268+ Hash uint32 // copy of Type.Hash. Used for type switches.
269+ Fun [1]uintptr // variable sized. fun[0]==0 means Type does not implement Inter.
270+}
271+ 
272+// Copy of structure from internal/abi/iface.go
273+type EmptyInterface struct {
274+ Type *Type
275+ Data unsafe.Pointer
276+}
@@ -18,7 +18,12 @@ import (
18 18 
19type rawFile interface {19type rawFile interface {
20 symbols() (syms []Sym, err error)20 symbols() (syms []Sym, err error)
21+ symbolSize(name string) (uint64, error)
21 pcln() (textStart uint64, symtab, pclntab []byte, err error)22 pcln() (textStart uint64, symtab, pclntab []byte, err error)
23+ firstmoduledata() ([]byte, error)
24+ buildVersion() (string, error)
25+ pcHeader(uint64) ([]byte, error)
26+ tableBufAt(addr uint64, size uint64) ([]byte, error)
22 text() (textStart uint64, text []byte, err error)27 text() (textStart uint64, text []byte, err error)
23 goarch() string28 goarch() string
24 loadAddress() (uint64, error)29 loadAddress() (uint64, error)
@@ -119,6 +124,10 @@ func (f *File) DWARF() (*dwarf.Data, error) {
119 return f.entries[0].DWARF()124 return f.entries[0].DWARF()
120}125}
121 126 
127+func (f *File) Moduledata(checkVersion bool) (*Moduledata, error) {
128+ return f.entries[0].Moduledata(checkVersion)
129+}
130+ 
122func (e *Entry) Name() string {131func (e *Entry) Name() string {
123 return e.name132 return e.name
124}133}
@@ -90,6 +90,34 @@ func (f *peFile) symbols() ([]Sym, error) {
90 return syms, nil90 return syms, nil
91}91}
92 92 
93+func (f *peFile) symbolSize(name string) (uint64, error) {
94+ imageBase, _ := f.imageBase()
95+ for _, s := range f.pe.Symbols {
96+ if s.Name == name {
97+ var symAddr uint64 = uint64(s.Value)
98+ if s.SectionNumber > 0 {
99+ symAddr += imageBase + uint64(f.pe.Sections[s.SectionNumber-1].VirtualAddress)
100+ }
101+ i := sort.Search(len(f.pe.Symbols), func(x int) bool {
102+ var otherAddr uint64 = uint64(f.pe.Symbols[x].Value)
103+ if f.pe.Symbols[x].SectionNumber > 0 {
104+ otherAddr += imageBase + uint64(f.pe.Sections[f.pe.Symbols[x].SectionNumber-1].VirtualAddress)
105+ }
106+ return otherAddr > symAddr
107+ })
108+ if i < len(f.pe.Symbols) {
109+ var otherAddr uint64 = uint64(f.pe.Symbols[i].Value)
110+ if f.pe.Symbols[i].SectionNumber > 0 {
111+ otherAddr += imageBase + uint64(f.pe.Sections[f.pe.Symbols[i].SectionNumber-1].VirtualAddress)
112+ }
113+ return otherAddr - symAddr, nil
114+ }
115+ return 0, nil
116+ }
117+ }
118+ return 0, fmt.Errorf("symbol %q not found", name)
119+}
120+ 
93func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {121func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
94 imageBase, err := f.imageBase()122 imageBase, err := f.imageBase()
95 if err != nil {123 if err != nil {
@@ -117,6 +145,22 @@ func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
117 return textStart, symtab, pclntab, nil145 return textStart, symtab, pclntab, nil
118}146}
119 147 
148+func (f *peFile) firstmoduledata() ([]byte, error) {
149+ return nil, fmt.Errorf("not implemented")
150+}
151+ 
152+func (f *peFile) buildVersion() (string, error) {
153+ return "", fmt.Errorf("not implemented")
154+}
155+ 
156+func (f *peFile) pcHeader(addr uint64) ([]byte, error) {
157+ return nil, fmt.Errorf("not implemented")
158+}
159+ 
160+func (f *peFile) tableBufAt(addr uint64, size uint64) ([]byte, error) {
161+ return nil, fmt.Errorf("not implemented")
162+}
163+ 
120func (f *peFile) text() (textStart uint64, text []byte, err error) {164func (f *peFile) text() (textStart uint64, text []byte, err error) {
121 imageBase, err := f.imageBase()165 imageBase, err := f.imageBase()
122 if err != nil {166 if err != nil {
@@ -29,6 +29,22 @@ type plan9File struct {
29 plan9 *plan9obj.File29 plan9 *plan9obj.File
30}30}
31 31 
32+func (f *plan9File) symbolSize(name string) (uint64, error) {
33+ plan9Syms, err := f.plan9.Symbols()
34+ if err != nil {
35+ return 0, err
36+ }
37+ for i, s := range plan9Syms {
38+ if s.Name == name {
39+ if i+1 < len(plan9Syms) {
40+ return plan9Syms[i+1].Value - s.Value, nil
41+ }
42+ return 0, nil
43+ }
44+ }
45+ return 0, fmt.Errorf("symbol %q not found", name)
46+}
47+ 
32func openPlan9(r io.ReaderAt) (rawFile, error) {48func openPlan9(r io.ReaderAt) (rawFile, error) {
33 f, err := plan9obj.NewFile(r)49 f, err := plan9obj.NewFile(r)
34 if err != nil {50 if err != nil {
@@ -91,6 +107,22 @@ func (f *plan9File) pcln() (textStart uint64, symtab, pclntab []byte, err error)
91 return textStart, symtab, pclntab, nil107 return textStart, symtab, pclntab, nil
92}108}
93 109 
110+func (f *plan9File) firstmoduledata() ([]byte, error) {
111+ return nil, fmt.Errorf("not implemented")
112+}
113+ 
114+func (f *plan9File) buildVersion() (string, error) {
115+ return "", fmt.Errorf("not implemented")
116+}
117+ 
118+func (f *plan9File) pcHeader(addr uint64) ([]byte, error) {
119+ return nil, fmt.Errorf("not implemented")
120+}
121+ 
122+func (f *plan9File) tableBufAt(addr uint64, size uint64) ([]byte, error) {
123+ return nil, fmt.Errorf("not implemented")
124+}
125+ 
94func (f *plan9File) text() (textStart uint64, text []byte, err error) {126func (f *plan9File) text() (textStart uint64, text []byte, err error) {
95 sect := f.plan9.Section("text")127 sect := f.plan9.Section("text")
96 if sect == nil {128 if sect == nil {
@@ -18,6 +18,18 @@ type xcoffFile struct {
18 xcoff *xcoff.File18 xcoff *xcoff.File
19}19}
20 20 
21+func (f *xcoffFile) symbolSize(name string) (uint64, error) {
22+ for i, s := range f.xcoff.Symbols {
23+ if s.Name == name {
24+ if i+1 < len(f.xcoff.Symbols) {
25+ return f.xcoff.Symbols[i+1].Value - s.Value, nil
26+ }
27+ return 0, nil
28+ }
29+ }
30+ return 0, fmt.Errorf("symbol %q not found", name)
31+}
32+ 
21func openXcoff(r io.ReaderAt) (rawFile, error) {33func openXcoff(r io.ReaderAt) (rawFile, error) {
22 f, err := xcoff.NewFile(r)34 f, err := xcoff.NewFile(r)
23 if err != nil {35 if err != nil {
@@ -98,6 +110,22 @@ func (f *xcoffFile) pcln() (textStart uint64, symtab, pclntab []byte, err error)
98 return textStart, symtab, pclntab, nil110 return textStart, symtab, pclntab, nil
99}111}
100 112 
113+func (f *xcoffFile) firstmoduledata() ([]byte, error) {
114+ return nil, fmt.Errorf("not implemented")
115+}
116+ 
117+func (f *xcoffFile) buildVersion() (string, error) {
118+ return "", fmt.Errorf("not implemented")
119+}
120+ 
121+func (f *xcoffFile) pcHeader(addr uint64) ([]byte, error) {
122+ return nil, fmt.Errorf("not implemented")
123+}
124+ 
125+func (f *xcoffFile) tableBufAt(addr uint64, size uint64) ([]byte, error) {
126+ return nil, fmt.Errorf("not implemented")
127+}
128+ 
101func (f *xcoffFile) text() (textStart uint64, text []byte, err error) {129func (f *xcoffFile) text() (textStart uint64, text []byte, err error) {
102 sect := f.xcoff.Section(".text")130 sect := f.xcoff.Section(".text")
103 if sect == nil {131 if sect == nil {
@@ -2,6 +2,8 @@
2// Use of this source code is governed by a BSD-style2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.3// license that can be found in the LICENSE file.
4 4 
5+//go:build !goboltci
6+ 
5// Package testdir_test runs tests in the GOROOT/test directory.7// Package testdir_test runs tests in the GOROOT/test directory.
6package testdir_test8package testdir_test
7 9 
@@ -14,10 +14,12 @@ import (
14 "fmt"14 "fmt"
15 "internal/platform"15 "internal/platform"
16 "internal/testenv"16 "internal/testenv"
17+ "io/ioutil"
17 "os"18 "os"
18 "os/exec"19 "os/exec"
19 "path/filepath"20 "path/filepath"
20 "runtime"21 "runtime"
22+ "sort"
21 "strings"23 "strings"
22 "sync"24 "sync"
23 "testing"25 "testing"
@@ -524,6 +526,153 @@ func TestPIESize(t *testing.T) {
524 }526 }
525}527}
526 528 
529+var goMappingSymbolsSrc = `
530+package main
531+import "unsafe"
532+type A struct {
533+ c [16384]uint8
534+}
535+//go:noinline
536+func foo(i int) A {
537+ var a [2]A
538+ b := uint16(0)
539+ *(*uint16)(unsafe.Pointer(&a[0].c[1])) = b
540+ b++
541+ *(*uint16)(unsafe.Pointer(&a[1].c[1])) = b
542+ return a[i]
543+}
544+func main() {
545+ _ = foo(0)
546+ return
547+}
548+`
549+ 
550+func TestMappingSymbols(t *testing.T) {
551+ if runtime.GOARCH != "arm64" {
552+ t.Skip("skipping arm64 only test")
553+ }
554+ 
555+ testenv.MustHaveGoBuild(t)
556+ t.Parallel()
557+ 
558+ buildmodes := []string{"exe", "pie"}
559+ enableMappingSymbols := []bool{true, false}
560+ 
561+ var wg sync.WaitGroup
562+ wg.Add(len(enableMappingSymbols) * len(buildmodes))
563+ for _, enable := range enableMappingSymbols {
564+ for _, buildmode := range buildmodes {
565+ go func(mode string, enable bool) {
566+ defer wg.Done()
567+ symbols := buildSymbols(t, mode, enable)
568+ checkMappingSymbols(t, symbols, enable)
569+ }(buildmode, enable)
570+ }
571+ }
572+ wg.Wait()
573+}
574+ 
575+// Builds a simple program, then returns a corresponding symbol table for that binary
576+func buildSymbols(t *testing.T, mode string, enableMappingSymbols bool) []elf.Symbol {
577+ goTool := testenv.GoToolPath(t)
578+ 
579+ dir := t.TempDir()
580+ 
581+ goFile := filepath.Join(dir, "main.go")
582+ if err := ioutil.WriteFile(goFile, []byte(goMappingSymbolsSrc), 0444); err != nil {
583+ t.Fatal(err)
584+ }
585+ if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
586+ t.Fatal(err)
587+ }
588+ 
589+ var cmd *exec.Cmd
590+ args := []string{"build", "-o", mode, "-buildmode=" + mode}
591+ if enableMappingSymbols {
592+ args = append(args, "-mappingsymbol")
593+ }
594+ 
595+ cmd = exec.Command(goTool, args...)
596+ 
597+ cmd.Dir = dir
598+ 
599+ if out, err := cmd.CombinedOutput(); err != nil {
600+ t.Logf("%s", out)
601+ t.Fatal(err)
602+ }
603+ 
604+ bin := filepath.Join(dir, mode)
605+ 
606+ elfexe, err := elf.Open(bin)
607+ if err != nil {
608+ t.Fatal(err)
609+ }
610+ 
611+ symbols, err := elfexe.Symbols()
612+ if err != nil {
613+ t.Fatal(err)
614+ }
615+ 
616+ return symbols
617+}
618+ 
619+// Checks that mapping symbols are inserted correctly inside a symbol table.
620+func checkMappingSymbols(t *testing.T, symbols []elf.Symbol, enableMappingSymbols bool) {
621+ // mappingSymbols variable keeps only "$x" and "$d" symbols sorted by their position.
622+ var mappingSymbols []elf.Symbol
623+ var fooSym *elf.Symbol = nil
624+ var fooDSymFound bool = false
625+ for _, symbol := range symbols {
626+ if symbol.Name == "$x" || symbol.Name == "$d" {
627+ if elf.ST_TYPE(symbol.Info) != elf.STT_NOTYPE || elf.ST_BIND(symbol.Info) != elf.STB_LOCAL {
628+ t.Fatalf("met \"%v\" symbol at %v position with incorrect info %v", symbol.Name, symbol.Value, symbol.Info)
629+ }
630+ mappingSymbols = append(mappingSymbols, symbol)
631+ }
632+ if symbol.Name == "main.foo" {
633+ if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC || elf.ST_BIND(symbol.Info) != elf.STB_GLOBAL {
634+ t.Fatalf("met \"%v\" symbol at %v position with incorrect info %v", symbol.Name, symbol.Value, symbol.Info)
635+ }
636+ fooSym = &symbol
637+ }
638+ }
639+ 
640+ if fooSym == nil {
641+ t.Fatal("Not found symbol for 'main.foo' function")
642+ }
643+ 
644+ if !enableMappingSymbols {
645+ if len(mappingSymbols) != 0 {
646+ t.Fatal("disable mapping symbols, but binary has mapping symbols")
647+ }
648+ return
649+ }
650+ 
651+ sort.Slice(mappingSymbols, func(i, j int) bool {
652+ return mappingSymbols[i].Value < mappingSymbols[j].Value
653+ })
654+ 
655+ if len(mappingSymbols) == 0 {
656+ t.Fatal("binary does not have mapping symbols")
657+ }
658+ 
659+ for i := 0; i < len(mappingSymbols)-1; i += 2 {
660+ if mappingSymbols[i].Name == "$d" {
661+ t.Fatalf("met unexpected \"$d\" symbol at %v position", mappingSymbols[i].Value)
662+ }
663+ if i+1 < len(mappingSymbols) && mappingSymbols[i+1].Name == "$x" {
664+ t.Fatalf("met unexpected \"$x\" symbol at %v position", mappingSymbols[i+1].Value)
665+ }
666+ if mappingSymbols[i+1].Value > fooSym.Value && mappingSymbols[i+1].Value < fooSym.Value+fooSym.Size {
667+ fooDSymFound = true
668+ }
669+ }
670+ 
671+ if enableMappingSymbols && !fooDSymFound {
672+ t.Fatal("Failed to find $d symbol inside 'main.foo' function")
673+ }
674+}
675+ 
527func TestIssue51939(t *testing.T) {676func TestIssue51939(t *testing.T) {
528 testenv.MustHaveGoBuild(t)677 testenv.MustHaveGoBuild(t)
529 t.Parallel()678 t.Parallel()
@@ -576,3 +725,131 @@ func TestFlagR(t *testing.T) {
576 t.Errorf("executable failed to run: %v\n%s", err, out)725 t.Errorf("executable failed to run: %v\n%s", err, out)
577 }726 }
578}727}
728+ 
729+func TestTextSplit(t *testing.T) {
730+ if runtime.GOARCH != "arm64" {
731+ t.Skip("skipping arm64 only test")
732+ }
733+ testenv.MustHaveGoBuild(t)
734+ t.Parallel()
735+ var linkers = []string{"internal", "external"}
736+ var enableMappingSymbols = []bool{true, false}
737+ var wg sync.WaitGroup
738+ wg.Add(len(enableMappingSymbols) * len(linkers))
739+ 
740+ for _, enableMappingSymbol := range enableMappingSymbols {
741+ for _, buildlinker := range linkers {
742+ go func(linkerMod string, enableMappingSymbol bool) {
743+ defer wg.Done()
744+ symb, objfile := buildSymbolsTextSplit(t, linkerMod, enableMappingSymbol)
745+ checkTextSplit(t, symb, (linkerMod == "external") && enableMappingSymbol)
746+ if linkerMod == "external" && enableMappingSymbol {
747+ checkTextSplitMappingSymbols(t, objfile)
748+ }
749+ }(buildlinker, enableMappingSymbol)
750+ }
751+ }
752+ 
753+ wg.Wait()
754+}
755+ 
756+func checkTextSplit(t *testing.T, symbols []elf.Symbol, expectSplit bool) {
757+ isSplit := false
758+ for _, symbol := range symbols {
759+ if symbol.Name == "runtime.text.1" {
760+ // If the symbol runtime.text.1 is contained, text has been split.
761+ isSplit = true
762+ break
763+ }
764+ }
765+ 
766+ if isSplit != expectSplit {
767+ t.Errorf("expect: %v actual is: %v ", expectSplit, isSplit)
768+ }
769+}
770+ 
771+// checkTextSplitMappingSymbols checks that every executable section in the
772+// object file emitted by the Go linker (go.o) begins with an $x mapping
773+// symbol, as required by the ARM64 AAELF64 specification. The check runs on
774+// the link-time object rather than the final executable because the external
775+// linker merges the split .text sections into a single output section.
776+func checkTextSplitMappingSymbols(t *testing.T, objfile string) {
777+ f, err := elf.Open(objfile)
778+ if err != nil {
779+ t.Fatal(err)
780+ }
781+ defer f.Close()
782+ 
783+ symbols, err := f.Symbols()
784+ if err != nil {
785+ t.Fatal(err)
786+ }
787+ 
788+ nexec := 0
789+ for i, sect := range f.Sections {
790+ if sect.Flags&elf.SHF_EXECINSTR == 0 {
791+ continue
792+ }
793+ nexec++
794+ hasCodeSym := false
795+ for _, sym := range symbols {
796+ if sym.Name == "$x" && sym.Section == elf.SectionIndex(i) && sym.Value == 0 {
797+ hasCodeSym = true
798+ break
799+ }
800+ }
801+ if !hasCodeSym {
802+ t.Errorf("executable section %s has no $x mapping symbol at offset 0", sect.Name)
803+ }
804+ }
805+ if nexec < 2 {
806+ t.Errorf("expected text to be split into at least 2 executable sections, got %d", nexec)
807+ }
808+}
809+ 
810+// buildSymbolsTextSplit builds a simple program with a small text size limit,
811+// which makes the linker split the text into multiple sections, and returns
812+// the symbol table of the final executable as well as the path of the object
813+// file emitted by the Go linker (go.o).
814+func buildSymbolsTextSplit(t *testing.T, linkerMod string, enableMappingSymbols bool) ([]elf.Symbol, string) {
815+ goTool := testenv.GoToolPath(t)
816+ dir := t.TempDir()
817+ objdir := t.TempDir()
818+ 
819+ goFile := filepath.Join(dir, "main.go")
820+ if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
821+ t.Fatal(err)
822+ }
823+ if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
824+ t.Fatal(err)
825+ }
826+ 
827+ var cmd *exec.Cmd
828+ args := []string{"build", "-o", linkerMod, "-buildmode=exe",
829+ "-ldflags=-debugtextsize=360000 -linkmode=" + linkerMod + " -tmpdir=" + objdir}
830+ if enableMappingSymbols {
831+ args = append(args, "-mappingsymbol")
832+ }
833+ 
834+ cmd = exec.Command(goTool, args...)
835+ 
836+ cmd.Dir = dir
837+ 
838+ if _, err := cmd.CombinedOutput(); err != nil {
839+ t.Fatal(err)
840+ }
841+ 
842+ bin := filepath.Join(dir, linkerMod)
843+ 
844+ elfexe, err := elf.Open(bin)
845+ if err != nil {
846+ t.Fatal(err)
847+ }
848+ 
849+ symbols, err := elfexe.Symbols()
850+ if err != nil {
851+ t.Fatal(err)
852+ }
853+ 
854+ return symbols, filepath.Join(objdir, "go.o")
855+}
@@ -34,6 +34,7 @@ package ld
34import (34import (
35 "bytes"35 "bytes"
36 "cmd/internal/gcprog"36 "cmd/internal/gcprog"
37+ "cmd/internal/goobj"
37 "cmd/internal/objabi"38 "cmd/internal/objabi"
38 "cmd/internal/sys"39 "cmd/internal/sys"
39 "cmd/link/internal/loader"40 "cmd/link/internal/loader"
@@ -2605,6 +2606,12 @@ func (ctxt *Link) textaddress() {
2605 2606 
2606 // Assign actual address for current symbol.2607 // Assign actual address for current symbol.
2607 sect, n, va = assignAddress(ctxt, sect, n, s, va, false, big)2608 sect, n, va = assignAddress(ctxt, sect, n, s, va, false, big)
2609+ if goobj.EnableMappingSymbols && ctxt.IsARM64() && ctxt.IsELF && ctxt.IsExternal() {
2610+ // do not generate trampoline if goobj.EnableMappingSymbols == true
2611+ // and then the ntramps always zero, do not need to use EnableMappingSymbols
2612+ // to split calc newtextp
2613+ continue
2614+ }
2608 2615 
2609 // Resolve jumps, adding trampolines if they are needed.2616 // Resolve jumps, adding trampolines if they are needed.
2610 trampoline(ctxt, s)2617 trampoline(ctxt, s)
@@ -2672,6 +2679,10 @@ func assignAddress(ctxt *Link, sect *sym.Section, n int, s loader.Sym, va uint64
2672 funcsize = uint64(ldr.SymSize(s))2679 funcsize = uint64(ldr.SymSize(s))
2673 }2680 }
2674 2681 
2682+ if *padfuncFlag && funcsize < abi.PADMINFUNC {
2683+ funcsize = abi.PADMINFUNC
2684+ }
2685+ 
2675 // If we need to split text sections, and this function doesn't fit in the current2686 // If we need to split text sections, and this function doesn't fit in the current
2676 // section, then create a new one.2687 // section, then create a new one.
2677 //2688 //
@@ -2781,7 +2792,16 @@ func resetAddress(ctxt *Link, s loader.Sym) {
2781// machinery in the external linker; see #58425 for more on the2792// machinery in the external linker; see #58425 for more on the
2782// history here.2793// history here.
2783func splitTextSections(ctxt *Link) bool {2794func splitTextSections(ctxt *Link) bool {
2784- return (ctxt.IsARM() || ctxt.IsPPC64() || (ctxt.IsARM64() && ctxt.IsDarwin())) && ctxt.IsExternal()2795+ if !ctxt.IsExternal() {
2796+ return false
2797+ }
2798+ if ctxt.IsARM() || ctxt.IsPPC64() || (ctxt.IsARM64() && ctxt.IsDarwin()) {
2799+ // upstream behavior, independent of -mappingsymbol
2800+ return true
2801+ }
2802+ // split ARM64 ELF text sections as well, so mapping symbols
2803+ // stay within section boundaries for ARM64 mapping symbols builds
2804+ return goobj.EnableMappingSymbols && ctxt.IsARM64() && ctxt.IsELF
2785}2805}
2786 2806 
2787// On Wasm, we reserve 4096 bytes for zero page, then 8192 bytes for wasm_exec.js2807// On Wasm, we reserve 4096 bytes for zero page, then 8192 bytes for wasm_exec.js
@@ -5,6 +5,7 @@
5package ld5package ld
6 6 
7import (7import (
8+ "cmd/internal/goobj"
8 "cmd/internal/objabi"9 "cmd/internal/objabi"
9 "cmd/internal/sys"10 "cmd/internal/sys"
10 "cmd/link/internal/loader"11 "cmd/link/internal/loader"
@@ -90,3 +91,131 @@ func TestAddGotSym(t *testing.T) {
90 }91 }
91 }92 }
92}93}
94+ 
95+func TestSplitTextSections(t *testing.T) {
96+ tests := []struct {
97+ name string
98+ arch *sys.Arch
99+ ht objabi.HeadType
100+ iself bool
101+ bm, lm string
102+ mappingSymbols bool
103+ expectSplit bool
104+ }{
105+ {
106+ name: "arm64_linux_elf_external_ms",
107+ arch: sys.ArchARM64,
108+ ht: objabi.Hlinux,
109+ iself: true,
110+ bm: "pie",
111+ lm: "external",
112+ mappingSymbols: true,
113+ expectSplit: true,
114+ },
115+ {
116+ name: "arm64_linux_elf_internal_ms",
117+ arch: sys.ArchARM64,
118+ ht: objabi.Hlinux,
119+ iself: true,
120+ bm: "pie",
121+ lm: "internal",
122+ mappingSymbols: true,
123+ expectSplit: false,
124+ },
125+ {
126+ name: "arm64_darwin_external_ms",
127+ arch: sys.ArchARM64,
128+ ht: objabi.Hdarwin,
129+ iself: false,
130+ bm: "pie",
131+ lm: "external",
132+ mappingSymbols: true,
133+ expectSplit: true,
134+ },
135+ {
136+ // regression test: ARM32 splitting must not be lost when
137+ // mapping symbols are enabled (issue #58425)
138+ name: "arm_linux_elf_external_ms",
139+ arch: sys.ArchARM,
140+ ht: objabi.Hlinux,
141+ iself: true,
142+ bm: "exe",
143+ lm: "external",
144+ mappingSymbols: true,
145+ expectSplit: true,
146+ },
147+ {
148+ // regression test: PPC64 splitting must not be lost when
149+ // mapping symbols are enabled ('bl' offset limit)
150+ name: "ppc64_linux_elf_external_ms",
151+ arch: sys.ArchPPC64,
152+ ht: objabi.Hlinux,
153+ iself: true,
154+ bm: "exe",
155+ lm: "external",
156+ mappingSymbols: true,
157+ expectSplit: true,
158+ },
159+ {
160+ name: "amd64_linux_elf_external_ms",
161+ arch: sys.ArchAMD64,
162+ ht: objabi.Hlinux,
163+ iself: true,
164+ bm: "exe",
165+ lm: "external",
166+ mappingSymbols: true,
167+ expectSplit: false,
168+ },
169+ {
170+ name: "arm64_linux_elf_external",
171+ arch: sys.ArchARM64,
172+ ht: objabi.Hlinux,
173+ iself: true,
174+ bm: "exe",
175+ lm: "external",
176+ mappingSymbols: false,
177+ expectSplit: false,
178+ },
179+ {
180+ name: "arm_linux_elf_external",
181+ arch: sys.ArchARM,
182+ ht: objabi.Hlinux,
183+ iself: true,
184+ bm: "exe",
185+ lm: "external",
186+ mappingSymbols: false,
187+ expectSplit: true,
188+ },
189+ {
190+ name: "arm64_darwin_external",
191+ arch: sys.ArchARM64,
192+ ht: objabi.Hdarwin,
193+ iself: false,
194+ bm: "pie",
195+ lm: "external",
196+ mappingSymbols: false,
197+ expectSplit: true,
198+ },
199+ }
200+ 
201+ // Save the architecture and the flag as we're going to set them on each test run.
202+ origArch := buildcfg.GOARCH
203+ origMappingSymbols := goobj.EnableMappingSymbols
204+ 
205+ defer func(goarch string, state bool) {
206+ buildcfg.GOARCH = goarch
207+ goobj.EnableMappingSymbols = state
208+ }(origArch, origMappingSymbols)
209+ 
210+ for _, test := range tests {
211+ t.Run(test.name, func(t *testing.T) {
212+ buildcfg.GOARCH = test.arch.Name
213+ goobj.EnableMappingSymbols = test.mappingSymbols
214+ ctxt := setUpContext(test.arch, test.iself, test.ht, test.bm, test.lm)
215+ isSplit := splitTextSections(ctxt)
216+ if isSplit != test.expectSplit {
217+ t.Errorf("expect: %v actual is: %v", test.expectSplit, isSplit)
218+ }
219+ })
220+ }
221+}
@@ -114,6 +114,7 @@ var (
114 memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")114 memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
115 benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")115 benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
116 benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")116 benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")
117+ padfuncFlag = flag.Bool("padfunc", false, "padding the minimum function size to 32 bytes")
117 118 
118 flagW ternaryFlag119 flagW ternaryFlag
119 FlagW = new(bool) // the -w flag, computed in main from flagW120 FlagW = new(bool) // the -w flag, computed in main from flagW
@@ -199,6 +200,7 @@ func Main(arch *sys.Arch, theArch Arch) {
199 flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")200 flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
200 flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")201 flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
201 flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")202 flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
203+ flag.BoolVar(&goobj.EnableMappingSymbols, "mappingsymbol", false, "generate mapping symbols for arm64")
202 objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })204 objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
203 objabi.AddVersionFlag() // -V205 objabi.AddVersionFlag() // -V
204 objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })206 objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
@@ -324,6 +326,13 @@ func Main(arch *sys.Arch, theArch Arch) {
324 bench.Start("Archinit")326 bench.Start("Archinit")
325 thearch.Archinit(ctxt)327 thearch.Archinit(ctxt)
326 328 
329+ if goobj.EnableMappingSymbols && (!ctxt.IsARM64() || !ctxt.IsELF) {
330+ // mapping symbols are only generated for arm64 ELF; on any other
331+ // target the flag is a no-op and must not alter linking behavior
332+ // (text splitting, trampolines)
333+ goobj.EnableMappingSymbols = false
334+ }
335+ 
327 if ctxt.linkShared && !ctxt.IsELF {336 if ctxt.linkShared && !ctxt.IsELF {
328 Exitf("-linkshared can only be used on elf systems")337 Exitf("-linkshared can only be used on elf systems")
329 }338 }
@@ -31,8 +31,10 @@
31package ld31package ld
32 32 
33import (33import (
34+ "cmd/internal/goobj"
34 "cmd/internal/obj"35 "cmd/internal/obj"
35 "cmd/internal/objabi"36 "cmd/internal/objabi"
37+ "cmd/internal/sys"
36 "cmd/link/internal/loader"38 "cmd/link/internal/loader"
37 "cmd/link/internal/sym"39 "cmd/link/internal/sym"
38 "debug/elf"40 "debug/elf"
@@ -78,30 +80,9 @@ func putelfsyment(out *OutBuf, off int, addr int64, size int64, info uint8, shnd
78 80 
79func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {81func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
80 ldr := ctxt.loader82 ldr := ctxt.loader
81- addr := ldr.SymValue(x)83+ addr, size, sect, elfshnum, ok := getSymInfo(ctxt, x)
82- size := ldr.SymSize(x)84+ if !ok {
83- 85+ return
84- xo := x
85- if ldr.OuterSym(x) != 0 {
86- xo = ldr.OuterSym(x)
87- }
88- xot := ldr.SymType(xo)
89- xosect := ldr.SymSect(xo)
90- 
91- var elfshnum elf.SectionIndex
92- if xot == sym.SDYNIMPORT || xot == sym.SHOSTOBJ || xot == sym.SUNDEFEXT {
93- elfshnum = elf.SHN_UNDEF
94- size = 0
95- } else {
96- if xosect == nil {
97- ldr.Errorf(x, "missing section in putelfsym")
98- return
99- }
100- if xosect.Elfsect == nil {
101- ldr.Errorf(x, "missing ELF section in putelfsym")
102- return
103- }
104- elfshnum = xosect.Elfsect.(*ElfShdr).shnum
105 }86 }
106 87 
107 sname := ldr.SymExtname(x)88 sname := ldr.SymExtname(x)
@@ -126,7 +107,7 @@ func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
126 }107 }
127 108 
128 if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF {109 if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF {
129- addr -= int64(xosect.Vaddr)110+ addr -= int64(sect.Vaddr)
130 }111 }
131 other := int(elf.STV_DEFAULT)112 other := int(elf.STV_DEFAULT)
132 if ldr.AttrVisibilityHidden(x) {113 if ldr.AttrVisibilityHidden(x) {
@@ -180,6 +161,117 @@ func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
180 ctxt.numelfsym++161 ctxt.numelfsym++
181}162}
182 163 
164+// Generates special mapping symbols that are required by ARM64 ELF standtard.
165+// https://github.com/ARM-software/abi-aa/blob/2020q4/aaelf64/aaelf64.rst#mapping-symbols
166+func genElfMappingSymbols(ctxt *Link) {
167+ if !goobj.EnableMappingSymbols || ctxt.Arch.Family != sys.ARM64 || !ctxt.IsELF {
168+ return
169+ }
170+ 
171+ ldr := ctxt.loader
172+ 
173+ // There are 2 symbols designed to mark inline transitions between code adn data:
174+ // $d in the beginning of a sequence of data.
175+ // $x in the beginning of a sequence of ARM64 instructions.
176+ // Mapping symbols have type STT_NOTYPE and binding STB_LOCAL, the st_size field is unused and must be zero.
177+ datasymb := putelfstr("$d")
178+ codesymb := putelfstr("$x")
179+ 
180+ needcodesymb := true
181+ // The section index of the section the previously emitted mapping symbol
182+ // belongs to. ^elf.SectionIndex(0) (-1) is never a valid section index,
183+ // so the first section is always treated as a new section.
184+ prevshnum := ^elf.SectionIndex(0)
185+ for _, s := range ctxt.Textp {
186+ var numpoolinfo uint32 = 0
187+ funcinfo := ldr.FuncInfo(s)
188+ if funcinfo.Valid() {
189+ funcinfo.Preload()
190+ numpoolinfo = funcinfo.NumPoolInfo()
191+ }
192+ 
193+ // sz is ignored, because accroding to the documentation the st_size field must be zero.
194+ addr, _, sect, elfshnum, ok := getSymInfo(ctxt, s)
195+ if !ok || elfshnum == elf.SHN_UNDEF {
196+ continue
197+ }
198+ 
199+ // AAELF64 requires every section containing instructions to define a
200+ // mapping symbol at the beginning of the section, so reset the code
201+ // state whenever the section changes.
202+ if elfshnum != prevshnum {
203+ prevshnum = elfshnum
204+ needcodesymb = true
205+ }
206+ 
207+ if !needcodesymb && numpoolinfo == 0 {
208+ continue
209+ }
210+ 
211+ if needcodesymb {
212+ if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF {
213+ addr -= int64(sect.Vaddr)
214+ }
215+ putelfsyment(ctxt.Out, codesymb, addr, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE), elfshnum, 0)
216+ ctxt.numelfsym++
217+ needcodesymb = false
218+ }
219+ 
220+ for at := uint32(0); at < numpoolinfo; at++ {
221+ poolInfo := funcinfo.PoolInfo(at)
222+ poollocation := ldr.SymValue(s) + int64(poolInfo.PoolOff)
223+ codelocation := ldr.SymValue(s) + int64(poolInfo.CodeOff)
224+ 
225+ if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF {
226+ poollocation -= int64(sect.Vaddr)
227+ codelocation -= int64(sect.Vaddr)
228+ }
229+ 
230+ putelfsyment(ctxt.Out, datasymb, poollocation+4, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE), elfshnum, 0)
231+ ctxt.numelfsym++
232+ needcodesymb = true
233+ 
234+ if poolInfo.CodeOff != 0 {
235+ putelfsyment(ctxt.Out, codesymb, codelocation, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE), elfshnum, 0)
236+ ctxt.numelfsym++
237+ needcodesymb = false
238+ }
239+ }
240+ }
241+}
242+ 
243+// getSymInfo returns generic information about symbol size and position that's needed inside putelfsym and genElfMappingSymbols.
244+func getSymInfo(ctxt *Link, x loader.Sym) (addr int64, sz int64, sect *sym.Section, elfshnum elf.SectionIndex, ok bool) {
245+ ldr := ctxt.loader
246+ addr = ldr.SymValue(x)
247+ sz = ldr.SymSize(x)
248+ 
249+ xo := x
250+ if ldr.OuterSym(x) != 0 {
251+ xo = ldr.OuterSym(x)
252+ }
253+ xot := ldr.SymType(xo)
254+ sect = ldr.SymSect(xo)
255+ 
256+ if xot == sym.SDYNIMPORT || xot == sym.SHOSTOBJ || xot == sym.SUNDEFEXT {
257+ elfshnum = elf.SHN_UNDEF
258+ sz = 0
259+ } else {
260+ if sect == nil {
261+ ldr.Errorf(x, "missing section in getSymInfo")
262+ return
263+ }
264+ if sect.Elfsect == nil {
265+ ldr.Errorf(x, "missing ELF section in getSymInfo")
266+ return
267+ }
268+ elfshnum = sect.Elfsect.(*ElfShdr).shnum
269+ }
270+ 
271+ ok = true
272+ return
273+}
274+ 
183func putelfsectionsym(ctxt *Link, out *OutBuf, s loader.Sym, shndx elf.SectionIndex) {275func putelfsectionsym(ctxt *Link, out *OutBuf, s loader.Sym, shndx elf.SectionIndex) {
184 putelfsyment(out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_SECTION), shndx, 0)276 putelfsyment(out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_SECTION), shndx, 0)
185 ctxt.loader.SetSymElfSym(s, int32(ctxt.numelfsym))277 ctxt.loader.SetSymElfSym(s, int32(ctxt.numelfsym))
@@ -278,6 +370,8 @@ func asmElfSym(ctxt *Link) {
278 putelfsyment(ctxt.Out, putelfstr("go.go"), 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_FILE), elf.SHN_ABS, 0)370 putelfsyment(ctxt.Out, putelfstr("go.go"), 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_FILE), elf.SHN_ABS, 0)
279 ctxt.numelfsym++371 ctxt.numelfsym++
280 372 
373+ genElfMappingSymbols(ctxt)
374+ 
281 bindings := []elf.SymBind{elf.STB_LOCAL, elf.STB_GLOBAL}375 bindings := []elf.SymBind{elf.STB_LOCAL, elf.STB_GLOBAL}
282 for _, elfbind := range bindings {376 for _, elfbind := range bindings {
283 if elfbind == elf.STB_GLOBAL {377 if elfbind == elf.STB_GLOBAL {
@@ -2127,6 +2127,26 @@ func (fi *FuncInfo) InlTree(k int) InlTreeNode {
2127 }2127 }
2128}2128}
2129 2129 
2130+func (fi *FuncInfo) NumPoolInfo() uint32 {
2131+ if !goobj.EnableMappingSymbols {
2132+ panic("disabled mapping symbols, but (*FuncInfo).NumPoolInfo is called, is there a bug?")
2133+ }
2134+ if !fi.lengths.Initialized {
2135+ panic("need to call Preload first")
2136+ }
2137+ return fi.lengths.NumPoolInfo
2138+}
2139+ 
2140+func (fi *FuncInfo) PoolInfo(k uint32) goobj.PoolInfo {
2141+ if !goobj.EnableMappingSymbols {
2142+ panic("disabled mapping symbols, but (*FuncInfo).PoolInfo is called, is there a bug?")
2143+ }
2144+ if !fi.lengths.Initialized {
2145+ panic("need to call Preload first")
2146+ }
2147+ return (*goobj.FuncInfo)(nil).ReadPoolInfo(fi.data, fi.lengths.PoolInfoOff, k)
2148+}
2149+ 
2130func (l *Loader) FuncInfo(i Sym) FuncInfo {2150func (l *Loader) FuncInfo(i Sym) FuncInfo {
2131 r, auxs := l.auxs(i)2151 r, auxs := l.auxs(i)
2132 for j := range auxs {2152 for j := range auxs {
@@ -0,0 +1,37 @@
1+package main
2+ 
3+import (
4+ "fmt"
5+ "reflect"
6+ "runtime"
7+)
8+ 
9+//go:noinline
10+func zyes() bool {
11+ return true
12+}
13+ 
14+//go:noinline
15+func zno() bool {
16+ return false
17+}
18+ 
19+func main() {
20+ padfunccheck()
21+}
22+ 
23+const PADMINFUNC = 32 // padding the minimum function size to 32 bytes
24+ 
25+func padfunccheck() {
26+ if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
27+ fmt.Print("SKIP\n")
28+ return
29+ }
30+ var zyesptr uintptr = reflect.ValueOf(zyes).Pointer()
31+ var znoptr uintptr = reflect.ValueOf(zno).Pointer()
32+ fmt.Printf("zyes:0x%x zno:0x%x\n", zyesptr, znoptr)
33+ if (znoptr - zyesptr) != PADMINFUNC {
34+ panic("FAIL")
35+ }
36+ fmt.Print("OK\n")
37+}
@@ -34,6 +34,7 @@ package main
34import (34import (
35 "flag"35 "flag"
36 "fmt"36 "fmt"
37+ "io"
37 "log"38 "log"
38 "os"39 "os"
39 "regexp"40 "regexp"
@@ -45,17 +46,88 @@ import (
45 "cmd/internal/telemetry/counter"46 "cmd/internal/telemetry/counter"
46)47)
47 48 
49+var printModuledata = flag.Bool("m", false, "(Experimental, ELF only) print moduledata")
50+var printPCHeader = flag.Bool("p", false, "(Experimental, ELF only) print pcHeader")
51+var printITabLinks = flag.Bool("I", false, "(Experimental, ELF only) print moduledata itablinks")
52+var printTypes = flag.Bool("T", false, "(Experimental, ELF only) print moduledata types")
53+var printTypesDepth = flag.Int("t", 0, "(Experimental, ELF only) print types detail depth (0=name only, 1=fields/methods, 2+=nested types) (implies -T)")
54+var funcDataModeFlag = flag.String("f", "", "(Experimental, ELF only) comma-separated list: pcsp,unsafepoint,stackmap,inltree,arglive,block")
55+var funcDataBothFlag = flag.Bool("F", false, "(Experimental, ELF only) short for -f block,pcsp,unsafepoint")
56+var moduleDataOnly = flag.Bool("M", false, "(Experimental, ELF only) print all moduledata sections, without assembly")
57+var omitVersionCheck = flag.Bool("n", false, "Omit version check in moduledata")
58+var printFindfunctab = flag.Bool("b", false, "(Experimental, ELF only) print findfunctab bucket table")
59+var verifyModuledataFlag = flag.Bool("d", false, "(Debug) verify moduledata integrity")
60+var verifyAllBuckets = flag.Bool("D", false, "(Debug) verify all findfunctab buckets (slow, implies -d)")
48var printCode = flag.Bool("S", false, "print Go code alongside assembly")61var printCode = flag.Bool("S", false, "print Go code alongside assembly")
49var symregexp = flag.String("s", "", "only dump symbols matching this regexp")62var symregexp = flag.String("s", "", "only dump symbols matching this regexp")
50var gnuAsm = flag.Bool("gnu", false, "print GNU assembly next to Go assembly (where supported)")63var gnuAsm = flag.Bool("gnu", false, "print GNU assembly next to Go assembly (where supported)")
51var symRE *regexp.Regexp64var symRE *regexp.Regexp
52 65 
53func usage() {66func usage() {
54- fmt.Fprintf(os.Stderr, "usage: go tool objdump [-S] [-gnu] [-s symregexp] binary [start end]\n\n")67+ fmt.Fprintf(os.Stderr, "usage: go tool objdump [options] binary [start end]\n\n")
55- flag.PrintDefaults()68+ fmt.Fprintf(os.Stderr, "Disassembly options:\n")
69+ fmt.Fprintf(os.Stderr, " -S print Go code alongside assembly\n")
70+ fmt.Fprintf(os.Stderr, " -gnu print GNU assembly next to Go assembly\n")
71+ fmt.Fprintf(os.Stderr, " -s only dump symbols matching regexp\n\n")
72+ fmt.Fprintf(os.Stderr, "Moduledata options (Experimental, ELF only):\n")
73+ fmt.Fprintf(os.Stderr, " -m print moduledata, with assembly\n")
74+ fmt.Fprintf(os.Stderr, " -p print pcHeader (only)\n")
75+ fmt.Fprintf(os.Stderr, " -I print moduledata itablinks (only)\n")
76+ fmt.Fprintf(os.Stderr, " -T print moduledata types (only)\n")
77+ fmt.Fprintf(os.Stderr, " -t N type detail depth (0=no details, 1=fields/methods, 2+=nested)\n")
78+ fmt.Fprintf(os.Stderr, " -M print all moduledata sections (only)\n")
79+ fmt.Fprintf(os.Stderr, " -f comma-separated funcdata list: pcsp,unsafepoint,stackmap,inltree,arglive,block\n")
80+ fmt.Fprintf(os.Stderr, " -F short for -f block,pcsp,unsafepoint\n")
81+ fmt.Fprintf(os.Stderr, " -n omit version check in moduledata\n")
82+ fmt.Fprintf(os.Stderr, " -b print findfunctab bucket table\n")
83+ fmt.Fprintf(os.Stderr, " -d (Debug) verify moduledata integrity\n")
84+ fmt.Fprintf(os.Stderr, " -D (Debug) verify all findfunctab buckets (slow, implies -d)\n\n")
56 os.Exit(2)85 os.Exit(2)
57}86}
58 87 
88+func printModuledataIfNeeded(w io.Writer, mdp *objfile.Moduledata, filter *regexp.Regexp) {
89+ if *moduleDataOnly {
90+ if err := mdp.PrintModuledata(w); err != nil {
91+ log.Fatalf("%v", err)
92+ }
93+ if err := mdp.PrintPCHeader(w); err != nil {
94+ log.Fatalf("%v", err)
95+ }
96+ if err := mdp.PrintITabLinks(w, filter); err != nil {
97+ log.Fatalf("%v", err)
98+ }
99+ if err := mdp.PrintTypes(w, filter, *printTypesDepth); err != nil {
100+ log.Fatalf("%v", err)
101+ }
102+ return
103+ }
104+ if *printModuledata {
105+ if err := mdp.PrintModuledata(w); err != nil {
106+ log.Fatalf("%v", err)
107+ }
108+ }
109+ if *printPCHeader {
110+ if err := mdp.PrintPCHeader(w); err != nil {
111+ log.Fatalf("%v", err)
112+ }
113+ }
114+ if *printITabLinks {
115+ if err := mdp.PrintITabLinks(w, filter); err != nil {
116+ log.Fatalf("%v", err)
117+ }
118+ }
119+ if *printTypes || *printTypesDepth > 0 {
120+ if err := mdp.PrintTypes(w, filter, *printTypesDepth); err != nil {
121+ log.Fatalf("%v", err)
122+ }
123+ }
124+ if *printFindfunctab {
125+ if err := mdp.PrintFindfunctab(w); err != nil {
126+ log.Fatalf("%v", err)
127+ }
128+ }
129+}
130+ 
59func main() {131func main() {
60 log.SetFlags(0)132 log.SetFlags(0)
61 log.SetPrefix("objdump: ")133 log.SetPrefix("objdump: ")
@@ -83,20 +155,135 @@ func main() {
83 }155 }
84 defer f.Close()156 defer f.Close()
85 157 
158+ var mdp *objfile.Moduledata
159+ 
160+ mode := *funcDataModeFlag
161+ if *funcDataBothFlag {
162+ mode = "block,pcsp,unsafepoint"
163+ }
164+ hasBlock := strings.Contains(mode, "block")
165+ hasPCSP := strings.Contains(mode, "pcsp")
166+ hasUnsafePoint := strings.Contains(mode, "unsafepoint")
167+ hasStackMap := strings.Contains(mode, "stackmap")
168+ hasInlTree := strings.Contains(mode, "inltree")
169+ hasArgLive := strings.Contains(mode, "arglive")
170+ printFuncBlock := hasBlock
171+ printFuncData := printFuncBlock || hasPCSP || hasUnsafePoint || hasStackMap || hasInlTree || hasArgLive
172+ 
173+ mdOpts := disasm.MDOptions{
174+ PrintFuncData: printFuncData,
175+ InlinePCSP: hasPCSP,
176+ InlineUnsafePoint: hasUnsafePoint,
177+ InlineStackMap: hasStackMap,
178+ InlineInlTree: hasInlTree,
179+ InlineArgLive: hasArgLive,
180+ }
181+ 
182+ if *printModuledata || *printPCHeader || *printITabLinks || *printTypes || *printTypesDepth > 0 || *moduleDataOnly || printFuncData || *printFindfunctab || *verifyModuledataFlag || *verifyAllBuckets {
183+ checkVersion := true
184+ if *omitVersionCheck {
185+ checkVersion = false
186+ }
187+ mdp, err = f.Moduledata(checkVersion)
188+ if err != nil {
189+ log.Fatalf("reading moduledata %s: %v", flag.Arg(0), err)
190+ }
191+ if printFuncBlock {
192+ objfile.SetPrintFuncData(true)
193+ }
194+ 
195+ // Run moduledata verification if requested (-d or -D)
196+ if *verifyModuledataFlag || *verifyAllBuckets {
197+ fmt.Fprintf(os.Stderr, "objdump: verifying moduledata integrity...\n")
198+ 
199+ // Build symbol list from ELF for accurate function boundaries
200+ syms, err := f.Symbols()
201+ if err != nil {
202+ log.Fatalf("reading symbols: %v", err)
203+ }
204+ var symbols []objfile.FuncSymbol
205+ for _, sym := range syms {
206+ if sym.Code == 'T' || sym.Code == 't' {
207+ symbols = append(symbols, objfile.FuncSymbol{
208+ Name: sym.Name,
209+ Addr: sym.Addr,
210+ Size: uint64(sym.Size),
211+ })
212+ }
213+ }
214+ 
215+ // -D implies full verification (no -s filter), so it always uses VerifyModuledata
216+ if symRE != nil && !*verifyAllBuckets {
217+ // -d with -s: verify matching functions, then continue to disassembly
218+ vr := mdp.VerifyModuledataForFuncs(symbols, symRE)
219+ if len(vr.Issues) > 0 {
220+ fmt.Fprintf(os.Stderr, "%s\n", vr.String())
221+ } else {
222+ fmt.Fprintf(os.Stderr, "objdump: no issues found\n")
223+ }
224+ if vr.ExitCode() != 0 {
225+ os.Exit(vr.ExitCode())
226+ }
227+ // Continue to disassembly below
228+ } else {
229+ // -d without -s OR -D: full verification
230+ vr := mdp.VerifyModuledata(symbols)
231+ if len(vr.Issues) > 0 {
232+ fmt.Fprintf(os.Stderr, "%s\n", vr.String())
233+ } else {
234+ fmt.Fprintf(os.Stderr, "objdump: no issues found\n")
235+ }
236+ 
237+ // -D: continue with full findfunctab bucket verification
238+ if *verifyAllBuckets {
239+ fmt.Fprintf(os.Stderr, "objdump: verifying all findfunctab buckets...\n")
240+ vr2 := mdp.VerifyAllFindfunctabBuckets()
241+ if len(vr2.Issues) > 0 {
242+ fmt.Fprintf(os.Stderr, "%s\n", vr2.String())
243+ } else {
244+ fmt.Fprintf(os.Stderr, "objdump: no issues found\n")
245+ }
246+ // Exit with error if either verification found issues
247+ if vr.ExitCode() != 0 || vr2.ExitCode() != 0 {
248+ os.Exit(1)
249+ }
250+ os.Exit(0)
251+ }
252+ 
253+ // -d only: exit after moduledata verification
254+ os.Exit(vr.ExitCode())
255+ }
256+ }
257+ }
258+ 
86 dis, err := disasm.DisasmForFile(f)259 dis, err := disasm.DisasmForFile(f)
87 if err != nil {260 if err != nil {
88 log.Fatalf("disassemble %s: %v", flag.Arg(0), err)261 log.Fatalf("disassemble %s: %v", flag.Arg(0), err)
89 }262 }
263+ if mdp != nil {
264+ dis.SetModuledata(mdp)
265+ }
90 266 
91 switch flag.NArg() {267 switch flag.NArg() {
92 default:268 default:
93 usage()269 usage()
94 case 1:270 case 1:
95- // disassembly of entire object271+ if mdp != nil {
96- dis.Print(os.Stdout, symRE, 0, ^uint64(0), *printCode, *gnuAsm)272+ printModuledataIfNeeded(os.Stdout, mdp, symRE)
273+ if *moduleDataOnly || *printPCHeader || *printITabLinks || *printTypes || *printFindfunctab {
274+ return
275+ }
276+ if !printFuncData || printFuncBlock {
277+ if err := mdp.PrintFunc(os.Stdout, symRE, 0, ^uint64(0)); err != nil {
278+ log.Fatalf("%v", err)
279+ }
280+ }
281+ }
282+ if mdp == nil || !*moduleDataOnly {
283+ dis.Print(os.Stdout, symRE, 0, ^uint64(0), *printCode, *gnuAsm, mdOpts)
284+ }
97 285 
98 case 3:286 case 3:
99- // disassembly of PC range
100 start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64)287 start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64)
101 if err != nil {288 if err != nil {
102 log.Fatalf("invalid start PC: %v", err)289 log.Fatalf("invalid start PC: %v", err)
@@ -105,6 +292,19 @@ func main() {
105 if err != nil {292 if err != nil {
106 log.Fatalf("invalid end PC: %v", err)293 log.Fatalf("invalid end PC: %v", err)
107 }294 }
108- dis.Print(os.Stdout, symRE, start, end, *printCode, *gnuAsm)295+ if mdp != nil {
296+ printModuledataIfNeeded(os.Stdout, mdp, symRE)
297+ if *moduleDataOnly || *printPCHeader || *printITabLinks || *printTypes || *printFindfunctab {
298+ return
299+ }
300+ if !printFuncData || printFuncBlock {
301+ if err := mdp.PrintFunc(os.Stdout, symRE, start, end); err != nil {
302+ log.Fatalf("%v", err)
303+ }
304+ }
305+ }
306+ if mdp == nil || !*moduleDataOnly {
307+ dis.Print(os.Stdout, symRE, start, end, *printCode, *gnuAsm, mdOpts)
308+ }
109 }309 }
110}310}
@@ -0,0 +1,167 @@
1+package main
2+ 
3+import (
4+ "fmt"
5+ "reflect"
6+)
7+ 
8+type Inner struct {
9+ ID int
10+ Name string
11+ Value float64
12+}
13+ 
14+type Outer struct {
15+ Regular int
16+ Nested Inner
17+ Anonymous struct {
18+ DeepField string
19+ Count int
20+ }
21+}
22+ 
23+var globalOuter Outer
24+var globalInner Inner
25+ 
26+type CustomReader interface {
27+ Read(p []byte) (n int, err error)
28+}
29+ 
30+type CustomWriter interface {
31+ Write(p []byte) (n int, err error)
32+}
33+ 
34+type CustomCloser interface {
35+ Close() error
36+}
37+ 
38+type ReadWriter interface {
39+ CustomReader
40+ CustomWriter
41+}
42+ 
43+type ReadWriteCloser interface {
44+ ReadWriter
45+ CustomCloser
46+}
47+ 
48+type Formatter interface {
49+ Format(fmt string, args ...interface{}) string
50+}
51+ 
52+type Stringer interface {
53+ String() string
54+}
55+ 
56+type myReader struct{}
57+ 
58+func (r *myReader) Read(p []byte) (n int, err error) {
59+ return 0, nil
60+}
61+ 
62+type myWriter struct{}
63+ 
64+func (w *myWriter) Write(p []byte) (n int, err error) {
65+ return len(p), nil
66+}
67+ 
68+type myCloser struct{}
69+ 
70+func (c *myCloser) Close() error {
71+ return nil
72+}
73+ 
74+type myRWCloser struct {
75+ *myReader
76+ *myWriter
77+ *myCloser
78+}
79+ 
80+func (rwc *myRWCloser) Read(p []byte) (n int, err error) {
81+ return 0, nil
82+}
83+ 
84+func (rwc *myRWCloser) Write(p []byte) (n int, err error) {
85+ return len(p), nil
86+}
87+ 
88+func (rwc *myRWCloser) Close() error {
89+ return nil
90+}
91+ 
92+type myFormatter struct{}
93+ 
94+func (f *myFormatter) Format(fmtStr string, args ...interface{}) string {
95+ return fmt.Sprintf(fmtStr, args...)
96+}
97+ 
98+type myStringer struct{}
99+ 
100+func (s *myStringer) String() string {
101+ return "myStringer"
102+}
103+ 
104+type myError struct{}
105+ 
106+func (e *myError) Error() string {
107+ return "myError"
108+}
109+ 
110+var (
111+ globalReader CustomReader = &myReader{}
112+ globalWriter CustomWriter = &myWriter{}
113+ globalCloser CustomCloser = &myCloser{}
114+ globalRWCloser ReadWriteCloser = &myRWCloser{}
115+ globalFormatter Formatter = &myFormatter{}
116+ globalStringer Stringer = &myStringer{}
117+ globalErr error = &myError{}
118+)
119+ 
120+func useInterface(v interface{}) {}
121+ 
122+func useType(v reflect.Type) {}
123+ 
124+func main() {
125+ useInterface(struct{ A, B int }{1, 2})
126+ useInterface(struct{ X, Y float64 }{1.0, 2.0})
127+ useInterface(globalOuter)
128+ useInterface(globalInner)
129+ useInterface(fmt.Sprintf("test"))
130+ 
131+ useInterface(globalReader)
132+ useInterface(globalWriter)
133+ useInterface(globalCloser)
134+ useInterface(globalRWCloser)
135+ useInterface(globalFormatter)
136+ useInterface(globalStringer)
137+ useInterface(globalErr)
138+ 
139+ useType(reflect.TypeOf(globalReader))
140+ useType(reflect.TypeOf(globalWriter))
141+ useType(reflect.TypeOf(globalCloser))
142+ useType(reflect.TypeOf(globalRWCloser))
143+ useType(reflect.TypeOf(globalFormatter))
144+ useType(reflect.TypeOf(globalStringer))
145+ useType(reflect.TypeOf(globalErr))
146+ 
147+ var i interface{} = globalRWCloser
148+ switch v := i.(type) {
149+ case CustomReader:
150+ if _, err := v.Read(nil); err != nil {
151+ fmt.Println("read error:", err)
152+ }
153+ case CustomWriter:
154+ if _, err := v.Write(nil); err != nil {
155+ fmt.Println("write error:", err)
156+ }
157+ case CustomCloser:
158+ if err := v.Close(); err != nil {
159+ fmt.Println("close error:", err)
160+ }
161+ default:
162+ fmt.Println("unexpected type")
163+ }
164+ 
165+ _ = globalOuter.Nested.ID
166+ _ = globalInner.Name
167+}
@@ -0,0 +1,102 @@
1+package main
2+ 
3+import (
4+ "fmt"
5+ "reflect"
6+)
7+ 
8+type SimpleStruct struct {
9+ A int
10+ B string
11+ C float64
12+}
13+ 
14+type WithArray struct {
15+ Count int
16+ Items [5]int
17+}
18+ 
19+type WithSlice struct {
20+ Name string
21+ Slice []byte
22+}
23+ 
24+type WithMap struct {
25+ ID int
26+ Data map[string]int
27+}
28+ 
29+type SimpleInterface interface {
30+ Do() error
31+}
32+ 
33+type AnotherInterface interface {
34+ Process(string) int
35+}
36+ 
37+type Implementor struct{}
38+ 
39+func (i *Implementor) Do() error { return nil }
40+func (i *Implementor) Process(s string) int { return len(s) }
41+ 
42+type StructWithGaps struct {
43+ A int64
44+ B int8
45+ C int64
46+ D int32
47+ E int8
48+ F int64
49+}
50+ 
51+type CompactStruct struct {
52+ A int64
53+ B int64
54+ C int64
55+}
56+ 
57+func useType(t reflect.Type) {
58+ fmt.Printf("Type: %s, Kind: %s, Size: %d\n", t.Name(), t.Kind(), t.Size())
59+ if t.Kind() == reflect.Struct {
60+ for i := 0; i < t.NumField(); i++ {
61+ f := t.Field(i)
62+ fmt.Printf(" Field[%d]: Name:%s, Type:%s, Offset:%d, Size:%d\n",
63+ i, f.Name, f.Type.String(), f.Offset, f.Type.Size())
64+ }
65+ }
66+}
67+ 
68+func useInterface(v interface{}) {}
69+ 
70+func main() {
71+ var s SimpleStruct
72+ var arr WithArray
73+ var sl WithSlice
74+ var mp WithMap
75+ var si SimpleInterface = &Implementor{}
76+ var ai AnotherInterface = &Implementor{}
77+ var gaps StructWithGaps
78+ var compact CompactStruct
79+ 
80+ useInterface(s)
81+ useInterface(arr)
82+ useInterface(sl)
83+ useInterface(mp)
84+ useInterface(si)
85+ useInterface(ai)
86+ useInterface(gaps)
87+ useInterface(compact)
88+ 
89+ _ = gaps
90+ _ = compact
91+ 
92+ useType(reflect.TypeOf(s))
93+ useType(reflect.TypeOf(arr))
94+ useType(reflect.TypeOf(sl))
95+ useType(reflect.TypeOf(mp))
96+ useType(reflect.TypeOf(si))
97+ useType(reflect.TypeOf(ai))
98+ useType(reflect.TypeOf(gaps))
99+ useType(reflect.TypeOf(compact))
100+ 
101+ fmt.Println("Done")
102+}
@@ -2,6 +2,8 @@
2// Use of this source code is governed by a BSD-style2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.3// license that can be found in the LICENSE file.
4 4 
5+//go:build !bolt
6+ 
5#include "textflag.h"7#include "textflag.h"
6 8 
7// These functions are no-ops, but you can search for their implementations9// These functions are no-ops, but you can search for their implementations
@@ -7,7 +7,7 @@
7// search a particular binary to see if they are present.7// search a particular binary to see if they are present.
8// On other platforms (those using this source file), they don't.8// On other platforms (those using this source file), they don't.
9 9 
10-//go:build !amd6410+//go:build !amd64 || bolt
11 11 
12TEXT ·BoringCrypto(SB),$012TEXT ·BoringCrypto(SB),$0
13 RET13 RET
@@ -108,4 +108,6 @@ const (
108 108 
109const MINFUNC = 16 // minimum size for a function109const MINFUNC = 16 // minimum size for a function
110 110 
111+const PADMINFUNC = 32 // padding the minimum function size to 32 bytes, amd64 has funcAlign=32
112+ 
111const FuncTabBucketSize = 256 * MINFUNC // size of bucket in the pc->func lookup table113const FuncTabBucketSize = 256 * MINFUNC // size of bucket in the pc->func lookup table
@@ -473,6 +473,8 @@ goodm:
473// The frame layout needs to match systemstack473// The frame layout needs to match systemstack
474// so that it can pretend to be systemstack_switch.474// so that it can pretend to be systemstack_switch.
475TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0475TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0
476+ // Align for consistency with offset used in gosave_systemstack_switch
477+ PCALIGN $8
476 UNDEF478 UNDEF
477 // Make sure this function is not leaf,479 // Make sure this function is not leaf,
478 // so the frame is saved.480 // so the frame is saved.
@@ -822,8 +824,9 @@ TEXT ·publicationBarrier<ABIInternal>(SB),NOSPLIT,$0-0
822// Smashes R9.824// Smashes R9.
823TEXT gosave_systemstack_switch<>(SB),NOSPLIT|NOFRAME,$0825TEXT gosave_systemstack_switch<>(SB),NOSPLIT|NOFRAME,$0
824 // Take systemstack_switch PC and add 8 bytes to skip826 // Take systemstack_switch PC and add 8 bytes to skip
825- // the prologue. The final location does not matter827+ // the prologue. Keep 8 bytes offset consistent with
826- // as long as we are between the prologue and the epilogue.828+ // PCALIGN $8 in systemstack_swtich, pointing start of
829+ // UNDEF instruction beyond prologue.
827 MOVQ $runtime·systemstack_switch+8(SB), R9830 MOVQ $runtime·systemstack_switch+8(SB), R9
828 MOVQ R9, (g_sched+gobuf_pc)(R14)831 MOVQ R9, (g_sched+gobuf_pc)(R14)
829 LEAQ 8(SP), R9832 LEAQ 8(SP), R9
@@ -0,0 +1,148 @@
1+#!/bin/bash
2+
3+# Parse args
4+exe=$1
5+exe_path=$(dirname $exe)
6+shift
7+args=$@
8+
9+# Set vars
10+ret=0
11+test_var=""
12+RUN_SUBREAPER=${RUN_SUBREAPER:-""}
13+PERF=${PERF:-perf}
14+LOGSFILE=${LOGSFILE:-/tmp/test.log}
15+DEVNULL=${DEVNULL:-/dev/null}
16+MAX_SLEEP_TIME=${MAX_SLEEP_TIME:-30}
17+
18+BOLT_GOLANG="-golang=1"
19+BOLT_FLAGS="-reorder-blocks=cache+ -reorder-functions=hfsort+ --keep-nops"
20+BOLT_INSTR_FLAGS="-instrument -conservative-instrumentation -instrumentation-file=$exe_path/prof.fdata -instrumentation-sleep-time=$MAX_SLEEP_TIME -instrumentation-no-counters-clear -instrumentation-wait-forks=1"
21+
22+# Extra flags
23+PERF_FLAGS="-b"
24+PERF2BOLT_FLAGS=""
25+if [ "$(uname -p)" == "aarch64" ]; then
26+ # Disable LBR for aarch64
27+ PERF_FLAGS=""
28+ PERF2BOLT_FLAGS="-nl"
29+fi
30+
31+get_test_var() {
32+ local name=$1
33+ local tmp="$name$(basename $exe_path)"
34+ eval "test_var=\${$tmp}"
35+}
36+
37+bad_exit() {
38+ local ret=$1
39+ local msg=$2
40+ get_test_var "filename"
41+ printf "%-10s\t %-80s\t %s\n" "$exe" "$test_var" "$msg" >> $LOGSFILE
42+ sync $LOGSFILE
43+ exit $ret
44+}
45+
46+check_ret() {
47+ local ret=$1
48+ local msg=$2
49+ if [ $ret -ne 0 ]; then
50+ bad_exit "$ret" "$msg"
51+ fi
52+}
53+
54+check_exp_ret() {
55+ local ret=$1
56+ local msg=$2
57+ local log=$3
58+
59+ get_test_var "expect"
60+ if ([ $ret -ne 0 ] && [ "$test_var" != "true" ]) || ([ $ret -eq 0 ] && [ "$test_var" == "true" ]); then
61+ bad_exit "$ret" "$msg"
62+ fi
63+
64+ get_test_var "outputIsSet"
65+ if [ "$test_var" == "true" ]; then
66+ get_test_var "output"
67+ diff $log <(echo -n "$test_var") &> $exe_path/diff.log
68+ check_ret $? "Wrong output for $msg"
69+ fi
70+}
71+
72+check_file() {
73+ local file=$1
74+ if [ ! -f $file ]; then
75+ return -1
76+ fi
77+
78+ fuser -s $file &> $DEVNULL
79+ if [ $? -eq 0 ]; then
80+ return -1
81+ fi
82+
83+ return 0
84+}
85+
86+check_prof_file() {
87+ local file=$1
88+ local max_sleep=$MAX_SLEEP_TIME
89+
90+ check_file $file
91+ local ret=$?
92+ while [ $ret -ne 0 ] && [ $max_sleep -ne 0 ]; do
93+ sleep 1
94+ max_sleep=$((max_sleep - 1))
95+ check_file $file
96+ ret=$?
97+ done
98+
99+ if [ $max_sleep -eq 0 ]; then
100+ bad_exit -1 "Failed to get prof file"
101+ fi
102+
103+ sync $file
104+}
105+
106+# Check original file
107+$exe $args &> $exe_path/orig.log
108+ret=$?
109+check_exp_ret $ret "Original exec run" $exe_path/orig.log
110+case $GO_TEST_MODE in
111+ "run_only")
112+ cat $exe_path/orig.log
113+ ;;
114+
115+ "perf_test")
116+ $PERF record -N -e cycles:u $PERF_FLAGS -F max -o $exe_path/perf.data -- $exe $args &> $exe_path/perf.log
117+ check_ret $? "Perf record"
118+ check_prof_file $exe_path/perf.data
119+ $BOLT_DIR/perf2bolt $PERF2BOLT_FLAGS -p $exe_path/perf.data -o $exe_path/perf.fdata $exe &> $exe_path/perf2bolt.log
120+ check_ret $? "perf2bolt"
121+ $BOLT_DIR/llvm-bolt $exe -o $exe.perf.bolt -data $exe_path/perf.fdata $BOLT_GOLANG $BOLT_FLAGS &> $exe_path/bolt.log
122+ check_ret $? "llvm-bolt"
123+ $exe.perf.bolt $args |& tee $exe_path/bolted.log
124+ ret=${PIPESTATUS[0]}
125+ check_exp_ret $ret "Bolted exec run" $exe_path/bolted.log
126+ ;;
127+
128+ "instr_test")
129+ $BOLT_DIR/llvm-bolt $exe -o $exe.instr.tmp $BOLT_GOLANG $BOLT_INSTR_FLAGS &> $exe_path/bolt_instr.log
130+ check_ret $? "llvm-bolt instr"
131+ $RUN_SUBREAPER $exe.instr.tmp $args &> $exe_path/bolted_tmp.log
132+ check_ret $? "Instr exec run"
133+ check_prof_file $exe_path/prof.fdata
134+ $BOLT_DIR/llvm-bolt $exe -o $exe.instr.bolt -data $exe_path/prof.fdata $BOLT_GOLANG $BOLT_FLAGS &> $exe_path/bolt.log
135+ check_ret $? "llvm-bolt"
136+ $exe.instr.bolt $args |& tee $exe_path/bolted.log
137+ ret=${PIPESTATUS[0]}
138+ check_exp_ret $ret "Bolted exec run" $exe_path/bolted.log
139+ ;;
140+
141+ *)
142+ echo "Bad GO_TEST_MODE"
143+ exit 1
144+ ;;
145+esac
146+
147+sync
148+exit $ret
@@ -0,0 +1,35 @@
1+#!/bin/bash
2+ 
3+rm winbatch.go
4+# Remove originally failing tests
5+rm fixedbugs/issue21576.go
6+rm fixedbugs/issue21317.go
7+rm fixedbugs/issue33275_run.go
8+rm nilptr.go
9+rm nosplit.go
10+rm linkx_run.go
11+rm heapsampling.go # Unstable test
12+# Remove tests with meta-information check
13+# inline_caller.go is fixed in patch
14+rm fixedbugs/bug347.go
15+rm fixedbugs/bug348.go
16+rm fixedbugs/issue14646.go
17+rm fixedbugs/issue18149.go
18+rm fixedbugs/issue22083.go
19+rm fixedbugs/issue22662.go
20+rm fixedbugs/issue27201.go
21+rm fixedbugs/issue29504.go
22+rm fixedbugs/issue34123.go
23+rm fixedbugs/issue4562.go
24+rm fixedbugs/issue5856.go
25+rm fixedbugs/issue7690.go
26+# 1.16.5
27+rm fixedbugs/issue36437.go
28+# 1.17
29+rm const7.go
30+rm fixedbugs/issue20014.go
31+rm fixedbugs/bug513.go
32+rm fixedbugs/issue20780b.go
33+rm fixedbugs/issue29329.go
34+rm fixedbugs/issue36516.go
35+rm fixedbugs/issue46234.go
@@ -1,5 +1,8 @@
1// run -gcflags -l=41// run -gcflags -l=4
2 2 
3+//go:build !goboltci
4+ 
5+//
3// Copyright 2017 The Go Authors. All rights reserved.6// Copyright 2017 The Go Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style7// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.8// license that can be found in the LICENSE file.
@@ -54,11 +57,11 @@ type wantFrame struct {
54 57 
55// -1 means don't care58// -1 means don't care
56var expected = []wantFrame{59var expected = []wantFrame{
57- 0: {"main.h", 36},60+ 0: {"main.h", 39},
58- 1: {"main.g", 31},61+ 1: {"main.g", 34},
59- 2: {"main.f", 27},62+ 2: {"main.f", 30},
60- 3: {"main.testCaller", 42},63+ 3: {"main.testCaller", 45},
61- 4: {"main.main", 68},64+ 4: {"main.main", 71},
62 5: {"runtime.main", -1},65 5: {"runtime.main", -1},
63 6: {"runtime.goexit", -1},66 6: {"runtime.goexit", -1},
64}67}
@@ -0,0 +1,77 @@
1+// run -gcflags -l=4
2+ 
3+// Copyright 2017 The Go Authors. All rights reserved.
4+// Use of this source code is governed by a BSD-style
5+// license that can be found in the LICENSE file.
6+ 
7+package main
8+ 
9+import (
10+ "fmt"
11+ "runtime"
12+)
13+ 
14+type frame struct {
15+ pc uintptr
16+ file string
17+ line int
18+ ok bool
19+}
20+ 
21+var (
22+ skip int
23+ globalFrame frame
24+)
25+ 
26+func f() {
27+ g() // line 27
28+}
29+ 
30+func g() {
31+ h() // line 31
32+}
33+ 
34+func h() {
35+ x := &globalFrame
36+ x.pc, x.file, x.line, x.ok = runtime.Caller(skip) // line 36
37+}
38+ 
39+//go:noinline
40+func testCaller(skp int) frame {
41+ skip = skp
42+ f() // line 42
43+ frame := globalFrame
44+ if !frame.ok {
45+ panic(fmt.Sprintf("skip=%d runtime.Caller failed", skp))
46+ }
47+ return frame
48+}
49+ 
50+type wantFrame struct {
51+ funcName string
52+ line int
53+}
54+ 
55+// -1 means don't care
56+var expected = []wantFrame{
57+ 0: {"main.h", 36},
58+ 1: {"main.g", 31},
59+ 2: {"main.f", 27},
60+ 3: {"main.testCaller", 42},
61+ 4: {"main.main", 68},
62+ 5: {"runtime.main", -1},
63+ 6: {"runtime.goexit", -1},
64+}
65+ 
66+func main() {
67+ for i := 0; i <= 6; i++ {
68+ frame := testCaller(i) // line 68
69+ fn := runtime.FuncForPC(frame.pc)
70+ //if expected[i].line >= 0 && frame.line != expected[i].line {
71+ // panic(fmt.Sprintf("skip=%d expected line %d, got line %d", i, expected[i].line, frame.line))
72+ //}
73+ if fn.Name() != expected[i].funcName {
74+ panic(fmt.Sprintf("skip=%d expected function %s, got %s", i, expected[i].funcName, fn.Name()))
75+ }
76+ }
77+}
@@ -0,0 +1,33 @@
1+#include <errno.h>
2+#include <unistd.h>
3+#include <sys/prctl.h>
4+#include <sys/types.h>
5+#include <sys/wait.h>
6+
7+int main(int argc, char *argv[]) {
8+ int ret = -1;
9+ pid_t pid, forked_pid;
10+ if (argc == 1)
11+ return -1;
12+
13+ prctl(PR_SET_CHILD_SUBREAPER, 1);
14+
15+ switch((forked_pid = fork())) {
16+ case 0:
17+ return execv(argv[1], &argv[1]);
18+ case -1:
19+ return -1;
20+ default:
21+ break;
22+ }
23+
24+ do {
25+ int wstatus;
26+ pid = waitpid(-1, &wstatus, 0);
27+ if (pid == forked_pid)
28+ ret = WIFEXITED(wstatus) ? WEXITSTATUS(wstatus) : 1;
29+ } while (pid >= 0 || errno == EINTR);
30+
31+ return ret;
32+}
33+ 
Atest/t.sh+35-0
@@ -0,0 +1,35 @@
1+#!/bin/bash
2+export TMPDIR=${PWD}/tmp
3+export TEST_GCFLAGS='-d=go119usejumptables=0'
4+export TEST_LDFLAGS='-linkmode=external -extld=gcc -extldflags "-no-pie -Wl,--compress-debug-sections=none -Wl,-q"'
5+export TEST_BUILDFLAGS="-buildmode=pie -tags=goboltci,bolt"
6+if [[ $(uname -i) == aarch64 ]]; then
7+ export TEST_BUILDFLAGS="${TEST_BUILDFLAGS} -mappingsymbol"
8+fi
9+export GO_TEST_SCRIPT=${PWD}/bolt.sh
10+
11+export RUN_SUBREAPER=${PWD}/run_subreaper
12+
13+export GO_TEST_MODE="instr_test"
14+export LOGSFILE=${PWD}/logs.log
15+
16+rm -rf $LOGSFILE
17+rm -rf ${TMPDIR}
18+mkdir -p ${TMPDIR}
19+ 
20+if [[ ! -d ${BOLT_DIR} ]]; then
21+ echo "ERROR: BOLT_DIR env var should point to existing directory with llvm-bolt. Exiting."
22+ exit 1
23+fi
24+ 
25+if [[ ! -x ${PWD}/run_subreaper ]]; then
26+ gcc ${PWD}/run_subreaper.c -o ${PWD}/run_subreaper
27+fi
28+ 
29+${PWD}/goboltrm.bash
30+ 
31+set -x
32+ 
33+${TASKSET} ../bin/go clean -cache
34+${TASKSET} ../bin/go test -a -tags=goboltci,bolt cmd/internal/testdir -k -test.timeout=30m -test.v
35+