diff options
Diffstat (limited to 'src/cmd/internal/goobj2/objfile.go')
| -rw-r--r-- | src/cmd/internal/goobj2/objfile.go | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go new file mode 100644 index 0000000000..4c364b0c54 --- /dev/null +++ b/src/cmd/internal/goobj2/objfile.go @@ -0,0 +1,587 @@ +// 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 new object file format, reading and writing. + +package goobj2 // TODO: replace the goobj package? + +import ( + "bytes" + "cmd/internal/bio" + "encoding/binary" + "errors" + "fmt" + "io" + "unsafe" +) + +// New object file format. +// +// Header struct { +// Magic [...]byte // "\x00go114LD" +// Flags uint32 +// // TODO: Fingerprint +// Offsets [...]uint32 // byte offset of each block below +// } +// +// Strings [...]struct { +// Len uint32 +// Data [...]byte +// } +// +// Autolib [...]stringOff // imported packages (for file loading) // TODO: add fingerprints +// PkgIndex [...]stringOff // referenced packages by index +// +// DwarfFiles [...]stringOff +// +// SymbolDefs [...]struct { +// Name stringOff +// 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 +// +// stringOff is a uint32 (?) offset that points to the corresponding +// string, which is a uint32 length followed by that number of 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. + +// 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 + NBlk +) + +// File header. +// TODO: probably no need to export this. +type Header struct { + Magic string + Flags uint32 + Offsets [NBlk]uint32 +} + +const Magic = "\x00go114LD" + +func (h *Header) Write(w *Writer) { + w.RawString(h.Magic) + 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)) + 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) +} + +// Symbol definition. +type Sym struct { + Name string + ABI uint16 + Type uint8 + Flag uint8 + Siz uint32 +} + +const SymABIstatic = ^uint16(0) + +const ( + ObjFlagShared = 1 << iota +) + +const ( + SymFlagDupok = 1 << iota + SymFlagLocal + SymFlagTypelink + SymFlagLeaf + SymFlagCFunc + SymFlagReflectMethod + SymFlagGoType + SymFlagTopFrame +) + +func (s *Sym) Write(w *Writer) { + w.StringRef(s.Name) + w.Uint16(s.ABI) + w.Uint8(s.Type) + w.Uint8(s.Flag) + w.Uint32(s.Siz) +} + +func (s *Sym) Read(r *Reader, off uint32) { + s.Name = r.StringRef(off) + s.ABI = r.uint16At(off + 4) + s.Type = r.uint8At(off + 6) + s.Flag = r.uint8At(off + 7) + s.Siz = r.uint32At(off + 8) +} + +func (s *Sym) Size() int { + return 4 + 2 + 1 + 1 + 4 +} + +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) CFunc() bool { return s.Flag&SymFlagCFunc != 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 } + +// Symbol reference. +type SymRef struct { + PkgIdx uint32 + SymIdx uint32 +} + +func (s *SymRef) Write(w *Writer) { + w.Uint32(s.PkgIdx) + w.Uint32(s.SymIdx) +} + +func (s *SymRef) Read(r *Reader, off uint32) { + s.PkgIdx = r.uint32At(off) + s.SymIdx = r.uint32At(off + 4) +} + +func (s *SymRef) Size() int { + return 4 + 4 +} + +// Relocation. +type Reloc struct { + Off int32 + Siz uint8 + Type uint8 + Add int64 + Sym SymRef +} + +func (r *Reloc) Write(w *Writer) { + w.Uint32(uint32(r.Off)) + w.Uint8(r.Siz) + w.Uint8(r.Type) + w.Uint64(uint64(r.Add)) + r.Sym.Write(w) +} + +func (o *Reloc) Read(r *Reader, off uint32) { + o.Off = r.int32At(off) + o.Siz = r.uint8At(off + 4) + o.Type = r.uint8At(off + 5) + o.Add = r.int64At(off + 6) + o.Sym.Read(r, off+14) +} + +func (r *Reloc) Size() int { + return 4 + 1 + 1 + 8 + r.Sym.Size() +} + +// Aux symbol info. +type Aux struct { + Type uint8 + Sym SymRef +} + +// Aux Type +const ( + AuxGotype = iota + AuxFuncInfo + AuxFuncdata + AuxDwarfInfo + AuxDwarfLoc + AuxDwarfRanges + AuxDwarfLines + + // TODO: more. Pcdata? +) + +func (a *Aux) Write(w *Writer) { + w.Uint8(a.Type) + a.Sym.Write(w) +} + +func (a *Aux) Read(r *Reader, off uint32) { + a.Type = r.uint8At(off) + a.Sym.Read(r, off+1) +} + +func (a *Aux) Size() int { + return 1 + a.Sym.Size() +} + +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.Uint32(uint32(len(s))) + w.RawString(s) +} + +func (w *Writer) StringRef(s string) { + off, ok := w.stringMap[s] + if !ok { + panic(fmt.Sprintf("writeStringRef: string not added: %q", s)) + } + w.Uint32(off) +} + +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) string { + l := r.uint32At(off) + b := r.b[off+4 : off+4+l] + if r.readonly { + return toString(b) // backed by RO memory, ok to make unsafe string + } + return string(b) +} + +func toString(b []byte) string { + type stringHeader struct { + str unsafe.Pointer + len int + } + + if len(b) == 0 { + return "" + } + ss := stringHeader{str: unsafe.Pointer(&b[0]), len: len(b)} + s := *(*string)(unsafe.Pointer(&ss)) + return s +} + +func (r *Reader) StringRef(off uint32) string { + return r.StringAt(r.uint32At(off)) +} + +func (r *Reader) Autolib() []string { + n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / 4 + s := make([]string, n) + for i := range s { + off := r.h.Offsets[BlkAutolib] + uint32(i)*4 + s[i] = r.StringRef(off) + } + return s +} + +func (r *Reader) Pkglist() []string { + n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / 4 + s := make([]string, n) + for i := range s { + off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4 + s[i] = r.StringRef(off) + } + return s +} + +func (r *Reader) NPkg() int { + return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / 4 +} + +func (r *Reader) Pkg(i int) string { + off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4 + return r.StringRef(off) +} + +func (r *Reader) NDwarfFile() int { + return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / 4 +} + +func (r *Reader) DwarfFile(i int) string { + off := r.h.Offsets[BlkDwarfFile] + uint32(i)*4 + return r.StringRef(off) +} + +func (r *Reader) NSym() int { + symsiz := (&Sym{}).Size() + return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / symsiz +} + +func (r *Reader) NNonpkgdef() int { + symsiz := (&Sym{}).Size() + return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / symsiz +} + +func (r *Reader) NNonpkgref() int { + symsiz := (&Sym{}).Size() + return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / symsiz +} + +// SymOff returns the offset of the i-th symbol. +func (r *Reader) SymOff(i int) uint32 { + symsiz := (&Sym{}).Size() + return r.h.Offsets[BlkSymdef] + uint32(i*symsiz) +} + +// 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) + relocsiz := (&Reloc{}).Size() + return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(relocsiz) +} + +// 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) + auxsiz := (&Aux{}).Size() + return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(auxsiz) +} + +// 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 { + return int(r.DataOff(i+1) - r.DataOff(i)) +} + +// Data returns the i-th symbol's data. +func (r *Reader) Data(i int) []byte { + return r.BytesAt(r.DataOff(i), r.DataSize(i)) +} + +// AuxDataBase returns the base offset of the aux data block. +func (r *Reader) PcdataBase() uint32 { + return r.h.Offsets[BlkPcdata] +} + +// 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 +} |
