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