Newer
Older
// 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.
// Parsing of Go intermediate object files and archives.
package objfile
"cmd/internal/archive"
"cmd/internal/objabi"
"debug/dwarf"
"io"
type goobjFile struct {
goobj *archive.GoObj
f *os.File
func openGoFile(f *os.File) (*File, error) {
a, err := archive.Parse(f, false)
if err != nil {
return nil, err
}
entries := make([]*Entry, 0, len(a.Entries))
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 := goobj.NewReaderFromBytes(b, false)
var arch *sys.Arch
for _, a := range sys.Archs {
if a.Name == e.Obj.Arch {
arch = a
break
}
}
entries = append(entries, &Entry{
name: e.Name,
raw: &goobjFile{e.Obj, r, f, arch},
})
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
return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name)
return &File{f, entries}, nil
func goobjName(name string, ver int) string {
if ver == 0 {
return name
return fmt.Sprintf("%s<%d>", name, ver)
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
// Name of referenced indexed symbols.
nrefName := r.NRefName()
refNames := make(map[goobj.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
// Static symbol
ver = 1
}
return ver
}
resolveSymRef := func(s goobj.SymRef) string {
var i uint32
switch p := s.PkgIdx; p {
if s.SymIdx != 0 {
panic("bad sym ref")
}
return ""
i = s.SymIdx + uint32(r.NSym())
i = s.SymIdx + uint32(r.NSym()+r.NHashed64def())
i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef())
case goobj.PkgIdxBuiltin:
name, abi := goobj.BuiltinName(int(s.SymIdx))
return goobjName(name, abi)
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 {
Michael Hudson-Doyle
committed
case objabi.STEXT:
code = 'T'
Michael Hudson-Doyle
committed
case objabi.SRODATA:
code = 'R'
Michael Hudson-Doyle
committed
case objabi.SDATA:
code = 'D'
case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
code = 'B'
code += 'a' - 'A'
sym := Sym{
Name: name,
Addr: uint64(r.DataOff(i)),
Size: int64(osym.Siz()),
Code: code,
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
}
func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
// Should never be called. We implement Liner below, callers
return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
// 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) {
r := f.r
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))
}
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()) {
isym := ^uint32(0)
auxs := r.Auxs(i)
for j := range auxs {
a := &auxs[j]
if a.Type() != goobj.AuxFuncInfo {
continue
}
if a.Sym().PkgIdx != goobj.PkgIdxSelf {
panic("funcinfo symbol not defined in current package")
}
isym = a.Sym().SymIdx
if isym == ^uint32(0) {
continue
b := r.BytesAt(r.DataOff(isym), r.DataSize(isym))
pcline := getSymData(info.ReadPcline(b))
line := int(pcValue(pcline, pc-addr, f.arch))
pcfile := getSymData(info.ReadPcfile(b))
fileID := pcValue(pcfile, pc-addr, f.arch)
fileName := r.File(int(fileID))
// 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: osym.Name(r)}}
}
return "", 0, nil
}
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
// 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
}
// We treat the whole object file as the text section.
func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
text = make([]byte, f.goobj.Size)
_, err = f.f.ReadAt(text, int64(f.goobj.Offset))
}
func (f *goobjFile) goarch() string {
func (f *goobjFile) loadAddress() (uint64, error) {
return 0, fmt.Errorf("unknown load address")
}
func (f *goobjFile) dwarf() (*dwarf.Data, error) {
return nil, errors.New("no DWARF data in go object file")
}