已合并
Go Compiler changes for llvm-bolt support #95
Vasily Leonenko创建于 8月25日
Go Compiler changes for llvm-bolt support #95
已合并
共 47 个文件变更+9215-69
| @@ -6,6 +6,7 @@ | |||
| 6 | package flags | 6 | package flags |
| 7 | 7 | ||
| 8 | import ( | 8 | import ( |
| 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() // -V | 52 | 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.PrintOut | 49 | ctxt.Debugasm = flags.PrintOut |
| 44 | ctxt.Debugvlog = flags.DebugV | 50 | 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 | ||
| 7 | import ( | 7 | import ( |
| 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() // -V | 212 | 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. |
| 297 | func (s *exprSwitch) tryJumpTable(cc []exprClause, out *ir.Nodes) bool { | 297 | func (s *exprSwitch) tryJumpTable(cc []exprClause, out *ir.Nodes) bool { |
| 298 | + var go119UseJumpTables = true | ||
| 298 | const minCases = 8 // have at least minCases cases in the switch | 299 | const minCases = 8 // have at least minCases cases in the switch |
| 299 | const minDensity = 4 // use at least 1 out of every minDensity entries | 300 | 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 false | 306 | 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. |
| 776 | func (s *typeSwitch) tryJumpTable(cc []typeClause, out *ir.Nodes) bool { | 780 | func (s *typeSwitch) tryJumpTable(cc []typeClause, out *ir.Nodes) bool { |
| 781 | + var go119UseJumpTables = true | ||
| 777 | const minCases = 5 // have at least minCases cases in the switch | 782 | 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 false | 787 | return false |
| 780 | } | 788 | } |
| 781 | if len(cc) < minCases { | 789 | if len(cc) < minCases { |
| @@ -110,6 +110,8 @@ var ( | |||
| 110 | GoPathError string | 110 | GoPathError string |
| 111 | GOPATHChanged bool | 111 | GOPATHChanged bool |
| 112 | CGOChanged bool | 112 | CGOChanged bool |
| 113 | + | ||
| 114 | + MappingSymbol bool // -mappingsymbol flag | ||
| 113 | ) | 115 | ) |
| 114 | 116 | ||
| 115 | func defaultContext() build.Context { | 117 | func 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 | ||
| 341 | type version struct { | 346 | type 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. |
| 38 | type Disasm struct { | 39 | type Disasm struct { |
| 39 | - syms []objfile.Sym // symbols in file, sorted by address | 40 | + syms []objfile.Sym // symbols in file, sorted by address |
| 40 | - pcln objfile.Liner // pcln table | 41 | + 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 text | 43 | + textStart uint64 // start PC of text |
| 43 | - textEnd uint64 // end PC of text | 44 | + textEnd uint64 // end PC of text |
| 44 | - goarch string // GOARCH string | 45 | + goarch string // GOARCH string |
| 45 | - disasm disasmFunc // disassembler function for goarch | 46 | + disasm disasmFunc // disassembler function for goarch |
| 46 | - byteOrder binary.ByteOrder // byte order for goarch | 47 | + 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 "", 0 | 113 | 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], nil | 203 | 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.textStart | 229 | 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 = true | 256 | 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 the | 15 | // CUFileIndex is used to index the filenames that are stored in the |
| 14 | // per-package/per-CU FileList. | 16 | // per-package/per-CU FileList. |
| 15 | type CUFileIndex uint32 | 17 | type CUFileIndex uint32 |
| @@ -24,6 +26,7 @@ type FuncInfo struct { | |||
| 24 | StartLine int32 | 26 | StartLine int32 |
| 25 | File []CUFileIndex | 27 | File []CUFileIndex |
| 26 | InlTree []InlTreeNode | 28 | InlTree []InlTreeNode |
| 29 | + PoolsInfo []PoolInfo | ||
| 27 | } | 30 | } |
| 28 | 31 | ||
| 29 | func (a *FuncInfo) Write(w *bytes.Buffer) { | 32 | func (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 and | 66 | // FuncInfoLengths is a cache containing a roadmap of offsets and |
| @@ -64,6 +73,8 @@ type FuncInfoLengths struct { | |||
| 64 | FileOff uint32 | 73 | FileOff uint32 |
| 65 | NumInlTree uint32 | 74 | NumInlTree uint32 |
| 66 | InlTreeOff uint32 | 75 | InlTreeOff uint32 |
| 76 | + NumPoolInfo uint32 | ||
| 77 | + PoolInfoOff uint32 | ||
| 67 | Initialized bool | 78 | 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 + 4 | 92 | 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 = true | 100 | result.Initialized = true |
| 84 | 101 | ||
| 85 | return result | 102 | 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 | + | ||
| 88 | func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } | 147 | func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } |
| 89 | 148 | ||
| 90 | func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) } | 149 | func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) } |
| @@ -31,6 +31,7 @@ | |||
| 31 | package arm64 | 31 | package arm64 |
| 32 | 32 | ||
| 33 | import ( | 33 | import ( |
| 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 the | 1464 | // 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.blitrl | 1473 | q.Link = c.blitrl |
| 1466 | q.Pos = p.Pos | 1474 | q.Pos = p.Pos |
| 1467 | c.blitrl = q | 1475 | 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 address | 8534 | + srcReg := p.From.Reg // Rn - register with base address |
| 8524 | - rangeReg := p.Reg // Rm - register with prefetch metadata | 8535 | + rangeReg := p.Reg // Rm - register with prefetch metadata |
| 8525 | var operation uint32 | 8536 | var operation uint32 |
| 8526 | var ok bool | 8537 | var ok bool |
| 8527 | 8538 | ||
| @@ -8552,7 +8563,7 @@ func (c *ctxt7) asmout(p *obj.Prog, out []uint32) (count int) { | |||
| 8552 | rt := operation & 7 | 8563 | 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 | rt | 8566 | + o1 |= (uint32(rangeReg&31) << 16) | (uint32(srcReg&31) << 5) | option2 | option0 | s | rt |
| 8556 | } | 8567 | } |
| 8557 | out[0] = o1 | 8568 | out[0] = o1 |
| 8558 | out[1] = o2 | 8569 | out[1] = o2 |
| @@ -511,6 +511,8 @@ type FuncInfo struct { | |||
| 511 | WasmExport *WasmExport | 511 | WasmExport *WasmExport |
| 512 | 512 | ||
| 513 | sehUnwindInfoSym *LSym | 513 | sehUnwindInfoSym *LSym |
| 514 | + | ||
| 515 | + Pool []*Prog | ||
| 514 | } | 516 | } |
| 515 | 517 | ||
| 516 | // JumpTable represents a table used for implementing multi-way | 518 | // 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.Pcln | 789 | pc := &fn.Pcln |
| 777 | i := 0 | 790 | 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 | ||
| 17 | type elfFile struct { | 18 | type elfFile struct { |
| 18 | elf *elf.File | 19 | elf *elf.File |
| 20 | + r io.ReaderAt | ||
| 19 | } | 21 | } |
| 20 | 22 | ||
| 21 | func openElf(r io.ReaderAt) (rawFile, error) { | 23 | func 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, err | 26 | return nil, err |
| 25 | } | 27 | } |
| 26 | - return &elfFile{f}, nil | 28 | + return &elfFile{f, r}, nil |
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | func (f *elfFile) symbols() ([]Sym, error) { | 31 | func (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, err | 34 | + elfDSyms, err2 := f.elf.DynamicSymbols() |
| 35 | + if err2 != nil { | ||
| 36 | + return nil, err2 | ||
| 37 | + } | ||
| 38 | + elfSyms = elfDSyms | ||
| 33 | } | 39 | } |
| 34 | 40 | ||
| 35 | var syms []Sym | 41 | var syms []Sym |
| @@ -64,6 +70,19 @@ func (f *elfFile) symbols() ([]Sym, error) { | |||
| 64 | return syms, nil | 70 | 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 | + | ||
| 67 | func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { | 86 | func (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.Addr | 88 | textStart = sect.Addr |
| @@ -100,6 +119,33 @@ func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { | |||
| 100 | return textStart, symtab, pclntab, nil | 119 | 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 | + | ||
| 103 | func (f *elfFile) text() (textStart uint64, text []byte, err error) { | 149 | func (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) { | |||
| 158 | func (f *elfFile) symbolData(start, end string) []byte { | 204 | func (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 nil | 207 | + elfDSyms, err2 := f.elf.DynamicSymbols() |
| 208 | + if err2 != nil { | ||
| 209 | + return nil | ||
| 210 | + } | ||
| 211 | + elfSyms = elfDSyms | ||
| 162 | } | 212 | } |
| 163 | var addr, eaddr uint64 | 213 | 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 nil | 225 | return nil |
| 176 | } | 226 | } |
| 177 | size := eaddr - addr | 227 | 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 nil | 232 | + elfSyms, err := f.elf.Symbols() |
| 183 | - } | 233 | + if err != nil { |
| 184 | - return data | 234 | + 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 nil | 276 | 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.Arch | 26 | 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 | + | ||
| 29 | func openGoFile(f *os.File) (*File, error) { | 33 | func 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, nil | 79 | 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 | + | ||
| 82 | func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { | 100 | func (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.Addr | 102 | textStart = sect.Addr |
| @@ -96,6 +114,22 @@ func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) | |||
| 96 | return textStart, symtab, pclntab, nil | 114 | 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 | + | ||
| 99 | func (f *machoFile) text() (textStart uint64, text []byte, err error) { | 133 | func (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 | ||
| 19 | type rawFile interface { | 19 | type 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() string | 28 | 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 | + | ||
| 122 | func (e *Entry) Name() string { | 131 | func (e *Entry) Name() string { |
| 123 | return e.name | 132 | return e.name |
| 124 | } | 133 | } |
| @@ -90,6 +90,34 @@ func (f *peFile) symbols() ([]Sym, error) { | |||
| 90 | return syms, nil | 90 | 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 | + | ||
| 93 | func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { | 121 | func (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, nil | 145 | 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 | + | ||
| 120 | func (f *peFile) text() (textStart uint64, text []byte, err error) { | 164 | func (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.File | 29 | 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 | + | ||
| 32 | func openPlan9(r io.ReaderAt) (rawFile, error) { | 48 | func 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, nil | 107 | 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 | + | ||
| 94 | func (f *plan9File) text() (textStart uint64, text []byte, err error) { | 126 | func (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.File | 18 | 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 | + | ||
| 21 | func openXcoff(r io.ReaderAt) (rawFile, error) { | 33 | func 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, nil | 110 | 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 | + | ||
| 101 | func (f *xcoffFile) text() (textStart uint64, text []byte, err error) { | 129 | func (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-style | 2 | // 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. |
| 6 | package testdir_test | 8 | package 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 | + | ||
| 527 | func TestIssue51939(t *testing.T) { | 676 | func 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 | |||
| 34 | import ( | 34 | import ( |
| 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 current | 2686 | // 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 the | 2792 | // machinery in the external linker; see #58425 for more on the |
| 2782 | // history here. | 2793 | // history here. |
| 2783 | func splitTextSections(ctxt *Link) bool { | 2794 | func 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.js | 2807 | // On Wasm, we reserve 4096 bytes for zero page, then 8192 bytes for wasm_exec.js |
| @@ -5,6 +5,7 @@ | |||
| 5 | package ld | 5 | package ld |
| 6 | 6 | ||
| 7 | import ( | 7 | import ( |
| 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 ternaryFlag | 119 | flagW ternaryFlag |
| 119 | FlagW = new(bool) // the -w flag, computed in main from flagW | 120 | 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() // -V | 205 | 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 @@ | |||
| 31 | package ld | 31 | package ld |
| 32 | 32 | ||
| 33 | import ( | 33 | import ( |
| 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 | ||
| 79 | func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) { | 81 | func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) { |
| 80 | ldr := ctxt.loader | 82 | 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 | + | ||
| 183 | func putelfsectionsym(ctxt *Link, out *OutBuf, s loader.Sym, shndx elf.SectionIndex) { | 275 | func 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 | + | ||
| 2130 | func (l *Loader) FuncInfo(i Sym) FuncInfo { | 2150 | func (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 { |
| @@ -7,8 +7,10 @@ package main | |||
| 7 | import ( | 7 | import ( |
| 8 | "bufio" | 8 | "bufio" |
| 9 | "bytes" | 9 | "bytes" |
| 10 | + "debug/elf" | ||
| 10 | "debug/macho" | 11 | "debug/macho" |
| 11 | "errors" | 12 | "errors" |
| 13 | + "fmt" | ||
| 12 | "internal/platform" | 14 | "internal/platform" |
| 13 | "internal/testenv" | 15 | "internal/testenv" |
| 14 | "os" | 16 | "os" |
| @@ -16,6 +18,7 @@ import ( | |||
| 16 | "path/filepath" | 18 | "path/filepath" |
| 17 | "regexp" | 19 | "regexp" |
| 18 | "runtime" | 20 | "runtime" |
| 21 | + "sort" | ||
| 19 | "strconv" | 22 | "strconv" |
| 20 | "strings" | 23 | "strings" |
| 21 | "testing" | 24 | "testing" |
| @@ -1092,6 +1095,20 @@ func TestContentAddressableSymbols(t *testing.T) { | |||
| 1092 | } | 1095 | } |
| 1093 | } | 1096 | } |
| 1094 | 1097 | ||
| 1098 | +func TestCoreGoPadFunc(t *testing.T) { | ||
| 1099 | + // Test that the linker handles padfunc correctly. | ||
| 1100 | + testenv.MustHaveGoBuild(t) | ||
| 1101 | + | ||
| 1102 | + t.Parallel() | ||
| 1103 | + | ||
| 1104 | + src := filepath.Join("testdata", "testCoreGo", "padfunc.go") | ||
| 1105 | + cmd := testenv.Command(t, testenv.GoToolPath(t), "run", "-ldflags=-padfunc", src) | ||
| 1106 | + out, err := cmd.CombinedOutput() | ||
| 1107 | + if err != nil { | ||
| 1108 | + t.Errorf("command %s failed: %v\n%s", cmd, err, out) | ||
| 1109 | + } | ||
| 1110 | +} | ||
| 1111 | + | ||
| 1095 | func TestReadOnly(t *testing.T) { | 1112 | func TestReadOnly(t *testing.T) { |
| 1096 | // Test that read-only data is indeed read-only. | 1113 | // Test that read-only data is indeed read-only. |
| 1097 | testenv.MustHaveGoBuild(t) | 1114 | testenv.MustHaveGoBuild(t) |
| @@ -1676,3 +1693,143 @@ func TestLinknameBSS(t *testing.T) { | |||
| 1676 | t.Errorf("executable failed to run: %v\n%s", err, out) | 1693 | t.Errorf("executable failed to run: %v\n%s", err, out) |
| 1677 | } | 1694 | } |
| 1678 | } | 1695 | } |
| 1696 | + | ||
| 1697 | +const testMappingSymbolSrcHead = ` | ||
| 1698 | +package main | ||
| 1699 | + | ||
| 1700 | +import "unsafe" | ||
| 1701 | + | ||
| 1702 | +type A struct { | ||
| 1703 | + c [16384]uint8 | ||
| 1704 | +} | ||
| 1705 | + | ||
| 1706 | +//go:noinline | ||
| 1707 | +func foo(i int) A { | ||
| 1708 | + var a [65536]A | ||
| 1709 | + b := uint16(0) | ||
| 1710 | +` | ||
| 1711 | + | ||
| 1712 | +const testMappingSymbolSrcTail = ` | ||
| 1713 | + return a[i] | ||
| 1714 | +} | ||
| 1715 | + | ||
| 1716 | +func main() { | ||
| 1717 | + _ = foo(1023) | ||
| 1718 | + return | ||
| 1719 | +} | ||
| 1720 | +` | ||
| 1721 | + | ||
| 1722 | +func TestMappingSymbol(t *testing.T) { | ||
| 1723 | + if runtime.GOARCH != "arm64" { | ||
| 1724 | + t.Skip("skipping arm64 only test") | ||
| 1725 | + } | ||
| 1726 | + | ||
| 1727 | + testenv.MustHaveGoBuild(t) | ||
| 1728 | + | ||
| 1729 | + t.Parallel() | ||
| 1730 | + | ||
| 1731 | + var sb strings.Builder | ||
| 1732 | + | ||
| 1733 | + tmpdir := t.TempDir() | ||
| 1734 | + | ||
| 1735 | + src := filepath.Join(tmpdir, "main.go") | ||
| 1736 | + sb.WriteString(testMappingSymbolSrcHead) | ||
| 1737 | + | ||
| 1738 | + for i := 0; i < 65535; i++ { | ||
| 1739 | + str := fmt.Sprintf("*(*uint16)(unsafe.Pointer(&a[%d].c[1])) = b\nb++\n", i) | ||
| 1740 | + sb.WriteString(str) | ||
| 1741 | + } | ||
| 1742 | + sb.WriteString(testMappingSymbolSrcTail) | ||
| 1743 | + err := os.WriteFile(src, []byte(sb.String()), 0666) | ||
| 1744 | + if err != nil { | ||
| 1745 | + t.Fatal(err) | ||
| 1746 | + } | ||
| 1747 | + exe := filepath.Join(tmpdir, "main.exe") | ||
| 1748 | + objfile := filepath.Join(tmpdir, "go.o") | ||
| 1749 | + | ||
| 1750 | + // The debug text size limit is larger than main.foo (about 1MB), but | ||
| 1751 | + // smaller than the runtime text plus main.foo, so the text is split | ||
| 1752 | + // right before main.foo and main.foo starts the second text section. | ||
| 1753 | + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode=default", "-mappingsymbol", "-o", exe, | ||
| 1754 | + "-ldflags=-linkmode=external -debugtextsize=0x140000 -tmpdir="+tmpdir, src) | ||
| 1755 | + out, err := cmd.CombinedOutput() | ||
| 1756 | + if err != nil { | ||
| 1757 | + t.Fatalf("build failed: %v\n%s", err, out) | ||
| 1758 | + } | ||
| 1759 | + | ||
| 1760 | + // The object file emitted by the Go linker is inspected rather than the | ||
| 1761 | + // final executable because the external linker merges the split .text | ||
| 1762 | + // sections into a single output section. | ||
| 1763 | + checkTextSplitMappingSymbols(t, objfile) | ||
| 1764 | + checkMappingSymbolPositions(t, objfile) | ||
| 1765 | + | ||
| 1766 | + cmd = testenv.Command(t, exe) | ||
| 1767 | + out, err = cmd.CombinedOutput() | ||
| 1768 | + if err != nil { | ||
| 1769 | + t.Errorf("executable failed to run: %v\n%s", err, out) | ||
| 1770 | + } | ||
| 1771 | +} | ||
| 1772 | + | ||
| 1773 | +// checkMappingSymbolPositions checks that the $x and $d mapping symbols of | ||
| 1774 | +// main.foo are at their expected positions: an $x at the start of the | ||
| 1775 | +// function, then a $d at every literal pool inside it and an $x where the | ||
| 1776 | +// code resumes after the pool, strictly alternating in address order. | ||
| 1777 | +func checkMappingSymbolPositions(t *testing.T, objfile string) { | ||
| 1778 | + f, err := elf.Open(objfile) | ||
| 1779 | + if err != nil { | ||
| 1780 | + t.Fatal(err) | ||
| 1781 | + } | ||
| 1782 | + defer f.Close() | ||
| 1783 | + | ||
| 1784 | + symbols, err := f.Symbols() | ||
| 1785 | + if err != nil { | ||
| 1786 | + t.Fatal(err) | ||
| 1787 | + } | ||
| 1788 | + | ||
| 1789 | + var foo *elf.Symbol | ||
| 1790 | + for i := range symbols { | ||
| 1791 | + if symbols[i].Name == "main.foo" { | ||
| 1792 | + foo = &symbols[i] | ||
| 1793 | + break | ||
| 1794 | + } | ||
| 1795 | + } | ||
| 1796 | + if foo == nil { | ||
| 1797 | + t.Fatal("main.foo symbol not found") | ||
| 1798 | + } | ||
| 1799 | + | ||
| 1800 | + // Collect the mapping symbols inside main.foo, in address order. | ||
| 1801 | + var syms []elf.Symbol | ||
| 1802 | + for _, sym := range symbols { | ||
| 1803 | + if sym.Section != foo.Section || (sym.Name != "$x" && sym.Name != "$d") { | ||
| 1804 | + continue | ||
| 1805 | + } | ||
| 1806 | + if sym.Value < foo.Value || sym.Value >= foo.Value+foo.Size { | ||
| 1807 | + continue | ||
| 1808 | + } | ||
| 1809 | + if elf.ST_TYPE(sym.Info) != elf.STT_NOTYPE || elf.ST_BIND(sym.Info) != elf.STB_LOCAL { | ||
| 1810 | + t.Errorf("mapping symbol %s at %#x has incorrect info %v", sym.Name, sym.Value, sym.Info) | ||
| 1811 | + continue | ||
| 1812 | + } | ||
| 1813 | + syms = append(syms, sym) | ||
| 1814 | + } | ||
| 1815 | + sort.Slice(syms, func(i, j int) bool { return syms[i].Value < syms[j].Value }) | ||
| 1816 | + | ||
| 1817 | + if len(syms) < 3 { | ||
| 1818 | + t.Fatalf("expected an $x plus at least one $d/$x pair in main.foo, got %d mapping symbols", len(syms)) | ||
| 1819 | + } | ||
| 1820 | + if syms[0].Value != foo.Value { | ||
| 1821 | + t.Errorf("first mapping symbol of main.foo is at %#x, want %#x", syms[0].Value, foo.Value) | ||
| 1822 | + } | ||
| 1823 | + for i, sym := range syms { | ||
| 1824 | + want := "$d" | ||
| 1825 | + if i%2 == 0 { | ||
| 1826 | + want = "$x" | ||
| 1827 | + } | ||
| 1828 | + if sym.Name != want { | ||
| 1829 | + t.Errorf("mapping symbol %d of main.foo at %#x is %s, want %s", i, sym.Value, sym.Name, want) | ||
| 1830 | + } | ||
| 1831 | + } | ||
| 1832 | + if syms[len(syms)-1].Name != "$x" { | ||
| 1833 | + t.Errorf("last mapping symbol of main.foo is %s, want $x", syms[len(syms)-1].Name) | ||
| 1834 | + } | ||
| 1835 | +} | ||
| @@ -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 | |||
| 34 | import ( | 34 | import ( |
| 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)") | ||
| 48 | var printCode = flag.Bool("S", false, "print Go code alongside assembly") | 61 | var printCode = flag.Bool("S", false, "print Go code alongside assembly") |
| 49 | var symregexp = flag.String("s", "", "only dump symbols matching this regexp") | 62 | var symregexp = flag.String("s", "", "only dump symbols matching this regexp") |
| 50 | var gnuAsm = flag.Bool("gnu", false, "print GNU assembly next to Go assembly (where supported)") | 63 | var gnuAsm = flag.Bool("gnu", false, "print GNU assembly next to Go assembly (where supported)") |
| 51 | var symRE *regexp.Regexp | 64 | var symRE *regexp.Regexp |
| 52 | 65 | ||
| 53 | func usage() { | 66 | func 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 | + | ||
| 59 | func main() { | 131 | func 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 object | 271 | + 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-style | 2 | // 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 implementations | 9 | // 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 !amd64 | 10 | +//go:build !amd64 || bolt |
| 11 | 11 | ||
| 12 | TEXT ·BoringCrypto(SB),$0 | 12 | TEXT ·BoringCrypto(SB),$0 |
| 13 | RET | 13 | RET |
| @@ -108,4 +108,6 @@ const ( | |||
| 108 | 108 | ||
| 109 | const MINFUNC = 16 // minimum size for a function | 109 | const 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 | + | ||
| 111 | const FuncTabBucketSize = 256 * MINFUNC // size of bucket in the pc->func lookup table | 113 | const FuncTabBucketSize = 256 * MINFUNC // size of bucket in the pc->func lookup table |
| @@ -473,6 +473,8 @@ goodm: | |||
| 473 | // The frame layout needs to match systemstack | 473 | // 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. |
| 475 | TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0 | 475 | TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0 |
| 476 | + // Align for consistency with offset used in gosave_systemstack_switch | ||
| 477 | + PCALIGN $8 | ||
| 476 | UNDEF | 478 | 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. |
| 823 | TEXT gosave_systemstack_switch<>(SB),NOSPLIT|NOFRAME,$0 | 825 | TEXT gosave_systemstack_switch<>(SB),NOSPLIT|NOFRAME,$0 |
| 824 | // Take systemstack_switch PC and add 8 bytes to skip | 826 | // Take systemstack_switch PC and add 8 bytes to skip |
| 825 | - // the prologue. The final location does not matter | 827 | + // 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), R9 | 830 | 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), R9 | 832 | 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=4 | 1 | // 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-style | 7 | // 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 care | 58 | // -1 means don't care |
| 56 | var expected = []wantFrame{ | 59 | var 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 | + | ||
| @@ -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 | + | ||