go/src/cmd/internal/objfile/goobj.go
Michael Hudson-Doyle be2ee2a4b4 cmd/internal/objabi, cmd/link: move linker-only symkind values into linker
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>
2017-04-27 21:56:12 +00:00

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")
}