summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/goobj
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/internal/goobj')
-rw-r--r--src/cmd/internal/goobj/builtin.go47
-rw-r--r--src/cmd/internal/goobj/builtinlist.go253
-rw-r--r--src/cmd/internal/goobj/funcinfo.go145
-rw-r--r--src/cmd/internal/goobj/mkbuiltin.go161
-rw-r--r--src/cmd/internal/goobj/objfile.go876
-rw-r--r--src/cmd/internal/goobj/objfile_test.go133
6 files changed, 1615 insertions, 0 deletions
diff --git a/src/cmd/internal/goobj/builtin.go b/src/cmd/internal/goobj/builtin.go
new file mode 100644
index 0000000..aa665fd
--- /dev/null
+++ b/src/cmd/internal/goobj/builtin.go
@@ -0,0 +1,47 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package goobj
+
+import "internal/buildcfg"
+
+// Builtin (compiler-generated) function references appear
+// frequently. We assign special indices for them, so they
+// don't need to be referenced by name.
+
+// NBuiltin returns the number of listed builtin
+// symbols.
+func NBuiltin() int {
+ return len(builtins)
+}
+
+// BuiltinName returns the name and ABI of the i-th
+// builtin symbol.
+func BuiltinName(i int) (string, int) {
+ return builtins[i].name, builtins[i].abi
+}
+
+// BuiltinIdx returns the index of the builtin with the
+// given name and abi, or -1 if it is not a builtin.
+func BuiltinIdx(name string, abi int) int {
+ i, ok := builtinMap[name]
+ if !ok {
+ return -1
+ }
+ if buildcfg.Experiment.RegabiWrappers && builtins[i].abi != abi {
+ return -1
+ }
+ return i
+}
+
+//go:generate go run mkbuiltin.go
+
+var builtinMap map[string]int
+
+func init() {
+ builtinMap = make(map[string]int, len(builtins))
+ for i, b := range builtins {
+ builtinMap[b.name] = i
+ }
+}
diff --git a/src/cmd/internal/goobj/builtinlist.go b/src/cmd/internal/goobj/builtinlist.go
new file mode 100644
index 0000000..ae2e6cc
--- /dev/null
+++ b/src/cmd/internal/goobj/builtinlist.go
@@ -0,0 +1,253 @@
+// Code generated by mkbuiltin.go. DO NOT EDIT.
+
+package goobj
+
+var builtins = [...]struct {
+ name string
+ abi int
+}{
+ {"runtime.newobject", 1},
+ {"runtime.mallocgc", 1},
+ {"runtime.panicdivide", 1},
+ {"runtime.panicshift", 1},
+ {"runtime.panicmakeslicelen", 1},
+ {"runtime.panicmakeslicecap", 1},
+ {"runtime.throwinit", 1},
+ {"runtime.panicwrap", 1},
+ {"runtime.gopanic", 1},
+ {"runtime.gorecover", 1},
+ {"runtime.goschedguarded", 1},
+ {"runtime.goPanicIndex", 1},
+ {"runtime.goPanicIndexU", 1},
+ {"runtime.goPanicSliceAlen", 1},
+ {"runtime.goPanicSliceAlenU", 1},
+ {"runtime.goPanicSliceAcap", 1},
+ {"runtime.goPanicSliceAcapU", 1},
+ {"runtime.goPanicSliceB", 1},
+ {"runtime.goPanicSliceBU", 1},
+ {"runtime.goPanicSlice3Alen", 1},
+ {"runtime.goPanicSlice3AlenU", 1},
+ {"runtime.goPanicSlice3Acap", 1},
+ {"runtime.goPanicSlice3AcapU", 1},
+ {"runtime.goPanicSlice3B", 1},
+ {"runtime.goPanicSlice3BU", 1},
+ {"runtime.goPanicSlice3C", 1},
+ {"runtime.goPanicSlice3CU", 1},
+ {"runtime.goPanicSliceConvert", 1},
+ {"runtime.printbool", 1},
+ {"runtime.printfloat", 1},
+ {"runtime.printint", 1},
+ {"runtime.printhex", 1},
+ {"runtime.printuint", 1},
+ {"runtime.printcomplex", 1},
+ {"runtime.printstring", 1},
+ {"runtime.printpointer", 1},
+ {"runtime.printuintptr", 1},
+ {"runtime.printiface", 1},
+ {"runtime.printeface", 1},
+ {"runtime.printslice", 1},
+ {"runtime.printnl", 1},
+ {"runtime.printsp", 1},
+ {"runtime.printlock", 1},
+ {"runtime.printunlock", 1},
+ {"runtime.concatstring2", 1},
+ {"runtime.concatstring3", 1},
+ {"runtime.concatstring4", 1},
+ {"runtime.concatstring5", 1},
+ {"runtime.concatstrings", 1},
+ {"runtime.cmpstring", 1},
+ {"runtime.intstring", 1},
+ {"runtime.slicebytetostring", 1},
+ {"runtime.slicebytetostringtmp", 1},
+ {"runtime.slicerunetostring", 1},
+ {"runtime.stringtoslicebyte", 1},
+ {"runtime.stringtoslicerune", 1},
+ {"runtime.slicecopy", 1},
+ {"runtime.decoderune", 1},
+ {"runtime.countrunes", 1},
+ {"runtime.convI2I", 1},
+ {"runtime.convT16", 1},
+ {"runtime.convT32", 1},
+ {"runtime.convT64", 1},
+ {"runtime.convTstring", 1},
+ {"runtime.convTslice", 1},
+ {"runtime.convT2E", 1},
+ {"runtime.convT2Enoptr", 1},
+ {"runtime.convT2I", 1},
+ {"runtime.convT2Inoptr", 1},
+ {"runtime.assertE2I", 1},
+ {"runtime.assertE2I2", 1},
+ {"runtime.assertI2I", 1},
+ {"runtime.assertI2I2", 1},
+ {"runtime.panicdottypeE", 1},
+ {"runtime.panicdottypeI", 1},
+ {"runtime.panicnildottype", 1},
+ {"runtime.ifaceeq", 1},
+ {"runtime.efaceeq", 1},
+ {"runtime.fastrand", 1},
+ {"runtime.makemap64", 1},
+ {"runtime.makemap", 1},
+ {"runtime.makemap_small", 1},
+ {"runtime.mapaccess1", 1},
+ {"runtime.mapaccess1_fast32", 1},
+ {"runtime.mapaccess1_fast64", 1},
+ {"runtime.mapaccess1_faststr", 1},
+ {"runtime.mapaccess1_fat", 1},
+ {"runtime.mapaccess2", 1},
+ {"runtime.mapaccess2_fast32", 1},
+ {"runtime.mapaccess2_fast64", 1},
+ {"runtime.mapaccess2_faststr", 1},
+ {"runtime.mapaccess2_fat", 1},
+ {"runtime.mapassign", 1},
+ {"runtime.mapassign_fast32", 1},
+ {"runtime.mapassign_fast32ptr", 1},
+ {"runtime.mapassign_fast64", 1},
+ {"runtime.mapassign_fast64ptr", 1},
+ {"runtime.mapassign_faststr", 1},
+ {"runtime.mapiterinit", 1},
+ {"runtime.mapdelete", 1},
+ {"runtime.mapdelete_fast32", 1},
+ {"runtime.mapdelete_fast64", 1},
+ {"runtime.mapdelete_faststr", 1},
+ {"runtime.mapiternext", 1},
+ {"runtime.mapclear", 1},
+ {"runtime.makechan64", 1},
+ {"runtime.makechan", 1},
+ {"runtime.chanrecv1", 1},
+ {"runtime.chanrecv2", 1},
+ {"runtime.chansend1", 1},
+ {"runtime.closechan", 1},
+ {"runtime.writeBarrier", 0},
+ {"runtime.typedmemmove", 1},
+ {"runtime.typedmemclr", 1},
+ {"runtime.typedslicecopy", 1},
+ {"runtime.selectnbsend", 1},
+ {"runtime.selectnbrecv", 1},
+ {"runtime.selectsetpc", 1},
+ {"runtime.selectgo", 1},
+ {"runtime.block", 1},
+ {"runtime.makeslice", 1},
+ {"runtime.makeslice64", 1},
+ {"runtime.makeslicecopy", 1},
+ {"runtime.growslice", 1},
+ {"runtime.unsafeslice", 1},
+ {"runtime.unsafeslice64", 1},
+ {"runtime.memmove", 1},
+ {"runtime.memclrNoHeapPointers", 1},
+ {"runtime.memclrHasPointers", 1},
+ {"runtime.memequal", 1},
+ {"runtime.memequal0", 1},
+ {"runtime.memequal8", 1},
+ {"runtime.memequal16", 1},
+ {"runtime.memequal32", 1},
+ {"runtime.memequal64", 1},
+ {"runtime.memequal128", 1},
+ {"runtime.f32equal", 1},
+ {"runtime.f64equal", 1},
+ {"runtime.c64equal", 1},
+ {"runtime.c128equal", 1},
+ {"runtime.strequal", 1},
+ {"runtime.interequal", 1},
+ {"runtime.nilinterequal", 1},
+ {"runtime.memhash", 1},
+ {"runtime.memhash0", 1},
+ {"runtime.memhash8", 1},
+ {"runtime.memhash16", 1},
+ {"runtime.memhash32", 1},
+ {"runtime.memhash64", 1},
+ {"runtime.memhash128", 1},
+ {"runtime.f32hash", 1},
+ {"runtime.f64hash", 1},
+ {"runtime.c64hash", 1},
+ {"runtime.c128hash", 1},
+ {"runtime.strhash", 1},
+ {"runtime.interhash", 1},
+ {"runtime.nilinterhash", 1},
+ {"runtime.int64div", 1},
+ {"runtime.uint64div", 1},
+ {"runtime.int64mod", 1},
+ {"runtime.uint64mod", 1},
+ {"runtime.float64toint64", 1},
+ {"runtime.float64touint64", 1},
+ {"runtime.float64touint32", 1},
+ {"runtime.int64tofloat64", 1},
+ {"runtime.uint64tofloat64", 1},
+ {"runtime.uint32tofloat64", 1},
+ {"runtime.complex128div", 1},
+ {"runtime.getcallerpc", 1},
+ {"runtime.getcallersp", 1},
+ {"runtime.racefuncenter", 1},
+ {"runtime.racefuncexit", 1},
+ {"runtime.raceread", 1},
+ {"runtime.racewrite", 1},
+ {"runtime.racereadrange", 1},
+ {"runtime.racewriterange", 1},
+ {"runtime.msanread", 1},
+ {"runtime.msanwrite", 1},
+ {"runtime.msanmove", 1},
+ {"runtime.checkptrAlignment", 1},
+ {"runtime.checkptrArithmetic", 1},
+ {"runtime.libfuzzerTraceCmp1", 1},
+ {"runtime.libfuzzerTraceCmp2", 1},
+ {"runtime.libfuzzerTraceCmp4", 1},
+ {"runtime.libfuzzerTraceCmp8", 1},
+ {"runtime.libfuzzerTraceConstCmp1", 1},
+ {"runtime.libfuzzerTraceConstCmp2", 1},
+ {"runtime.libfuzzerTraceConstCmp4", 1},
+ {"runtime.libfuzzerTraceConstCmp8", 1},
+ {"runtime.libfuzzerHookStrCmp", 1},
+ {"runtime.libfuzzerHookEqualFold", 1},
+ {"runtime.x86HasPOPCNT", 0},
+ {"runtime.x86HasSSE41", 0},
+ {"runtime.x86HasFMA", 0},
+ {"runtime.armHasVFPv4", 0},
+ {"runtime.arm64HasATOMICS", 0},
+ {"runtime.deferproc", 1},
+ {"runtime.deferprocStack", 1},
+ {"runtime.deferreturn", 1},
+ {"runtime.newproc", 1},
+ {"runtime.panicoverflow", 1},
+ {"runtime.sigpanic", 1},
+ {"runtime.gcWriteBarrier", 1},
+ {"runtime.duffzero", 1},
+ {"runtime.duffcopy", 1},
+ {"runtime.morestack", 0},
+ {"runtime.morestackc", 0},
+ {"runtime.morestack_noctxt", 0},
+ {"type:int8", 0},
+ {"type:*int8", 0},
+ {"type:uint8", 0},
+ {"type:*uint8", 0},
+ {"type:int16", 0},
+ {"type:*int16", 0},
+ {"type:uint16", 0},
+ {"type:*uint16", 0},
+ {"type:int32", 0},
+ {"type:*int32", 0},
+ {"type:uint32", 0},
+ {"type:*uint32", 0},
+ {"type:int64", 0},
+ {"type:*int64", 0},
+ {"type:uint64", 0},
+ {"type:*uint64", 0},
+ {"type:float32", 0},
+ {"type:*float32", 0},
+ {"type:float64", 0},
+ {"type:*float64", 0},
+ {"type:complex64", 0},
+ {"type:*complex64", 0},
+ {"type:complex128", 0},
+ {"type:*complex128", 0},
+ {"type:unsafe.Pointer", 0},
+ {"type:*unsafe.Pointer", 0},
+ {"type:uintptr", 0},
+ {"type:*uintptr", 0},
+ {"type:bool", 0},
+ {"type:*bool", 0},
+ {"type:string", 0},
+ {"type:*string", 0},
+ {"type:error", 0},
+ {"type:*error", 0},
+ {"type:func(error) string", 0},
+ {"type:*func(error) string", 0},
+}
diff --git a/src/cmd/internal/goobj/funcinfo.go b/src/cmd/internal/goobj/funcinfo.go
new file mode 100644
index 0000000..fbcf9d9
--- /dev/null
+++ b/src/cmd/internal/goobj/funcinfo.go
@@ -0,0 +1,145 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package goobj
+
+import (
+ "bytes"
+ "cmd/internal/objabi"
+ "encoding/binary"
+)
+
+// CUFileIndex is used to index the filenames that are stored in the
+// per-package/per-CU FileList.
+type CUFileIndex uint32
+
+// FuncInfo is serialized as a symbol (aux symbol). The symbol data is
+// the binary encoding of the struct below.
+type FuncInfo struct {
+ Args uint32
+ Locals uint32
+ FuncID objabi.FuncID
+ FuncFlag objabi.FuncFlag
+ StartLine int32
+ File []CUFileIndex
+ InlTree []InlTreeNode
+}
+
+func (a *FuncInfo) Write(w *bytes.Buffer) {
+ writeUint8 := func(x uint8) {
+ w.WriteByte(x)
+ }
+ var b [4]byte
+ writeUint32 := func(x uint32) {
+ binary.LittleEndian.PutUint32(b[:], x)
+ w.Write(b[:])
+ }
+
+ writeUint32(a.Args)
+ writeUint32(a.Locals)
+ writeUint8(uint8(a.FuncID))
+ writeUint8(uint8(a.FuncFlag))
+ writeUint8(0) // pad to uint32 boundary
+ writeUint8(0)
+ writeUint32(uint32(a.StartLine))
+
+ writeUint32(uint32(len(a.File)))
+ for _, f := range a.File {
+ writeUint32(uint32(f))
+ }
+ writeUint32(uint32(len(a.InlTree)))
+ for i := range a.InlTree {
+ a.InlTree[i].Write(w)
+ }
+}
+
+// FuncInfoLengths is a cache containing a roadmap of offsets and
+// lengths for things within a serialized FuncInfo. Each length field
+// stores the number of items (e.g. files, inltree nodes, etc), and the
+// corresponding "off" field stores the byte offset of the start of
+// the items in question.
+type FuncInfoLengths struct {
+ NumFile uint32
+ FileOff uint32
+ NumInlTree uint32
+ InlTreeOff uint32
+ Initialized bool
+}
+
+func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths {
+ var result FuncInfoLengths
+
+ // Offset to the number of the file table. This value is determined by counting
+ // the number of bytes until we write funcdataoff to the file.
+ const numfileOff = 16
+ result.NumFile = binary.LittleEndian.Uint32(b[numfileOff:])
+ result.FileOff = numfileOff + 4
+
+ numinltreeOff := result.FileOff + 4*result.NumFile
+ result.NumInlTree = binary.LittleEndian.Uint32(b[numinltreeOff:])
+ result.InlTreeOff = numinltreeOff + 4
+
+ result.Initialized = true
+
+ return result
+}
+
+func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }
+
+func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) }
+
+func (*FuncInfo) ReadFuncID(b []byte) objabi.FuncID { return objabi.FuncID(b[8]) }
+
+func (*FuncInfo) ReadFuncFlag(b []byte) objabi.FuncFlag { return objabi.FuncFlag(b[9]) }
+
+func (*FuncInfo) ReadStartLine(b []byte) int32 { return int32(binary.LittleEndian.Uint32(b[12:])) }
+
+func (*FuncInfo) ReadFile(b []byte, filesoff uint32, k uint32) CUFileIndex {
+ return CUFileIndex(binary.LittleEndian.Uint32(b[filesoff+4*k:]))
+}
+
+func (*FuncInfo) ReadInlTree(b []byte, inltreeoff uint32, k uint32) InlTreeNode {
+ const inlTreeNodeSize = 4 * 6
+ var result InlTreeNode
+ result.Read(b[inltreeoff+k*inlTreeNodeSize:])
+ return result
+}
+
+// InlTreeNode is the serialized form of FileInfo.InlTree.
+type InlTreeNode struct {
+ Parent int32
+ File CUFileIndex
+ Line int32
+ Func SymRef
+ ParentPC int32
+}
+
+func (inl *InlTreeNode) Write(w *bytes.Buffer) {
+ var b [4]byte
+ writeUint32 := func(x uint32) {
+ binary.LittleEndian.PutUint32(b[:], x)
+ w.Write(b[:])
+ }
+ writeUint32(uint32(inl.Parent))
+ writeUint32(uint32(inl.File))
+ writeUint32(uint32(inl.Line))
+ writeUint32(inl.Func.PkgIdx)
+ writeUint32(inl.Func.SymIdx)
+ writeUint32(uint32(inl.ParentPC))
+}
+
+// Read an InlTreeNode from b, return the remaining bytes.
+func (inl *InlTreeNode) Read(b []byte) []byte {
+ readUint32 := func() uint32 {
+ x := binary.LittleEndian.Uint32(b)
+ b = b[4:]
+ return x
+ }
+ inl.Parent = int32(readUint32())
+ inl.File = CUFileIndex(readUint32())
+ inl.Line = int32(readUint32())
+ inl.Func = SymRef{readUint32(), readUint32()}
+ inl.ParentPC = int32(readUint32())
+ return b
+}
diff --git a/src/cmd/internal/goobj/mkbuiltin.go b/src/cmd/internal/goobj/mkbuiltin.go
new file mode 100644
index 0000000..57e39dc
--- /dev/null
+++ b/src/cmd/internal/goobj/mkbuiltin.go
@@ -0,0 +1,161 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+// +build ignore
+
+// Generate builtinlist.go from cmd/compile/internal/typecheck/builtin/runtime.go.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+var stdout = flag.Bool("stdout", false, "write to stdout instead of builtinlist.go")
+
+func main() {
+ flag.Parse()
+
+ var b bytes.Buffer
+ fmt.Fprintln(&b, "// Code generated by mkbuiltin.go. DO NOT EDIT.")
+ fmt.Fprintln(&b)
+ fmt.Fprintln(&b, "package goobj")
+
+ mkbuiltin(&b)
+
+ out, err := format.Source(b.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ if *stdout {
+ _, err = os.Stdout.Write(out)
+ } else {
+ err = os.WriteFile("builtinlist.go", out, 0666)
+ }
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func mkbuiltin(w io.Writer) {
+ pkg := "runtime"
+ fset := token.NewFileSet()
+ path := filepath.Join("..", "..", "compile", "internal", "typecheck", "builtin", "runtime.go")
+ f, err := parser.ParseFile(fset, path, nil, 0)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ decls := make(map[string]bool)
+
+ fmt.Fprintf(w, "var builtins = [...]struct{ name string; abi int }{\n")
+ for _, decl := range f.Decls {
+ switch decl := decl.(type) {
+ case *ast.FuncDecl:
+ if decl.Recv != nil {
+ log.Fatal("methods unsupported")
+ }
+ if decl.Body != nil {
+ log.Fatal("unexpected function body")
+ }
+ declName := pkg + "." + decl.Name.Name
+ decls[declName] = true
+ fmt.Fprintf(w, "{%q, 1},\n", declName) // functions are ABIInternal (1)
+ case *ast.GenDecl:
+ if decl.Tok == token.IMPORT {
+ continue
+ }
+ if decl.Tok != token.VAR {
+ log.Fatal("unhandled declaration kind", decl.Tok)
+ }
+ for _, spec := range decl.Specs {
+ spec := spec.(*ast.ValueSpec)
+ if len(spec.Values) != 0 {
+ log.Fatal("unexpected values")
+ }
+ for _, name := range spec.Names {
+ declName := pkg + "." + name.Name
+ decls[declName] = true
+ fmt.Fprintf(w, "{%q, 0},\n", declName) // variables are ABI0
+ }
+ }
+ default:
+ log.Fatal("unhandled decl type", decl)
+ }
+ }
+
+ // The list above only contains ones that are used by the frontend.
+ // The backend may create more references of builtin functions.
+ // We also want to include predefined types.
+ // Add them.
+ extras := append(fextras[:], enumerateBasicTypes()...)
+ for _, b := range extras {
+ prefix := ""
+ if !strings.HasPrefix(b.name, "type:") {
+ prefix = pkg + "."
+ }
+ name := prefix + b.name
+ if decls[name] {
+ log.Fatalf("%q already added -- mkbuiltin.go out of sync?", name)
+ }
+ fmt.Fprintf(w, "{%q, %d},\n", name, b.abi)
+ }
+ fmt.Fprintln(w, "}")
+}
+
+// enumerateBasicTypes returns the symbol names for basic types that are
+// defined in the runtime and referenced in other packages.
+// Needs to be kept in sync with reflect.go:WriteBasicTypes() and
+// reflect.go:writeType() in the compiler.
+func enumerateBasicTypes() []extra {
+ names := [...]string{
+ "int8", "uint8", "int16", "uint16",
+ "int32", "uint32", "int64", "uint64",
+ "float32", "float64", "complex64", "complex128",
+ "unsafe.Pointer", "uintptr", "bool", "string", "error",
+ "func(error) string"}
+ result := []extra{}
+ for _, n := range names {
+ result = append(result, extra{"type:" + n, 0})
+ result = append(result, extra{"type:*" + n, 0})
+ }
+ return result
+}
+
+type extra struct {
+ name string
+ abi int
+}
+
+var fextras = [...]extra{
+ // compiler frontend inserted calls (sysfunc)
+ {"deferproc", 1},
+ {"deferprocStack", 1},
+ {"deferreturn", 1},
+ {"newproc", 1},
+ {"panicoverflow", 1},
+ {"sigpanic", 1},
+
+ // compiler backend inserted calls
+ {"gcWriteBarrier", 1},
+ {"duffzero", 1},
+ {"duffcopy", 1},
+
+ // assembler backend inserted calls
+ {"morestack", 0}, // asm function, ABI0
+ {"morestackc", 0}, // asm function, ABI0
+ {"morestack_noctxt", 0}, // asm function, ABI0
+}
diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go
new file mode 100644
index 0000000..ae215df
--- /dev/null
+++ b/src/cmd/internal/goobj/objfile.go
@@ -0,0 +1,876 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This package defines the Go object file format, and provide "low-level" functions
+// for reading and writing object files.
+
+// The object file is understood by the compiler, assembler, linker, and tools. They
+// have "high level" code that operates on object files, handling application-specific
+// logics, and use this package for the actual reading and writing. Specifically, the
+// code below:
+//
+// - cmd/internal/obj/objfile.go (used by cmd/asm and cmd/compile)
+// - cmd/internal/objfile/goobj.go (used cmd/nm, cmd/objdump)
+// - cmd/link/internal/loader package (used by cmd/link)
+//
+// If the object file format changes, they may (or may not) need to change.
+
+package goobj
+
+import (
+ "cmd/internal/bio"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "internal/unsafeheader"
+ "unsafe"
+)
+
+// New object file format.
+//
+// Header struct {
+// Magic [...]byte // "\x00go120ld"
+// Fingerprint [8]byte
+// Flags uint32
+// Offsets [...]uint32 // byte offset of each block below
+// }
+//
+// Strings [...]struct {
+// Data [...]byte
+// }
+//
+// Autolib [...]struct { // imported packages (for file loading)
+// Pkg string
+// Fingerprint [8]byte
+// }
+//
+// PkgIndex [...]string // referenced packages by index
+//
+// Files [...]string
+//
+// SymbolDefs [...]struct {
+// Name string
+// ABI uint16
+// Type uint8
+// Flag uint8
+// Flag2 uint8
+// Size uint32
+// }
+// Hashed64Defs [...]struct { // short hashed (content-addressable) symbol definitions
+// ... // same as SymbolDefs
+// }
+// HashedDefs [...]struct { // hashed (content-addressable) symbol definitions
+// ... // same as SymbolDefs
+// }
+// NonPkgDefs [...]struct { // non-pkg symbol definitions
+// ... // same as SymbolDefs
+// }
+// NonPkgRefs [...]struct { // non-pkg symbol references
+// ... // same as SymbolDefs
+// }
+//
+// RefFlags [...]struct { // referenced symbol flags
+// Sym symRef
+// Flag uint8
+// Flag2 uint8
+// }
+//
+// Hash64 [...][8]byte
+// Hash [...][N]byte
+//
+// RelocIndex [...]uint32 // index to Relocs
+// AuxIndex [...]uint32 // index to Aux
+// DataIndex [...]uint32 // offset to Data
+//
+// Relocs [...]struct {
+// Off int32
+// Size uint8
+// Type uint16
+// Add int64
+// Sym symRef
+// }
+//
+// Aux [...]struct {
+// Type uint8
+// Sym symRef
+// }
+//
+// Data [...]byte
+//
+// // blocks only used by tools (objdump, nm)
+//
+// RefNames [...]struct { // referenced symbol names
+// Sym symRef
+// Name string
+// // TODO: include ABI version as well?
+// }
+//
+// string is encoded as is a uint32 length followed by a uint32 offset
+// that points to the corresponding string bytes.
+//
+// symRef is struct { PkgIdx, SymIdx uint32 }.
+//
+// Slice type (e.g. []symRef) is encoded as a length prefix (uint32)
+// followed by that number of elements.
+//
+// The types below correspond to the encoded data structure in the
+// object file.
+
+// Symbol indexing.
+//
+// Each symbol is referenced with a pair of indices, { PkgIdx, SymIdx },
+// as the symRef struct above.
+//
+// PkgIdx is either a predeclared index (see PkgIdxNone below) or
+// an index of an imported package. For the latter case, PkgIdx is the
+// index of the package in the PkgIndex array. 0 is an invalid index.
+//
+// SymIdx is the index of the symbol in the given package.
+// - If PkgIdx is PkgIdxSelf, SymIdx is the index of the symbol in the
+// SymbolDefs array.
+// - If PkgIdx is PkgIdxHashed64, SymIdx is the index of the symbol in the
+// Hashed64Defs array.
+// - If PkgIdx is PkgIdxHashed, SymIdx is the index of the symbol in the
+// HashedDefs array.
+// - If PkgIdx is PkgIdxNone, SymIdx is the index of the symbol in the
+// NonPkgDefs array (could natually overflow to NonPkgRefs array).
+// - Otherwise, SymIdx is the index of the symbol in some other package's
+// SymbolDefs array.
+//
+// {0, 0} represents a nil symbol. Otherwise PkgIdx should not be 0.
+//
+// Hash contains the content hashes of content-addressable symbols, of
+// which PkgIdx is PkgIdxHashed, in the same order of HashedDefs array.
+// Hash64 is similar, for PkgIdxHashed64 symbols.
+//
+// RelocIndex, AuxIndex, and DataIndex contains indices/offsets to
+// Relocs/Aux/Data blocks, one element per symbol, first for all the
+// defined symbols, then all the defined hashed and non-package symbols,
+// in the same order of SymbolDefs/Hashed64Defs/HashedDefs/NonPkgDefs
+// arrays. For N total defined symbols, the array is of length N+1. The
+// last element is the total number of relocations (aux symbols, data
+// blocks, etc.).
+//
+// They can be accessed by index. For the i-th symbol, its relocations
+// are the RelocIndex[i]-th (inclusive) to RelocIndex[i+1]-th (exclusive)
+// elements in the Relocs array. Aux/Data are likewise. (The index is
+// 0-based.)
+
+// Auxiliary symbols.
+//
+// Each symbol may (or may not) be associated with a number of auxiliary
+// symbols. They are described in the Aux block. See Aux struct below.
+// Currently a symbol's Gotype, FuncInfo, and associated DWARF symbols
+// are auxiliary symbols.
+
+const stringRefSize = 8 // two uint32s
+
+type FingerprintType [8]byte
+
+func (fp FingerprintType) IsZero() bool { return fp == FingerprintType{} }
+
+// Package Index.
+const (
+ PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols
+ PkgIdxHashed64 // Short hashed (content-addressable) symbols
+ PkgIdxHashed // Hashed (content-addressable) symbols
+ PkgIdxBuiltin // Predefined runtime symbols (ex: runtime.newobject)
+ PkgIdxSelf // Symbols defined in the current package
+ PkgIdxSpecial = PkgIdxSelf // Indices above it has special meanings
+ PkgIdxInvalid = 0
+ // The index of other referenced packages starts from 1.
+)
+
+// Blocks
+const (
+ BlkAutolib = iota
+ BlkPkgIdx
+ BlkFile
+ BlkSymdef
+ BlkHashed64def
+ BlkHasheddef
+ BlkNonpkgdef
+ BlkNonpkgref
+ BlkRefFlags
+ BlkHash64
+ BlkHash
+ BlkRelocIdx
+ BlkAuxIdx
+ BlkDataIdx
+ BlkReloc
+ BlkAux
+ BlkData
+ BlkRefName
+ BlkEnd
+ NBlk
+)
+
+// File header.
+// TODO: probably no need to export this.
+type Header struct {
+ Magic string
+ Fingerprint FingerprintType
+ Flags uint32
+ Offsets [NBlk]uint32
+}
+
+const Magic = "\x00go120ld"
+
+func (h *Header) Write(w *Writer) {
+ w.RawString(h.Magic)
+ w.Bytes(h.Fingerprint[:])
+ w.Uint32(h.Flags)
+ for _, x := range h.Offsets {
+ w.Uint32(x)
+ }
+}
+
+func (h *Header) Read(r *Reader) error {
+ b := r.BytesAt(0, len(Magic))
+ h.Magic = string(b)
+ if h.Magic != Magic {
+ return errors.New("wrong magic, not a Go object file")
+ }
+ off := uint32(len(h.Magic))
+ copy(h.Fingerprint[:], r.BytesAt(off, len(h.Fingerprint)))
+ off += 8
+ h.Flags = r.uint32At(off)
+ off += 4
+ for i := range h.Offsets {
+ h.Offsets[i] = r.uint32At(off)
+ off += 4
+ }
+ return nil
+}
+
+func (h *Header) Size() int {
+ return len(h.Magic) + 4 + 4*len(h.Offsets)
+}
+
+// Autolib
+type ImportedPkg struct {
+ Pkg string
+ Fingerprint FingerprintType
+}
+
+const importedPkgSize = stringRefSize + 8
+
+func (p *ImportedPkg) Write(w *Writer) {
+ w.StringRef(p.Pkg)
+ w.Bytes(p.Fingerprint[:])
+}
+
+// Symbol definition.
+//
+// Serialized format:
+//
+// Sym struct {
+// Name string
+// ABI uint16
+// Type uint8
+// Flag uint8
+// Flag2 uint8
+// Siz uint32
+// Align uint32
+// }
+type Sym [SymSize]byte
+
+const SymSize = stringRefSize + 2 + 1 + 1 + 1 + 4 + 4
+
+const SymABIstatic = ^uint16(0)
+
+const (
+ ObjFlagShared = 1 << iota // this object is built with -shared
+ _ // was ObjFlagNeedNameExpansion
+ ObjFlagFromAssembly // object is from asm src, not go
+ ObjFlagUnlinkable // unlinkable package (linker will emit an error)
+)
+
+// Sym.Flag
+const (
+ SymFlagDupok = 1 << iota
+ SymFlagLocal
+ SymFlagTypelink
+ SymFlagLeaf
+ SymFlagNoSplit
+ SymFlagReflectMethod
+ SymFlagGoType
+)
+
+// Sym.Flag2
+const (
+ SymFlagUsedInIface = 1 << iota
+ SymFlagItab
+ SymFlagDict
+)
+
+// Returns the length of the name of the symbol.
+func (s *Sym) NameLen(r *Reader) int {
+ return int(binary.LittleEndian.Uint32(s[:]))
+}
+
+func (s *Sym) Name(r *Reader) string {
+ len := binary.LittleEndian.Uint32(s[:])
+ off := binary.LittleEndian.Uint32(s[4:])
+ return r.StringAt(off, len)
+}
+
+func (s *Sym) ABI() uint16 { return binary.LittleEndian.Uint16(s[8:]) }
+func (s *Sym) Type() uint8 { return s[10] }
+func (s *Sym) Flag() uint8 { return s[11] }
+func (s *Sym) Flag2() uint8 { return s[12] }
+func (s *Sym) Siz() uint32 { return binary.LittleEndian.Uint32(s[13:]) }
+func (s *Sym) Align() uint32 { return binary.LittleEndian.Uint32(s[17:]) }
+
+func (s *Sym) Dupok() bool { return s.Flag()&SymFlagDupok != 0 }
+func (s *Sym) Local() bool { return s.Flag()&SymFlagLocal != 0 }
+func (s *Sym) Typelink() bool { return s.Flag()&SymFlagTypelink != 0 }
+func (s *Sym) Leaf() bool { return s.Flag()&SymFlagLeaf != 0 }
+func (s *Sym) NoSplit() bool { return s.Flag()&SymFlagNoSplit != 0 }
+func (s *Sym) ReflectMethod() bool { return s.Flag()&SymFlagReflectMethod != 0 }
+func (s *Sym) IsGoType() bool { return s.Flag()&SymFlagGoType != 0 }
+func (s *Sym) UsedInIface() bool { return s.Flag2()&SymFlagUsedInIface != 0 }
+func (s *Sym) IsItab() bool { return s.Flag2()&SymFlagItab != 0 }
+func (s *Sym) IsDict() bool { return s.Flag2()&SymFlagDict != 0 }
+
+func (s *Sym) SetName(x string, w *Writer) {
+ binary.LittleEndian.PutUint32(s[:], uint32(len(x)))
+ binary.LittleEndian.PutUint32(s[4:], w.stringOff(x))
+}
+
+func (s *Sym) SetABI(x uint16) { binary.LittleEndian.PutUint16(s[8:], x) }
+func (s *Sym) SetType(x uint8) { s[10] = x }
+func (s *Sym) SetFlag(x uint8) { s[11] = x }
+func (s *Sym) SetFlag2(x uint8) { s[12] = x }
+func (s *Sym) SetSiz(x uint32) { binary.LittleEndian.PutUint32(s[13:], x) }
+func (s *Sym) SetAlign(x uint32) { binary.LittleEndian.PutUint32(s[17:], x) }
+
+func (s *Sym) Write(w *Writer) { w.Bytes(s[:]) }
+
+// for testing
+func (s *Sym) fromBytes(b []byte) { copy(s[:], b) }
+
+// Symbol reference.
+type SymRef struct {
+ PkgIdx uint32
+ SymIdx uint32
+}
+
+func (s SymRef) IsZero() bool { return s == SymRef{} }
+
+// Hash64
+type Hash64Type [Hash64Size]byte
+
+const Hash64Size = 8
+
+// Hash
+type HashType [HashSize]byte
+
+const HashSize = 16 // truncated SHA256
+
+// Relocation.
+//
+// Serialized format:
+//
+// Reloc struct {
+// Off int32
+// Siz uint8
+// Type uint16
+// Add int64
+// Sym SymRef
+// }
+type Reloc [RelocSize]byte
+
+const RelocSize = 4 + 1 + 2 + 8 + 8
+
+func (r *Reloc) Off() int32 { return int32(binary.LittleEndian.Uint32(r[:])) }
+func (r *Reloc) Siz() uint8 { return r[4] }
+func (r *Reloc) Type() uint16 { return binary.LittleEndian.Uint16(r[5:]) }
+func (r *Reloc) Add() int64 { return int64(binary.LittleEndian.Uint64(r[7:])) }
+func (r *Reloc) Sym() SymRef {
+ return SymRef{binary.LittleEndian.Uint32(r[15:]), binary.LittleEndian.Uint32(r[19:])}
+}
+
+func (r *Reloc) SetOff(x int32) { binary.LittleEndian.PutUint32(r[:], uint32(x)) }
+func (r *Reloc) SetSiz(x uint8) { r[4] = x }
+func (r *Reloc) SetType(x uint16) { binary.LittleEndian.PutUint16(r[5:], x) }
+func (r *Reloc) SetAdd(x int64) { binary.LittleEndian.PutUint64(r[7:], uint64(x)) }
+func (r *Reloc) SetSym(x SymRef) {
+ binary.LittleEndian.PutUint32(r[15:], x.PkgIdx)
+ binary.LittleEndian.PutUint32(r[19:], x.SymIdx)
+}
+
+func (r *Reloc) Set(off int32, size uint8, typ uint16, add int64, sym SymRef) {
+ r.SetOff(off)
+ r.SetSiz(size)
+ r.SetType(typ)
+ r.SetAdd(add)
+ r.SetSym(sym)
+}
+
+func (r *Reloc) Write(w *Writer) { w.Bytes(r[:]) }
+
+// for testing
+func (r *Reloc) fromBytes(b []byte) { copy(r[:], b) }
+
+// Aux symbol info.
+//
+// Serialized format:
+//
+// Aux struct {
+// Type uint8
+// Sym SymRef
+// }
+type Aux [AuxSize]byte
+
+const AuxSize = 1 + 8
+
+// Aux Type
+const (
+ AuxGotype = iota
+ AuxFuncInfo
+ AuxFuncdata
+ AuxDwarfInfo
+ AuxDwarfLoc
+ AuxDwarfRanges
+ AuxDwarfLines
+ AuxPcsp
+ AuxPcfile
+ AuxPcline
+ AuxPcinline
+ AuxPcdata
+)
+
+func (a *Aux) Type() uint8 { return a[0] }
+func (a *Aux) Sym() SymRef {
+ return SymRef{binary.LittleEndian.Uint32(a[1:]), binary.LittleEndian.Uint32(a[5:])}
+}
+
+func (a *Aux) SetType(x uint8) { a[0] = x }
+func (a *Aux) SetSym(x SymRef) {
+ binary.LittleEndian.PutUint32(a[1:], x.PkgIdx)
+ binary.LittleEndian.PutUint32(a[5:], x.SymIdx)
+}
+
+func (a *Aux) Write(w *Writer) { w.Bytes(a[:]) }
+
+// for testing
+func (a *Aux) fromBytes(b []byte) { copy(a[:], b) }
+
+// Referenced symbol flags.
+//
+// Serialized format:
+//
+// RefFlags struct {
+// Sym symRef
+// Flag uint8
+// Flag2 uint8
+// }
+type RefFlags [RefFlagsSize]byte
+
+const RefFlagsSize = 8 + 1 + 1
+
+func (r *RefFlags) Sym() SymRef {
+ return SymRef{binary.LittleEndian.Uint32(r[:]), binary.LittleEndian.Uint32(r[4:])}
+}
+func (r *RefFlags) Flag() uint8 { return r[8] }
+func (r *RefFlags) Flag2() uint8 { return r[9] }
+
+func (r *RefFlags) SetSym(x SymRef) {
+ binary.LittleEndian.PutUint32(r[:], x.PkgIdx)
+ binary.LittleEndian.PutUint32(r[4:], x.SymIdx)
+}
+func (r *RefFlags) SetFlag(x uint8) { r[8] = x }
+func (r *RefFlags) SetFlag2(x uint8) { r[9] = x }
+
+func (r *RefFlags) Write(w *Writer) { w.Bytes(r[:]) }
+
+// Used to construct an artificially large array type when reading an
+// item from the object file relocs section or aux sym section (needs
+// to work on 32-bit as well as 64-bit). See issue 41621.
+const huge = (1<<31 - 1) / RelocSize
+
+// Referenced symbol name.
+//
+// Serialized format:
+//
+// RefName struct {
+// Sym symRef
+// Name string
+// }
+type RefName [RefNameSize]byte
+
+const RefNameSize = 8 + stringRefSize
+
+func (n *RefName) Sym() SymRef {
+ return SymRef{binary.LittleEndian.Uint32(n[:]), binary.LittleEndian.Uint32(n[4:])}
+}
+func (n *RefName) Name(r *Reader) string {
+ len := binary.LittleEndian.Uint32(n[8:])
+ off := binary.LittleEndian.Uint32(n[12:])
+ return r.StringAt(off, len)
+}
+
+func (n *RefName) SetSym(x SymRef) {
+ binary.LittleEndian.PutUint32(n[:], x.PkgIdx)
+ binary.LittleEndian.PutUint32(n[4:], x.SymIdx)
+}
+func (n *RefName) SetName(x string, w *Writer) {
+ binary.LittleEndian.PutUint32(n[8:], uint32(len(x)))
+ binary.LittleEndian.PutUint32(n[12:], w.stringOff(x))
+}
+
+func (n *RefName) Write(w *Writer) { w.Bytes(n[:]) }
+
+type Writer struct {
+ wr *bio.Writer
+ stringMap map[string]uint32
+ off uint32 // running offset
+
+ b [8]byte // scratch space for writing bytes
+}
+
+func NewWriter(wr *bio.Writer) *Writer {
+ return &Writer{wr: wr, stringMap: make(map[string]uint32)}
+}
+
+func (w *Writer) AddString(s string) {
+ if _, ok := w.stringMap[s]; ok {
+ return
+ }
+ w.stringMap[s] = w.off
+ w.RawString(s)
+}
+
+func (w *Writer) stringOff(s string) uint32 {
+ off, ok := w.stringMap[s]
+ if !ok {
+ panic(fmt.Sprintf("writeStringRef: string not added: %q", s))
+ }
+ return off
+}
+
+func (w *Writer) StringRef(s string) {
+ w.Uint32(uint32(len(s)))
+ w.Uint32(w.stringOff(s))
+}
+
+func (w *Writer) RawString(s string) {
+ w.wr.WriteString(s)
+ w.off += uint32(len(s))
+}
+
+func (w *Writer) Bytes(s []byte) {
+ w.wr.Write(s)
+ w.off += uint32(len(s))
+}
+
+func (w *Writer) Uint64(x uint64) {
+ binary.LittleEndian.PutUint64(w.b[:], x)
+ w.wr.Write(w.b[:])
+ w.off += 8
+}
+
+func (w *Writer) Uint32(x uint32) {
+ binary.LittleEndian.PutUint32(w.b[:4], x)
+ w.wr.Write(w.b[:4])
+ w.off += 4
+}
+
+func (w *Writer) Uint16(x uint16) {
+ binary.LittleEndian.PutUint16(w.b[:2], x)
+ w.wr.Write(w.b[:2])
+ w.off += 2
+}
+
+func (w *Writer) Uint8(x uint8) {
+ w.wr.WriteByte(x)
+ w.off++
+}
+
+func (w *Writer) Offset() uint32 {
+ return w.off
+}
+
+type Reader struct {
+ b []byte // mmapped bytes, if not nil
+ readonly bool // whether b is backed with read-only memory
+
+ start uint32
+ h Header // keep block offsets
+}
+
+func NewReaderFromBytes(b []byte, readonly bool) *Reader {
+ r := &Reader{b: b, readonly: readonly, start: 0}
+ err := r.h.Read(r)
+ if err != nil {
+ return nil
+ }
+ return r
+}
+
+func (r *Reader) BytesAt(off uint32, len int) []byte {
+ if len == 0 {
+ return nil
+ }
+ end := int(off) + len
+ return r.b[int(off):end:end]
+}
+
+func (r *Reader) uint64At(off uint32) uint64 {
+ b := r.BytesAt(off, 8)
+ return binary.LittleEndian.Uint64(b)
+}
+
+func (r *Reader) int64At(off uint32) int64 {
+ return int64(r.uint64At(off))
+}
+
+func (r *Reader) uint32At(off uint32) uint32 {
+ b := r.BytesAt(off, 4)
+ return binary.LittleEndian.Uint32(b)
+}
+
+func (r *Reader) int32At(off uint32) int32 {
+ return int32(r.uint32At(off))
+}
+
+func (r *Reader) uint16At(off uint32) uint16 {
+ b := r.BytesAt(off, 2)
+ return binary.LittleEndian.Uint16(b)
+}
+
+func (r *Reader) uint8At(off uint32) uint8 {
+ b := r.BytesAt(off, 1)
+ return b[0]
+}
+
+func (r *Reader) StringAt(off uint32, len uint32) string {
+ b := r.b[off : off+len]
+ if r.readonly {
+ return toString(b) // backed by RO memory, ok to make unsafe string
+ }
+ return string(b)
+}
+
+func toString(b []byte) string {
+ if len(b) == 0 {
+ return ""
+ }
+
+ var s string
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
+ hdr.Data = unsafe.Pointer(&b[0])
+ hdr.Len = len(b)
+
+ return s
+}
+
+func (r *Reader) StringRef(off uint32) string {
+ l := r.uint32At(off)
+ return r.StringAt(r.uint32At(off+4), l)
+}
+
+func (r *Reader) Fingerprint() FingerprintType {
+ return r.h.Fingerprint
+}
+
+func (r *Reader) Autolib() []ImportedPkg {
+ n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / importedPkgSize
+ s := make([]ImportedPkg, n)
+ off := r.h.Offsets[BlkAutolib]
+ for i := range s {
+ s[i].Pkg = r.StringRef(off)
+ copy(s[i].Fingerprint[:], r.BytesAt(off+stringRefSize, len(s[i].Fingerprint)))
+ off += importedPkgSize
+ }
+ return s
+}
+
+func (r *Reader) Pkglist() []string {
+ n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / stringRefSize
+ s := make([]string, n)
+ off := r.h.Offsets[BlkPkgIdx]
+ for i := range s {
+ s[i] = r.StringRef(off)
+ off += stringRefSize
+ }
+ return s
+}
+
+func (r *Reader) NPkg() int {
+ return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / stringRefSize
+}
+
+func (r *Reader) Pkg(i int) string {
+ off := r.h.Offsets[BlkPkgIdx] + uint32(i)*stringRefSize
+ return r.StringRef(off)
+}
+
+func (r *Reader) NFile() int {
+ return int(r.h.Offsets[BlkFile+1]-r.h.Offsets[BlkFile]) / stringRefSize
+}
+
+func (r *Reader) File(i int) string {
+ off := r.h.Offsets[BlkFile] + uint32(i)*stringRefSize
+ return r.StringRef(off)
+}
+
+func (r *Reader) NSym() int {
+ return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / SymSize
+}
+
+func (r *Reader) NHashed64def() int {
+ return int(r.h.Offsets[BlkHashed64def+1]-r.h.Offsets[BlkHashed64def]) / SymSize
+}
+
+func (r *Reader) NHasheddef() int {
+ return int(r.h.Offsets[BlkHasheddef+1]-r.h.Offsets[BlkHasheddef]) / SymSize
+}
+
+func (r *Reader) NNonpkgdef() int {
+ return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / SymSize
+}
+
+func (r *Reader) NNonpkgref() int {
+ return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / SymSize
+}
+
+// SymOff returns the offset of the i-th symbol.
+func (r *Reader) SymOff(i uint32) uint32 {
+ return r.h.Offsets[BlkSymdef] + uint32(i*SymSize)
+}
+
+// Sym returns a pointer to the i-th symbol.
+func (r *Reader) Sym(i uint32) *Sym {
+ off := r.SymOff(i)
+ return (*Sym)(unsafe.Pointer(&r.b[off]))
+}
+
+// NRefFlags returns the number of referenced symbol flags.
+func (r *Reader) NRefFlags() int {
+ return int(r.h.Offsets[BlkRefFlags+1]-r.h.Offsets[BlkRefFlags]) / RefFlagsSize
+}
+
+// RefFlags returns a pointer to the i-th referenced symbol flags.
+// Note: here i is not a local symbol index, just a counter.
+func (r *Reader) RefFlags(i int) *RefFlags {
+ off := r.h.Offsets[BlkRefFlags] + uint32(i*RefFlagsSize)
+ return (*RefFlags)(unsafe.Pointer(&r.b[off]))
+}
+
+// Hash64 returns the i-th short hashed symbol's hash.
+// Note: here i is the index of short hashed symbols, not all symbols
+// (unlike other accessors).
+func (r *Reader) Hash64(i uint32) uint64 {
+ off := r.h.Offsets[BlkHash64] + uint32(i*Hash64Size)
+ return r.uint64At(off)
+}
+
+// Hash returns a pointer to the i-th hashed symbol's hash.
+// Note: here i is the index of hashed symbols, not all symbols
+// (unlike other accessors).
+func (r *Reader) Hash(i uint32) *HashType {
+ off := r.h.Offsets[BlkHash] + uint32(i*HashSize)
+ return (*HashType)(unsafe.Pointer(&r.b[off]))
+}
+
+// NReloc returns the number of relocations of the i-th symbol.
+func (r *Reader) NReloc(i uint32) int {
+ relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
+ return int(r.uint32At(relocIdxOff+4) - r.uint32At(relocIdxOff))
+}
+
+// RelocOff returns the offset of the j-th relocation of the i-th symbol.
+func (r *Reader) RelocOff(i uint32, j int) uint32 {
+ relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
+ relocIdx := r.uint32At(relocIdxOff)
+ return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(RelocSize)
+}
+
+// Reloc returns a pointer to the j-th relocation of the i-th symbol.
+func (r *Reader) Reloc(i uint32, j int) *Reloc {
+ off := r.RelocOff(i, j)
+ return (*Reloc)(unsafe.Pointer(&r.b[off]))
+}
+
+// Relocs returns a pointer to the relocations of the i-th symbol.
+func (r *Reader) Relocs(i uint32) []Reloc {
+ off := r.RelocOff(i, 0)
+ n := r.NReloc(i)
+ return (*[huge]Reloc)(unsafe.Pointer(&r.b[off]))[:n:n]
+}
+
+// NAux returns the number of aux symbols of the i-th symbol.
+func (r *Reader) NAux(i uint32) int {
+ auxIdxOff := r.h.Offsets[BlkAuxIdx] + i*4
+ return int(r.uint32At(auxIdxOff+4) - r.uint32At(auxIdxOff))
+}
+
+// AuxOff returns the offset of the j-th aux symbol of the i-th symbol.
+func (r *Reader) AuxOff(i uint32, j int) uint32 {
+ auxIdxOff := r.h.Offsets[BlkAuxIdx] + i*4
+ auxIdx := r.uint32At(auxIdxOff)
+ return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(AuxSize)
+}
+
+// Aux returns a pointer to the j-th aux symbol of the i-th symbol.
+func (r *Reader) Aux(i uint32, j int) *Aux {
+ off := r.AuxOff(i, j)
+ return (*Aux)(unsafe.Pointer(&r.b[off]))
+}
+
+// Auxs returns the aux symbols of the i-th symbol.
+func (r *Reader) Auxs(i uint32) []Aux {
+ off := r.AuxOff(i, 0)
+ n := r.NAux(i)
+ return (*[huge]Aux)(unsafe.Pointer(&r.b[off]))[:n:n]
+}
+
+// DataOff returns the offset of the i-th symbol's data.
+func (r *Reader) DataOff(i uint32) uint32 {
+ dataIdxOff := r.h.Offsets[BlkDataIdx] + i*4
+ return r.h.Offsets[BlkData] + r.uint32At(dataIdxOff)
+}
+
+// DataSize returns the size of the i-th symbol's data.
+func (r *Reader) DataSize(i uint32) int {
+ dataIdxOff := r.h.Offsets[BlkDataIdx] + i*4
+ return int(r.uint32At(dataIdxOff+4) - r.uint32At(dataIdxOff))
+}
+
+// Data returns the i-th symbol's data.
+func (r *Reader) Data(i uint32) []byte {
+ dataIdxOff := r.h.Offsets[BlkDataIdx] + i*4
+ base := r.h.Offsets[BlkData]
+ off := r.uint32At(dataIdxOff)
+ end := r.uint32At(dataIdxOff + 4)
+ return r.BytesAt(base+off, int(end-off))
+}
+
+// NRefName returns the number of referenced symbol names.
+func (r *Reader) NRefName() int {
+ return int(r.h.Offsets[BlkRefName+1]-r.h.Offsets[BlkRefName]) / RefNameSize
+}
+
+// RefName returns a pointer to the i-th referenced symbol name.
+// Note: here i is not a local symbol index, just a counter.
+func (r *Reader) RefName(i int) *RefName {
+ off := r.h.Offsets[BlkRefName] + uint32(i*RefNameSize)
+ return (*RefName)(unsafe.Pointer(&r.b[off]))
+}
+
+// ReadOnly returns whether r.BytesAt returns read-only bytes.
+func (r *Reader) ReadOnly() bool {
+ return r.readonly
+}
+
+// Flags returns the flag bits read from the object file header.
+func (r *Reader) Flags() uint32 {
+ return r.h.Flags
+}
+
+func (r *Reader) Shared() bool { return r.Flags()&ObjFlagShared != 0 }
+func (r *Reader) FromAssembly() bool { return r.Flags()&ObjFlagFromAssembly != 0 }
+func (r *Reader) Unlinkable() bool { return r.Flags()&ObjFlagUnlinkable != 0 }
diff --git a/src/cmd/internal/goobj/objfile_test.go b/src/cmd/internal/goobj/objfile_test.go
new file mode 100644
index 0000000..10e0564
--- /dev/null
+++ b/src/cmd/internal/goobj/objfile_test.go
@@ -0,0 +1,133 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package goobj
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "internal/buildcfg"
+ "internal/testenv"
+ "os"
+ "testing"
+
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+)
+
+func dummyWriter(buf *bytes.Buffer) *Writer {
+ wr := &bio.Writer{Writer: bufio.NewWriter(buf)} // hacky: no file, so cannot seek
+ return NewWriter(wr)
+}
+
+func TestReadWrite(t *testing.T) {
+ // Test that we get the same data in a write-read roundtrip.
+
+ // Write a symbol, a relocation, and an aux info.
+ var buf bytes.Buffer
+ w := dummyWriter(&buf)
+
+ var s Sym
+ s.SetABI(1)
+ s.SetType(uint8(objabi.STEXT))
+ s.SetFlag(0x12)
+ s.SetSiz(12345)
+ s.SetAlign(8)
+ s.Write(w)
+
+ var r Reloc
+ r.SetOff(12)
+ r.SetSiz(4)
+ r.SetType(uint16(objabi.R_ADDR))
+ r.SetAdd(54321)
+ r.SetSym(SymRef{11, 22})
+ r.Write(w)
+
+ var a Aux
+ a.SetType(AuxFuncInfo)
+ a.SetSym(SymRef{33, 44})
+ a.Write(w)
+
+ w.wr.Flush()
+
+ // Read them back and check.
+ b := buf.Bytes()
+ var s2 Sym
+ s2.fromBytes(b)
+ if s2.ABI() != 1 || s2.Type() != uint8(objabi.STEXT) || s2.Flag() != 0x12 || s2.Siz() != 12345 || s2.Align() != 8 {
+ t.Errorf("read Sym2 mismatch: got %v %v %v %v %v", s2.ABI(), s2.Type(), s2.Flag(), s2.Siz(), s2.Align())
+ }
+
+ b = b[SymSize:]
+ var r2 Reloc
+ r2.fromBytes(b)
+ if r2.Off() != 12 || r2.Siz() != 4 || r2.Type() != uint16(objabi.R_ADDR) || r2.Add() != 54321 || r2.Sym() != (SymRef{11, 22}) {
+ t.Errorf("read Reloc2 mismatch: got %v %v %v %v %v", r2.Off(), r2.Siz(), r2.Type(), r2.Add(), r2.Sym())
+ }
+
+ b = b[RelocSize:]
+ var a2 Aux
+ a2.fromBytes(b)
+ if a2.Type() != AuxFuncInfo || a2.Sym() != (SymRef{33, 44}) {
+ t.Errorf("read Aux2 mismatch: got %v %v", a2.Type(), a2.Sym())
+ }
+}
+
+var issue41621prolog = `
+package main
+var lines = []string{
+`
+
+var issue41621epilog = `
+}
+func getLines() []string {
+ return lines
+}
+func main() {
+ println(getLines())
+}
+`
+
+func TestIssue41621LargeNumberOfRelocations(t *testing.T) {
+ if testing.Short() || (buildcfg.GOARCH != "amd64") {
+ t.Skipf("Skipping large number of relocations test in short mode or on %s", buildcfg.GOARCH)
+ }
+ testenv.MustHaveGoBuild(t)
+
+ tmpdir, err := os.MkdirTemp("", "lotsofrelocs")
+ if err != nil {
+ t.Fatalf("can't create temp directory: %v\n", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ // Emit testcase.
+ var w bytes.Buffer
+ fmt.Fprintf(&w, issue41621prolog)
+ for i := 0; i < 1048576+13; i++ {
+ fmt.Fprintf(&w, "\t\"%d\",\n", i)
+ }
+ fmt.Fprintf(&w, issue41621epilog)
+ err = os.WriteFile(tmpdir+"/large.go", w.Bytes(), 0666)
+ if err != nil {
+ t.Fatalf("can't write output: %v\n", err)
+ }
+
+ // Emit go.mod
+ w.Reset()
+ fmt.Fprintf(&w, "module issue41621\n\ngo 1.12\n")
+ err = os.WriteFile(tmpdir+"/go.mod", w.Bytes(), 0666)
+ if err != nil {
+ t.Fatalf("can't write output: %v\n", err)
+ }
+ w.Reset()
+
+ // Build.
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "large")
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Build failed: %v, output: %s", err, out)
+ }
+}