mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Many (most!) of the values of objapi.SymKind are used only in the linker, so this creates a separate cmd/link/internal/ld.SymKind type, removes most values from SymKind and maps one to the other when reading object files in the linker. Two of the remaining objapi.SymKind values are only checked for, never set and so will never be actually found but I wanted to keep this to the most mechanical change possible. Change-Id: I4bbc5aed6713cab3e8de732e6e288eb77be0474c Reviewed-on: https://go-review.googlesource.com/40985 Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
202 lines
4.9 KiB
Go
202 lines
4.9 KiB
Go
// 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
|
|
|
|
import (
|
|
"cmd/internal/goobj"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/sys"
|
|
"debug/dwarf"
|
|
"debug/gosym"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
type goobjFile struct {
|
|
goobj *goobj.Package
|
|
f *os.File // the underlying .o or .a file
|
|
}
|
|
|
|
func openGoobj(r *os.File) (rawFile, error) {
|
|
f, err := goobj.Parse(r, `""`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &goobjFile{goobj: f, f: r}, nil
|
|
}
|
|
|
|
func goobjName(id goobj.SymID) string {
|
|
if id.Version == 0 {
|
|
return id.Name
|
|
}
|
|
return fmt.Sprintf("%s<%d>", id.Name, id.Version)
|
|
}
|
|
|
|
func (f *goobjFile) symbols() ([]Sym, error) {
|
|
seen := make(map[goobj.SymID]bool)
|
|
|
|
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: int64(s.Size), Type: s.Type.Name, Code: '?'}
|
|
switch s.Kind {
|
|
case objabi.STEXT:
|
|
sym.Code = 'T'
|
|
case objabi.SRODATA:
|
|
sym.Code = 'R'
|
|
case objabi.SDATA:
|
|
sym.Code = 'D'
|
|
case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
|
|
sym.Code = 'B'
|
|
case objabi.SXREF, objabi.SCONST:
|
|
sym.Code = 'X' // should not see
|
|
}
|
|
if s.Version != 0 {
|
|
sym.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]})
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
return syms, nil
|
|
}
|
|
|
|
func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
|
|
// Should never be called. We implement Liner below, callers
|
|
// should use that instead.
|
|
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) {
|
|
// TODO: this is really inefficient. Binary search? Memoize last result?
|
|
var arch *sys.Arch
|
|
for _, a := range sys.Archs {
|
|
if a.Name == f.goobj.Arch {
|
|
arch = a
|
|
break
|
|
}
|
|
}
|
|
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) {
|
|
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
|
|
}
|
|
fileID := int(pcValue(pcfile, pc-uint64(s.Data.Offset), arch))
|
|
fileName := s.Func.File[fileID]
|
|
pcline := make([]byte, s.Func.PCLine.Size)
|
|
_, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset)
|
|
if err != nil {
|
|
return "", 0, nil
|
|
}
|
|
line := int(pcValue(pcline, pc-uint64(s.Data.Offset), arch))
|
|
// 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 "", 0, nil
|
|
}
|
|
|
|
// 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) {
|
|
var info os.FileInfo
|
|
info, err = f.f.Stat()
|
|
if err != nil {
|
|
return
|
|
}
|
|
text = make([]byte, info.Size())
|
|
_, err = f.f.ReadAt(text, 0)
|
|
return
|
|
}
|
|
|
|
func (f *goobjFile) goarch() string {
|
|
return f.goobj.Arch
|
|
}
|
|
|
|
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")
|
|
}
|