// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
//go:generate bundle -import cmd/internal/bio=github.com/Binject/debug/goobj2/internal/bio -import internal/unsafeheader=github.com/Binject/debug/goobj2/internal/unsafeheader -o goobj2.go -prefix= cmd/internal/goobj2

package goobj2

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"unsafe"

	"github.com/Binject/debug/goobj2/internal/bio"
	"github.com/Binject/debug/goobj2/internal/unsafeheader"
)

// 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 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
	}
}

var builtins = [...]struct {
	name string
	abi  int
}{
	{"runtime.newobject", 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.printbool", 1},
	{"runtime.printfloat", 1},
	{"runtime.printint", 1},
	{"runtime.printhex", 1},
	{"runtime.printuint", 1},
	{"runtime.printcomplex", 1},
	{"runtime.printstring", 1},
	{"runtime.printpointer", 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.slicestringcopy", 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.selectnbrecv2", 1},
	{"runtime.selectsetpc", 1},
	{"runtime.selectgo", 1},
	{"runtime.block", 1},
	{"runtime.makeslice", 1},
	{"runtime.makeslice64", 1},
	{"runtime.growslice", 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.racefuncenter", 1},
	{"runtime.racefuncenterfp", 1},
	{"runtime.racefuncexit", 1},
	{"runtime.raceread", 1},
	{"runtime.racewrite", 1},
	{"runtime.racereadrange", 1},
	{"runtime.racewriterange", 1},
	{"runtime.msanread", 1},
	{"runtime.msanwrite", 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.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", 0},
	{"runtime.morestack", 0},
	{"runtime.morestackc", 0},
	{"runtime.morestack_noctxt", 0},
}

// FuncInfo is serialized as a symbol (aux symbol). The symbol data is
// the binary encoding of the struct below.
//
// TODO: make each pcdata a separate symbol?
type FuncInfo struct {
	Args   uint32
	Locals uint32

	Pcsp        uint32
	Pcfile      uint32
	Pcline      uint32
	Pcinline    uint32
	Pcdata      []uint32
	PcdataEnd   uint32
	Funcdataoff []uint32
	File        []SymRef // TODO: just use string?

	InlTree []InlTreeNode
}

func (a *FuncInfo) Write(w *bytes.Buffer) {
	var b [4]byte
	writeUint32 := func(x uint32) {
		binary.LittleEndian.PutUint32(b[:], x)
		w.Write(b[:])
	}

	writeUint32(a.Args)
	writeUint32(a.Locals)

	writeUint32(a.Pcsp)
	writeUint32(a.Pcfile)
	writeUint32(a.Pcline)
	writeUint32(a.Pcinline)
	writeUint32(uint32(len(a.Pcdata)))
	for _, x := range a.Pcdata {
		writeUint32(x)
	}
	writeUint32(a.PcdataEnd)
	writeUint32(uint32(len(a.Funcdataoff)))
	for _, x := range a.Funcdataoff {
		writeUint32(x)
	}
	writeUint32(uint32(len(a.File)))
	for _, f := range a.File {
		writeUint32(f.PkgIdx)
		writeUint32(f.SymIdx)
	}
	writeUint32(uint32(len(a.InlTree)))
	for i := range a.InlTree {
		a.InlTree[i].Write(w)
	}
}

func (a *FuncInfo) Read(b []byte) {
	readUint32 := func() uint32 {
		x := binary.LittleEndian.Uint32(b)
		b = b[4:]
		return x
	}

	a.Args = readUint32()
	a.Locals = readUint32()

	a.Pcsp = readUint32()
	a.Pcfile = readUint32()
	a.Pcline = readUint32()
	a.Pcinline = readUint32()
	pcdatalen := readUint32()
	a.Pcdata = make([]uint32, pcdatalen)
	for i := range a.Pcdata {
		a.Pcdata[i] = readUint32()
	}
	a.PcdataEnd = readUint32()
	funcdataofflen := readUint32()
	a.Funcdataoff = make([]uint32, funcdataofflen)
	for i := range a.Funcdataoff {
		a.Funcdataoff[i] = readUint32()
	}
	filelen := readUint32()
	a.File = make([]SymRef, filelen)
	for i := range a.File {
		a.File[i] = SymRef{readUint32(), readUint32()}
	}
	inltreelen := readUint32()
	a.InlTree = make([]InlTreeNode, inltreelen)
	for i := range a.InlTree {
		b = a.InlTree[i].Read(b)
	}
}

// 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 {
	NumPcdata      uint32
	PcdataOff      uint32
	NumFuncdataoff uint32
	FuncdataoffOff uint32
	NumFile        uint32
	FileOff        uint32
	NumInlTree     uint32
	InlTreeOff     uint32
	Initialized    bool
}

func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths {
	var result FuncInfoLengths

	const numpcdataOff = 24
	result.NumPcdata = binary.LittleEndian.Uint32(b[numpcdataOff:])
	result.PcdataOff = numpcdataOff + 4

	numfuncdataoffOff := result.PcdataOff + 4*(result.NumPcdata+1)
	result.NumFuncdataoff = binary.LittleEndian.Uint32(b[numfuncdataoffOff:])
	result.FuncdataoffOff = numfuncdataoffOff + 4

	numfileOff := result.FuncdataoffOff + 4*result.NumFuncdataoff
	result.NumFile = binary.LittleEndian.Uint32(b[numfileOff:])
	result.FileOff = numfileOff + 4

	const symRefSize = 4 + 4
	numinltreeOff := result.FileOff + symRefSize*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:]) }

// return start and end offsets.
func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) {
	return binary.LittleEndian.Uint32(b[8:]), binary.LittleEndian.Uint32(b[12:])
}

// return start and end offsets.
func (*FuncInfo) ReadPcfile(b []byte) (uint32, uint32) {
	return binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])
}

// return start and end offsets.
func (*FuncInfo) ReadPcline(b []byte) (uint32, uint32) {
	return binary.LittleEndian.Uint32(b[16:]), binary.LittleEndian.Uint32(b[20:])
}

// return start and end offsets.
func (*FuncInfo) ReadPcinline(b []byte, pcdataoffset uint32) (uint32, uint32) {
	return binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[pcdataoffset:])
}

// return start and end offsets.
func (*FuncInfo) ReadPcdata(b []byte, pcdataoffset uint32, k uint32) (uint32, uint32) {
	return binary.LittleEndian.Uint32(b[pcdataoffset+4*k:]), binary.LittleEndian.Uint32(b[pcdataoffset+4+4*k:])
}

func (*FuncInfo) ReadFuncdataoff(b []byte, funcdataofffoff uint32, k uint32) int64 {
	return int64(binary.LittleEndian.Uint32(b[funcdataofffoff+4*k:]))
}

func (*FuncInfo) ReadFile(b []byte, filesoff uint32, k uint32) SymRef {
	p := binary.LittleEndian.Uint32(b[filesoff+8*k:])
	s := binary.LittleEndian.Uint32(b[filesoff+4+8*k:])
	return SymRef{p, s}
}

func (*FuncInfo) ReadInlTree(b []byte, inltreeoff uint32, k uint32) InlTreeNode {
	const inlTreeNodeSize = 4 * 7
	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     SymRef
	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(inl.File.PkgIdx)
	writeUint32(inl.File.SymIdx)
	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 = SymRef{readUint32(), readUint32()}
	inl.Line = int32(readUint32())
	inl.Func = SymRef{readUint32(), readUint32()}
	inl.ParentPC = int32(readUint32())
	return b
}

// New object file format.
//
//    Header struct {
//       Magic       [...]byte   // "\x00go115ld"
//       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
//
//    DwarfFiles [...]string
//
//    SymbolDefs [...]struct {
//       Name string
//       ABI  uint16
//       Type uint8
//       Flag uint8
//       Size uint32
//    }
//    NonPkgDefs [...]struct { // non-pkg symbol definitions
//       ... // same as SymbolDefs
//    }
//    NonPkgRefs [...]struct { // non-pkg symbol references
//       ... // same as SymbolDefs
//    }
//
//    RelocIndex [...]uint32 // index to Relocs
//    AuxIndex   [...]uint32 // index to Aux
//    DataIndex  [...]uint32 // offset to Data
//
//    Relocs [...]struct {
//       Off  int32
//       Size uint8
//       Type uint8
//       Add  int64
//       Sym  symRef
//    }
//
//    Aux [...]struct {
//       Type uint8
//       Sym  symRef
//    }
//
//    Data   [...]byte
//    Pcdata [...]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 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.
//
// 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 non-package symbols, in the
// same order of SymbolDefs/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 and FuncInfo are auxiliary symbols. We
// may make use of aux symbols in more cases, e.g. DWARF 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
	PkgIdxBuiltin                      // Predefined symbols // TODO: not used for now, we could use it for compiler-generated symbols like runtime.newobject
	PkgIdxSelf                         // Symbols defined in the current package
	PkgIdxInvalid = 0
	// The index of other referenced packages starts from 1.
)

// Blocks
const (
	BlkAutolib = iota
	BlkPkgIdx
	BlkDwarfFile
	BlkSymdef
	BlkNonpkgdef
	BlkNonpkgref
	BlkRelocIdx
	BlkAuxIdx
	BlkDataIdx
	BlkReloc
	BlkAux
	BlkData
	BlkPcdata
	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 = "\x00go115ld"

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
//    Siz   uint32
//    Align uint32
// }
type Sym [SymSize]byte

const SymSize = stringRefSize + 2 + 1 + 1 + 4 + 4

const SymABIstatic = ^uint16(0)

const (
	ObjFlagShared = 1 << iota
)

const (
	SymFlagDupok = 1 << iota
	SymFlagLocal
	SymFlagTypelink
	SymFlagLeaf
	SymFlagNoSplit
	SymFlagReflectMethod
	SymFlagGoType
	SymFlagTopFrame
)

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) Siz() uint32 { return binary.LittleEndian.Uint32(s[12:]) }

func (s *Sym) Align() uint32 { return binary.LittleEndian.Uint32(s[16:]) }

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) TopFrame() bool { return s.Flag()&SymFlagTopFrame != 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) SetSiz(x uint32) { binary.LittleEndian.PutUint32(s[12:], x) }

func (s *Sym) SetAlign(x uint32) { binary.LittleEndian.PutUint32(s[16:], 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
}

// Relocation.
//
// Serialized format:
// Reloc struct {
//    Off  int32
//    Siz  uint8
//    Type uint8
//    Add  int64
//    Sym  SymRef
// }
type Reloc [RelocSize]byte

const RelocSize = 4 + 1 + 1 + 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() uint8 { return r[5] }

func (r *Reloc) Add() int64 { return int64(binary.LittleEndian.Uint64(r[6:])) }

func (r *Reloc) Sym() SymRef {
	return SymRef{binary.LittleEndian.Uint32(r[14:]), binary.LittleEndian.Uint32(r[18:])}
}

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 uint8) { r[5] = x }

func (r *Reloc) SetAdd(x int64) { binary.LittleEndian.PutUint64(r[6:], uint64(x)) }

func (r *Reloc) SetSym(x SymRef) {
	binary.LittleEndian.PutUint32(r[14:], x.PkgIdx)
	binary.LittleEndian.PutUint32(r[18:], x.SymIdx)
}

func (r *Reloc) Set(off int32, size uint8, typ uint8, 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

	// TODO: more. Pcdata?
)

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 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
}

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) {
	var b [8]byte
	binary.LittleEndian.PutUint64(b[:], x)
	w.wr.Write(b[:])
	w.off += 8
}

func (w *Writer) Uint32(x uint32) {
	var b [4]byte
	binary.LittleEndian.PutUint32(b[:], x)
	w.wr.Write(b[:])
	w.off += 4
}

func (w *Writer) Uint16(x uint16) {
	var b [2]byte
	binary.LittleEndian.PutUint16(b[:], x)
	w.wr.Write(b[:])
	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

	rd    io.ReaderAt
	start uint32
	h     Header // keep block offsets
}

func NewReaderFromBytes(b []byte, readonly bool) *Reader {
	r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), 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) NDwarfFile() int {
	return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / stringRefSize
}

func (r *Reader) DwarfFile(i int) string {
	off := r.h.Offsets[BlkDwarfFile] + 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) 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 int) uint32 {
	return r.h.Offsets[BlkSymdef] + uint32(i*SymSize)
}

// Sym returns a pointer to the i-th symbol.
func (r *Reader) Sym(i int) *Sym {
	off := r.SymOff(i)
	return (*Sym)(unsafe.Pointer(&r.b[off]))
}

// NReloc returns the number of relocations of the i-th symbol.
func (r *Reader) NReloc(i int) 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 int, 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 int, 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 int) []Reloc {
	off := r.RelocOff(i, 0)
	n := r.NReloc(i)
	return (*[1 << 20]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 int) int {
	auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(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 int, j int) uint32 {
	auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(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 int, 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 int) []Aux {
	off := r.AuxOff(i, 0)
	n := r.NAux(i)
	return (*[1 << 20]Aux)(unsafe.Pointer(&r.b[off]))[:n:n]
}

// DataOff returns the offset of the i-th symbol's data.
func (r *Reader) DataOff(i int) uint32 {
	dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(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 int) int {
	dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4)
	return int(r.uint32At(dataIdxOff+4) - r.uint32At(dataIdxOff))
}

// Data returns the i-th symbol's data.
func (r *Reader) Data(i int) []byte {
	dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4)
	base := r.h.Offsets[BlkData]
	off := r.uint32At(dataIdxOff)
	end := r.uint32At(dataIdxOff + 4)
	return r.BytesAt(base+off, int(end-off))
}

// AuxDataBase returns the base offset of the aux data block.
func (r *Reader) PcdataBase() uint32 {
	return r.h.Offsets[BlkPcdata]
}

// 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
}
