| // Copyright 2013 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 ld |
| |
| // Reading of Go object files. |
| |
| import ( |
| "bufio" |
| "bytes" |
| "cmd/internal/bio" |
| "cmd/internal/dwarf" |
| "cmd/internal/objabi" |
| "crypto/sha1" |
| "encoding/base64" |
| "io" |
| "log" |
| "strconv" |
| "strings" |
| ) |
| |
| const ( |
| startmagic = "\x00\x00go19ld" |
| endmagic = "\xff\xffgo19ld" |
| ) |
| |
| var emptyPkg = []byte(`"".`) |
| |
| // objReader reads Go object files. |
| type objReader struct { |
| rd *bufio.Reader |
| ctxt *Link |
| lib *Library |
| pn string |
| dupSym *Symbol |
| localSymVersion int |
| |
| // rdBuf is used by readString and readSymName as scratch for reading strings. |
| rdBuf []byte |
| |
| // List of symbol references for the file being read. |
| refs []*Symbol |
| data []byte |
| reloc []Reloc |
| pcdata []Pcdata |
| autom []Auto |
| funcdata []*Symbol |
| funcdataoff []int64 |
| file []*Symbol |
| } |
| |
| func LoadObjFile(ctxt *Link, f *bio.Reader, lib *Library, length int64, pn string) { |
| |
| start := f.Offset() |
| r := &objReader{ |
| rd: f.Reader, |
| lib: lib, |
| ctxt: ctxt, |
| pn: pn, |
| dupSym: &Symbol{Name: ".dup"}, |
| localSymVersion: ctxt.Syms.IncVersion(), |
| } |
| r.loadObjFile() |
| if f.Offset() != start+length { |
| log.Fatalf("%s: unexpected end at %d, want %d", pn, f.Offset(), start+length) |
| } |
| } |
| |
| func (r *objReader) loadObjFile() { |
| pkg := objabi.PathToPrefix(r.lib.Pkg) |
| |
| // Magic header |
| var buf [8]uint8 |
| r.readFull(buf[:]) |
| if string(buf[:]) != startmagic { |
| log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", r.pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]) |
| } |
| |
| // Version |
| c, err := r.rd.ReadByte() |
| if err != nil || c != 1 { |
| log.Fatalf("%s: invalid file version number %d", r.pn, c) |
| } |
| |
| // Autolib |
| for { |
| lib := r.readString() |
| if lib == "" { |
| break |
| } |
| l := addlib(r.ctxt, pkg, r.pn, lib) |
| if l != nil { |
| r.lib.imports = append(r.lib.imports, l) |
| } |
| } |
| |
| // Symbol references |
| r.refs = []*Symbol{nil} // zeroth ref is nil |
| for { |
| c, err := r.rd.Peek(1) |
| if err != nil { |
| log.Fatalf("%s: peeking: %v", r.pn, err) |
| } |
| if c[0] == 0xff { |
| r.rd.ReadByte() |
| break |
| } |
| r.readRef() |
| } |
| |
| // Lengths |
| r.readSlices() |
| |
| // Data section |
| r.readFull(r.data) |
| |
| // Defined symbols |
| for { |
| c, err := r.rd.Peek(1) |
| if err != nil { |
| log.Fatalf("%s: peeking: %v", r.pn, err) |
| } |
| if c[0] == 0xff { |
| break |
| } |
| r.readSym() |
| } |
| |
| // Magic footer |
| buf = [8]uint8{} |
| r.readFull(buf[:]) |
| if string(buf[:]) != endmagic { |
| log.Fatalf("%s: invalid file end", r.pn) |
| } |
| } |
| |
| func (r *objReader) readSlices() { |
| n := r.readInt() |
| r.data = make([]byte, n) |
| n = r.readInt() |
| r.reloc = make([]Reloc, n) |
| n = r.readInt() |
| r.pcdata = make([]Pcdata, n) |
| n = r.readInt() |
| r.autom = make([]Auto, n) |
| n = r.readInt() |
| r.funcdata = make([]*Symbol, n) |
| r.funcdataoff = make([]int64, n) |
| n = r.readInt() |
| r.file = make([]*Symbol, n) |
| } |
| |
| // Symbols are prefixed so their content doesn't get confused with the magic footer. |
| const symPrefix = 0xfe |
| |
| func (r *objReader) readSym() { |
| var c byte |
| var err error |
| if c, err = r.rd.ReadByte(); c != symPrefix || err != nil { |
| log.Fatalln("readSym out of sync") |
| } |
| if c, err = r.rd.ReadByte(); err != nil { |
| log.Fatalln("error reading input: ", err) |
| } |
| t := abiSymKindToSymKind[c] |
| s := r.readSymIndex() |
| flags := r.readInt() |
| dupok := flags&1 != 0 |
| local := flags&2 != 0 |
| makeTypelink := flags&4 != 0 |
| size := r.readInt() |
| typ := r.readSymIndex() |
| data := r.readData() |
| nreloc := r.readInt() |
| pkg := objabi.PathToPrefix(r.lib.Pkg) |
| isdup := false |
| |
| var dup *Symbol |
| if s.Type != 0 && s.Type != SXREF { |
| if (t == SDATA || t == SBSS || t == SNOPTRBSS) && len(data) == 0 && nreloc == 0 { |
| if s.Size < int64(size) { |
| s.Size = int64(size) |
| } |
| if typ != nil && s.Gotype == nil { |
| s.Gotype = typ |
| } |
| return |
| } |
| |
| if (s.Type == SDATA || s.Type == SBSS || s.Type == SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 { |
| goto overwrite |
| } |
| if s.Type != SBSS && s.Type != SNOPTRBSS && !dupok && !s.Attr.DuplicateOK() { |
| log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, r.pn) |
| } |
| if len(s.P) > 0 { |
| dup = s |
| s = r.dupSym |
| isdup = true |
| } |
| } |
| |
| overwrite: |
| s.File = pkg |
| if dupok { |
| s.Attr |= AttrDuplicateOK |
| } |
| if t == SXREF { |
| log.Fatalf("bad sxref") |
| } |
| if t == 0 { |
| log.Fatalf("missing type for %s in %s", s.Name, r.pn) |
| } |
| if t == SBSS && (s.Type == SRODATA || s.Type == SNOPTRBSS) { |
| t = s.Type |
| } |
| s.Type = t |
| if s.Size < int64(size) { |
| s.Size = int64(size) |
| } |
| s.Attr.Set(AttrLocal, local) |
| s.Attr.Set(AttrMakeTypelink, makeTypelink) |
| if typ != nil { |
| s.Gotype = typ |
| } |
| if isdup && typ != nil { // if bss sym defined multiple times, take type from any one def |
| dup.Gotype = typ |
| } |
| s.P = data |
| if nreloc > 0 { |
| s.R = r.reloc[:nreloc:nreloc] |
| if !isdup { |
| r.reloc = r.reloc[nreloc:] |
| } |
| |
| for i := 0; i < nreloc; i++ { |
| s.R[i] = Reloc{ |
| Off: r.readInt32(), |
| Siz: r.readUint8(), |
| Type: objabi.RelocType(r.readInt32()), |
| Add: r.readInt64(), |
| Sym: r.readSymIndex(), |
| } |
| } |
| } |
| |
| if s.Type == STEXT { |
| s.FuncInfo = new(FuncInfo) |
| pc := s.FuncInfo |
| |
| pc.Args = r.readInt32() |
| pc.Locals = r.readInt32() |
| if r.readUint8() != 0 { |
| s.Attr |= AttrNoSplit |
| } |
| flags := r.readInt() |
| if flags&(1<<2) != 0 { |
| s.Attr |= AttrReflectMethod |
| } |
| if flags&(1<<3) != 0 { |
| s.Attr |= AttrShared |
| } |
| n := r.readInt() |
| pc.Autom = r.autom[:n:n] |
| if !isdup { |
| r.autom = r.autom[n:] |
| } |
| |
| for i := 0; i < n; i++ { |
| pc.Autom[i] = Auto{ |
| Asym: r.readSymIndex(), |
| Aoffset: r.readInt32(), |
| Name: r.readInt16(), |
| Gotype: r.readSymIndex(), |
| } |
| } |
| |
| pc.Pcsp.P = r.readData() |
| pc.Pcfile.P = r.readData() |
| pc.Pcline.P = r.readData() |
| pc.Pcinline.P = r.readData() |
| n = r.readInt() |
| pc.Pcdata = r.pcdata[:n:n] |
| if !isdup { |
| r.pcdata = r.pcdata[n:] |
| } |
| for i := 0; i < n; i++ { |
| pc.Pcdata[i].P = r.readData() |
| } |
| n = r.readInt() |
| pc.Funcdata = r.funcdata[:n:n] |
| pc.Funcdataoff = r.funcdataoff[:n:n] |
| if !isdup { |
| r.funcdata = r.funcdata[n:] |
| r.funcdataoff = r.funcdataoff[n:] |
| } |
| for i := 0; i < n; i++ { |
| pc.Funcdata[i] = r.readSymIndex() |
| } |
| for i := 0; i < n; i++ { |
| pc.Funcdataoff[i] = r.readInt64() |
| } |
| n = r.readInt() |
| pc.File = r.file[:n:n] |
| if !isdup { |
| r.file = r.file[n:] |
| } |
| for i := 0; i < n; i++ { |
| pc.File[i] = r.readSymIndex() |
| } |
| n = r.readInt() |
| pc.InlTree = make([]InlinedCall, n) |
| for i := 0; i < n; i++ { |
| pc.InlTree[i].Parent = r.readInt32() |
| pc.InlTree[i].File = r.readSymIndex() |
| pc.InlTree[i].Line = r.readInt32() |
| pc.InlTree[i].Func = r.readSymIndex() |
| } |
| |
| if !dupok { |
| if s.Attr.OnList() { |
| log.Fatalf("symbol %s listed multiple times", s.Name) |
| } |
| s.Attr |= AttrOnList |
| r.lib.textp = append(r.lib.textp, s) |
| } else { |
| // there may ba a dup in another package |
| // put into a temp list and add to text later |
| if !isdup { |
| r.lib.dupTextSyms = append(r.lib.dupTextSyms, s) |
| } else { |
| r.lib.dupTextSyms = append(r.lib.dupTextSyms, dup) |
| } |
| } |
| } |
| if s.Type == SDWARFINFO { |
| r.patchDWARFName(s) |
| } |
| } |
| |
| func (r *objReader) patchDWARFName(s *Symbol) { |
| // This is kind of ugly. Really the package name should not |
| // even be included here. |
| if s.Size < 1 || s.P[0] != dwarf.DW_ABRV_FUNCTION { |
| return |
| } |
| e := bytes.IndexByte(s.P, 0) |
| if e == -1 { |
| return |
| } |
| p := bytes.Index(s.P[:e], emptyPkg) |
| if p == -1 { |
| return |
| } |
| pkgprefix := []byte(objabi.PathToPrefix(r.lib.Pkg) + ".") |
| patched := bytes.Replace(s.P[:e], emptyPkg, pkgprefix, -1) |
| |
| s.P = append(patched, s.P[e:]...) |
| delta := int64(len(s.P)) - s.Size |
| s.Size = int64(len(s.P)) |
| for i := range s.R { |
| r := &s.R[i] |
| if r.Off > int32(e) { |
| r.Off += int32(delta) |
| } |
| } |
| } |
| |
| func (r *objReader) readFull(b []byte) { |
| _, err := io.ReadFull(r.rd, b) |
| if err != nil { |
| log.Fatalf("%s: error reading %s", r.pn, err) |
| } |
| } |
| |
| func (r *objReader) readRef() { |
| if c, err := r.rd.ReadByte(); c != symPrefix || err != nil { |
| log.Fatalf("readSym out of sync") |
| } |
| name := r.readSymName() |
| v := r.readInt() |
| if v != 0 && v != 1 { |
| log.Fatalf("invalid symbol version for %q: %d", name, v) |
| } |
| if v == 1 { |
| v = r.localSymVersion |
| } |
| s := r.ctxt.Syms.Lookup(name, v) |
| r.refs = append(r.refs, s) |
| |
| if s == nil || v != 0 { |
| return |
| } |
| if s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 { |
| x, err := strconv.ParseUint(s.Name[5:], 16, 64) |
| if err != nil { |
| log.Panicf("failed to parse $-symbol %s: %v", s.Name, err) |
| } |
| s.Type = SRODATA |
| s.Attr |= AttrLocal |
| switch s.Name[:5] { |
| case "$f32.": |
| if uint64(uint32(x)) != x { |
| log.Panicf("$-symbol %s too large: %d", s.Name, x) |
| } |
| Adduint32(r.ctxt, s, uint32(x)) |
| case "$f64.", "$i64.": |
| Adduint64(r.ctxt, s, x) |
| default: |
| log.Panicf("unrecognized $-symbol: %s", s.Name) |
| } |
| s.Attr.Set(AttrReachable, false) |
| } |
| if strings.HasPrefix(s.Name, "runtime.gcbits.") { |
| s.Attr |= AttrLocal |
| } |
| } |
| |
| func (r *objReader) readInt64() int64 { |
| uv := uint64(0) |
| for shift := uint(0); ; shift += 7 { |
| if shift >= 64 { |
| log.Fatalf("corrupt input") |
| } |
| c, err := r.rd.ReadByte() |
| if err != nil { |
| log.Fatalln("error reading input: ", err) |
| } |
| uv |= uint64(c&0x7F) << shift |
| if c&0x80 == 0 { |
| break |
| } |
| } |
| |
| return int64(uv>>1) ^ (int64(uv<<63) >> 63) |
| } |
| |
| func (r *objReader) readInt() int { |
| n := r.readInt64() |
| if int64(int(n)) != n { |
| log.Panicf("%v out of range for int", n) |
| } |
| return int(n) |
| } |
| |
| func (r *objReader) readInt32() int32 { |
| n := r.readInt64() |
| if int64(int32(n)) != n { |
| log.Panicf("%v out of range for int32", n) |
| } |
| return int32(n) |
| } |
| |
| func (r *objReader) readInt16() int16 { |
| n := r.readInt64() |
| if int64(int16(n)) != n { |
| log.Panicf("%v out of range for int16", n) |
| } |
| return int16(n) |
| } |
| |
| func (r *objReader) readUint8() uint8 { |
| n := r.readInt64() |
| if int64(uint8(n)) != n { |
| log.Panicf("%v out of range for uint8", n) |
| } |
| return uint8(n) |
| } |
| |
| func (r *objReader) readString() string { |
| n := r.readInt() |
| if cap(r.rdBuf) < n { |
| r.rdBuf = make([]byte, 2*n) |
| } |
| r.readFull(r.rdBuf[:n]) |
| return string(r.rdBuf[:n]) |
| } |
| |
| func (r *objReader) readData() []byte { |
| n := r.readInt() |
| p := r.data[:n:n] |
| r.data = r.data[n:] |
| return p |
| } |
| |
| // readSymName reads a symbol name, replacing all "". with pkg. |
| func (r *objReader) readSymName() string { |
| pkg := objabi.PathToPrefix(r.lib.Pkg) |
| n := r.readInt() |
| if n == 0 { |
| r.readInt64() |
| return "" |
| } |
| if cap(r.rdBuf) < n { |
| r.rdBuf = make([]byte, 2*n) |
| } |
| origName, err := r.rd.Peek(n) |
| if err == bufio.ErrBufferFull { |
| // Long symbol names are rare but exist. One source is type |
| // symbols for types with long string forms. See #15104. |
| origName = make([]byte, n) |
| r.readFull(origName) |
| } else if err != nil { |
| log.Fatalf("%s: error reading symbol: %v", r.pn, err) |
| } |
| adjName := r.rdBuf[:0] |
| for { |
| i := bytes.Index(origName, emptyPkg) |
| if i == -1 { |
| s := string(append(adjName, origName...)) |
| // Read past the peeked origName, now that we're done with it, |
| // using the rfBuf (also no longer used) as the scratch space. |
| // TODO: use bufio.Reader.Discard if available instead? |
| if err == nil { |
| r.readFull(r.rdBuf[:n]) |
| } |
| r.rdBuf = adjName[:0] // in case 2*n wasn't enough |
| |
| if Buildmode == BuildmodeShared || *FlagLinkshared { |
| // These types are included in the symbol |
| // table when dynamically linking. To keep |
| // binary size down, we replace the names |
| // with SHA-1 prefixes. |
| // |
| // Keep the type.. prefix, which parts of the |
| // linker (like the DWARF generator) know means |
| // the symbol is not decodable. |
| // |
| // Leave type.runtime. symbols alone, because |
| // other parts of the linker manipulates them, |
| // and also symbols whose names would not be |
| // shortened by this process. |
| if len(s) > 14 && strings.HasPrefix(s, "type.") && !strings.HasPrefix(s, "type.runtime.") { |
| hash := sha1.Sum([]byte(s)) |
| prefix := "type." |
| if s[5] == '.' { |
| prefix = "type.." |
| } |
| s = prefix + base64.StdEncoding.EncodeToString(hash[:6]) |
| } |
| } |
| return s |
| } |
| adjName = append(adjName, origName[:i]...) |
| adjName = append(adjName, pkg...) |
| adjName = append(adjName, '.') |
| origName = origName[i+len(emptyPkg):] |
| } |
| } |
| |
| // Reads the index of a symbol reference and resolves it to a symbol |
| func (r *objReader) readSymIndex() *Symbol { |
| i := r.readInt() |
| return r.refs[i] |
| } |