2016-03-01 22:57:46 +00:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
2013-12-16 12:52:11 -05:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
// Parsing of Go intermediate object files and archives.
|
|
|
|
|
|
2014-08-07 12:33:06 -04:00
|
|
|
package objfile
|
2013-12-16 12:52:11 -05:00
|
|
|
|
|
|
|
|
import (
|
2020-07-30 20:49:29 -04:00
|
|
|
"cmd/internal/archive"
|
2020-08-02 19:36:28 -04:00
|
|
|
"cmd/internal/goobj"
|
2017-04-18 12:53:25 -07:00
|
|
|
"cmd/internal/objabi"
|
2016-07-02 17:19:25 -07:00
|
|
|
"cmd/internal/sys"
|
2016-03-23 17:10:18 -07:00
|
|
|
"debug/dwarf"
|
2016-07-02 17:19:25 -07:00
|
|
|
"debug/gosym"
|
2016-03-23 17:10:18 -07:00
|
|
|
"errors"
|
2013-12-16 12:52:11 -05:00
|
|
|
"fmt"
|
2020-07-30 20:49:29 -04:00
|
|
|
"io"
|
2013-12-16 12:52:11 -05:00
|
|
|
"os"
|
|
|
|
|
)
|
|
|
|
|
|
2014-08-07 12:33:06 -04:00
|
|
|
type goobjFile struct {
|
2020-07-30 20:49:29 -04:00
|
|
|
goobj *archive.GoObj
|
2020-08-02 19:36:28 -04:00
|
|
|
r *goobj.Reader
|
2020-07-30 20:49:29 -04:00
|
|
|
f *os.File
|
2020-08-19 01:38:43 +00:00
|
|
|
arch *sys.Arch
|
2014-08-07 12:33:06 -04:00
|
|
|
}
|
|
|
|
|
|
2020-07-30 20:49:29 -04:00
|
|
|
func openGoFile(f *os.File) (*File, error) {
|
2020-08-05 21:16:52 -04:00
|
|
|
a, err := archive.Parse(f, false)
|
2014-08-07 12:33:06 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
entries := make([]*Entry, 0, len(a.Entries))
|
2017-09-16 15:28:14 +09:00
|
|
|
L:
|
2020-07-30 20:49:29 -04:00
|
|
|
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
|
|
|
|
|
}
|
2020-08-02 19:36:28 -04:00
|
|
|
r := goobj.NewReaderFromBytes(b, false)
|
2020-08-19 01:38:43 +00:00
|
|
|
var arch *sys.Arch
|
|
|
|
|
for _, a := range sys.Archs {
|
|
|
|
|
if a.Name == e.Obj.Arch {
|
|
|
|
|
arch = a
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
entries = append(entries, &Entry{
|
|
|
|
|
name: e.Name,
|
2020-08-19 01:38:43 +00:00
|
|
|
raw: &goobjFile{e.Obj, r, f, arch},
|
2020-07-30 20:49:29 -04:00
|
|
|
})
|
|
|
|
|
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
|
2017-09-16 15:28:14 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name)
|
2017-09-16 15:28:14 +09:00
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
return &File{f, entries}, nil
|
2014-08-07 12:33:06 -04:00
|
|
|
}
|
|
|
|
|
|
2020-07-30 20:49:29 -04:00
|
|
|
func goobjName(name string, ver int) string {
|
|
|
|
|
if ver == 0 {
|
|
|
|
|
return name
|
2013-12-16 12:52:11 -05:00
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
return fmt.Sprintf("%s<%d>", name, ver)
|
2013-12-16 12:52:11 -05:00
|
|
|
}
|
|
|
|
|
|
2020-07-30 20:49:29 -04:00
|
|
|
type goobjReloc struct {
|
|
|
|
|
Off int32
|
|
|
|
|
Size uint8
|
|
|
|
|
Type objabi.RelocType
|
|
|
|
|
Add int64
|
|
|
|
|
Sym string
|
|
|
|
|
}
|
2013-12-16 12:52:11 -05:00
|
|
|
|
2020-07-30 20:49:29 -04:00
|
|
|
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
|
2013-12-16 12:52:11 -05:00
|
|
|
var syms []Sym
|
2020-07-30 20:49:29 -04:00
|
|
|
|
|
|
|
|
// Name of referenced indexed symbols.
|
|
|
|
|
nrefName := r.NRefName()
|
2020-08-02 19:36:28 -04:00
|
|
|
refNames := make(map[goobj.SymRef]string, nrefName)
|
2020-07-30 20:49:29 -04:00
|
|
|
for i := 0; i < nrefName; i++ {
|
|
|
|
|
rn := r.RefName(i)
|
|
|
|
|
refNames[rn.Sym()] = rn.Name(r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
abiToVer := func(abi uint16) int {
|
|
|
|
|
var ver int
|
2020-08-02 19:36:28 -04:00
|
|
|
if abi == goobj.SymABIstatic {
|
2020-07-30 20:49:29 -04:00
|
|
|
// Static symbol
|
|
|
|
|
ver = 1
|
|
|
|
|
}
|
|
|
|
|
return ver
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-02 19:36:28 -04:00
|
|
|
resolveSymRef := func(s goobj.SymRef) string {
|
2020-07-30 20:49:29 -04:00
|
|
|
var i uint32
|
|
|
|
|
switch p := s.PkgIdx; p {
|
2020-08-02 19:36:28 -04:00
|
|
|
case goobj.PkgIdxInvalid:
|
2020-07-30 20:49:29 -04:00
|
|
|
if s.SymIdx != 0 {
|
|
|
|
|
panic("bad sym ref")
|
|
|
|
|
}
|
|
|
|
|
return ""
|
2020-08-02 19:36:28 -04:00
|
|
|
case goobj.PkgIdxHashed64:
|
2020-07-30 20:49:29 -04:00
|
|
|
i = s.SymIdx + uint32(r.NSym())
|
2020-08-02 19:36:28 -04:00
|
|
|
case goobj.PkgIdxHashed:
|
2020-07-30 20:49:29 -04:00
|
|
|
i = s.SymIdx + uint32(r.NSym()+r.NHashed64def())
|
2020-08-02 19:36:28 -04:00
|
|
|
case goobj.PkgIdxNone:
|
2020-07-30 20:49:29 -04:00
|
|
|
i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef())
|
2020-08-02 19:36:28 -04:00
|
|
|
case goobj.PkgIdxBuiltin:
|
|
|
|
|
name, abi := goobj.BuiltinName(int(s.SymIdx))
|
2020-07-30 20:49:29 -04:00
|
|
|
return goobjName(name, abi)
|
2020-08-02 19:36:28 -04:00
|
|
|
case goobj.PkgIdxSelf:
|
2020-07-30 20:49:29 -04:00
|
|
|
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 {
|
2017-04-19 15:15:35 +12:00
|
|
|
case objabi.STEXT:
|
2020-07-30 20:49:29 -04:00
|
|
|
code = 'T'
|
2017-04-19 15:15:35 +12:00
|
|
|
case objabi.SRODATA:
|
2020-07-30 20:49:29 -04:00
|
|
|
code = 'R'
|
2020-12-12 09:45:45 +00:00
|
|
|
case objabi.SNOPTRDATA, objabi.SDATA:
|
2020-07-30 20:49:29 -04:00
|
|
|
code = 'D'
|
2017-04-18 12:53:25 -07:00
|
|
|
case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
|
2020-07-30 20:49:29 -04:00
|
|
|
code = 'B'
|
2013-12-16 12:52:11 -05:00
|
|
|
}
|
2020-08-02 19:36:28 -04:00
|
|
|
if ver >= goobj.SymABIstatic {
|
2020-07-30 20:49:29 -04:00
|
|
|
code += 'a' - 'A'
|
2013-12-16 12:52:11 -05:00
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
|
|
|
|
|
sym := Sym{
|
|
|
|
|
Name: name,
|
|
|
|
|
Addr: uint64(r.DataOff(i)),
|
|
|
|
|
Size: int64(osym.Siz()),
|
|
|
|
|
Code: code,
|
2016-07-02 17:19:25 -07:00
|
|
|
}
|
2013-12-16 12:52:11 -05:00
|
|
|
|
2020-07-30 20:49:29 -04:00
|
|
|
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()),
|
|
|
|
|
},
|
2013-12-16 12:52:11 -05:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
|
|
|
|
|
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)
|
2013-12-16 12:52:11 -05:00
|
|
|
}
|
|
|
|
|
|
2014-08-07 12:33:06 -04:00
|
|
|
return syms, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
|
2017-08-19 22:33:51 +02:00
|
|
|
// Should never be called. We implement Liner below, callers
|
2016-07-02 17:19:25 -07:00
|
|
|
// should use that instead.
|
2014-08-07 12:33:06 -04:00
|
|
|
return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
|
2013-12-16 12:52:11 -05:00
|
|
|
}
|
2014-10-29 18:07:24 -04:00
|
|
|
|
2016-07-02 17:19:25 -07:00
|
|
|
// Find returns the file name, line, and function data for the given pc.
|
|
|
|
|
// Returns "",0,nil if unknown.
|
|
|
|
|
// This function implements the Liner interface in preference to pcln() above.
|
|
|
|
|
func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
2020-07-30 20:49:29 -04:00
|
|
|
r := f.r
|
2020-08-19 01:38:43 +00:00
|
|
|
if f.arch == nil {
|
2016-07-02 17:19:25 -07:00
|
|
|
return "", 0, nil
|
|
|
|
|
}
|
2020-08-07 11:31:20 -04:00
|
|
|
getSymData := func(s goobj.SymRef) []byte {
|
|
|
|
|
if s.PkgIdx != goobj.PkgIdxHashed {
|
|
|
|
|
// We don't need the data for non-hashed symbols, yet.
|
|
|
|
|
panic("not supported")
|
|
|
|
|
}
|
|
|
|
|
i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def()))
|
|
|
|
|
return r.BytesAt(r.DataOff(i), r.DataSize(i))
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 20:49:29 -04:00
|
|
|
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()) {
|
2016-07-02 17:19:25 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2021-09-27 15:55:53 -04:00
|
|
|
var pcfileSym, pclineSym goobj.SymRef
|
|
|
|
|
for _, a := range r.Auxs(i) {
|
|
|
|
|
switch a.Type() {
|
|
|
|
|
case goobj.AuxPcfile:
|
|
|
|
|
pcfileSym = a.Sym()
|
|
|
|
|
case goobj.AuxPcline:
|
|
|
|
|
pclineSym = a.Sym()
|
2020-07-30 20:49:29 -04:00
|
|
|
}
|
2016-07-02 17:19:25 -07:00
|
|
|
}
|
2021-09-27 15:55:53 -04:00
|
|
|
if pcfileSym.IsZero() || pclineSym.IsZero() {
|
2020-07-30 20:49:29 -04:00
|
|
|
continue
|
2016-07-02 17:19:25 -07:00
|
|
|
}
|
2021-09-27 15:55:53 -04:00
|
|
|
pcline := getSymData(pclineSym)
|
2020-08-19 01:38:43 +00:00
|
|
|
line := int(pcValue(pcline, pc-addr, f.arch))
|
2021-09-27 15:55:53 -04:00
|
|
|
pcfile := getSymData(pcfileSym)
|
2020-08-19 01:38:43 +00:00
|
|
|
fileID := pcValue(pcfile, pc-addr, f.arch)
|
2020-10-15 18:04:08 -04:00
|
|
|
fileName := r.File(int(fileID))
|
2016-07-02 17:19:25 -07:00
|
|
|
// Note: we provide only the name in the Func structure.
|
|
|
|
|
// We could provide more if needed.
|
2020-07-30 20:49:29 -04:00
|
|
|
return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}}
|
2016-07-02 17:19:25 -07:00
|
|
|
}
|
|
|
|
|
return "", 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-01 10:10:17 -08:00
|
|
|
// pcValue looks up the given PC in a pc value table. target is the
|
|
|
|
|
// offset of the pc from the entry point.
|
|
|
|
|
func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 {
|
|
|
|
|
val := int32(-1)
|
|
|
|
|
var pc uint64
|
|
|
|
|
for step(&tab, &pc, &val, pc == 0, arch) {
|
|
|
|
|
if target < pc {
|
|
|
|
|
return val
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// step advances to the next pc, value pair in the encoded table.
|
|
|
|
|
func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool {
|
|
|
|
|
uvdelta := readvarint(p)
|
|
|
|
|
if uvdelta == 0 && !first {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if uvdelta&1 != 0 {
|
|
|
|
|
uvdelta = ^(uvdelta >> 1)
|
|
|
|
|
} else {
|
|
|
|
|
uvdelta >>= 1
|
|
|
|
|
}
|
|
|
|
|
vdelta := int32(uvdelta)
|
|
|
|
|
pcdelta := readvarint(p) * uint32(arch.MinLC)
|
|
|
|
|
*pc += uint64(pcdelta)
|
|
|
|
|
*val += vdelta
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// readvarint reads, removes, and returns a varint from *p.
|
|
|
|
|
func readvarint(p *[]byte) uint32 {
|
|
|
|
|
var v, shift uint32
|
|
|
|
|
s := *p
|
|
|
|
|
for shift = 0; ; shift += 7 {
|
|
|
|
|
b := s[0]
|
|
|
|
|
s = s[1:]
|
|
|
|
|
v |= (uint32(b) & 0x7F) << shift
|
|
|
|
|
if b&0x80 == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*p = s
|
|
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-02 17:19:25 -07:00
|
|
|
// We treat the whole object file as the text section.
|
2014-10-29 18:07:24 -04:00
|
|
|
func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
|
2020-07-30 20:49:29 -04:00
|
|
|
text = make([]byte, f.goobj.Size)
|
|
|
|
|
_, err = f.f.ReadAt(text, int64(f.goobj.Offset))
|
2016-07-02 17:19:25 -07:00
|
|
|
return
|
2014-10-29 18:07:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *goobjFile) goarch() string {
|
2020-08-19 01:38:43 +00:00
|
|
|
return f.goobj.Arch
|
2014-10-29 18:07:24 -04:00
|
|
|
}
|
2016-03-23 17:10:18 -07:00
|
|
|
|
2016-05-27 16:03:44 -07:00
|
|
|
func (f *goobjFile) loadAddress() (uint64, error) {
|
|
|
|
|
return 0, fmt.Errorf("unknown load address")
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-23 17:10:18 -07:00
|
|
|
func (f *goobjFile) dwarf() (*dwarf.Data, error) {
|
|
|
|
|
return nil, errors.New("no DWARF data in go object file")
|
|
|
|
|
}
|