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"
|
2018-06-20 10:20:35 +00:00
|
|
|
"encoding/hex"
|
2017-04-06 18:14:01 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
2018-11-07 22:21:35 +00:00
|
|
|
"path/filepath"
|
2017-09-01 13:30:45 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2017-04-06 18:14:01 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2019-01-04 18:39:25 +00:00
|
|
|
"github.com/go-delve/delve/pkg/dwarf/frame"
|
|
|
|
"github.com/go-delve/delve/pkg/dwarf/godwarf"
|
|
|
|
"github.com/go-delve/delve/pkg/dwarf/line"
|
|
|
|
"github.com/go-delve/delve/pkg/dwarf/op"
|
|
|
|
"github.com/go-delve/delve/pkg/dwarf/reader"
|
|
|
|
"github.com/go-delve/delve/pkg/goversion"
|
2017-04-06 18:14:01 +00:00
|
|
|
)
|
|
|
|
|
2019-03-20 17:32:51 +00:00
|
|
|
// BinaryInfo holds information on the binaries being executed (this
|
|
|
|
// includes both the executable and also any loaded libraries).
|
2017-04-06 18:14:01 +00:00
|
|
|
type BinaryInfo struct {
|
2018-11-06 22:32:14 +00:00
|
|
|
// Architecture of this binary.
|
|
|
|
Arch Arch
|
|
|
|
|
|
|
|
// GOOS operating system this binary is executing on.
|
|
|
|
GOOS string
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
debugInfoDirectories []string
|
|
|
|
|
2018-11-06 22:32:14 +00:00
|
|
|
// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
|
|
|
|
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
|
|
|
|
|
2019-03-20 17:32:51 +00:00
|
|
|
// Images is a list of loaded shared libraries (also known as
|
2019-05-30 15:02:28 +00:00
|
|
|
// shared objects on linux or DLLs on windows).
|
2019-03-20 17:32:51 +00:00
|
|
|
Images []*Image
|
|
|
|
|
|
|
|
ElfDynamicSection ElfDynamicSection
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
lastModified time.Time // Time the executable of this process was last modified
|
|
|
|
|
2018-06-20 10:20:35 +00:00
|
|
|
closer io.Closer
|
|
|
|
sepDebugCloser io.Closer
|
2017-04-06 18:14:01 +00:00
|
|
|
|
2018-05-29 15:01:51 +00:00
|
|
|
staticBase uint64
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
// Maps package names to package paths, needed to lookup types inside DWARF info
|
|
|
|
packageMap map[string]string
|
|
|
|
|
2018-11-06 22:32:14 +00:00
|
|
|
frameEntries frame.FrameDescriptionEntries
|
2019-06-24 15:02:14 +00:00
|
|
|
|
|
|
|
compileUnits []*compileUnit // compileUnits is sorted by increasing DWARF offset
|
|
|
|
|
|
|
|
types map[string]dwarfRef
|
|
|
|
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
|
2017-04-06 18:14:01 +00:00
|
|
|
|
2018-11-06 22:32:14 +00:00
|
|
|
gStructOffset uint64
|
2017-05-29 13:20:01 +00:00
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
// nameOfRuntimeType maps an address of a runtime._type struct to its
|
|
|
|
// decoded name. Used with versions of Go <= 1.10 to figure out the DIE of
|
|
|
|
// the concrete type of interfaces.
|
|
|
|
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
2018-04-18 18:09:33 +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
|
2019-05-08 21:06:38 +00:00
|
|
|
|
|
|
|
initialized bool
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
|
|
|
var ErrUnsupportedLinuxArch = errors.New("unsupported architecture - only linux/amd64 is supported")
|
|
|
|
|
|
|
|
// ErrUnsupportedWindowsArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
|
|
|
var ErrUnsupportedWindowsArch = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
|
|
|
|
|
|
|
// ErrUnsupportedDarwinArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
|
|
|
var ErrUnsupportedDarwinArch = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
2017-04-06 18:14:01 +00:00
|
|
|
|
2018-11-06 22:34:26 +00:00
|
|
|
// ErrCouldNotDetermineRelocation is an error returned when Delve could not determine the base address of a
|
|
|
|
// position independant executable.
|
2018-05-29 15:01:51 +00:00
|
|
|
var ErrCouldNotDetermineRelocation = errors.New("could not determine the base address of a PIE")
|
|
|
|
|
2019-02-21 17:11:54 +00:00
|
|
|
// ErrNoDebugInfoFound is returned when Delve cannot open the debug_info
|
|
|
|
// section or find an external debug info file.
|
|
|
|
var ErrNoDebugInfoFound = errors.New("could not open debug info")
|
2018-11-07 22:21:35 +00:00
|
|
|
|
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 {
|
2018-11-06 22:34:26 +00:00
|
|
|
name string // univocal name for non-go compile units
|
|
|
|
lowPC uint64
|
|
|
|
ranges [][2]uint64
|
2018-08-07 01:08:25 +00:00
|
|
|
|
|
|
|
entry *dwarf.Entry // debug_info entry describing this compile unit
|
|
|
|
isgo bool // true if this is the go compile unit
|
|
|
|
lineInfo *line.DebugLineInfo // debug_line segment associated with this compile unit
|
|
|
|
concreteInlinedFns []inlinedFn // list of concrete inlined functions within this compile unit
|
|
|
|
optimized bool // this compile unit is optimized
|
|
|
|
producer string // producer attribute
|
2018-08-17 06:17:22 +00:00
|
|
|
|
2019-06-24 15:02:14 +00:00
|
|
|
offset dwarf.Offset // offset of the entry describing the compile unit
|
2019-05-08 21:06:38 +00:00
|
|
|
|
|
|
|
image *Image // parent image of this compilation unit.
|
|
|
|
}
|
|
|
|
|
|
|
|
// dwarfRef is a reference to a Debug Info Entry inside a shared object.
|
|
|
|
type dwarfRef struct {
|
|
|
|
imageIndex int
|
|
|
|
offset dwarf.Offset
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
|
|
|
|
2018-08-07 01:08:25 +00:00
|
|
|
// inlinedFn represents a concrete inlined function, e.g.
|
|
|
|
// an entry for the generated code of an inlined function.
|
|
|
|
type inlinedFn struct {
|
|
|
|
Name string // Name of the function that was inlined
|
|
|
|
LowPC, HighPC uint64 // Address range of the generated inlined instructions
|
|
|
|
CallFile string // File of the call site of the inlined function
|
|
|
|
CallLine int64 // Line of the call site of the inlined function
|
|
|
|
Parent *Function // The function that contains this inlined function
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
type constantsMap map[dwarfRef]*constantType
|
2017-09-08 10:31:20 +00:00
|
|
|
|
|
|
|
type constantType struct {
|
|
|
|
initialized bool
|
|
|
|
values []constantValue
|
|
|
|
}
|
|
|
|
|
|
|
|
type constantValue struct {
|
|
|
|
name string
|
|
|
|
fullName string
|
|
|
|
value int64
|
|
|
|
singleBit bool
|
|
|
|
}
|
|
|
|
|
2017-10-26 11:37:19 +00:00
|
|
|
// packageVar represents a package-level variable (or a C global variable).
|
|
|
|
// If a global variable does not have an address (for example it's stored in
|
|
|
|
// a register, or non-contiguously) addr will be 0.
|
|
|
|
type packageVar struct {
|
|
|
|
name string
|
2019-05-08 21:06:38 +00:00
|
|
|
cu *compileUnit
|
2017-10-26 11:37:19 +00:00
|
|
|
offset dwarf.Offset
|
|
|
|
addr uint64
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-04-18 18:09:33 +00:00
|
|
|
type runtimeTypeDIE struct {
|
|
|
|
offset dwarf.Offset
|
|
|
|
kind int64
|
|
|
|
}
|
|
|
|
|
2017-08-24 07:46:47 +00:00
|
|
|
func (e *loclistEntry) BaseAddressSelection() bool {
|
|
|
|
return e.lowpc == ^uint64(0)
|
|
|
|
}
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
type buildIDHeader struct {
|
2018-06-20 10:20:35 +00:00
|
|
|
Namesz uint32
|
|
|
|
Descsz uint32
|
|
|
|
Type uint32
|
|
|
|
}
|
|
|
|
|
2019-03-20 17:32:51 +00:00
|
|
|
// ElfDynamicSection describes the .dynamic section of an ELF executable.
|
|
|
|
type ElfDynamicSection struct {
|
|
|
|
Addr uint64 // relocated address of where the .dynamic section is mapped in memory
|
|
|
|
Size uint64 // size of the .dynamic section of the executable
|
|
|
|
}
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
|
|
|
|
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
2019-05-08 21:06:38 +00:00
|
|
|
r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)}
|
2017-04-06 18:14:01 +00:00
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// TODO: find better way to determine proc arch (perhaps use executable file info).
|
2017-04-06 18:14:01 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// LoadBinaryInfo will load and store the information from the binary at 'path'.
|
2018-11-12 22:52:13 +00:00
|
|
|
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, debugInfoDirs []string) error {
|
2017-04-06 18:14:01 +00:00
|
|
|
fi, err := os.Stat(path)
|
|
|
|
if err == nil {
|
2018-08-31 18:08:18 +00:00
|
|
|
bi.lastModified = fi.ModTime()
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
bi.debugInfoDirectories = debugInfoDirs
|
|
|
|
|
|
|
|
return bi.AddImage(path, entryPoint)
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadBinaryInfo(bi *BinaryInfo, image *Image, path string, entryPoint uint64) error {
|
2018-11-12 22:52:13 +00:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
defer wg.Wait()
|
2019-05-08 21:06:38 +00:00
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
switch bi.GOOS {
|
2019-07-13 01:28:04 +00:00
|
|
|
case "linux", "freebsd":
|
2019-05-08 21:06:38 +00:00
|
|
|
return loadBinaryInfoElf(bi, image, path, entryPoint, &wg)
|
2017-04-06 18:14:01 +00:00
|
|
|
case "windows":
|
2019-05-08 21:06:38 +00:00
|
|
|
return loadBinaryInfoPE(bi, image, path, entryPoint, &wg)
|
2017-04-06 18:14:01 +00:00
|
|
|
case "darwin":
|
2019-05-08 21:06:38 +00:00
|
|
|
return loadBinaryInfoMacho(bi, image, path, entryPoint, &wg)
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// LastModified returns the last modified time of the binary.
|
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
|
2019-05-08 21:06:38 +00:00
|
|
|
func (so *Image) DwarfReader() *reader.Reader {
|
|
|
|
return reader.New(so.dwarf)
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2019-05-30 15:11:21 +00:00
|
|
|
fileFound := false
|
2017-09-01 13:30:45 +00:00
|
|
|
for _, cu := range bi.compileUnits {
|
|
|
|
if cu.lineInfo.Lookup[filename] != nil {
|
2019-05-30 15:11:21 +00:00
|
|
|
fileFound = true
|
|
|
|
pc := cu.lineInfo.LineToPC(filename, lineno)
|
2018-08-07 01:08:25 +00:00
|
|
|
if pc == 0 {
|
|
|
|
// Check to see if this file:line belongs to the call site
|
|
|
|
// of an inlined function.
|
|
|
|
for _, ifn := range cu.concreteInlinedFns {
|
|
|
|
if strings.Contains(ifn.CallFile, filename) && ifn.CallLine == int64(lineno) {
|
2019-05-30 15:11:21 +00:00
|
|
|
return ifn.LowPC, ifn.Parent, nil
|
2018-08-07 01:08:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-30 15:11:21 +00:00
|
|
|
if fn := bi.PCToFunc(pc); fn != nil {
|
|
|
|
return pc, fn, nil
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-30 15:11:21 +00:00
|
|
|
if fileFound {
|
|
|
|
return 0, nil, fmt.Errorf("could not find statement at %s:%d, please use a line with a statement", filename, lineno)
|
|
|
|
} else {
|
|
|
|
return 0, nil, fmt.Errorf("could not find file %s", filename)
|
|
|
|
}
|
2017-02-10 14:11:40 +00:00
|
|
|
}
|
|
|
|
|
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
|
|
|
// AllPCsForFileLine returns all PC addresses for the given filename:lineno.
|
|
|
|
func (bi *BinaryInfo) AllPCsForFileLine(filename string, lineno int) []uint64 {
|
|
|
|
r := make([]uint64, 0, 1)
|
|
|
|
for _, cu := range bi.compileUnits {
|
|
|
|
if cu.lineInfo.Lookup[filename] != nil {
|
|
|
|
r = append(r, cu.lineInfo.AllPCsForFileLine(filename, lineno)...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2019-06-28 02:39:15 +00:00
|
|
|
// AllPCsForFileLines returns a map providing all PC addresses for filename and each line in linenos
|
|
|
|
func (bi *BinaryInfo) AllPCsForFileLines(filename string, linenos []int) map[int][]uint64 {
|
|
|
|
r := make(map[int][]uint64)
|
|
|
|
for _, line := range linenos {
|
|
|
|
r[line] = make([]uint64, 0, 1)
|
|
|
|
}
|
|
|
|
for _, cu := range bi.compileUnits {
|
|
|
|
if cu.lineInfo.Lookup[filename] != nil {
|
|
|
|
cu.lineInfo.AllPCsForFileLines(filename, r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-08-08 03:15:20 +00:00
|
|
|
// PCToImage returns the image containing the given PC address.
|
|
|
|
func (bi *BinaryInfo) PCToImage(pc uint64) *Image {
|
2019-05-08 21:06:38 +00:00
|
|
|
fn := bi.PCToFunc(pc)
|
|
|
|
return bi.funcToImage(fn)
|
|
|
|
}
|
|
|
|
|
2019-03-20 17:32:51 +00:00
|
|
|
// Image represents a loaded library file (shared object on linux, DLL on windows).
|
|
|
|
type Image struct {
|
2019-05-08 21:06:38 +00:00
|
|
|
Path string
|
|
|
|
StaticBase uint64
|
|
|
|
addr uint64
|
|
|
|
|
|
|
|
index int // index of this object in BinaryInfo.SharedObjects
|
|
|
|
|
|
|
|
closer io.Closer
|
|
|
|
sepDebugCloser io.Closer
|
|
|
|
|
|
|
|
dwarf *dwarf.Data
|
|
|
|
dwarfReader *dwarf.Reader
|
|
|
|
loclist loclistReader
|
|
|
|
|
|
|
|
typeCache map[dwarf.Offset]godwarf.Type
|
|
|
|
|
|
|
|
// runtimeTypeToDIE maps between the offset of a runtime._type in
|
|
|
|
// runtime.moduledata.types and the offset of the DIE in debug_info. This
|
|
|
|
// map is filled by using the extended attribute godwarf.AttrGoRuntimeType
|
|
|
|
// which was added in go 1.11.
|
|
|
|
runtimeTypeToDIE map[uint64]runtimeTypeDIE
|
|
|
|
|
|
|
|
loadErrMu sync.Mutex
|
|
|
|
loadErr error
|
2019-03-20 17:32:51 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
// AddImage adds the specified image to bi, loading data asynchronously.
|
|
|
|
// Addr is the relocated entry point for the executable and staticBase (i.e.
|
|
|
|
// the relocation offset) for all other images.
|
|
|
|
// The first image added must be the executable file.
|
|
|
|
func (bi *BinaryInfo) AddImage(path string, addr uint64) error {
|
|
|
|
// Check if the image is already present.
|
|
|
|
if len(bi.Images) > 0 && !strings.HasPrefix(path, "/") {
|
|
|
|
return nil
|
2019-03-20 17:32:51 +00:00
|
|
|
}
|
|
|
|
for _, image := range bi.Images {
|
|
|
|
if image.Path == path && image.addr == addr {
|
2019-05-08 21:06:38 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actually add the image.
|
|
|
|
image := &Image{Path: path, addr: addr, typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
|
|
|
// add Image regardless of error so that we don't attempt to re-add it every time we stop
|
|
|
|
image.index = len(bi.Images)
|
|
|
|
bi.Images = append(bi.Images, image)
|
|
|
|
err := loadBinaryInfo(bi, image, path, addr)
|
|
|
|
if err != nil {
|
|
|
|
bi.Images[len(bi.Images)-1].loadErr = err
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// moduleDataToImage finds the image corresponding to the given module data object.
|
|
|
|
func (bi *BinaryInfo) moduleDataToImage(md *moduleData) *Image {
|
|
|
|
return bi.funcToImage(bi.PCToFunc(uint64(md.text)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// imageToModuleData finds the module data in mds corresponding to the given image.
|
|
|
|
func (bi *BinaryInfo) imageToModuleData(image *Image, mds []moduleData) *moduleData {
|
|
|
|
for _, md := range mds {
|
|
|
|
im2 := bi.moduleDataToImage(&md)
|
|
|
|
if im2.index == image.index {
|
|
|
|
return &md
|
2019-03-20 17:32:51 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
return nil
|
2019-03-20 17:32:51 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
// typeToImage returns the image containing the give type.
|
|
|
|
func (bi *BinaryInfo) typeToImage(typ godwarf.Type) *Image {
|
|
|
|
return bi.Images[typ.Common().Index]
|
|
|
|
}
|
|
|
|
|
|
|
|
var errBinaryInfoClose = errors.New("multiple errors closing executable files")
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// Close closes all internal readers.
|
2017-04-06 18:14:01 +00:00
|
|
|
func (bi *BinaryInfo) Close() error {
|
2019-05-08 21:06:38 +00:00
|
|
|
var errs []error
|
|
|
|
for _, image := range bi.Images {
|
|
|
|
if err := image.Close(); err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
2018-06-20 10:20:35 +00:00
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
switch len(errs) {
|
|
|
|
case 0:
|
|
|
|
return nil
|
|
|
|
case 1:
|
|
|
|
return errs[0]
|
|
|
|
default:
|
|
|
|
return errBinaryInfoClose
|
2019-01-28 13:23:10 +00:00
|
|
|
}
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
func (image *Image) Close() error {
|
|
|
|
var err1, err2 error
|
|
|
|
if image.sepDebugCloser != nil {
|
|
|
|
err := image.sepDebugCloser.Close()
|
|
|
|
if err != nil {
|
|
|
|
err1 = fmt.Errorf("closing shared object %q (split dwarf): %v", image.Path, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if image.closer != nil {
|
|
|
|
err := image.closer.Close()
|
|
|
|
if err != nil {
|
|
|
|
err2 = fmt.Errorf("closing shared object %q: %v", image.Path, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err1 != nil && err2 != nil {
|
|
|
|
return errBinaryInfoClose
|
|
|
|
}
|
|
|
|
if err1 != nil {
|
|
|
|
return err1
|
|
|
|
}
|
|
|
|
return err2
|
2017-08-15 06:21:24 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
func (image *Image) setLoadError(fmtstr string, args ...interface{}) {
|
|
|
|
image.loadErrMu.Lock()
|
|
|
|
image.loadErr = fmt.Errorf(fmtstr, args...)
|
|
|
|
image.loadErrMu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadError returns any error incurred while loading this image.
|
|
|
|
func (image *Image) LoadError() error {
|
|
|
|
return image.loadErr
|
2017-08-15 06:21:24 +00:00
|
|
|
}
|
|
|
|
|
2017-08-22 14:40:01 +00:00
|
|
|
type nilCloser struct{}
|
|
|
|
|
|
|
|
func (c *nilCloser) Close() error { return nil }
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
// LoadImageFromData creates a new Image, using the specified data, and adds it to bi.
|
2017-08-24 07:46:47 +00:00
|
|
|
// This is used for debugging BinaryInfo, you should use LoadBinary instead.
|
2019-05-08 21:06:38 +00:00
|
|
|
func (bi *BinaryInfo) LoadImageFromData(dwdata *dwarf.Data, debugFrameBytes, debugLineBytes, debugLocBytes []byte) {
|
|
|
|
image := &Image{}
|
|
|
|
image.closer = (*nilCloser)(nil)
|
|
|
|
image.sepDebugCloser = (*nilCloser)(nil)
|
|
|
|
image.dwarf = dwdata
|
|
|
|
image.typeCache = make(map[dwarf.Offset]godwarf.Type)
|
2017-08-22 14:40:01 +00:00
|
|
|
|
|
|
|
if debugFrameBytes != nil {
|
2019-05-08 21:06:38 +00:00
|
|
|
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0)
|
2017-08-22 14:40:01 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
image.loclistInit(debugLocBytes, bi.Arch.PtrSize())
|
|
|
|
|
|
|
|
bi.loadDebugInfoMaps(image, debugLineBytes, nil, nil)
|
2017-08-24 07:46:47 +00:00
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
bi.Images = append(bi.Images, image)
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
func (image *Image) loclistInit(data []byte, ptrSz int) {
|
|
|
|
image.loclist.data = data
|
|
|
|
image.loclist.ptrSz = ptrSz
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
|
2018-07-28 19:12:07 +00:00
|
|
|
func (bi *BinaryInfo) locationExpr(entry reader.Entry, attr dwarf.Attr, pc uint64) ([]byte, string, error) {
|
2017-08-24 07:46:47 +00:00
|
|
|
a := entry.Val(attr)
|
|
|
|
if a == nil {
|
2018-07-28 19:12:07 +00:00
|
|
|
return 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)
|
2018-07-28 19:12:07 +00:00
|
|
|
return instr, descr.String(), nil
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
off, ok := a.(int64)
|
|
|
|
if !ok {
|
2018-07-28 19:12:07 +00:00
|
|
|
return nil, "", fmt.Errorf("could not interpret location attribute %s", attr)
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
instr := bi.loclistEntry(off, pc)
|
|
|
|
if instr == nil {
|
2018-07-28 19:12:07 +00:00
|
|
|
return 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)
|
2018-07-28 19:12:07 +00:00
|
|
|
return instr, descr.String(), nil
|
|
|
|
}
|
|
|
|
|
2019-06-24 15:02:14 +00:00
|
|
|
// LocationCovers returns the list of PC addresses that is covered by the
|
|
|
|
// location attribute 'attr' of entry 'entry'.
|
|
|
|
func (bi *BinaryInfo) LocationCovers(entry *dwarf.Entry, attr dwarf.Attr) ([][2]uint64, error) {
|
|
|
|
a := entry.Val(attr)
|
|
|
|
if a == nil {
|
|
|
|
return nil, fmt.Errorf("attribute %s not found", attr)
|
|
|
|
}
|
|
|
|
if _, isblock := a.([]byte); isblock {
|
|
|
|
return [][2]uint64{[2]uint64{0, ^uint64(0)}}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
off, ok := a.(int64)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("attribute %s of unsupported type %T", attr, a)
|
|
|
|
}
|
|
|
|
cu := bi.findCompileUnitForOffset(entry.Offset)
|
|
|
|
if cu == nil {
|
|
|
|
return nil, errors.New("could not find compile unit")
|
|
|
|
}
|
|
|
|
|
|
|
|
image := cu.image
|
|
|
|
base := cu.lowPC
|
|
|
|
if image == nil || image.loclist.data == nil {
|
|
|
|
return nil, errors.New("malformed executable")
|
|
|
|
}
|
|
|
|
|
|
|
|
r := [][2]uint64{}
|
|
|
|
image.loclist.Seek(int(off))
|
|
|
|
var e loclistEntry
|
|
|
|
for image.loclist.Next(&e) {
|
|
|
|
if e.BaseAddressSelection() {
|
|
|
|
base = e.highpc
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r = append(r, [2]uint64{e.lowpc + base, e.highpc + base})
|
|
|
|
}
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
2018-07-28 19:12:07 +00:00
|
|
|
// 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).
|
|
|
|
func (bi *BinaryInfo) Location(entry reader.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, string, error) {
|
|
|
|
instr, descr, err := bi.locationExpr(entry, attr, pc)
|
|
|
|
if err != nil {
|
|
|
|
return 0, nil, "", err
|
|
|
|
}
|
2017-11-21 08:50:14 +00:00
|
|
|
addr, pieces, err := op.ExecuteStackProgram(regs, instr)
|
2018-07-28 19:12:07 +00:00
|
|
|
return addr, pieces, descr, 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
|
2019-05-08 21:06:38 +00:00
|
|
|
image := bi.Images[0]
|
2017-08-24 07:46:47 +00:00
|
|
|
if cu := bi.findCompileUnit(pc); cu != nil {
|
2018-11-06 22:34:26 +00:00
|
|
|
base = cu.lowPC
|
2019-05-08 21:06:38 +00:00
|
|
|
image = cu.image
|
|
|
|
}
|
|
|
|
if image == nil || image.loclist.data == nil {
|
|
|
|
return nil
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
image.loclist.Seek(int(off))
|
2017-08-24 07:46:47 +00:00
|
|
|
var e loclistEntry
|
2019-05-08 21:06:38 +00:00
|
|
|
for image.loclist.Next(&e) {
|
2017-08-24 07:46:47 +00:00
|
|
|
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 {
|
2018-11-06 22:34:26 +00:00
|
|
|
for _, rng := range cu.ranges {
|
2018-05-29 15:01:51 +00:00
|
|
|
if pc >= rng[0] && pc < rng[1] {
|
|
|
|
return cu
|
|
|
|
}
|
2018-08-17 06:17:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
|
2019-06-24 15:02:14 +00:00
|
|
|
i := sort.Search(len(bi.compileUnits), func(i int) bool {
|
|
|
|
return bi.compileUnits[i].offset >= off
|
|
|
|
})
|
|
|
|
if i > 0 {
|
|
|
|
i--
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
2019-06-24 15:02:14 +00:00
|
|
|
return bi.compileUnits[i]
|
2017-08-22 14:40:01 +00:00
|
|
|
}
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// Producer returns the value of DW_AT_producer.
|
2018-04-14 09:04:14 +00:00
|
|
|
func (bi *BinaryInfo) Producer() string {
|
|
|
|
for _, cu := range bi.compileUnits {
|
|
|
|
if cu.isgo && cu.producer != "" {
|
|
|
|
return cu.producer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2018-05-04 17:31:45 +00:00
|
|
|
// Type returns the Dwarf type entry at `offset`.
|
2019-05-08 21:06:38 +00:00
|
|
|
func (image *Image) Type(offset dwarf.Offset) (godwarf.Type, error) {
|
|
|
|
return godwarf.ReadType(image.dwarf, image.index, offset, image.typeCache)
|
|
|
|
}
|
|
|
|
|
|
|
|
// funcToImage returns the Image containing function fn, or the
|
|
|
|
// executable file as a fallback.
|
|
|
|
func (bi *BinaryInfo) funcToImage(fn *Function) *Image {
|
|
|
|
if fn == nil {
|
|
|
|
return bi.Images[0]
|
|
|
|
}
|
|
|
|
return fn.cu.image
|
2018-05-04 17:31:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
// ELF ///////////////////////////////////////////////////////////////
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// ErrNoBuildIDNote is used in openSeparateDebugInfo to signal there's no
|
2018-06-20 10:20:35 +00:00
|
|
|
// build-id note on the binary, so LoadBinaryInfoElf will return
|
|
|
|
// the error message coming from elfFile.DWARF() instead.
|
2018-08-31 18:08:18 +00:00
|
|
|
type ErrNoBuildIDNote struct{}
|
2018-06-20 10:20:35 +00:00
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
func (e *ErrNoBuildIDNote) Error() string {
|
2018-06-20 10:20:35 +00:00
|
|
|
return "can't find build-id note on binary"
|
|
|
|
}
|
|
|
|
|
|
|
|
// openSeparateDebugInfo searches for a file containing the separate
|
|
|
|
// debug info for the binary using the "build ID" method as described
|
|
|
|
// in GDB's documentation [1], and if found returns two handles, one
|
|
|
|
// for the bare file, and another for its corresponding elf.File.
|
|
|
|
// [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
|
2018-11-07 22:21:35 +00:00
|
|
|
//
|
|
|
|
// Alternatively, if the debug file cannot be found be the build-id, Delve
|
|
|
|
// will look in directories specified by the debug-info-directories config value.
|
2019-05-08 21:06:38 +00:00
|
|
|
func (bi *BinaryInfo) openSeparateDebugInfo(image *Image, exe *elf.File, debugInfoDirectories []string) (*os.File, *elf.File, error) {
|
2018-11-07 22:21:35 +00:00
|
|
|
var debugFilePath string
|
|
|
|
for _, dir := range debugInfoDirectories {
|
|
|
|
var potentialDebugFilePath string
|
|
|
|
if strings.Contains(dir, "build-id") {
|
|
|
|
desc1, desc2, err := parseBuildID(exe)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
potentialDebugFilePath = fmt.Sprintf("%s/%s/%s.debug", dir, desc1, desc2)
|
|
|
|
} else {
|
2019-05-08 21:06:38 +00:00
|
|
|
potentialDebugFilePath = fmt.Sprintf("%s/%s.debug", dir, filepath.Base(image.Path))
|
2018-11-07 22:21:35 +00:00
|
|
|
}
|
|
|
|
_, err := os.Stat(potentialDebugFilePath)
|
|
|
|
if err == nil {
|
|
|
|
debugFilePath = potentialDebugFilePath
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if debugFilePath == "" {
|
|
|
|
return nil, nil, ErrNoDebugInfoFound
|
|
|
|
}
|
|
|
|
sepFile, err := os.OpenFile(debugFilePath, 0, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, errors.New("can't open separate debug file: " + err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
elfFile, err := elf.NewFile(sepFile)
|
|
|
|
if err != nil {
|
|
|
|
sepFile.Close()
|
|
|
|
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if elfFile.Machine != elf.EM_X86_64 {
|
|
|
|
sepFile.Close()
|
|
|
|
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, ErrUnsupportedLinuxArch.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return sepFile, elfFile, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseBuildID(exe *elf.File) (string, string, error) {
|
2018-06-20 10:20:35 +00:00
|
|
|
buildid := exe.Section(".note.gnu.build-id")
|
|
|
|
if buildid == nil {
|
2018-11-07 22:21:35 +00:00
|
|
|
return "", "", &ErrNoBuildIDNote{}
|
2018-06-20 10:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
br := buildid.Open()
|
2018-08-31 18:08:18 +00:00
|
|
|
bh := new(buildIDHeader)
|
2018-06-20 10:20:35 +00:00
|
|
|
if err := binary.Read(br, binary.LittleEndian, bh); err != nil {
|
2018-11-07 22:21:35 +00:00
|
|
|
return "", "", errors.New("can't read build-id header: " + err.Error())
|
2018-06-20 10:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
name := make([]byte, bh.Namesz)
|
|
|
|
if err := binary.Read(br, binary.LittleEndian, name); err != nil {
|
2018-11-07 22:21:35 +00:00
|
|
|
return "", "", errors.New("can't read build-id name: " + err.Error())
|
2018-06-20 10:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if strings.TrimSpace(string(name)) != "GNU\x00" {
|
2018-11-07 22:21:35 +00:00
|
|
|
return "", "", errors.New("invalid build-id signature")
|
2018-06-20 10:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
descBinary := make([]byte, bh.Descsz)
|
|
|
|
if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil {
|
2018-11-07 22:21:35 +00:00
|
|
|
return "", "", errors.New("can't read build-id desc: " + err.Error())
|
2018-06-20 10:20:35 +00:00
|
|
|
}
|
|
|
|
desc := hex.EncodeToString(descBinary)
|
2018-11-07 22:21:35 +00:00
|
|
|
return desc[:2], desc[2:], nil
|
2018-06-20 10:20:35 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
// loadBinaryInfoElf specifically loads information from an ELF binary.
|
|
|
|
func loadBinaryInfoElf(bi *BinaryInfo, image *Image, path string, addr uint64, wg *sync.WaitGroup) error {
|
2017-04-06 18:14:01 +00:00
|
|
|
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
image.closer = exe
|
2017-04-06 18:14:01 +00:00
|
|
|
elfFile, err := elf.NewFile(exe)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if elfFile.Machine != elf.EM_X86_64 {
|
2018-08-31 18:08:18 +00:00
|
|
|
return ErrUnsupportedLinuxArch
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2018-05-29 15:01:51 +00:00
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
if image.index == 0 {
|
|
|
|
// adding executable file:
|
|
|
|
// - addr is entryPoint therefore staticBase needs to be calculated by
|
|
|
|
// subtracting the entry point specified in the executable file from addr.
|
|
|
|
// - memory address of the .dynamic section needs to be recorded in
|
|
|
|
// BinaryInfo so that we can find loaded libraries.
|
|
|
|
if addr != 0 {
|
|
|
|
image.StaticBase = addr - elfFile.Entry
|
|
|
|
} else if elfFile.Type == elf.ET_DYN {
|
2018-05-29 15:01:51 +00:00
|
|
|
return ErrCouldNotDetermineRelocation
|
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
if dynsec := elfFile.Section(".dynamic"); dynsec != nil {
|
|
|
|
bi.ElfDynamicSection.Addr = dynsec.Addr + image.StaticBase
|
|
|
|
bi.ElfDynamicSection.Size = dynsec.Size
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
image.StaticBase = addr
|
2019-03-20 17:32:51 +00:00
|
|
|
}
|
|
|
|
|
2018-06-20 10:20:35 +00:00
|
|
|
dwarfFile := elfFile
|
2018-05-29 15:01:51 +00:00
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
image.dwarf, err = elfFile.DWARF()
|
2017-04-06 18:14:01 +00:00
|
|
|
if err != nil {
|
2018-06-20 10:20:35 +00:00
|
|
|
var sepFile *os.File
|
|
|
|
var serr error
|
2019-05-08 21:06:38 +00:00
|
|
|
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(image, elfFile, bi.debugInfoDirectories)
|
2018-06-20 10:20:35 +00:00
|
|
|
if serr != nil {
|
|
|
|
return serr
|
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
image.sepDebugCloser = sepFile
|
|
|
|
image.dwarf, err = dwarfFile.DWARF()
|
2018-06-20 10:20:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
image.dwarfReader = image.dwarf.Reader()
|
2018-02-13 17:20:45 +00:00
|
|
|
|
2018-06-18 08:05:21 +00:00
|
|
|
debugLineBytes, err := godwarf.GetDebugSectionElf(dwarfFile, "line")
|
2017-09-01 13:30:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-06-18 08:05:21 +00:00
|
|
|
debugLocBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "loc")
|
2019-05-08 21:06:38 +00:00
|
|
|
image.loclistInit(debugLocBytes, bi.Arch.PtrSize())
|
2017-09-01 13:30:45 +00:00
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
wg.Add(2)
|
|
|
|
go bi.parseDebugFrameElf(image, dwarfFile, wg)
|
|
|
|
go bi.loadDebugInfoMaps(image, debugLineBytes, wg, nil)
|
|
|
|
if image.index == 0 {
|
|
|
|
// determine g struct offset only when loading the executable file
|
|
|
|
wg.Add(1)
|
|
|
|
go bi.setGStructOffsetElf(image, dwarfFile, wg)
|
|
|
|
}
|
2017-04-06 18:14:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
func (bi *BinaryInfo) parseDebugFrameElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
|
2017-04-06 18:14:01 +00:00
|
|
|
defer wg.Done()
|
|
|
|
|
2018-06-18 08:05:21 +00:00
|
|
|
debugFrameData, err := godwarf.GetDebugSectionElf(exe, "frame")
|
|
|
|
if err != nil {
|
2019-05-08 21:06:38 +00:00
|
|
|
image.setLoadError("could not get .debug_frame section: %v", err)
|
2017-08-15 06:21:24 +00:00
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2018-06-18 08:05:21 +00:00
|
|
|
debugInfoData, err := godwarf.GetDebugSectionElf(exe, "info")
|
|
|
|
if err != nil {
|
2019-05-08 21:06:38 +00:00
|
|
|
image.setLoadError("could not get .debug_info section: %v", err)
|
2018-06-18 08:05:21 +00:00
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoData), image.StaticBase))
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
|
2017-06-21 22:40:42 +00:00
|
|
|
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 {
|
2019-05-08 21:06:38 +00:00
|
|
|
image.setLoadError("could not parse ELF symbols: %v", err)
|
2017-08-15 06:21:24 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2019-02-26 17:17:05 +00:00
|
|
|
if tls == nil {
|
|
|
|
bi.gStructOffset = ^uint64(8) + 1 // -8
|
|
|
|
return
|
|
|
|
}
|
2018-11-28 17:26:01 +00:00
|
|
|
memsz := tls.Memsz
|
|
|
|
|
|
|
|
memsz = (memsz + uint64(bi.Arch.PtrSize()) - 1) & ^uint64(bi.Arch.PtrSize()-1) // align to pointer-sized-boundary
|
2017-06-21 22:40:42 +00:00
|
|
|
// 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.
|
2018-11-28 17:26:01 +00:00
|
|
|
bi.gStructOffset = ^(memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value
|
2017-06-21 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 18:14:01 +00:00
|
|
|
// PE ////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-05-29 15:01:51 +00:00
|
|
|
const _IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
// loadBinaryInfoPE specifically loads information from a PE binary.
|
|
|
|
func loadBinaryInfoPE(bi *BinaryInfo, image *Image, path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
2017-04-06 18:14:01 +00:00
|
|
|
peFile, closer, err := openExecutablePathPE(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
image.closer = closer
|
2017-04-06 18:14:01 +00:00
|
|
|
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
2018-08-31 18:08:18 +00:00
|
|
|
return ErrUnsupportedWindowsArch
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
image.dwarf, err = peFile.DWARF()
|
2017-04-06 18:14:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-05-29 15:01:51 +00:00
|
|
|
//TODO(aarzilli): actually test this when Go supports PIE buildmode on Windows.
|
|
|
|
opth := peFile.OptionalHeader.(*pe.OptionalHeader64)
|
|
|
|
if entryPoint != 0 {
|
2019-05-08 21:06:38 +00:00
|
|
|
image.StaticBase = entryPoint - opth.ImageBase
|
2018-05-29 15:01:51 +00:00
|
|
|
} else {
|
|
|
|
if opth.DllCharacteristics&_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE != 0 {
|
|
|
|
return ErrCouldNotDetermineRelocation
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
image.dwarfReader = image.dwarf.Reader()
|
2018-02-13 17:20:45 +00:00
|
|
|
|
2018-06-18 08:05:21 +00:00
|
|
|
debugLineBytes, err := godwarf.GetDebugSectionPE(peFile, "line")
|
2017-09-01 13:30:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-06-18 08:05:21 +00:00
|
|
|
debugLocBytes, _ := godwarf.GetDebugSectionPE(peFile, "loc")
|
2019-05-08 21:06:38 +00:00
|
|
|
image.loclistInit(debugLocBytes, bi.Arch.PtrSize())
|
2017-09-01 13:30:45 +00:00
|
|
|
|
|
|
|
wg.Add(2)
|
2019-05-08 21:06:38 +00:00
|
|
|
go bi.parseDebugFramePE(image, peFile, wg)
|
|
|
|
go bi.loadDebugInfoMaps(image, debugLineBytes, wg, nil)
|
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
|
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
func (bi *BinaryInfo) parseDebugFramePE(image *Image, exe *pe.File, wg *sync.WaitGroup) {
|
2017-04-06 18:14:01 +00:00
|
|
|
defer wg.Done()
|
|
|
|
|
2018-06-18 08:05:21 +00:00
|
|
|
debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame")
|
|
|
|
if err != nil {
|
2019-05-08 21:06:38 +00:00
|
|
|
image.setLoadError("could not get .debug_frame section: %v", err)
|
2017-08-15 06:21:24 +00:00
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2018-06-18 08:05:21 +00:00
|
|
|
debugInfoBytes, err := godwarf.GetDebugSectionPE(exe, "info")
|
|
|
|
if err != nil {
|
2019-05-08 21:06:38 +00:00
|
|
|
image.setLoadError("could not get .debug_info section: %v", err)
|
2018-06-18 08:05:21 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase))
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MACH-O ////////////////////////////////////////////////////////////
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
// loadBinaryInfoMacho specifically loads information from a Mach-O binary.
|
|
|
|
func loadBinaryInfoMacho(bi *BinaryInfo, image *Image, path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
2017-04-06 18:14:01 +00:00
|
|
|
exe, err := macho.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
image.closer = exe
|
2017-04-06 18:14:01 +00:00
|
|
|
if exe.Cpu != macho.CpuAmd64 {
|
2018-08-31 18:08:18 +00:00
|
|
|
return ErrUnsupportedDarwinArch
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2019-05-08 21:06:38 +00:00
|
|
|
image.dwarf, err = exe.DWARF()
|
2017-04-06 18:14:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
image.dwarfReader = image.dwarf.Reader()
|
2018-02-13 17:20:45 +00:00
|
|
|
|
2018-06-18 08:05:21 +00:00
|
|
|
debugLineBytes, err := godwarf.GetDebugSectionMacho(exe, "line")
|
2017-09-01 13:30:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-06-18 08:05:21 +00:00
|
|
|
debugLocBytes, _ := godwarf.GetDebugSectionMacho(exe, "loc")
|
2019-05-08 21:06:38 +00:00
|
|
|
image.loclistInit(debugLocBytes, bi.Arch.PtrSize())
|
2017-09-01 13:30:45 +00:00
|
|
|
|
|
|
|
wg.Add(2)
|
2019-05-08 21:06:38 +00:00
|
|
|
go bi.parseDebugFrameMacho(image, exe, wg)
|
|
|
|
go bi.loadDebugInfoMaps(image, debugLineBytes, wg, bi.setGStructOffsetMacho)
|
2017-04-06 18:14:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-14 09:04:14 +00:00
|
|
|
func (bi *BinaryInfo) setGStructOffsetMacho() {
|
|
|
|
// In go1.11 it's 0x30, before 0x8a0, see:
|
|
|
|
// https://github.com/golang/go/issues/23617
|
|
|
|
// and go commit b3a854c733257c5249c3435ffcee194f8439676a
|
|
|
|
producer := bi.Producer()
|
|
|
|
if producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 11) {
|
|
|
|
bi.gStructOffset = 0x30
|
|
|
|
return
|
|
|
|
}
|
|
|
|
bi.gStructOffset = 0x8a0
|
|
|
|
}
|
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
func (bi *BinaryInfo) parseDebugFrameMacho(image *Image, exe *macho.File, wg *sync.WaitGroup) {
|
2017-04-06 18:14:01 +00:00
|
|
|
defer wg.Done()
|
|
|
|
|
2018-06-18 08:05:21 +00:00
|
|
|
debugFrameBytes, err := godwarf.GetDebugSectionMacho(exe, "frame")
|
|
|
|
if err != nil {
|
2019-05-08 21:06:38 +00:00
|
|
|
image.setLoadError("could not get __debug_frame section: %v", err)
|
2017-08-15 06:21:24 +00:00
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2018-06-18 08:05:21 +00:00
|
|
|
debugInfoBytes, err := godwarf.GetDebugSectionMacho(exe, "info")
|
|
|
|
if err != nil {
|
2019-05-08 21:06:38 +00:00
|
|
|
image.setLoadError("could not get .debug_info section: %v", err)
|
2018-06-18 08:05:21 +00:00
|
|
|
return
|
2017-04-06 18:14:01 +00:00
|
|
|
}
|
2017-08-24 07:46:47 +00:00
|
|
|
|
2019-05-08 21:06:38 +00:00
|
|
|
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase))
|
2017-08-24 07:46:47 +00:00
|
|
|
}
|