2017-04-06 18:14:01 +00:00
|
|
|
package proc
|
|
|
|
|
|
|
|
import (
|
2017-11-21 08:50:14 +00:00
|
|
|
"bytes"
|
2017-05-29 13:20:01 +00:00
|
|
|
"debug/dwarf"
|
|
|
|
"debug/elf"
|
|
|
|
"debug/macho"
|
2017-04-06 18:14:01 +00:00
|
|
|
"debug/pe"
|
2017-08-24 07:46:47 +00:00
|
|
|
"encoding/binary"
|
2017-04-06 18:14:01 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
2017-09-01 13:30:45 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2017-04-06 18:14:01 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/derekparker/delve/pkg/dwarf/frame"
|
2017-05-29 13:20:01 +00:00
|
|
|
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
2017-04-06 18:14:01 +00:00
|
|
|
"github.com/derekparker/delve/pkg/dwarf/line"
|
2017-08-24 07:46:47 +00:00
|
|
|
"github.com/derekparker/delve/pkg/dwarf/op"
|
2017-04-06 18:14:01 +00:00
|
|
|
"github.com/derekparker/delve/pkg/dwarf/reader"
|
|
|
|
)
|
|
|
|
|
|
|
|
type BinaryInfo struct {
|
|
|
|
lastModified time.Time // Time the executable of this process was last modified
|
|
|
|
|
2017-04-21 06:55:53 +00:00
|
|
|
GOOS string
|
2017-04-06 18:14:01 +00:00
|
|
|
closer io.Closer
|
|
|
|
|
|
|
|
// Maps package names to package paths, needed to lookup types inside DWARF info
|
|
|
|
packageMap map[string]string
|
|
|
|
|
2017-06-21 22:40:42 +00:00
|
|
|
Arch Arch
|
|
|
|
dwarf *dwarf.Data
|
|
|
|
frameEntries frame.FrameDescriptionEntries
|
2017-08-24 07:46:47 +00:00
|
|
|
loclist loclistReader
|
2017-09-01 13:30:45 +00:00
|
|
|
compileUnits []*compileUnit
|
2017-06-21 22:40:42 +00:00
|
|
|
types map[string]dwarf.Offset
|
2017-07-18 18:55:24 +00:00
|
|
|
packageVars map[string]dwarf.Offset
|
2017-06-21 22:40:42 +00:00
|
|
|
gStructOffset uint64
|
2017-04-06 18:14:01 +00:00
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
// Functions is a list of all DW_TAG_subprogram entries in debug_info.
|
|
|
|
Functions []Function
|
|
|
|
// Sources is a list of all source files found in debug_line.
|
|
|
|
Sources []string
|
|
|
|
// LookupFunc maps function names to a description of the function.
|
|
|
|
LookupFunc map[string]*Function
|
|
|
|
|
2017-05-29 13:20:01 +00:00
|
|
|
typeCache map[dwarf.Offset]godwarf.Type
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
loadModuleDataOnce sync.Once
|
|
|
|
moduleData []moduleData
|
|
|
|
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
2017-08-15 06:21:24 +00:00
|
|
|
|
2017-09-08 10:31:20 +00:00
|
|
|
// consts[off] lists all the constants with the type defined at offset off.
|
|
|
|
consts constantsMap
|
|
|
|
|
2017-08-15 06:21:24 +00:00
|
|
|
loadErrMu sync.Mutex
|
|
|
|
loadErr error
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var UnsupportedLinuxArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
|
|
|
|
var UnsupportedWindowsArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
|
|
|
var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
|
|
|
|
|
|
|
|
type compileUnit struct {
|
|
|
|
entry *dwarf.Entry // debug_info entry describing this compile unit
|
|
|
|
isgo bool // true if this is the go compile unit
|
|
|
|
Name string // univocal name for non-go compile units
|
|
|
|
lineInfo *line.DebugLineInfo // debug_line segment associated with this compile unit
|
|
|
|
LowPC, HighPC uint64
|
2017-10-27 15:00:41 +00:00
|
|
|
optimized bool // this compile unit is optimized
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Function describes a function in the target program.
|
|
|
|
type Function struct {
|
|
|
|
Name string
|
|
|
|
Entry, End uint64 // same as DW_AT_lowpc and DW_AT_highpc
|
|
|
|
offset dwarf.Offset
|
|
|
|
cu *compileUnit
|
|
|
|
}
|
|
|
|
|
|
|
|
// PackageName returns the package part of the symbol name,
|
|
|
|
// or the empty string if there is none.
|
|
|
|
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
|
|
|
func (fn *Function) PackageName() string {
|
2017-09-08 10:31:20 +00:00
|
|
|
return packageName(fn.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func packageName(name string) string {
|
|
|
|
pathend := strings.LastIndex(name, "/")
|
2017-09-01 13:30:45 +00:00
|
|
|
if pathend < 0 {
|
|
|
|
pathend = 0
|
|
|
|
}
|
|
|
|
|
2017-09-08 10:31:20 +00:00
|
|
|
if i := strings.Index(name[pathend:], "."); i != -1 {
|
|
|
|
return name[:pathend+i]
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReceiverName returns the receiver type name of this symbol,
|
|
|
|
// or the empty string if there is none.
|
|
|
|
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
|
|
|
func (fn *Function) ReceiverName() string {
|
|
|
|
pathend := strings.LastIndex(fn.Name, "/")
|
|
|
|
if pathend < 0 {
|
|
|
|
pathend = 0
|
|
|
|
}
|
|
|
|
l := strings.Index(fn.Name[pathend:], ".")
|
|
|
|
r := strings.LastIndex(fn.Name[pathend:], ".")
|
|
|
|
if l == -1 || r == -1 || l == r {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return fn.Name[pathend+l+1 : pathend+r]
|
|
|
|
}
|
|
|
|
|
|
|
|
// BaseName returns the symbol name without the package or receiver name.
|
|
|
|
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
|
|
|
func (fn *Function) BaseName() string {
|
|
|
|
if i := strings.LastIndex(fn.Name, "."); i != -1 {
|
|
|
|
return fn.Name[i+1:]
|
|
|
|
}
|
|
|
|
return fn.Name
|
|
|
|
}
|
|
|
|
|
2017-10-27 15:00:41 +00:00
|
|
|
// Optimized returns true if the function was optimized by the compiler.
|
|
|
|
func (fn *Function) Optimized() bool {
|
|
|
|
return fn.cu.optimized
|
|
|
|
}
|
|
|
|
|
2017-09-08 10:31:20 +00:00
|
|
|
type constantsMap map[dwarf.Offset]*constantType
|
|
|
|
|
|
|
|
type constantType struct {
|
|
|
|
initialized bool
|
|
|
|
values []constantValue
|
|
|
|
}
|
|
|
|
|
|
|
|
type constantValue struct {
|
|
|
|
name string
|
|
|
|
fullName string
|
|
|
|
value int64
|
|
|
|
singleBit bool
|
|
|
|
}
|
|
|
|
|
2017-08-24 07:46:47 +00:00
|
|
|
type loclistReader struct {
|
|
|
|
data []byte
|
|
|
|
cur int
|
|
|
|
ptrSz int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rdr *loclistReader) Seek(off int) {
|
|
|
|
rdr.cur = off
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rdr *loclistReader) read(sz int) []byte {
|
|
|
|
r := rdr.data[rdr.cur : rdr.cur+sz]
|
|
|
|
rdr.cur += sz
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rdr *loclistReader) oneAddr() uint64 {
|
|
|
|
switch rdr.ptrSz {
|
|
|
|
case 4:
|
|
|
|
addr := binary.LittleEndian.Uint32(rdr.read(rdr.ptrSz))
|
|
|
|
if addr == ^uint32(0) {
|
|
|
|
return ^uint64(0)
|
|
|
|
}
|
|
|
|
return uint64(addr)
|
|
|
|
case 8:
|
|
|
|
addr := uint64(binary.LittleEndian.Uint64(rdr.read(rdr.ptrSz)))
|
|
|
|
return addr
|
|
|
|
default:
|
|
|
|
panic("bad address size")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rdr *loclistReader) Next(e *loclistEntry) bool {
|
|
|
|
e.lowpc = rdr.oneAddr()
|
|
|
|
e.highpc = rdr.oneAddr()
|
|
|
|
|
|
|
|
if e.lowpc == 0 && e.highpc == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.BaseAddressSelection() {
|
|
|
|
e.instr = nil
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
instrlen := binary.LittleEndian.Uint16(rdr.read(2))
|
|
|
|
e.instr = rdr.read(int(instrlen))
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
type loclistEntry struct {
|
|
|
|
lowpc, highpc uint64
|
|
|
|
instr []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *loclistEntry) BaseAddressSelection() bool {
|
|
|
|
return e.lowpc == ^uint64(0)
|
|
|
|
}
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
func NewBinaryInfo(goos, goarch string) BinaryInfo {
|
2017-05-29 13:20:01 +00:00
|
|
|
r := BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
2017-04-06 18:14:01 +00:00
|
|
|
|
|
|
|
// TODO: find better way to determine proc arch (perhaps use executable file info)
|
|
|
|
switch goarch {
|
|
|
|
case "amd64":
|
2017-04-21 06:55:53 +00:00
|
|
|
r.Arch = AMD64Arch(goos)
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bininfo *BinaryInfo) LoadBinaryInfo(path string, wg *sync.WaitGroup) error {
|
|
|
|
fi, err := os.Stat(path)
|
|
|
|
if err == nil {
|
|
|
|
bininfo.lastModified = fi.ModTime()
|
|
|
|
}
|
|
|
|
|
2017-04-21 06:55:53 +00:00
|
|
|
switch bininfo.GOOS {
|
2017-04-06 18:14:01 +00:00
|
|
|
case "linux":
|
|
|
|
return bininfo.LoadBinaryInfoElf(path, wg)
|
|
|
|
case "windows":
|
|
|
|
return bininfo.LoadBinaryInfoPE(path, wg)
|
|
|
|
case "darwin":
|
|
|
|
return bininfo.LoadBinaryInfoMacho(path, wg)
|
|
|
|
}
|
|
|
|
return errors.New("unsupported operating system")
|
|
|
|
}
|
|
|
|
|
2017-06-21 22:40:42 +00:00
|
|
|
// GStructOffset returns the offset of the G
|
|
|
|
// struct in thread local storage.
|
|
|
|
func (bi *BinaryInfo) GStructOffset() uint64 {
|
|
|
|
return bi.gStructOffset
|
|
|
|
}
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
func (bi *BinaryInfo) LastModified() time.Time {
|
|
|
|
return bi.lastModified
|
|
|
|
}
|
|
|
|
|
|
|
|
// DwarfReader returns a reader for the dwarf data
|
|
|
|
func (bi *BinaryInfo) DwarfReader() *reader.Reader {
|
|
|
|
return reader.New(bi.dwarf)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Types returns list of types present in the debugged program.
|
|
|
|
func (bi *BinaryInfo) Types() ([]string, error) {
|
|
|
|
types := make([]string, 0, len(bi.types))
|
|
|
|
for k := range bi.types {
|
|
|
|
types = append(types, k)
|
|
|
|
}
|
|
|
|
return types, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// PCToLine converts an instruction address to a file/line/function.
|
2017-09-01 13:30:45 +00:00
|
|
|
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *Function) {
|
|
|
|
fn := bi.PCToFunc(pc)
|
|
|
|
if fn == nil {
|
|
|
|
return "", 0, nil
|
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
f, ln := fn.cu.lineInfo.PCToLine(fn.Entry, pc)
|
2017-09-01 13:30:45 +00:00
|
|
|
return f, ln, fn
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 14:11:40 +00:00
|
|
|
// LineToPC converts a file:line into a memory address.
|
2017-09-01 13:30:45 +00:00
|
|
|
func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *Function, err error) {
|
|
|
|
for _, cu := range bi.compileUnits {
|
|
|
|
if cu.lineInfo.Lookup[filename] != nil {
|
|
|
|
pc = cu.lineInfo.LineToPC(filename, lineno)
|
|
|
|
fn = bi.PCToFunc(pc)
|
|
|
|
if fn == nil {
|
|
|
|
err = fmt.Errorf("no code at %s:%d", filename, lineno)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = fmt.Errorf("could not find %s:%d", filename, lineno)
|
|
|
|
return
|
2017-02-10 14:11:40 +00:00
|
|
|
}
|
|
|
|
|
2017-04-21 06:55:53 +00:00
|
|
|
// PCToFunc returns the function containing the given PC address
|
2017-09-01 13:30:45 +00:00
|
|
|
func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
|
|
|
|
i := sort.Search(len(bi.Functions), func(i int) bool {
|
|
|
|
fn := bi.Functions[i]
|
|
|
|
return pc <= fn.Entry || (fn.Entry <= pc && pc < fn.End)
|
|
|
|
})
|
|
|
|
if i != len(bi.Functions) {
|
|
|
|
fn := &bi.Functions[i]
|
|
|
|
if fn.Entry <= pc && pc < fn.End {
|
|
|
|
return fn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2017-04-21 06:55:53 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
func (bi *BinaryInfo) Close() error {
|
|
|
|
return bi.closer.Close()
|
|
|
|
}
|
|
|
|
|
2017-08-15 06:21:24 +00:00
|
|
|
func (bi *BinaryInfo) setLoadError(fmtstr string, args ...interface{}) {
|
|
|
|
bi.loadErrMu.Lock()
|
|
|
|
bi.loadErr = fmt.Errorf(fmtstr, args...)
|
|
|
|
bi.loadErrMu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) LoadError() error {
|
|
|
|
return bi.loadErr
|
|
|
|
}
|
|
|
|
|
2017-08-22 14:40:01 +00:00
|
|
|
type nilCloser struct{}
|
|
|
|
|
|
|
|
func (c *nilCloser) Close() error { return nil }
|
|
|
|
|
2017-08-24 07:46:47 +00:00
|
|
|
// LoadFromData creates a new BinaryInfo object using the specified data.
|
|
|
|
// This is used for debugging BinaryInfo, you should use LoadBinary instead.
|
|
|
|
func (bi *BinaryInfo) LoadFromData(dwdata *dwarf.Data, debugFrameBytes, debugLineBytes, debugLocBytes []byte) {
|
2017-08-22 14:40:01 +00:00
|
|
|
bi.closer = (*nilCloser)(nil)
|
|
|
|
bi.dwarf = dwdata
|
|
|
|
|
|
|
|
if debugFrameBytes != nil {
|
|
|
|
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes))
|
|
|
|
}
|
|
|
|
|
2017-08-24 07:46:47 +00:00
|
|
|
bi.loclistInit(debugLocBytes)
|
|
|
|
|
|
|
|
bi.loadDebugInfoMaps(debugLineBytes, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) loclistInit(data []byte) {
|
|
|
|
bi.loclist.data = data
|
|
|
|
bi.loclist.ptrSz = bi.Arch.PtrSize()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Location returns the location described by attribute attr of entry.
|
|
|
|
// This will either be an int64 address or a slice of Pieces for locations
|
|
|
|
// that don't correspond to a single memory address (registers, composite
|
|
|
|
// locations).
|
2017-11-21 08:50:14 +00:00
|
|
|
func (bi *BinaryInfo) Location(entry *dwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, string, error) {
|
2017-08-24 07:46:47 +00:00
|
|
|
a := entry.Val(attr)
|
|
|
|
if a == nil {
|
2017-11-21 08:50:14 +00:00
|
|
|
return 0, nil, "", fmt.Errorf("no location attribute %s", attr)
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
if instr, ok := a.([]byte); ok {
|
2017-11-21 08:50:14 +00:00
|
|
|
var descr bytes.Buffer
|
|
|
|
fmt.Fprintf(&descr, "[block] ")
|
|
|
|
op.PrettyPrint(&descr, instr)
|
|
|
|
addr, pieces, err := op.ExecuteStackProgram(regs, instr)
|
|
|
|
return addr, pieces, descr.String(), err
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
off, ok := a.(int64)
|
|
|
|
if !ok {
|
2017-11-21 08:50:14 +00:00
|
|
|
return 0, nil, "", fmt.Errorf("could not interpret location attribute %s", attr)
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
if bi.loclist.data == nil {
|
2017-11-21 08:50:14 +00:00
|
|
|
return 0, nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x (no debug_loc section found)", off, pc)
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
instr := bi.loclistEntry(off, pc)
|
|
|
|
if instr == nil {
|
2017-11-21 08:50:14 +00:00
|
|
|
return 0, nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
2017-11-21 08:50:14 +00:00
|
|
|
var descr bytes.Buffer
|
|
|
|
fmt.Fprintf(&descr, "[%#x:%#x] ", off, pc)
|
|
|
|
op.PrettyPrint(&descr, instr)
|
|
|
|
addr, pieces, err := op.ExecuteStackProgram(regs, instr)
|
|
|
|
return addr, pieces, descr.String(), err
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// loclistEntry returns the loclist entry in the loclist starting at off,
|
|
|
|
// for address pc.
|
|
|
|
func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
|
|
|
|
var base uint64
|
|
|
|
if cu := bi.findCompileUnit(pc); cu != nil {
|
|
|
|
base = cu.LowPC
|
|
|
|
}
|
|
|
|
|
|
|
|
bi.loclist.Seek(int(off))
|
|
|
|
var e loclistEntry
|
|
|
|
for bi.loclist.Next(&e) {
|
|
|
|
if e.BaseAddressSelection() {
|
|
|
|
base = e.highpc
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if pc >= e.lowpc+base && pc < e.highpc+base {
|
|
|
|
return e.instr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// findCompileUnit returns the compile unit containing address pc.
|
|
|
|
func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
|
|
|
|
for _, cu := range bi.compileUnits {
|
|
|
|
if pc >= cu.LowPC && pc < cu.HighPC {
|
|
|
|
return cu
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2017-08-22 14:40:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
// ELF ///////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
|
|
|
|
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bi.closer = exe
|
|
|
|
elfFile, err := elf.NewFile(exe)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if elfFile.Machine != elf.EM_X86_64 {
|
|
|
|
return UnsupportedLinuxArchErr
|
|
|
|
}
|
|
|
|
bi.dwarf, err = elfFile.DWARF()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
debugLineBytes, err := getDebugLineInfoElf(elfFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-24 07:46:47 +00:00
|
|
|
bi.loclistInit(getDebugLocElf(elfFile))
|
2017-09-01 13:30:45 +00:00
|
|
|
|
|
|
|
wg.Add(3)
|
2017-04-06 18:14:01 +00:00
|
|
|
go bi.parseDebugFrameElf(elfFile, wg)
|
2017-09-01 13:30:45 +00:00
|
|
|
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
2017-06-21 22:40:42 +00:00
|
|
|
go bi.setGStructOffsetElf(elfFile, wg)
|
2017-04-06 18:14:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) parseDebugFrameElf(exe *elf.File, wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
debugFrameSec := exe.Section(".debug_frame")
|
|
|
|
debugInfoSec := exe.Section(".debug_info")
|
|
|
|
|
|
|
|
if debugFrameSec != nil && debugInfoSec != nil {
|
|
|
|
debugFrame, err := exe.Section(".debug_frame").Data()
|
|
|
|
if err != nil {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not get .debug_frame section: %v", err)
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
dat, err := debugInfoSec.Data()
|
|
|
|
if err != nil {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not get .debug_frame section: %v", err)
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
|
|
|
} else {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not find .debug_frame section in binary")
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
func getDebugLineInfoElf(exe *elf.File) ([]byte, error) {
|
2017-04-06 18:14:01 +00:00
|
|
|
if sec := exe.Section(".debug_line"); sec != nil {
|
|
|
|
debugLine, err := exe.Section(".debug_line").Data()
|
|
|
|
if err != nil {
|
2017-09-01 13:30:45 +00:00
|
|
|
return nil, fmt.Errorf("could not get .debug_line section: %v", err)
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
return debugLine, nil
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
return nil, errors.New("could not find .debug_line section in binary")
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 07:46:47 +00:00
|
|
|
func getDebugLocElf(exe *elf.File) []byte {
|
|
|
|
if sec := exe.Section(".debug_loc"); sec != nil {
|
|
|
|
debugLoc, _ := exe.Section(".debug_loc").Data()
|
|
|
|
return debugLoc
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-06-21 22:40:42 +00:00
|
|
|
func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
// This is a bit arcane. Essentially:
|
|
|
|
// - If the program is pure Go, it can do whatever it wants, and puts the G
|
|
|
|
// pointer at %fs-8.
|
|
|
|
// - Otherwise, Go asks the external linker to place the G pointer by
|
|
|
|
// emitting runtime.tlsg, a TLS symbol, which is relocated to the chosen
|
|
|
|
// offset in libc's TLS block.
|
|
|
|
symbols, err := exe.Symbols()
|
|
|
|
if err != nil {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not parse ELF symbols: %v", err)
|
|
|
|
return
|
2017-06-21 22:40:42 +00:00
|
|
|
}
|
|
|
|
var tlsg *elf.Symbol
|
|
|
|
for _, symbol := range symbols {
|
|
|
|
if symbol.Name == "runtime.tlsg" {
|
|
|
|
s := symbol
|
|
|
|
tlsg = &s
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if tlsg == nil {
|
|
|
|
bi.gStructOffset = ^uint64(8) + 1 // -8
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var tls *elf.Prog
|
|
|
|
for _, prog := range exe.Progs {
|
|
|
|
if prog.Type == elf.PT_TLS {
|
|
|
|
tls = prog
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// The TLS register points to the end of the TLS block, which is
|
|
|
|
// tls.Memsz long. runtime.tlsg is an offset from the beginning of that block.
|
|
|
|
bi.gStructOffset = ^(tls.Memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value
|
|
|
|
}
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
// PE ////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) LoadBinaryInfoPE(path string, wg *sync.WaitGroup) error {
|
|
|
|
peFile, closer, err := openExecutablePathPE(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bi.closer = closer
|
|
|
|
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
|
|
|
return UnsupportedWindowsArchErr
|
|
|
|
}
|
2017-05-29 13:20:01 +00:00
|
|
|
bi.dwarf, err = peFile.DWARF()
|
2017-04-06 18:14:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
debugLineBytes, err := getDebugLineInfoPE(peFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-24 07:46:47 +00:00
|
|
|
bi.loclistInit(getDebugLocPE(peFile))
|
2017-09-01 13:30:45 +00:00
|
|
|
|
|
|
|
wg.Add(2)
|
2017-04-06 18:14:01 +00:00
|
|
|
go bi.parseDebugFramePE(peFile, wg)
|
2017-09-01 13:30:45 +00:00
|
|
|
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
2017-06-21 22:40:42 +00:00
|
|
|
|
|
|
|
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
|
|
|
// to G struct per:
|
|
|
|
// https://golang.org/src/runtime/cgo/gcc_windows_amd64.c
|
|
|
|
|
|
|
|
bi.gStructOffset = 0x28
|
2017-04-06 18:14:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
|
|
|
f, err := os.OpenFile(path, 0, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
peFile, err := pe.NewFile(f)
|
|
|
|
if err != nil {
|
|
|
|
f.Close()
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return peFile, f, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) parseDebugFramePE(exe *pe.File, wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
debugFrameSec := exe.Section(".debug_frame")
|
|
|
|
debugInfoSec := exe.Section(".debug_info")
|
|
|
|
|
|
|
|
if debugFrameSec != nil && debugInfoSec != nil {
|
|
|
|
debugFrame, err := debugFrameSec.Data()
|
|
|
|
if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not get .debug_frame section: %v", err)
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
|
|
|
|
debugFrame = debugFrame[:debugFrameSec.VirtualSize]
|
|
|
|
}
|
|
|
|
dat, err := debugInfoSec.Data()
|
|
|
|
if err != nil {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not get .debug_info section: %v", err)
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
|
|
|
} else {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not find .debug_frame section in binary")
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
|
|
|
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
|
|
|
for _, s := range f.Symbols {
|
|
|
|
if s.Name != name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if s.SectionNumber <= 0 {
|
|
|
|
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
|
|
|
|
}
|
|
|
|
if len(f.Sections) < int(s.SectionNumber) {
|
|
|
|
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
|
|
|
|
}
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("no %s symbol found", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
|
|
|
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
|
|
|
|
ssym, err := findPESymbol(f, sname)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
esym, err := findPESymbol(f, ename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if ssym.SectionNumber != esym.SectionNumber {
|
|
|
|
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
|
|
|
|
}
|
|
|
|
sect := f.Sections[ssym.SectionNumber-1]
|
|
|
|
data, err := sect.Data()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return data[ssym.Value:esym.Value], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
|
|
|
func pclnPE(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error) {
|
|
|
|
var imageBase uint64
|
|
|
|
switch oh := exe.OptionalHeader.(type) {
|
|
|
|
case *pe.OptionalHeader32:
|
|
|
|
imageBase = uint64(oh.ImageBase)
|
|
|
|
case *pe.OptionalHeader64:
|
|
|
|
imageBase = oh.ImageBase
|
|
|
|
default:
|
|
|
|
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
|
|
|
|
}
|
|
|
|
if sect := exe.Section(".text"); sect != nil {
|
|
|
|
textStart = imageBase + uint64(sect.VirtualAddress)
|
|
|
|
}
|
|
|
|
if pclntab, err = loadPETable(exe, "runtime.pclntab", "runtime.epclntab"); err != nil {
|
|
|
|
// We didn't find the symbols, so look for the names used in 1.3 and earlier.
|
|
|
|
// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
|
|
|
|
var err2 error
|
|
|
|
if pclntab, err2 = loadPETable(exe, "pclntab", "epclntab"); err2 != nil {
|
|
|
|
return 0, nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if symtab, err = loadPETable(exe, "runtime.symtab", "runtime.esymtab"); err != nil {
|
|
|
|
// Same as above.
|
|
|
|
var err2 error
|
|
|
|
if symtab, err2 = loadPETable(exe, "symtab", "esymtab"); err2 != nil {
|
|
|
|
return 0, nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return textStart, symtab, pclntab, nil
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
func getDebugLineInfoPE(exe *pe.File) ([]byte, error) {
|
2017-04-06 18:14:01 +00:00
|
|
|
if sec := exe.Section(".debug_line"); sec != nil {
|
|
|
|
debugLine, err := sec.Data()
|
|
|
|
if err != nil && uint32(len(debugLine)) < sec.Size {
|
2017-09-01 13:30:45 +00:00
|
|
|
return nil, fmt.Errorf("could not get .debug_line section: %v", err)
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
|
|
|
debugLine = debugLine[:sec.VirtualSize]
|
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
return debugLine, nil
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
return nil, errors.New("could not find .debug_line section in binary")
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 07:46:47 +00:00
|
|
|
func getDebugLocPE(exe *pe.File) []byte {
|
|
|
|
if sec := exe.Section(".debug_loc"); sec != nil {
|
|
|
|
debugLoc, _ := sec.Data()
|
|
|
|
return debugLoc
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
// MACH-O ////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error {
|
|
|
|
exe, err := macho.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bi.closer = exe
|
|
|
|
if exe.Cpu != macho.CpuAmd64 {
|
|
|
|
return UnsupportedDarwinArchErr
|
|
|
|
}
|
|
|
|
bi.dwarf, err = exe.DWARF()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
debugLineBytes, err := getDebugLineInfoMacho(exe)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-24 07:46:47 +00:00
|
|
|
bi.loclistInit(getDebugLocMacho(exe))
|
2017-09-01 13:30:45 +00:00
|
|
|
|
|
|
|
wg.Add(2)
|
2017-04-06 18:14:01 +00:00
|
|
|
go bi.parseDebugFrameMacho(exe, wg)
|
2017-09-01 13:30:45 +00:00
|
|
|
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
2017-06-21 22:40:42 +00:00
|
|
|
bi.gStructOffset = 0x8a0
|
2017-04-06 18:14:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) parseDebugFrameMacho(exe *macho.File, wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
debugFrameSec := exe.Section("__debug_frame")
|
|
|
|
debugInfoSec := exe.Section("__debug_info")
|
|
|
|
|
|
|
|
if debugFrameSec != nil && debugInfoSec != nil {
|
|
|
|
debugFrame, err := exe.Section("__debug_frame").Data()
|
|
|
|
if err != nil {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not get __debug_frame section: %v", err)
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
dat, err := debugInfoSec.Data()
|
|
|
|
if err != nil {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not get .debug_info section: %v", err)
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
|
|
|
} else {
|
2017-08-15 06:21:24 +00:00
|
|
|
bi.setLoadError("could not find __debug_frame section in binary")
|
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
func getDebugLineInfoMacho(exe *macho.File) ([]byte, error) {
|
2017-04-06 18:14:01 +00:00
|
|
|
if sec := exe.Section("__debug_line"); sec != nil {
|
|
|
|
debugLine, err := exe.Section("__debug_line").Data()
|
|
|
|
if err != nil {
|
2017-09-01 13:30:45 +00:00
|
|
|
return nil, fmt.Errorf("could not get __debug_line section: %v", err)
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
return debugLine, nil
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
return nil, errors.New("could not find __debug_line section in binary")
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2017-08-24 07:46:47 +00:00
|
|
|
|
|
|
|
func getDebugLocMacho(exe *macho.File) []byte {
|
|
|
|
if sec := exe.Section("__debug_loc"); sec != nil {
|
|
|
|
debugLoc, _ := sec.Data()
|
|
|
|
return debugLoc
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|