diff options
| author | Cherry Zhang <cherryyz@google.com> | 2020-07-30 20:49:29 -0400 |
|---|---|---|
| committer | Cherry Zhang <cherryyz@google.com> | 2020-08-11 17:15:15 +0000 |
| commit | 9559877543976f8e7b15ae02a7196c4f930fc371 (patch) | |
| tree | cfb69dcc4a81d16d03664d8f578b7544712a42b7 /src/cmd/internal/objfile | |
| parent | a8463c32823d451800aae14226c1e36481d692bc (diff) | |
| download | go-9559877543976f8e7b15ae02a7196c4f930fc371.tar.xz | |
[dev.link] cmd/internal/objfile: read Go object file using goobj2 package
Read Go object files using cmd/internal/goobj2 package directly,
instead of using cmd/internal/goobj as an intermediate layer.
Now cmd/internal/archive is only about reading archives.
Change-Id: Ifecb217fb26c16c26fc1bbc3fba0ed44710020ed
Reviewed-on: https://go-review.googlesource.com/c/go/+/246443
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Diffstat (limited to 'src/cmd/internal/objfile')
| -rw-r--r-- | src/cmd/internal/objfile/goobj.go | 283 |
1 files changed, 205 insertions, 78 deletions
diff --git a/src/cmd/internal/objfile/goobj.go b/src/cmd/internal/objfile/goobj.go index a7e45b9ecb..6bf9d3bf21 100644 --- a/src/cmd/internal/objfile/goobj.go +++ b/src/cmd/internal/objfile/goobj.go @@ -7,95 +7,207 @@ package objfile import ( - goobj "cmd/internal/archive" + "cmd/internal/archive" + "cmd/internal/goobj2" "cmd/internal/objabi" "cmd/internal/sys" "debug/dwarf" "debug/gosym" "errors" "fmt" + "io" "os" + "strings" ) type goobjFile struct { - goobj *goobj.Package - f *os.File // the underlying .o or .a file + goobj *archive.GoObj + r *goobj2.Reader + f *os.File } -func openGoFile(r *os.File) (*File, error) { - f, err := goobj.Parse(r, `""`) +func openGoFile(f *os.File) (*File, error) { + a, err := archive.Parse(f) if err != nil { return nil, err } - rf := &goobjFile{goobj: f, f: r} - if len(f.Native) == 0 { - return &File{r, []*Entry{{raw: rf}}}, nil - } - entries := make([]*Entry, len(f.Native)+1) - entries[0] = &Entry{ - raw: rf, - } + entries := make([]*Entry, 0, len(a.Entries)) L: - for i, nr := range f.Native { - for _, try := range openers { - if raw, err := try(nr); err == nil { - entries[i+1] = &Entry{ - name: nr.Name, - raw: raw, + for _, e := range a.Entries { + switch e.Type { + case archive.EntryPkgDef: + continue + case archive.EntryGoObj: + o := e.Obj + b := make([]byte, o.Size) + _, err := f.ReadAt(b, o.Offset) + if err != nil { + return nil, err + } + r := goobj2.NewReaderFromBytes(b, false) + entries = append(entries, &Entry{ + name: e.Name, + raw: &goobjFile{e.Obj, r, f}, + }) + continue + case archive.EntryNativeObj: + nr := io.NewSectionReader(f, e.Offset, e.Size) + for _, try := range openers { + if raw, err := try(nr); err == nil { + entries = append(entries, &Entry{ + name: e.Name, + raw: raw, + }) + continue L } - continue L } } - return nil, fmt.Errorf("open %s: unrecognized archive member %s", r.Name(), nr.Name) + return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name) } - return &File{r, entries}, nil + return &File{f, entries}, nil } -func goobjName(id goobj.SymID) string { - if id.Version == 0 { - return id.Name +func goobjName(name string, ver int) string { + if ver == 0 { + return name } - return fmt.Sprintf("%s<%d>", id.Name, id.Version) + return fmt.Sprintf("%s<%d>", name, ver) } -func (f *goobjFile) symbols() ([]Sym, error) { - seen := make(map[goobj.SymID]bool) +type goobjReloc struct { + Off int32 + Size uint8 + Type objabi.RelocType + Add int64 + Sym string +} +func (r goobjReloc) String(insnOffset uint64) string { + delta := int64(r.Off) - int64(insnOffset) + s := fmt.Sprintf("[%d:%d]%s", delta, delta+int64(r.Size), r.Type) + if r.Sym != "" { + if r.Add != 0 { + return fmt.Sprintf("%s:%s+%d", s, r.Sym, r.Add) + } + return fmt.Sprintf("%s:%s", s, r.Sym) + } + if r.Add != 0 { + return fmt.Sprintf("%s:%d", s, r.Add) + } + return s +} + +func (f *goobjFile) symbols() ([]Sym, error) { + r := f.r var syms []Sym - for _, s := range f.goobj.Syms { - seen[s.SymID] = true - sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: s.Size, Type: s.Type.Name, Code: '?'} - switch s.Kind { + + // Name of referenced indexed symbols. + nrefName := r.NRefName() + refNames := make(map[goobj2.SymRef]string, nrefName) + for i := 0; i < nrefName; i++ { + rn := r.RefName(i) + refNames[rn.Sym()] = rn.Name(r) + } + + abiToVer := func(abi uint16) int { + var ver int + if abi == goobj2.SymABIstatic { + // Static symbol + ver = 1 + } + return ver + } + + resolveSymRef := func(s goobj2.SymRef) string { + var i uint32 + switch p := s.PkgIdx; p { + case goobj2.PkgIdxInvalid: + if s.SymIdx != 0 { + panic("bad sym ref") + } + return "" + case goobj2.PkgIdxHashed64: + i = s.SymIdx + uint32(r.NSym()) + case goobj2.PkgIdxHashed: + i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()) + case goobj2.PkgIdxNone: + i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef()) + case goobj2.PkgIdxBuiltin: + name, abi := goobj2.BuiltinName(int(s.SymIdx)) + return goobjName(name, abi) + case goobj2.PkgIdxSelf: + i = s.SymIdx + default: + return refNames[s] + } + sym := r.Sym(i) + return goobjName(sym.Name(r), abiToVer(sym.ABI())) + } + + // Defined symbols + ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) + for i := uint32(0); i < ndef; i++ { + osym := r.Sym(i) + if osym.Name(r) == "" { + continue // not a real symbol + } + name := osym.Name(r) + ver := osym.ABI() + name = goobjName(name, abiToVer(ver)) + typ := objabi.SymKind(osym.Type()) + var code rune = '?' + switch typ { case objabi.STEXT: - sym.Code = 'T' + code = 'T' case objabi.SRODATA: - sym.Code = 'R' + code = 'R' case objabi.SDATA: - sym.Code = 'D' + code = 'D' case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS: - sym.Code = 'B' + code = 'B' } - if s.Version != 0 { - sym.Code += 'a' - 'A' + if ver >= goobj2.SymABIstatic { + code += 'a' - 'A' } - for i, r := range s.Reloc { - sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]}) + + sym := Sym{ + Name: name, + Addr: uint64(r.DataOff(i)), + Size: int64(osym.Siz()), + Code: code, } - syms = append(syms, sym) - } - for _, s := range f.goobj.Syms { - for _, r := range s.Reloc { - if !seen[r.Sym] { - seen[r.Sym] = true - sym := Sym{Name: goobjName(r.Sym), Code: 'U'} - if s.Version != 0 { - // should not happen but handle anyway - sym.Code = 'u' - } - syms = append(syms, sym) + relocs := r.Relocs(i) + sym.Relocs = make([]Reloc, len(relocs)) + for j := range relocs { + rel := &relocs[j] + sym.Relocs[j] = Reloc{ + Addr: uint64(r.DataOff(i)) + uint64(rel.Off()), + Size: uint64(rel.Siz()), + Stringer: goobjReloc{ + Off: rel.Off(), + Size: rel.Siz(), + Type: objabi.RelocType(rel.Type()), + Add: rel.Add(), + Sym: resolveSymRef(rel.Sym()), + }, } } + + syms = append(syms, sym) + } + + // Referenced symbols + n := ndef + uint32(r.NNonpkgref()) + for i := ndef; i < n; i++ { + osym := r.Sym(i) + sym := Sym{Name: osym.Name(r), Code: 'U'} + syms = append(syms, sym) + } + for i := 0; i < nrefName; i++ { + rn := r.RefName(i) + sym := Sym{Name: rn.Name(r), Code: 'U'} + syms = append(syms, sym) } return syms, nil @@ -112,9 +224,11 @@ func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) // This function implements the Liner interface in preference to pcln() above. func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { // TODO: this is really inefficient. Binary search? Memoize last result? + r := f.r var arch *sys.Arch + archname := f.goarch() for _, a := range sys.Archs { - if a.Name == f.goobj.Arch { + if a.Name == archname { arch = a break } @@ -122,29 +236,43 @@ func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { if arch == nil { return "", 0, nil } - for _, s := range f.goobj.Syms { - if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) { + pcdataBase := r.PcdataBase() + ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) + for i := uint32(0); i < ndef; i++ { + osym := r.Sym(i) + addr := uint64(r.DataOff(i)) + if pc < addr || pc >= addr+uint64(osym.Siz()) { continue } - if s.Func == nil { - return "", 0, nil - } - pcfile := make([]byte, s.Func.PCFile.Size) - _, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset) - if err != nil { - return "", 0, nil + isym := ^uint32(0) + auxs := r.Auxs(i) + for j := range auxs { + a := &auxs[j] + if a.Type() != goobj2.AuxFuncInfo { + continue + } + if a.Sym().PkgIdx != goobj2.PkgIdxSelf { + panic("funcinfo symbol not defined in current package") + } + isym = a.Sym().SymIdx } - fileID := int(pcValue(pcfile, pc-uint64(s.Data.Offset), arch)) - fileName := f.goobj.FileList[fileID] - pcline := make([]byte, s.Func.PCLine.Size) - _, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset) - if err != nil { - return "", 0, nil + if isym == ^uint32(0) { + continue } - line := int(pcValue(pcline, pc-uint64(s.Data.Offset), arch)) + b := r.BytesAt(r.DataOff(isym), r.DataSize(isym)) + var info *goobj2.FuncInfo + lengths := info.ReadFuncInfoLengths(b) + off, end := info.ReadPcline(b) + pcline := r.BytesAt(pcdataBase+off, int(end-off)) + line := int(pcValue(pcline, pc-addr, arch)) + off, end = info.ReadPcfile(b) + pcfile := r.BytesAt(pcdataBase+off, int(end-off)) + fileID := pcValue(pcfile, pc-addr, arch) + globalFileID := info.ReadFile(b, lengths.FileOff, uint32(fileID)) + fileName := r.File(int(globalFileID)) // Note: we provide only the name in the Func structure. // We could provide more if needed. - return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}} + return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}} } return "", 0, nil } @@ -198,18 +326,17 @@ func readvarint(p *[]byte) uint32 { // We treat the whole object file as the text section. func (f *goobjFile) text() (textStart uint64, text []byte, err error) { - var info os.FileInfo - info, err = f.f.Stat() - if err != nil { - return - } - text = make([]byte, info.Size()) - _, err = f.f.ReadAt(text, 0) + text = make([]byte, f.goobj.Size) + _, err = f.f.ReadAt(text, int64(f.goobj.Offset)) return } func (f *goobjFile) goarch() string { - return f.goobj.Arch + hs := strings.Fields(string(f.goobj.TextHeader)) + if len(hs) >= 4 { + return hs[3] + } + return "" } func (f *goobjFile) loadAddress() (uint64, error) { |
