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"
2019-08-08 19:25:50 +00:00
"go/ast"
"go/token"
2017-04-06 18:14:01 +00:00
"io"
"os"
2018-11-07 22:21:35 +00:00
"path/filepath"
2017-09-01 13:30:45 +00:00
"sort"
2019-08-08 19:25:50 +00:00
"strconv"
2017-09-01 13:30:45 +00:00
"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"
2019-11-12 19:44:08 +00:00
"github.com/go-delve/delve/pkg/dwarf/loclist"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/reader"
2020-03-10 16:34:40 +00:00
"github.com/go-delve/delve/pkg/dwarf/util"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/goversion"
2019-08-08 19:25:50 +00:00
"github.com/go-delve/delve/pkg/logflags"
2022-01-30 21:39:30 +00:00
"github.com/go-delve/delve/pkg/proc/debuginfod"
2020-03-20 17:23:10 +00:00
"github.com/hashicorp/golang-lru/simplelru"
2019-08-08 19:25:50 +00:00
"github.com/sirupsen/logrus"
2017-04-06 18:14:01 +00:00
)
2020-03-23 17:57:01 +00:00
const (
2020-08-18 00:19:46 +00:00
dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
dwarfAttrAddrBase = 0x74 // debug/dwarf.AttrAddrBase in Go 1.14, defined here for compatibility with Go < 1.14
dwarfTreeCacheSize = 512 // size of the dwarfTree cache of each image
2020-03-23 17:57: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.
2020-03-30 18:03:29 +00:00
Arch * Arch
2018-11-06 22:32:14 +00:00
// GOOS operating system this binary is executing on.
GOOS string
2019-05-08 21:06:38 +00:00
debugInfoDirectories [ ] string
2022-01-30 21:39:30 +00:00
// BuildID of this binary.
BuildID 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
2021-10-30 18:52:26 +00:00
// lookupGenericFunc maps function names, with their type parameters removed, to functions.
// Functions that are not generic are not added to this map.
lookupGenericFunc map [ string ] [ ] * Function
2018-11-06 22:32:14 +00:00
2020-03-10 16:34:40 +00:00
// SymNames maps addr to a description *elf.Symbol of this addr.
SymNames map [ uint64 ] * elf . Symbol
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
2019-11-25 17:10:18 +00:00
// PackageMap maps package names to package paths, needed to lookup types inside DWARF info.
// On Go1.12 this mapping is determined by using the last element of a package path, for example:
// github.com/go-delve/delve
// will map to 'delve' because it ends in '/delve'.
// Starting with Go1.13 debug_info will contain a special attribute
// (godwarf.AttrGoPackageName) containing the canonical package name for
// each package.
// If multiple packages have the same name the map entry will have more
// than one item in the slice.
PackageMap map [ string ] [ ] string
2017-04-06 18:14:01 +00:00
2018-11-06 22:32:14 +00:00
frameEntries frame . FrameDescriptionEntries
2019-06-24 15:02:14 +00:00
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.
2020-09-09 17:36:15 +00:00
nameOfRuntimeType map [ uint64 ] 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
2019-10-07 16:38:47 +00:00
2019-11-01 19:41:06 +00:00
// inlinedCallLines maps a file:line pair, corresponding to the header line
// of a function to a list of PC addresses where an inlined call to that
// function starts.
inlinedCallLines map [ fileLine ] [ ] uint64
2021-07-08 15:47:53 +00:00
// dwrapUnwrapCache caches unwrapping of defer wrapper functions (dwrap)
dwrapUnwrapCache map [ uint64 ] * Function
// Go 1.17 register ABI is enabled.
regabi bool
2019-10-07 16:38:47 +00:00
logger * logrus . Entry
2017-04-06 18:14:01 +00:00
}
2020-03-23 17:57:01 +00:00
var (
// ErrCouldNotDetermineRelocation is an error returned when Delve could not determine the base address of a
2020-08-24 17:19:50 +00:00
// position independent executable.
2020-03-23 17:57:01 +00:00
ErrCouldNotDetermineRelocation = errors . New ( "could not determine the base address of a PIE" )
2018-05-29 15:01:51 +00:00
2020-03-23 17:57:01 +00:00
// ErrNoDebugInfoFound is returned when Delve cannot open the debug_info
// section or find an external debug info file.
ErrNoDebugInfoFound = errors . New ( "could not open debug info" )
)
2018-11-07 22:21:35 +00:00
2020-03-23 17:57:01 +00:00
var (
supportedLinuxArch = map [ elf . Machine ] bool {
elf . EM_X86_64 : true ,
elf . EM_AARCH64 : true ,
elf . EM_386 : true ,
}
2021-02-10 17:31:37 +00:00
supportedWindowsArch = map [ _PEMachine ] bool {
_IMAGE_FILE_MACHINE_AMD64 : true ,
2020-03-23 17:57:01 +00:00
}
supportedDarwinArch = map [ macho . Cpu ] bool {
macho . CpuAmd64 : true ,
2021-01-04 16:52:04 +00:00
macho . CpuArm64 : true ,
2020-03-23 17:57:01 +00:00
}
)
// ErrFunctionNotFound is returned when failing to find the
// function named 'FuncName' within the binary.
type ErrFunctionNotFound struct {
FuncName string
2020-03-10 16:34:40 +00:00
}
2020-03-23 17:57:01 +00:00
func ( err * ErrFunctionNotFound ) Error ( ) string {
return fmt . Sprintf ( "could not find function %s\n" , err . FuncName )
}
// FindFileLocation returns the PC for a given file:line.
// Assumes that `file` is normalized to lower case and '/' on Windows.
2021-09-16 14:51:51 +00:00
func FindFileLocation ( p Process , filename string , lineno int ) ( [ ] uint64 , error ) {
// A single file:line can appear in multiple concrete functions, because of
// generics instantiation as well as multiple inlined calls into other
// concrete functions.
// 1. Find all instructions assigned in debug_line to filename:lineno.
bi := p . BinInfo ( )
fileFound := false
pcs := [ ] line . PCStmt { }
for _ , image := range bi . Images {
for _ , cu := range image . compileUnits {
if cu . lineInfo == nil || cu . lineInfo . Lookup [ filename ] == nil {
continue
}
fileFound = true
pcs = append ( pcs , cu . lineInfo . LineToPCs ( filename , lineno ) ... )
}
2020-03-23 17:57:01 +00:00
}
2021-09-16 14:51:51 +00:00
if len ( pcs ) == 0 {
// Check if the line contained a call to a function that was inlined, in
// that case it's possible for the line itself to not appear in debug_line
// at all, but it will still be in debug_info as the call site for an
// inlined subroutine entry.
for _ , pc := range bi . inlinedCallLines [ fileLine { filename , lineno } ] {
pcs = append ( pcs , line . PCStmt { PC : pc , Stmt : true } )
}
}
if len ( pcs ) == 0 {
return nil , & ErrCouldNotFindLine { fileFound , filename , lineno }
}
// 2. assign all occurences of filename:lineno to their containing function
pcByFunc := map [ * Function ] [ ] line . PCStmt { }
sort . Slice ( pcs , func ( i , j int ) bool { return pcs [ i ] . PC < pcs [ j ] . PC } )
2020-03-23 17:57:01 +00:00
var fn * Function
2021-09-16 14:51:51 +00:00
for _ , pcstmt := range pcs {
if fn == nil || ( pcstmt . PC < fn . Entry ) || ( pcstmt . PC >= fn . End ) {
fn = p . BinInfo ( ) . PCToFunc ( pcstmt . PC )
2020-03-23 17:57:01 +00:00
}
2021-09-16 14:51:51 +00:00
if fn != nil {
pcByFunc [ fn ] = append ( pcByFunc [ fn ] , pcstmt )
2020-03-23 17:57:01 +00:00
}
}
2021-09-16 14:51:51 +00:00
selectedPCs := [ ] uint64 { }
for fn , pcs := range pcByFunc {
// 3. for each concrete function split instruction between the inlined functions it contains
if strings . Contains ( fn . Name , "·dwrap·" ) || fn . trampoline {
// skip autogenerated functions
continue
}
dwtree , err := fn . cu . image . getDwarfTree ( fn . offset )
if err != nil {
return nil , fmt . Errorf ( "loading DWARF for %s@%#x: %v" , fn . Name , fn . offset , err )
}
inlrngs := allInlineCallRanges ( dwtree )
// findInlRng returns the DWARF offset of the inlined call containing pc.
// If multiple nested inlined calls contain pc the deepest one is returned
// (since allInlineCallRanges returns inlined call by decreasing depth
// this is the first matching entry of the slice).
findInlRng := func ( pc uint64 ) dwarf . Offset {
for _ , inlrng := range inlrngs {
if inlrng . rng [ 0 ] <= pc && pc < inlrng . rng [ 1 ] {
return inlrng . off
}
}
return fn . offset
}
pcsByOff := map [ dwarf . Offset ] [ ] line . PCStmt { }
for _ , pc := range pcs {
off := findInlRng ( pc . PC )
pcsByOff [ off ] = append ( pcsByOff [ off ] , pc )
}
// 4. pick the first instruction with stmt set for each inlined call as
// well as the main body of the concrete function. If nothing has
// is_stmt set pick the first instruction instead.
for off , pcs := range pcsByOff {
sort . Slice ( pcs , func ( i , j int ) bool { return pcs [ i ] . PC < pcs [ j ] . PC } )
var selectedPC uint64
for _ , pc := range pcs {
if pc . Stmt {
selectedPC = pc . PC
break
}
}
if selectedPC == 0 && len ( pcs ) > 0 {
selectedPC = pcs [ 0 ] . PC
}
if selectedPC == 0 {
continue
}
// 5. if we picked the entry point of the function, skip it
if off == fn . offset && fn . Entry == selectedPC {
selectedPC , _ = FirstPCAfterPrologue ( p , fn , true )
}
selectedPCs = append ( selectedPCs , selectedPC )
}
}
2021-10-30 18:52:26 +00:00
sort . Slice ( selectedPCs , func ( i , j int ) bool { return selectedPCs [ i ] < selectedPCs [ j ] } )
2021-09-16 14:51:51 +00:00
return selectedPCs , nil
}
// inlRnage is the range of an inlined call
type inlRange struct {
off dwarf . Offset
depth uint32
rng [ 2 ] uint64
}
// allInlineCallRanges returns all inlined calls contained inside 'tree' in
// reverse nesting order (i.e. the most nested calls are returned first).
// Note that a single inlined call might not have a continuous range of
// addresses and therefore appear multiple times in the returned slice.
func allInlineCallRanges ( tree * godwarf . Tree ) [ ] inlRange {
r := [ ] inlRange { }
var visit func ( * godwarf . Tree , uint32 )
visit = func ( n * godwarf . Tree , depth uint32 ) {
if n . Tag == dwarf . TagInlinedSubroutine {
for _ , rng := range n . Ranges {
r = append ( r , inlRange { off : n . Offset , depth : depth , rng : rng } )
}
}
for _ , child := range n . Children {
visit ( child , depth + 1 )
}
}
visit ( tree , 0 )
sort . SliceStable ( r , func ( i , j int ) bool { return r [ i ] . depth > r [ j ] . depth } )
return r
2020-03-23 17:57:01 +00:00
}
2021-10-30 18:52:26 +00:00
// FindFunction returns the functions with name funcName.
func ( bi * BinaryInfo ) FindFunction ( funcName string ) ( [ ] * Function , error ) {
if fn := bi . LookupFunc [ funcName ] ; fn != nil {
return [ ] * Function { fn } , nil
}
fns := bi . LookupGenericFunc ( ) [ funcName ]
if len ( fns ) == 0 {
return nil , & ErrFunctionNotFound { funcName }
}
return fns , nil
}
2020-03-23 17:57:01 +00:00
// FindFunctionLocation finds address of a function's line
// If lineOffset is passed FindFunctionLocation will return the address of that line
func FindFunctionLocation ( p Process , funcName string , lineOffset int ) ( [ ] uint64 , error ) {
bi := p . BinInfo ( )
2021-10-30 18:52:26 +00:00
origfns , err := bi . FindFunction ( funcName )
if err != nil {
return nil , err
2020-03-23 17:57:01 +00:00
}
2021-09-16 14:51:51 +00:00
if lineOffset > 0 {
2021-10-30 18:52:26 +00:00
fn := origfns [ 0 ]
filename , lineno := fn . cu . lineInfo . PCToLine ( fn . Entry , fn . Entry )
2021-09-16 14:51:51 +00:00
return FindFileLocation ( p , filename , lineno + lineOffset )
}
2021-10-30 18:52:26 +00:00
r := make ( [ ] uint64 , 0 , len ( origfns [ 0 ] . InlinedCalls ) + len ( origfns ) )
for _ , origfn := range origfns {
if origfn . Entry > 0 {
// add concrete implementation of the function
pc , err := FirstPCAfterPrologue ( p , origfn , false )
if err != nil {
return nil , err
}
r = append ( r , pc )
}
// add inlined calls to the function
for _ , call := range origfn . InlinedCalls {
r = append ( r , call . LowPC )
}
if len ( r ) == 0 {
return nil , & ErrFunctionNotFound { funcName }
2020-03-23 17:57:01 +00:00
}
}
2021-10-30 18:52:26 +00:00
sort . Slice ( r , func ( i , j int ) bool { return r [ i ] < r [ j ] } )
2021-09-16 14:51:51 +00:00
return r , nil
2020-03-23 17:57:01 +00:00
}
// FirstPCAfterPrologue returns the address of the first
// instruction after the prologue for function fn.
// If sameline is set FirstPCAfterPrologue will always return an
// address associated with the same line as fn.Entry.
func FirstPCAfterPrologue ( p Process , fn * Function , sameline bool ) ( uint64 , error ) {
pc , _ , line , ok := fn . cu . lineInfo . PrologueEndPC ( fn . Entry , fn . End )
if ok {
if ! sameline {
return pc , nil
}
_ , entryLine := fn . cu . lineInfo . PCToLine ( fn . Entry , fn . Entry )
if entryLine == line {
return pc , nil
}
}
pc , err := firstPCAfterPrologueDisassembly ( p , fn , sameline )
if err != nil {
return fn . Entry , err
}
if pc == fn . Entry {
// Look for the first instruction with the stmt flag set, so that setting a
// breakpoint with file:line and with the function name always result on
// the same instruction being selected.
if pc2 , _ , _ , ok := fn . cu . lineInfo . FirstStmtForLine ( fn . Entry , fn . End ) ; ok {
return pc2 , nil
}
}
return pc , nil
}
2021-02-10 17:31:37 +00:00
// cpuArch is a stringer interface representing CPU architectures.
type cpuArch interface {
2020-03-10 16:34:40 +00:00
String ( ) string
}
2020-03-23 17:57:01 +00:00
// ErrUnsupportedArch is returned when attempting to debug a binary compiled for an unsupported architecture.
type ErrUnsupportedArch struct {
os string
2021-02-10 17:31:37 +00:00
cpuArch cpuArch
2020-03-23 17:57:01 +00:00
}
2020-03-10 16:34:40 +00:00
func ( e * ErrUnsupportedArch ) Error ( ) string {
2021-02-10 17:31:37 +00:00
var supportArchs [ ] cpuArch
2020-03-10 16:34:40 +00:00
switch e . os {
case "linux" :
2021-09-28 19:07:42 +00:00
for linuxArch := range supportedLinuxArch {
2020-03-10 16:34:40 +00:00
supportArchs = append ( supportArchs , linuxArch )
}
case "windows" :
2021-09-28 19:07:42 +00:00
for windowArch := range supportedWindowsArch {
2020-03-10 16:34:40 +00:00
supportArchs = append ( supportArchs , windowArch )
}
case "darwin" :
2021-09-28 19:07:42 +00:00
for darwinArch := range supportedDarwinArch {
2020-03-10 16:34:40 +00:00
supportArchs = append ( supportArchs , darwinArch )
}
}
errStr := "unsupported architecture of " + e . os + "/" + e . cpuArch . String ( )
errStr += " - only"
for _ , arch := range supportArchs {
errStr += " " + e . os + "/" + arch . String ( ) + " "
}
if len ( supportArchs ) == 1 {
errStr += "is supported"
} else {
errStr += "are supported"
}
return errStr
}
2017-09-01 13:30:45 +00:00
type compileUnit struct {
2020-08-18 00:19:46 +00:00
name string // univocal name for non-go compile units
Version uint8 // DWARF version of this compile unit
lowPC uint64
ranges [ ] [ 2 ] uint64
2018-08-07 01:08:25 +00:00
2019-11-01 19:41:06 +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
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.
}
2019-11-01 19:41:06 +00:00
type fileLine struct {
file string
line int
}
2019-05-08 21:06:38 +00:00
// 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
}
2019-11-01 19:41:06 +00:00
// InlinedCall represents a concrete inlined call to a function.
type InlinedCall struct {
cu * compileUnit
LowPC , HighPC uint64 // Address range of the generated inlined instructions
2018-08-07 01:08:25 +00:00
}
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
2019-11-01 19:41:06 +00:00
2021-09-16 13:38:15 +00:00
trampoline bool // DW_AT_trampoline attribute set to true
2019-11-01 19:41:06 +00:00
// InlinedCalls lists all inlined calls to this function
InlinedCalls [ ] InlinedCall
2017-09-01 13:30:45 +00:00
}
2021-10-30 18:52:26 +00:00
// instRange returns the indexes in fn.Name of the type parameter
// instantiation, which is the position of the outermost '[' and ']'.
// If fn is not an instantiated function both returned values will be len(fn.Name)
func ( fn * Function ) instRange ( ) [ 2 ] int {
d := len ( fn . Name )
inst := [ 2 ] int { d , d }
if strings . HasPrefix ( fn . Name , "type.." ) {
return inst
}
inst [ 0 ] = strings . Index ( fn . Name , "[" )
if inst [ 0 ] < 0 {
inst [ 0 ] = d
return inst
}
inst [ 1 ] = strings . LastIndex ( fn . Name , "]" )
if inst [ 1 ] < 0 {
inst [ 0 ] = d
inst [ 1 ] = d
return inst
}
return inst
}
2017-09-01 13:30:45 +00:00
// 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 {
2021-10-30 18:52:26 +00:00
inst := fn . instRange ( )
return packageName ( fn . Name [ : inst [ 0 ] ] )
2017-09-08 10:31:20 +00:00
}
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 {
2021-10-30 18:52:26 +00:00
inst := fn . instRange ( )
pathend := strings . LastIndex ( fn . Name [ : inst [ 0 ] ] , "/" )
2017-09-01 13:30:45 +00:00
if pathend < 0 {
pathend = 0
}
l := strings . Index ( fn . Name [ pathend : ] , "." )
2021-10-30 18:52:26 +00:00
if l == - 1 {
2017-09-01 13:30:45 +00:00
return ""
}
2021-10-30 18:52:26 +00:00
if r := strings . LastIndex ( fn . Name [ inst [ 1 ] : ] , "." ) ; r != - 1 && pathend + l != inst [ 1 ] + r {
return fn . Name [ pathend + l + 1 : inst [ 1 ] + r ]
} else if r := strings . LastIndex ( fn . Name [ pathend : inst [ 0 ] ] , "." ) ; r != - 1 && l != r {
return fn . Name [ pathend + l + 1 : pathend + r ]
}
return ""
2017-09-01 13:30:45 +00:00
}
// BaseName returns the symbol name without the package or receiver name.
// Borrowed from $GOROOT/debug/gosym/symtab.go
func ( fn * Function ) BaseName ( ) string {
2021-10-30 18:52:26 +00:00
inst := fn . instRange ( )
if i := strings . LastIndex ( fn . Name [ inst [ 1 ] : ] , "." ) ; i != - 1 {
return fn . Name [ inst [ 1 ] + i + 1 : ]
} else if i := strings . LastIndex ( fn . Name [ : inst [ 0 ] ] , "." ) ; i != - 1 {
2017-09-01 13:30:45 +00:00
return fn . Name [ i + 1 : ]
}
return fn . Name
}
2021-10-30 18:52:26 +00:00
// NameWithoutTypeParams returns the function name without instantiation parameters
func ( fn * Function ) NameWithoutTypeParams ( ) string {
inst := fn . instRange ( )
if inst [ 0 ] == inst [ 1 ] {
return fn . Name
}
return fn . Name [ : inst [ 0 ] ] + fn . Name [ inst [ 1 ] + 1 : ]
}
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-08-13 18:13:28 +00:00
// PrologueEndPC returns the PC just after the function prologue
func ( fn * Function ) PrologueEndPC ( ) uint64 {
pc , _ , _ , ok := fn . cu . lineInfo . PrologueEndPC ( fn . Entry , fn . End )
if ! ok {
return fn . Entry
}
return pc
}
2020-03-09 09:47:56 +00:00
// From $GOROOT/src/runtime/traceback.go:597
// exportedRuntime reports whether the function is an exported runtime function.
// It is only for runtime functions, so ASCII A-Z is fine.
func ( fn * Function ) exportedRuntime ( ) bool {
name := fn . Name
const n = len ( "runtime." )
return len ( name ) > n && name [ : n ] == "runtime." && 'A' <= name [ n ] && name [ n ] <= 'Z'
}
// unexportedRuntime reports whether the function is a private runtime function.
func ( fn * Function ) privateRuntime ( ) bool {
name := fn . Name
const n = len ( "runtime." )
return len ( name ) > n && name [ : n ] == "runtime." && ! ( 'A' <= name [ n ] && name [ n ] <= 'Z' )
}
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
}
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 {
2020-09-09 17:36:15 +00:00
r := & BinaryInfo { GOOS : goos , nameOfRuntimeType : make ( map [ uint64 ] nameOfRuntimeTypeEntry ) , logger : logflags . DebuggerLogger ( ) }
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 {
2020-03-10 16:34:40 +00:00
case "386" :
r . Arch = I386Arch ( goos )
2017-04-06 18:14:01 +00:00
case "amd64" :
2017-04-21 06:55:53 +00:00
r . Arch = AMD64Arch ( goos )
2019-10-25 13:22:33 +00:00
case "arm64" :
r . Arch = ARM64Arch ( 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
}
2019-10-25 16:59:18 +00:00
type ErrCouldNotFindLine struct {
fileFound bool
filename string
lineno int
}
func ( err * ErrCouldNotFindLine ) Error ( ) string {
if err . fileFound {
return fmt . Sprintf ( "could not find statement at %s:%d, please use a line with a statement" , err . filename , err . lineno )
}
return fmt . Sprintf ( "could not find file %s" , err . filename )
}
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 )
}
2020-04-15 08:42:22 +00:00
for _ , image := range bi . Images {
for _ , cu := range image . compileUnits {
if cu . lineInfo != nil && cu . lineInfo . Lookup [ filename ] != nil {
cu . lineInfo . AllPCsForFileLines ( filename , r )
}
2019-06-28 02:39:15 +00:00
}
}
return r
}
2019-11-01 19:41:06 +00:00
// PCToFunc returns the concrete function containing the given PC address.
// If the PC address belongs to an inlined call it will return the containing function.
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
2021-06-22 11:37:46 +00:00
dwarf * dwarf . Data
dwarfReader * dwarf . Reader
loclist2 * loclist . Dwarf2Reader
loclist5 * loclist . Dwarf5Reader
debugAddr * godwarf . DebugAddrSection
debugLineStr [ ] byte
2019-05-08 21:06:38 +00:00
typeCache map [ dwarf . Offset ] godwarf . Type
2020-04-15 08:42:22 +00:00
compileUnits [ ] * compileUnit // compileUnits is sorted by increasing DWARF offset
2021-07-08 15:47:53 +00:00
dwarfTreeCache * simplelru . LRU
runtimeMallocgcTree * godwarf . Tree // patched version of runtime.mallocgc's DIE
2020-03-20 17:23:10 +00:00
2019-05-08 21:06:38 +00:00
// 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-08-08 19:25:50 +00:00
func ( image * Image ) registerRuntimeTypeToDIE ( entry * dwarf . Entry , ardr * reader . Reader ) {
if off , ok := entry . Val ( godwarf . AttrGoRuntimeType ) . ( uint64 ) ; ok {
if _ , ok := image . runtimeTypeToDIE [ off ] ; ! ok {
2021-05-19 17:42:10 +00:00
image . runtimeTypeToDIE [ off ] = runtimeTypeDIE { entry . Offset , - 1 }
2019-08-08 19:25:50 +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 ) }
2020-03-20 17:23:10 +00:00
image . dwarfTreeCache , _ = simplelru . NewLRU ( dwarfTreeCacheSize , nil )
2019-05-08 21:06:38 +00:00
// 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
}
2021-03-09 10:35:24 +00:00
bi . macOSDebugFrameBugWorkaround ( )
2019-05-08 21:06:38 +00:00
return err
}
// moduleDataToImage finds the image corresponding to the given module data object.
func ( bi * BinaryInfo ) moduleDataToImage ( md * moduleData ) * Image {
2021-07-23 16:21:24 +00:00
fn := bi . PCToFunc ( uint64 ( md . text ) )
if fn != nil {
return bi . funcToImage ( fn )
}
// Try searching for the image with the closest address preceding md.text
var so * Image
for i := range bi . Images {
2021-07-26 15:40:12 +00:00
if int64 ( bi . Images [ i ] . StaticBase ) > int64 ( md . text ) {
2021-07-23 16:21:24 +00:00
continue
}
2021-07-26 15:40:12 +00:00
if so == nil || int64 ( bi . Images [ i ] . StaticBase ) > int64 ( so . StaticBase ) {
2021-07-23 16:21:24 +00:00
so = bi . Images [ i ]
}
}
return so
2019-05-08 21:06:38 +00:00
}
// 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 )
2021-07-23 16:21:24 +00:00
if im2 != nil && im2 . index == image . index {
2019-05-08 21:06:38 +00:00
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
}
2021-11-26 16:11:29 +00:00
func ( image * Image ) setLoadError ( logger * logrus . Entry , fmtstr string , args ... interface { } ) {
2019-05-08 21:06:38 +00:00
image . loadErrMu . Lock ( )
image . loadErr = fmt . Errorf ( fmtstr , args ... )
image . loadErrMu . Unlock ( )
2021-11-26 16:11:29 +00:00
if logger != nil {
logger . Errorf ( "error loading binary %q: %v" , image . Path , image . loadErr )
}
2019-05-08 21:06:38 +00:00
}
// 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
}
2020-03-20 17:23:10 +00:00
func ( image * Image ) getDwarfTree ( off dwarf . Offset ) ( * godwarf . Tree , error ) {
2021-07-08 15:47:53 +00:00
if image . runtimeMallocgcTree != nil && off == image . runtimeMallocgcTree . Offset {
return image . runtimeMallocgcTree , nil
}
2020-03-20 17:23:10 +00:00
if r , ok := image . dwarfTreeCache . Get ( off ) ; ok {
return r . ( * godwarf . Tree ) , nil
}
r , err := godwarf . LoadTree ( off , image . dwarf , image . StaticBase )
if err != nil {
return nil , err
}
image . dwarfTreeCache . Add ( off , r )
return r , nil
}
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 )
2020-03-20 17:23:10 +00:00
image . dwarfTreeCache , _ = simplelru . NewLRU ( dwarfTreeCacheSize , nil )
2017-08-22 14:40:01 +00:00
if debugFrameBytes != nil {
2021-03-05 04:17:00 +00:00
bi . frameEntries , _ = frame . Parse ( debugFrameBytes , frame . DwarfEndian ( debugFrameBytes ) , 0 , bi . Arch . PtrSize ( ) , 0 )
2017-08-22 14:40:01 +00:00
}
2020-08-18 00:19:46 +00:00
image . loclist2 = loclist . NewDwarf2Reader ( debugLocBytes , bi . Arch . PtrSize ( ) )
2019-05-08 21:06:38 +00:00
2020-08-18 00:19:46 +00:00
bi . loadDebugInfoMaps ( image , nil , 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
}
2020-01-28 14:57:21 +00:00
func ( bi * BinaryInfo ) locationExpr ( entry godwarf . Entry , attr dwarf . Attr , pc uint64 ) ( [ ] byte , * locationExpr , error ) {
2020-08-18 00:19:46 +00:00
//TODO(aarzilli): handle DW_FORM_loclistx attribute form new in DWARFv5
2017-08-24 07:46:47 +00:00
a := entry . Val ( attr )
if a == nil {
2020-01-28 14:57:21 +00:00
return nil , nil , fmt . Errorf ( "no location attribute %s" , attr )
2017-08-24 07:46:47 +00:00
}
if instr , ok := a . ( [ ] byte ) ; ok {
2020-01-28 14:57:21 +00:00
return instr , & locationExpr { isBlock : true , instr : instr } , nil
2017-08-24 07:46:47 +00:00
}
off , ok := a . ( int64 )
if ! ok {
2020-01-28 14:57:21 +00:00
return nil , 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 {
2020-01-28 14:57:21 +00:00
return nil , nil , fmt . Errorf ( "could not find loclist entry at %#x for address %#x" , off , pc )
}
return instr , & locationExpr { pc : pc , off : off , instr : instr } , nil
}
type locationExpr struct {
isBlock bool
isEscaped bool
off int64
pc uint64
instr [ ] byte
}
func ( le * locationExpr ) String ( ) string {
if le == nil {
return ""
2017-08-24 07:46:47 +00:00
}
2017-11-21 08:50:14 +00:00
var descr bytes . Buffer
2020-01-28 14:57:21 +00:00
if le . isBlock {
fmt . Fprintf ( & descr , "[block] " )
op . PrettyPrint ( & descr , le . instr )
} else {
fmt . Fprintf ( & descr , "[%#x:%#x] " , le . off , le . pc )
op . PrettyPrint ( & descr , le . instr )
}
if le . isEscaped {
fmt . Fprintf ( & descr , " (escaped)" )
}
return descr . String ( )
2018-07-28 19:12:07 +00:00
}
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 )
}
2020-04-15 08:42:22 +00:00
cu := bi . Images [ 0 ] . findCompileUnitForOffset ( entry . Offset )
2019-06-24 15:02:14 +00:00
if cu == nil {
return nil , errors . New ( "could not find compile unit" )
}
2020-08-18 00:19:46 +00:00
if cu . Version >= 5 && cu . image . loclist5 != nil {
return nil , errors . New ( "LocationCovers does not support DWARFv5" )
}
2019-06-24 15:02:14 +00:00
image := cu . image
base := cu . lowPC
2020-08-18 00:19:46 +00:00
if image == nil || image . loclist2 . Empty ( ) {
2019-06-24 15:02:14 +00:00
return nil , errors . New ( "malformed executable" )
}
r := [ ] [ 2 ] uint64 { }
2019-11-12 19:44:08 +00:00
var e loclist . Entry
2020-08-18 00:19:46 +00:00
image . loclist2 . Seek ( int ( off ) )
for image . loclist2 . Next ( & e ) {
2019-06-24 15:02:14 +00:00
if e . BaseAddressSelection ( ) {
2019-11-12 19:44:08 +00:00
base = e . HighPC
2019-06-24 15:02:14 +00:00
continue
}
2019-11-12 19:44:08 +00:00
r = append ( r , [ 2 ] uint64 { e . LowPC + base , e . HighPC + base } )
2019-06-24 15:02:14 +00:00
}
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).
2021-08-03 16:51:15 +00:00
func ( bi * BinaryInfo ) Location ( entry godwarf . Entry , attr dwarf . Attr , pc uint64 , regs op . DwarfRegisters , mem MemoryReadWriter ) ( int64 , [ ] op . Piece , * locationExpr , error ) {
2018-07-28 19:12:07 +00:00
instr , descr , err := bi . locationExpr ( entry , attr , pc )
if err != nil {
2020-01-28 14:57:21 +00:00
return 0 , nil , nil , err
2018-07-28 19:12:07 +00:00
}
2021-08-03 16:51:15 +00:00
readMemory := op . ReadMemoryFunc ( nil )
if mem != nil {
readMemory = mem . ReadMemory
}
addr , pieces , err := op . ExecuteStackProgram ( regs , instr , bi . Arch . PtrSize ( ) , readMemory )
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 ]
2020-08-18 00:19:46 +00:00
cu := bi . findCompileUnit ( pc )
if cu != nil {
2018-11-06 22:34:26 +00:00
base = cu . lowPC
2019-05-08 21:06:38 +00:00
image = cu . image
}
2020-08-18 00:19:46 +00:00
if image == nil {
2019-05-08 21:06:38 +00:00
return nil
2017-08-24 07:46:47 +00:00
}
2020-08-18 00:19:46 +00:00
var loclist loclist . Reader = image . loclist2
var debugAddr * godwarf . DebugAddr
if cu != nil && cu . Version >= 5 && image . loclist5 != nil {
loclist = image . loclist5
if addrBase , ok := cu . entry . Val ( dwarfAttrAddrBase ) . ( int64 ) ; ok {
debugAddr = image . debugAddr . GetSubsection ( uint64 ( addrBase ) )
2017-08-24 07:46:47 +00:00
}
}
2020-08-18 00:19:46 +00:00
if loclist . Empty ( ) {
return nil
}
e , err := loclist . Find ( int ( off ) , image . StaticBase , base , pc , debugAddr )
if err != nil {
bi . logger . Errorf ( "error reading loclist section: %v" , err )
return nil
}
if e != nil {
return e . Instr
}
2017-08-24 07:46:47 +00:00
return nil
}
// findCompileUnit returns the compile unit containing address pc.
func ( bi * BinaryInfo ) findCompileUnit ( pc uint64 ) * compileUnit {
2020-04-15 08:42:22 +00:00
for _ , image := range bi . Images {
for _ , cu := range image . compileUnits {
for _ , rng := range cu . ranges {
if pc >= rng [ 0 ] && pc < rng [ 1 ] {
return cu
}
2018-05-29 15:01:51 +00:00
}
2018-08-17 06:17:22 +00:00
}
}
return nil
}
2020-04-15 08:42:22 +00:00
func ( bi * Image ) 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 {
2020-04-15 08:42:22 +00:00
for _ , cu := range bi . Images [ 0 ] . compileUnits {
2018-04-14 09:04:14 +00:00
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
}
2021-03-05 04:17:00 +00:00
// parseDebugFrameGeneral parses a debug_frame and a eh_frame section.
// At least one of the two must be present and parsed correctly, if
// debug_frame is present it must be parsable correctly.
func ( bi * BinaryInfo ) parseDebugFrameGeneral ( image * Image , debugFrameBytes [ ] byte , debugFrameName string , debugFrameErr error , ehFrameBytes [ ] byte , ehFrameAddr uint64 , ehFrameName string , byteOrder binary . ByteOrder ) {
if debugFrameBytes == nil && ehFrameBytes == nil {
2021-11-26 16:11:29 +00:00
image . setLoadError ( bi . logger , "could not get %s section: %v" , debugFrameName , debugFrameErr )
2021-03-05 04:17:00 +00:00
return
}
if debugFrameBytes != nil {
fe , err := frame . Parse ( debugFrameBytes , byteOrder , image . StaticBase , bi . Arch . PtrSize ( ) , 0 )
if err != nil {
2021-11-26 16:11:29 +00:00
image . setLoadError ( bi . logger , "could not parse %s section: %v" , debugFrameName , err )
2021-03-05 04:17:00 +00:00
return
}
bi . frameEntries = bi . frameEntries . Append ( fe )
}
if ehFrameBytes != nil && ehFrameAddr > 0 {
fe , err := frame . Parse ( ehFrameBytes , byteOrder , image . StaticBase , bi . Arch . PtrSize ( ) , ehFrameAddr )
if err != nil {
if debugFrameBytes == nil {
2021-11-26 16:11:29 +00:00
image . setLoadError ( bi . logger , "could not parse %s section: %v" , ehFrameName , err )
2021-03-05 04:17:00 +00:00
return
}
bi . logger . Warnf ( "could not parse %s section: %v" , ehFrameName , err )
return
}
bi . frameEntries = bi . frameEntries . Append ( fe )
}
}
2017-04-06 18:14:01 +00:00
// ELF ///////////////////////////////////////////////////////////////
2018-06-20 10:20:35 +00:00
// 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
2022-01-30 21:39:30 +00:00
var err error
2018-11-07 22:21:35 +00:00
for _ , dir := range debugInfoDirectories {
var potentialDebugFilePath string
2022-02-08 18:53:45 +00:00
if strings . Contains ( dir , "build-id" ) && len ( bi . BuildID ) > 2 {
2022-01-30 21:39:30 +00:00
potentialDebugFilePath = fmt . Sprintf ( "%s/%s/%s.debug" , dir , bi . BuildID [ : 2 ] , bi . BuildID [ 2 : ] )
2020-09-15 21:49:30 +00:00
} else if strings . HasPrefix ( image . Path , "/proc" ) {
path , err := filepath . EvalSymlinks ( image . Path )
if err == nil {
potentialDebugFilePath = fmt . Sprintf ( "%s/%s.debug" , dir , filepath . Base ( path ) )
}
2018-11-07 22:21:35 +00:00
} 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
}
}
2021-08-25 11:29:28 +00:00
// We cannot find the debug information locally on the system. Try and see if we're on a system that
// has debuginfod so that we can use that in order to find any relevant debug information.
2018-11-07 22:21:35 +00:00
if debugFilePath == "" {
2022-01-30 21:39:30 +00:00
debugFilePath , err = debuginfod . GetDebuginfo ( bi . BuildID )
if err != nil {
2021-08-25 11:29:28 +00:00
return nil , nil , ErrNoDebugInfoFound
}
2018-11-07 22:21:35 +00:00
}
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 ( ) )
}
2020-03-10 16:34:40 +00:00
if ! supportedLinuxArch [ elfFile . Machine ] {
2018-11-07 22:21:35 +00:00
sepFile . Close ( )
2020-03-10 16:34:40 +00:00
return nil , nil , fmt . Errorf ( "can't open separate debug file %q: %v" , debugFilePath , & ErrUnsupportedArch { os : "linux" , cpuArch : elfFile . Machine } )
2018-11-07 22:21:35 +00:00
}
return sepFile , elfFile , nil
}
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
}
2020-03-10 16:34:40 +00:00
if ! supportedLinuxArch [ elfFile . Machine ] {
return & ErrUnsupportedArch { os : "linux" , cpuArch : elfFile . Machine }
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
2022-01-30 21:39:30 +00:00
bi . loadBuildID ( image , elfFile )
2020-08-18 00:19:46 +00:00
var debugInfoBytes [ ] byte
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
}
2020-08-18 00:19:46 +00:00
debugInfoBytes , err = godwarf . GetDebugSectionElf ( dwarfFile , "info" )
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 . 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" )
2020-08-18 00:19:46 +00:00
image . loclist2 = loclist . NewDwarf2Reader ( debugLocBytes , bi . Arch . PtrSize ( ) )
debugLoclistBytes , _ := godwarf . GetDebugSectionElf ( dwarfFile , "loclists" )
image . loclist5 = loclist . NewDwarf5Reader ( debugLoclistBytes )
debugAddrBytes , _ := godwarf . GetDebugSectionElf ( dwarfFile , "addr" )
image . debugAddr = godwarf . ParseAddr ( debugAddrBytes )
2021-06-22 11:37:46 +00:00
debugLineStrBytes , _ := godwarf . GetDebugSectionElf ( dwarfFile , "line_str" )
image . debugLineStr = debugLineStrBytes
2017-09-01 13:30:45 +00:00
2020-03-10 16:34:40 +00:00
wg . Add ( 3 )
2022-01-19 18:40:23 +00:00
go bi . parseDebugFrameElf ( image , dwarfFile , elfFile , debugInfoBytes , wg )
2020-08-18 00:19:46 +00:00
go bi . loadDebugInfoMaps ( image , debugInfoBytes , debugLineBytes , wg , nil )
2020-03-10 16:34:40 +00:00
go bi . loadSymbolName ( image , elfFile , wg )
2019-05-08 21:06:38 +00:00
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
}
2021-02-10 17:31:37 +00:00
// _STT_FUNC is a code object, see /usr/include/elf.h for a full definition.
const _STT_FUNC = 2
2020-03-10 16:34:40 +00:00
func ( bi * BinaryInfo ) loadSymbolName ( image * Image , file * elf . File , wg * sync . WaitGroup ) {
defer wg . Done ( )
if bi . SymNames == nil {
bi . SymNames = make ( map [ uint64 ] * elf . Symbol )
}
symSecs , _ := file . Symbols ( )
2021-09-28 19:07:42 +00:00
for _ , symSec := range symSecs {
if symSec . Info == _STT_FUNC { // TODO(chainhelen), need to parse others types.
s := symSec
bi . SymNames [ symSec . Value + image . StaticBase ] = & s
2020-03-10 16:34:40 +00:00
}
}
}
2022-01-30 21:39:30 +00:00
func ( bi * BinaryInfo ) loadBuildID ( image * Image , file * elf . File ) {
buildid := file . Section ( ".note.gnu.build-id" )
if buildid == nil {
bi . logger . Error ( "can't find build-id note on binary" )
return
}
br := buildid . Open ( )
bh := new ( buildIDHeader )
if err := binary . Read ( br , binary . LittleEndian , bh ) ; err != nil {
bi . logger . Errorf ( "can't read build-id header: %v" , err )
return
}
name := make ( [ ] byte , bh . Namesz )
if err := binary . Read ( br , binary . LittleEndian , name ) ; err != nil {
bi . logger . Errorf ( "can't read build-id name: %v" , err )
return
}
if strings . TrimSpace ( string ( name ) ) != "GNU\x00" {
bi . logger . Error ( "invalid build-id signature" )
return
}
descBinary := make ( [ ] byte , bh . Descsz )
if err := binary . Read ( br , binary . LittleEndian , descBinary ) ; err != nil {
bi . logger . Errorf ( "can't read build-id desc: %v" , err )
return
}
bi . BuildID = hex . EncodeToString ( descBinary )
}
2022-01-19 18:40:23 +00:00
func ( bi * BinaryInfo ) parseDebugFrameElf ( image * Image , dwarfFile , exeFile * elf . File , debugInfoBytes [ ] byte , wg * sync . WaitGroup ) {
2017-04-06 18:14:01 +00:00
defer wg . Done ( )
2022-01-19 18:40:23 +00:00
debugFrameData , debugFrameErr := godwarf . GetDebugSectionElf ( dwarfFile , "frame" )
ehFrameSection := exeFile . Section ( ".eh_frame" )
2021-03-05 04:17:00 +00:00
var ehFrameData [ ] byte
var ehFrameAddr uint64
if ehFrameSection != nil {
ehFrameAddr = ehFrameSection . Addr
ehFrameData , _ = ehFrameSection . Data ( )
2017-04-06 18:14:01 +00:00
}
2021-03-05 04:17:00 +00:00
bi . parseDebugFrameGeneral ( image , debugFrameData , ".debug_frame" , debugFrameErr , ehFrameData , ehFrameAddr , ".eh_frame" , frame . DwarfEndian ( debugInfoBytes ) )
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
2020-03-10 16:34:40 +00:00
// pointer at %fs-8 on 64 bit.
// - %Gs is the index of private storage in GDT on 32 bit, and puts the G
// pointer at -4(tls).
2017-06-21 22:40:42 +00:00
// - 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.
2021-02-23 20:38:52 +00:00
// - On ARM64 (but really, any architecture other than i386 and 86x64) the
// offset is calculate using runtime.tls_g and the formula is different.
2017-06-21 22:40:42 +00:00
var tls * elf . Prog
for _ , prog := range exe . Progs {
if prog . Type == elf . PT_TLS {
tls = prog
break
}
}
2021-02-23 20:38:52 +00:00
switch exe . Machine {
case elf . EM_X86_64 , elf . EM_386 :
2021-11-26 16:11:29 +00:00
tlsg := getSymbol ( image , bi . logger , exe , "runtime.tlsg" )
2021-02-23 20:38:52 +00:00
if tlsg == nil || tls == nil {
bi . gStructOffset = ^ uint64 ( bi . Arch . PtrSize ( ) ) + 1 //-ptrSize
return
}
// According to https://reviews.llvm.org/D61824, linkers must pad the actual
// size of the TLS segment to ensure that (tlsoffset%align) == (vaddr%align).
// This formula, copied from the lld code, matches that.
// https://github.com/llvm-mirror/lld/blob/9aef969544981d76bea8e4d1961d3a6980980ef9/ELF/InputSection.cpp#L643
memsz := tls . Memsz + ( - tls . Vaddr - tls . Memsz ) & ( tls . Align - 1 )
// 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 = ^ ( memsz ) + 1 + tlsg . Value // -tls.Memsz + tlsg.Value
case elf . EM_AARCH64 :
2021-11-26 16:11:29 +00:00
tlsg := getSymbol ( image , bi . logger , exe , "runtime.tls_g" )
2021-02-23 20:38:52 +00:00
if tlsg == nil || tls == nil {
bi . gStructOffset = 2 * uint64 ( bi . Arch . PtrSize ( ) )
return
}
bi . gStructOffset = tlsg . Value + uint64 ( bi . Arch . PtrSize ( ) * 2 ) + ( ( tls . Vaddr - uint64 ( bi . Arch . PtrSize ( ) * 2 ) ) & ( tls . Align - 1 ) )
default :
// we should never get here
panic ( "architecture not supported" )
2019-02-26 17:17:05 +00:00
}
2021-02-23 20:38:52 +00:00
}
2018-11-28 17:26:01 +00:00
2021-11-26 16:11:29 +00:00
func getSymbol ( image * Image , logger * logrus . Entry , exe * elf . File , name string ) * elf . Symbol {
2021-02-23 20:38:52 +00:00
symbols , err := exe . Symbols ( )
if err != nil {
2021-11-26 16:11:29 +00:00
image . setLoadError ( logger , "could not parse ELF symbols: %v" , err )
2021-02-23 20:38:52 +00:00
return nil
}
2019-09-04 16:01:53 +00:00
2021-02-23 20:38:52 +00:00
for _ , symbol := range symbols {
if symbol . Name == name {
s := symbol
return & s
}
}
return nil
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
2021-02-10 17:31:37 +00:00
cpuArch := _PEMachine ( peFile . Machine )
2020-03-10 16:34:40 +00:00
if ! supportedWindowsArch [ cpuArch ] {
return & ErrUnsupportedArch { os : "windows" , cpuArch : cpuArch }
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
}
2020-08-18 00:19:46 +00:00
debugInfoBytes , err := godwarf . GetDebugSectionPE ( peFile , "info" )
if err != nil {
return err
}
2017-04-06 18:14:01 +00:00
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" )
2020-08-18 00:19:46 +00:00
image . loclist2 = loclist . NewDwarf2Reader ( debugLocBytes , bi . Arch . PtrSize ( ) )
debugLoclistBytes , _ := godwarf . GetDebugSectionPE ( peFile , "loclists" )
image . loclist5 = loclist . NewDwarf5Reader ( debugLoclistBytes )
debugAddrBytes , _ := godwarf . GetDebugSectionPE ( peFile , "addr" )
image . debugAddr = godwarf . ParseAddr ( debugAddrBytes )
2021-11-26 16:11:29 +00:00
debugLineStrBytes , _ := godwarf . GetDebugSectionPE ( peFile , "line_str" )
image . debugLineStr = debugLineStrBytes
2017-09-01 13:30:45 +00:00
wg . Add ( 2 )
2020-08-18 00:19:46 +00:00
go bi . parseDebugFramePE ( image , peFile , debugInfoBytes , wg )
go bi . loadDebugInfoMaps ( image , debugInfoBytes , 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
}
2020-08-18 00:19:46 +00:00
func ( bi * BinaryInfo ) parseDebugFramePE ( image * Image , exe * pe . File , debugInfoBytes [ ] byte , 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" )
2021-03-05 04:17:00 +00:00
bi . parseDebugFrameGeneral ( image , debugFrameBytes , ".debug_frame" , err , nil , 0 , "" , frame . DwarfEndian ( debugInfoBytes ) )
2017-04-06 18:14:01 +00:00
}
// 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 )
2021-01-04 16:52:04 +00:00
2017-04-06 18:14:01 +00:00
if err != nil {
return err
}
2021-01-04 16:52:04 +00:00
if entryPoint != 0 {
// This is a little bit hacky. We use the entryPoint variable, but it
// actually holds the address of the mach-o header. We can use this
// to calculate the offset to the non-aslr location of the mach-o header
// (which is 0x100000000)
image . StaticBase = entryPoint - 0x100000000
}
2019-05-08 21:06:38 +00:00
image . closer = exe
2020-03-10 16:34:40 +00:00
if ! supportedDarwinArch [ exe . Cpu ] {
return & ErrUnsupportedArch { os : "darwin" , cpuArch : exe . Cpu }
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
}
2020-08-18 00:19:46 +00:00
debugInfoBytes , err := godwarf . GetDebugSectionMacho ( exe , "info" )
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 . 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" )
2020-08-18 00:19:46 +00:00
image . loclist2 = loclist . NewDwarf2Reader ( debugLocBytes , bi . Arch . PtrSize ( ) )
debugLoclistBytes , _ := godwarf . GetDebugSectionMacho ( exe , "loclists" )
image . loclist5 = loclist . NewDwarf5Reader ( debugLoclistBytes )
debugAddrBytes , _ := godwarf . GetDebugSectionMacho ( exe , "addr" )
image . debugAddr = godwarf . ParseAddr ( debugAddrBytes )
2021-11-26 16:11:29 +00:00
debugLineStrBytes , _ := godwarf . GetDebugSectionMacho ( exe , "line_str" )
image . debugLineStr = debugLineStrBytes
2017-09-01 13:30:45 +00:00
wg . Add ( 2 )
2020-08-18 00:19:46 +00:00
go bi . parseDebugFrameMacho ( image , exe , debugInfoBytes , wg )
go bi . loadDebugInfoMaps ( image , debugInfoBytes , 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
}
2020-08-18 00:19:46 +00:00
func ( bi * BinaryInfo ) parseDebugFrameMacho ( image * Image , exe * macho . File , debugInfoBytes [ ] byte , wg * sync . WaitGroup ) {
2017-04-06 18:14:01 +00:00
defer wg . Done ( )
2021-03-05 04:17:00 +00:00
debugFrameBytes , debugFrameErr := godwarf . GetDebugSectionMacho ( exe , "frame" )
ehFrameSection := exe . Section ( "__eh_frame" )
var ehFrameBytes [ ] byte
var ehFrameAddr uint64
if ehFrameSection != nil {
ehFrameAddr = ehFrameSection . Addr
ehFrameBytes , _ = ehFrameSection . Data ( )
2017-04-06 18:14:01 +00:00
}
2017-08-24 07:46:47 +00:00
2021-03-05 04:17:00 +00:00
bi . parseDebugFrameGeneral ( image , debugFrameBytes , "__debug_frame" , debugFrameErr , ehFrameBytes , ehFrameAddr , "__eh_frame" , frame . DwarfEndian ( debugInfoBytes ) )
2017-08-24 07:46:47 +00:00
}
2019-08-08 19:25:50 +00:00
2021-03-09 10:35:24 +00:00
// macOSDebugFrameBugWorkaround applies a workaround for:
// https://github.com/golang/go/issues/25841
// It finds the Go function with the lowest entry point and the first
// debug_frame FDE, calculates the difference between the start of the
// function and the start of the FDE and sums it to all debug_frame FDEs.
// A number of additional checks are performed to make sure we don't ruin
// executables unaffected by this bug.
func ( bi * BinaryInfo ) macOSDebugFrameBugWorkaround ( ) {
//TODO: log extensively because of bugs in the field
if bi . GOOS != "darwin" || bi . Arch . Name != "arm64" {
return
}
if len ( bi . Images ) > 1 {
// Only do this for the first executable, but it might work for plugins as
// well if we had a way to distinguish where entries in bi.frameEntries
// come from
return
}
exe , ok := bi . Images [ 0 ] . closer . ( * macho . File )
if ! ok {
return
}
if exe . Flags & macho . FlagPIE == 0 {
bi . logger . Infof ( "debug_frame workaround not needed: not a PIE (%#x)" , exe . Flags )
return
}
// Find first Go function (first = lowest entry point)
var fn * Function
for i := range bi . Functions {
if bi . Functions [ i ] . cu . isgo && bi . Functions [ i ] . Entry > 0 {
fn = & bi . Functions [ i ]
break
}
}
if fn == nil {
bi . logger . Warn ( "debug_frame workaround not applied: could not find a Go function" )
return
}
if fde , _ := bi . frameEntries . FDEForPC ( fn . Entry ) ; fde != nil {
// Function is covered, no need to apply workaround
bi . logger . Warnf ( "debug_frame workaround not applied: function %s (at %#x) covered by %#x-%#x" , fn . Name , fn . Entry , fde . Begin ( ) , fde . End ( ) )
return
}
// Find lowest FDE in debug_frame
var fde * frame . FrameDescriptionEntry
for i := range bi . frameEntries {
if bi . frameEntries [ i ] . CIE . CIE_id == ^ uint32 ( 0 ) {
fde = bi . frameEntries [ i ]
break
}
}
if fde == nil {
bi . logger . Warnf ( "debug_frame workaround not applied because there are no debug_frame entries (%d)" , len ( bi . frameEntries ) )
return
}
fnsize := fn . End - fn . Entry
if fde . End ( ) - fde . Begin ( ) != fnsize || fde . Begin ( ) > fn . Entry {
bi . logger . Warnf ( "debug_frame workaround not applied: function %s (at %#x-%#x) has a different size than the first FDE (%#x-%#x) (or the FDE starts after the function)" , fn . Name , fn . Entry , fn . End , fde . Begin ( ) , fde . End ( ) )
return
}
delta := fn . Entry - fde . Begin ( )
bi . logger . Infof ( "applying debug_frame workaround +%#x: function %s (at %#x-%#x) and FDE %#x-%#x" , delta , fn . Name , fn . Entry , fn . End , fde . Begin ( ) , fde . End ( ) )
for i := range bi . frameEntries {
if bi . frameEntries [ i ] . CIE . CIE_id == ^ uint32 ( 0 ) {
bi . frameEntries [ i ] . Translate ( delta )
}
}
}
2019-08-08 19:25:50 +00:00
// Do not call this function directly it isn't able to deal correctly with package paths
func ( bi * BinaryInfo ) findType ( name string ) ( godwarf . Type , error ) {
ref , found := bi . types [ name ]
if ! found {
2021-09-28 19:07:42 +00:00
return nil , reader . ErrTypeNotFound
2019-08-08 19:25:50 +00:00
}
image := bi . Images [ ref . imageIndex ]
return godwarf . ReadType ( image . dwarf , ref . imageIndex , ref . offset , image . typeCache )
}
func ( bi * BinaryInfo ) findTypeExpr ( expr ast . Expr ) ( godwarf . Type , error ) {
if lit , islit := expr . ( * ast . BasicLit ) ; islit && lit . Kind == token . STRING {
// Allow users to specify type names verbatim as quoted
// string. Useful as a catch-all workaround for cases where we don't
// parse/serialize types correctly or can not resolve package paths.
typn , _ := strconv . Unquote ( lit . Value )
2019-10-26 15:15:33 +00:00
// Check if the type in question is an array type, in which case we try to
// fake it.
if len ( typn ) > 0 && typn [ 0 ] == '[' {
closedBrace := strings . Index ( typn , "]" )
if closedBrace > 1 {
n , err := strconv . Atoi ( typn [ 1 : closedBrace ] )
if err == nil {
return bi . findArrayType ( n , typn [ closedBrace + 1 : ] )
}
}
}
2019-08-08 19:25:50 +00:00
return bi . findType ( typn )
}
bi . expandPackagesInType ( expr )
if snode , ok := expr . ( * ast . StarExpr ) ; ok {
// Pointer types only appear in the dwarf informations when
// a pointer to the type is used in the target program, here
// we create a pointer type on the fly so that the user can
// specify a pointer to any variable used in the target program
ptyp , err := bi . findTypeExpr ( snode . X )
if err != nil {
return nil , err
}
return pointerTo ( ptyp , bi . Arch ) , nil
}
if anode , ok := expr . ( * ast . ArrayType ) ; ok {
2019-10-26 15:15:33 +00:00
// Array types (for example [N]byte) are only present in DWARF if they are
2019-08-08 19:25:50 +00:00
// used by the program, but it's convenient to make all of them available
2019-10-26 15:15:33 +00:00
// to the user for two reasons:
// 1. to allow reading arbitrary memory byte-by-byte (by casting an
// address to an array of bytes).
// 2. to read the contents of a channel's buffer (we create fake array
// types for them)
2019-08-08 19:25:50 +00:00
alen , litlen := anode . Len . ( * ast . BasicLit )
if litlen && alen . Kind == token . INT {
n , _ := strconv . Atoi ( alen . Value )
2019-10-26 15:15:33 +00:00
return bi . findArrayType ( n , exprToString ( anode . Elt ) )
2019-08-08 19:25:50 +00:00
}
}
return bi . findType ( exprToString ( expr ) )
}
2019-10-26 15:15:33 +00:00
func ( bi * BinaryInfo ) findArrayType ( n int , etyp string ) ( godwarf . Type , error ) {
switch etyp {
case "byte" , "uint8" :
etyp = "uint8"
fallthrough
default :
btyp , err := bi . findType ( etyp )
if err != nil {
return nil , err
}
return fakeArrayType ( uint64 ( n ) , btyp ) , nil
}
}
2019-08-08 19:25:50 +00:00
func complexType ( typename string ) bool {
for _ , ch := range typename {
switch ch {
case '*' , '[' , '<' , '{' , '(' , ' ' :
return true
}
}
return false
}
func ( bi * BinaryInfo ) registerTypeToPackageMap ( entry * dwarf . Entry ) {
if entry . Tag != dwarf . TagTypedef && entry . Tag != dwarf . TagBaseType && entry . Tag != dwarf . TagClassType && entry . Tag != dwarf . TagStructType {
return
}
typename , ok := entry . Val ( dwarf . AttrName ) . ( string )
if ! ok || complexType ( typename ) {
return
}
dot := strings . LastIndex ( typename , "." )
if dot < 0 {
return
}
path := typename [ : dot ]
slash := strings . LastIndex ( path , "/" )
if slash < 0 || slash + 1 >= len ( path ) {
return
}
name := path [ slash + 1 : ]
2019-11-25 17:10:18 +00:00
bi . PackageMap [ name ] = [ ] string { path }
2019-08-08 19:25:50 +00:00
}
2020-08-18 00:19:46 +00:00
func ( bi * BinaryInfo ) loadDebugInfoMaps ( image * Image , debugInfoBytes , debugLineBytes [ ] byte , wg * sync . WaitGroup , cont func ( ) ) {
2019-08-08 19:25:50 +00:00
if wg != nil {
defer wg . Done ( )
}
2019-08-08 19:39:52 +00:00
if bi . types == nil {
2019-08-08 19:25:50 +00:00
bi . types = make ( map [ string ] dwarfRef )
2019-08-08 19:39:52 +00:00
}
if bi . consts == nil {
2019-08-08 19:25:50 +00:00
bi . consts = make ( map [ dwarfRef ] * constantType )
2019-08-08 19:39:52 +00:00
}
2019-11-25 17:10:18 +00:00
if bi . PackageMap == nil {
bi . PackageMap = make ( map [ string ] [ ] string )
2019-08-08 19:25:50 +00:00
}
2019-11-01 19:41:06 +00:00
if bi . inlinedCallLines == nil {
bi . inlinedCallLines = make ( map [ fileLine ] [ ] uint64 )
}
2021-07-08 15:47:53 +00:00
if bi . dwrapUnwrapCache == nil {
bi . dwrapUnwrapCache = make ( map [ uint64 ] * Function )
}
2019-11-01 19:41:06 +00:00
2019-08-08 19:25:50 +00:00
image . runtimeTypeToDIE = make ( map [ uint64 ] runtimeTypeDIE )
2020-08-18 00:19:46 +00:00
ctxt := newLoadDebugInfoMapsContext ( bi , image , util . ReadUnitVersions ( debugInfoBytes ) )
2019-08-08 19:25:50 +00:00
reader := image . DwarfReader ( )
2021-11-26 16:11:29 +00:00
for {
entry , err := reader . Next ( )
2019-08-08 19:25:50 +00:00
if err != nil {
2021-11-26 16:11:29 +00:00
image . setLoadError ( bi . logger , "error reading debug_info: %v" , err )
break
}
if entry == nil {
2019-08-08 19:25:50 +00:00
break
}
switch entry . Tag {
case dwarf . TagCompileUnit :
cu := & compileUnit { }
cu . image = image
cu . entry = entry
cu . offset = entry . Offset
2020-08-18 00:19:46 +00:00
cu . Version = ctxt . offsetToVersion [ cu . offset ]
2019-08-08 19:25:50 +00:00
if lang , _ := entry . Val ( dwarf . AttrLanguage ) . ( int64 ) ; lang == dwarfGoLanguage {
cu . isgo = true
}
cu . name , _ = entry . Val ( dwarf . AttrName ) . ( string )
compdir , _ := entry . Val ( dwarf . AttrCompDir ) . ( string )
if compdir != "" {
cu . name = filepath . Join ( compdir , cu . name )
}
cu . ranges , _ = image . dwarf . Ranges ( entry )
for i := range cu . ranges {
cu . ranges [ i ] [ 0 ] += image . StaticBase
cu . ranges [ i ] [ 1 ] += image . StaticBase
}
if len ( cu . ranges ) >= 1 {
cu . lowPC = cu . ranges [ 0 ] [ 0 ]
}
2019-12-03 13:00:30 +00:00
lineInfoOffset , hasLineInfo := entry . Val ( dwarf . AttrStmtList ) . ( int64 )
if hasLineInfo && lineInfoOffset >= 0 && lineInfoOffset < int64 ( len ( debugLineBytes ) ) {
2019-08-08 19:25:50 +00:00
var logfn func ( string , ... interface { } )
if logflags . DebugLineErrors ( ) {
logger := logrus . New ( ) . WithFields ( logrus . Fields { "layer" : "dwarf-line" } )
logger . Logger . Level = logrus . DebugLevel
logfn = func ( fmt string , args ... interface { } ) {
logger . Printf ( fmt , args )
}
}
2021-06-22 11:37:46 +00:00
cu . lineInfo = line . Parse ( compdir , bytes . NewBuffer ( debugLineBytes [ lineInfoOffset : ] ) , image . debugLineStr , logfn , image . StaticBase , bi . GOOS == "windows" , bi . Arch . PtrSize ( ) )
2019-08-08 19:25:50 +00:00
}
cu . producer , _ = entry . Val ( dwarf . AttrProducer ) . ( string )
if cu . isgo && cu . producer != "" {
semicolon := strings . Index ( cu . producer , ";" )
if semicolon < 0 {
cu . optimized = goversion . ProducerAfterOrEqual ( cu . producer , 1 , 10 )
} else {
cu . optimized = ! strings . Contains ( cu . producer [ semicolon : ] , "-N" ) || ! strings . Contains ( cu . producer [ semicolon : ] , "-l" )
2021-07-08 15:47:53 +00:00
const regabi = " regabi"
if i := strings . Index ( cu . producer [ semicolon : ] , regabi ) ; i > 0 {
i += semicolon
if i + len ( regabi ) >= len ( cu . producer ) || cu . producer [ i + len ( regabi ) ] == ' ' {
bi . regabi = true
}
}
2019-08-08 19:25:50 +00:00
cu . producer = cu . producer [ : semicolon ]
}
}
2019-11-25 17:10:18 +00:00
gopkg , _ := entry . Val ( godwarf . AttrGoPackageName ) . ( string )
if cu . isgo && gopkg != "" {
bi . PackageMap [ gopkg ] = append ( bi . PackageMap [ gopkg ] , escapePackagePath ( strings . Replace ( cu . name , "\\" , "/" , - 1 ) ) )
}
2020-04-15 08:42:22 +00:00
image . compileUnits = append ( image . compileUnits , cu )
2019-08-08 19:25:50 +00:00
if entry . Children {
bi . loadDebugInfoMapsCompileUnit ( ctxt , image , reader , cu )
}
case dwarf . TagPartialUnit :
reader . SkipChildren ( )
default :
// ignore unknown tags
reader . SkipChildren ( )
}
}
2020-04-15 08:42:22 +00:00
sort . Sort ( compileUnitsByOffset ( image . compileUnits ) )
2019-08-08 19:25:50 +00:00
sort . Sort ( functionsDebugInfoByEntry ( bi . Functions ) )
sort . Sort ( packageVarsByAddr ( bi . packageVars ) )
bi . LookupFunc = make ( map [ string ] * Function )
2021-10-30 18:52:26 +00:00
bi . lookupGenericFunc = nil
2019-08-08 19:25:50 +00:00
for i := range bi . Functions {
bi . LookupFunc [ bi . Functions [ i ] . Name ] = & bi . Functions [ i ]
}
2020-04-15 08:42:22 +00:00
for _ , cu := range image . compileUnits {
2019-08-08 19:25:50 +00:00
if cu . lineInfo != nil {
for _ , fileEntry := range cu . lineInfo . FileNames {
bi . Sources = append ( bi . Sources , fileEntry . Path )
}
}
}
sort . Strings ( bi . Sources )
bi . Sources = uniq ( bi . Sources )
2021-07-08 15:47:53 +00:00
if bi . regabi {
// prepare patch for runtime.mallocgc's DIE
fn := bi . LookupFunc [ "runtime.mallocgc" ]
2021-09-03 12:20:21 +00:00
if fn != nil && fn . cu . image == image {
2021-07-08 15:47:53 +00:00
tree , err := image . getDwarfTree ( fn . offset )
if err == nil {
tree . Children , err = regabiMallocgcWorkaround ( bi )
if err != nil {
bi . logger . Errorf ( "could not patch runtime.mallogc: %v" , err )
} else {
image . runtimeMallocgcTree = tree
}
}
}
}
2019-08-08 19:25:50 +00:00
if cont != nil {
cont ( )
}
}
2021-10-30 18:52:26 +00:00
// LookupGenericFunc returns a map that allows searching for instantiations of generic function by specificying a function name without type parameters.
// For example the key "pkg.(*Receiver).Amethod" will find all instantiations of Amethod:
// - pkg.(*Receiver[.shape.int]).Amethod"
// - pkg.(*Receiver[.shape.*uint8]).Amethod"
// - etc.
func ( bi * BinaryInfo ) LookupGenericFunc ( ) map [ string ] [ ] * Function {
if bi . lookupGenericFunc == nil {
bi . lookupGenericFunc = make ( map [ string ] [ ] * Function )
for i := range bi . Functions {
dn := bi . Functions [ i ] . NameWithoutTypeParams ( )
if dn != bi . Functions [ i ] . Name {
bi . lookupGenericFunc [ dn ] = append ( bi . lookupGenericFunc [ dn ] , & bi . Functions [ i ] )
}
}
}
return bi . lookupGenericFunc
}
2019-08-08 19:25:50 +00:00
// loadDebugInfoMapsCompileUnit loads entry from a single compile unit.
func ( bi * BinaryInfo ) loadDebugInfoMapsCompileUnit ( ctxt * loadDebugInfoMapsContext , image * Image , reader * reader . Reader , cu * compileUnit ) {
2019-11-25 17:10:18 +00:00
hasAttrGoPkgName := goversion . ProducerAfterOrEqual ( cu . producer , 1 , 13 )
2020-07-16 12:53:59 +00:00
depth := 0
2021-11-26 16:11:29 +00:00
for {
entry , err := reader . Next ( )
2019-08-08 19:25:50 +00:00
if err != nil {
2021-11-26 16:11:29 +00:00
image . setLoadError ( bi . logger , "error reading debug_info: %v" , err )
2019-08-08 19:25:50 +00:00
return
}
2021-11-26 16:11:29 +00:00
if entry == nil {
break
}
2019-08-08 19:25:50 +00:00
switch entry . Tag {
case 0 :
2020-07-16 12:53:59 +00:00
if depth == 0 {
return
} else {
depth --
}
2019-08-08 19:25:50 +00:00
case dwarf . TagImportedUnit :
bi . loadDebugInfoMapsImportedUnit ( entry , ctxt , image , cu )
reader . SkipChildren ( )
case dwarf . TagArrayType , dwarf . TagBaseType , dwarf . TagClassType , dwarf . TagStructType , dwarf . TagUnionType , dwarf . TagConstType , dwarf . TagVolatileType , dwarf . TagRestrictType , dwarf . TagEnumerationType , dwarf . TagPointerType , dwarf . TagSubroutineType , dwarf . TagTypedef , dwarf . TagUnspecifiedType :
if name , ok := entry . Val ( dwarf . AttrName ) . ( string ) ; ok {
if ! cu . isgo {
name = "C." + name
}
if _ , exists := bi . types [ name ] ; ! exists {
bi . types [ name ] = dwarfRef { image . index , entry . Offset }
}
}
2019-11-25 17:10:18 +00:00
if cu != nil && cu . isgo && ! hasAttrGoPkgName {
2019-08-08 19:25:50 +00:00
bi . registerTypeToPackageMap ( entry )
}
image . registerRuntimeTypeToDIE ( entry , ctxt . ardr )
reader . SkipChildren ( )
case dwarf . TagVariable :
if n , ok := entry . Val ( dwarf . AttrName ) . ( string ) ; ok {
var addr uint64
if loc , ok := entry . Val ( dwarf . AttrLocation ) . ( [ ] byte ) ; ok {
if len ( loc ) == bi . Arch . PtrSize ( ) + 1 && op . Opcode ( loc [ 0 ] ) == op . DW_OP_addr {
2020-03-10 16:34:40 +00:00
addr , _ = util . ReadUintRaw ( bytes . NewReader ( loc [ 1 : ] ) , binary . LittleEndian , bi . Arch . PtrSize ( ) )
2019-08-08 19:25:50 +00:00
}
}
if ! cu . isgo {
n = "C." + n
}
if _ , known := ctxt . knownPackageVars [ n ] ; ! known {
bi . packageVars = append ( bi . packageVars , packageVar { n , cu , entry . Offset , addr + image . StaticBase } )
}
}
reader . SkipChildren ( )
case dwarf . TagConstant :
name , okName := entry . Val ( dwarf . AttrName ) . ( string )
typ , okType := entry . Val ( dwarf . AttrType ) . ( dwarf . Offset )
val , okVal := entry . Val ( dwarf . AttrConstValue ) . ( int64 )
if okName && okType && okVal {
if ! cu . isgo {
name = "C." + name
}
ct := bi . consts [ dwarfRef { image . index , typ } ]
if ct == nil {
ct = & constantType { }
bi . consts [ dwarfRef { image . index , typ } ] = ct
}
ct . values = append ( ct . values , constantValue { name : name , fullName : name , value : val } )
}
reader . SkipChildren ( )
case dwarf . TagSubprogram :
inlined := false
if inval , ok := entry . Val ( dwarf . AttrInline ) . ( int64 ) ; ok {
2022-01-06 17:07:53 +00:00
inlined = inval >= 1
2019-08-08 19:25:50 +00:00
}
2019-10-07 16:38:47 +00:00
if inlined {
bi . addAbstractSubprogram ( entry , ctxt , reader , image , cu )
} else {
2019-08-08 19:25:50 +00:00
originOffset , hasAbstractOrigin := entry . Val ( dwarf . AttrAbstractOrigin ) . ( dwarf . Offset )
if hasAbstractOrigin {
2019-10-07 16:38:47 +00:00
bi . addConcreteInlinedSubprogram ( entry , originOffset , ctxt , reader , cu )
} else {
bi . addConcreteSubprogram ( entry , ctxt , reader , cu )
2019-08-08 19:25:50 +00:00
}
}
2020-07-16 12:53:59 +00:00
default :
if entry . Children {
depth ++
}
2019-08-08 19:25:50 +00:00
}
}
}
// loadDebugInfoMapsImportedUnit loads entries into cu from the partial unit
// referenced in a DW_TAG_imported_unit entry.
func ( bi * BinaryInfo ) loadDebugInfoMapsImportedUnit ( entry * dwarf . Entry , ctxt * loadDebugInfoMapsContext , image * Image , cu * compileUnit ) {
off , ok := entry . Val ( dwarf . AttrImport ) . ( dwarf . Offset )
if ! ok {
return
}
reader := image . DwarfReader ( )
reader . Seek ( off )
imentry , err := reader . Next ( )
if err != nil {
return
}
if imentry . Tag != dwarf . TagPartialUnit {
return
}
bi . loadDebugInfoMapsCompileUnit ( ctxt , image , reader , cu )
}
2019-10-07 16:38:47 +00:00
// addAbstractSubprogram adds the abstract entry for an inlined function.
func ( bi * BinaryInfo ) addAbstractSubprogram ( entry * dwarf . Entry , ctxt * loadDebugInfoMapsContext , reader * reader . Reader , image * Image , cu * compileUnit ) {
name , ok := subprogramEntryName ( entry , cu )
if ! ok {
2019-11-25 17:06:30 +00:00
bi . logger . Warnf ( "reading debug_info: abstract subprogram without name at %#x" , entry . Offset )
2021-04-05 18:50:59 +00:00
// In some cases clang produces abstract subprograms that do not have a
// name, but we should process them anyway.
2019-10-07 16:38:47 +00:00
}
if entry . Children {
2019-11-01 19:41:06 +00:00
bi . loadDebugInfoMapsInlinedCalls ( ctxt , reader , cu )
2019-10-07 16:38:47 +00:00
}
2021-01-27 14:58:48 +00:00
originIdx := ctxt . lookupAbstractOrigin ( bi , entry . Offset )
fn := & bi . Functions [ originIdx ]
fn . Name = name
fn . offset = entry . Offset
fn . cu = cu
2019-10-07 16:38:47 +00:00
}
// addConcreteInlinedSubprogram adds the concrete entry of a subprogram that was also inlined.
func ( bi * BinaryInfo ) addConcreteInlinedSubprogram ( entry * dwarf . Entry , originOffset dwarf . Offset , ctxt * loadDebugInfoMapsContext , reader * reader . Reader , cu * compileUnit ) {
lowpc , highpc , ok := subprogramEntryRange ( entry , cu . image )
if ! ok {
2019-11-25 17:06:30 +00:00
bi . logger . Warnf ( "reading debug_info: concrete inlined subprogram without address range at %#x" , entry . Offset )
2019-10-07 16:38:47 +00:00
if entry . Children {
reader . SkipChildren ( )
}
return
}
2021-01-27 14:58:48 +00:00
originIdx := ctxt . lookupAbstractOrigin ( bi , originOffset )
2019-11-01 19:41:06 +00:00
fn := & bi . Functions [ originIdx ]
fn . offset = entry . Offset
fn . Entry = lowpc
fn . End = highpc
2021-04-05 18:50:59 +00:00
fn . cu = cu
2019-10-07 16:38:47 +00:00
if entry . Children {
2019-11-01 19:41:06 +00:00
bi . loadDebugInfoMapsInlinedCalls ( ctxt , reader , cu )
2019-10-07 16:38:47 +00:00
}
}
// addConcreteSubprogram adds a concrete subprogram (a normal subprogram
2019-11-01 19:41:06 +00:00
// that doesn't have abstract or inlined entries)
2019-10-07 16:38:47 +00:00
func ( bi * BinaryInfo ) addConcreteSubprogram ( entry * dwarf . Entry , ctxt * loadDebugInfoMapsContext , reader * reader . Reader , cu * compileUnit ) {
lowpc , highpc , ok := subprogramEntryRange ( entry , cu . image )
if ! ok {
2019-11-25 17:06:30 +00:00
bi . logger . Warnf ( "reading debug_info: concrete subprogram without address range at %#x" , entry . Offset )
2021-04-05 18:50:59 +00:00
// When clang inlines a function, in some cases, it produces a concrete
// subprogram without address range and then inlined calls that reference
// it, instead of producing an abstract subprogram.
// It is unclear if this behavior is standard.
2019-10-07 16:38:47 +00:00
}
name , ok := subprogramEntryName ( entry , cu )
if ! ok {
2019-11-25 17:06:30 +00:00
bi . logger . Warnf ( "reading debug_info: concrete subprogram without name at %#x" , entry . Offset )
2019-10-07 16:38:47 +00:00
}
2021-09-16 13:38:15 +00:00
trampoline , _ := entry . Val ( dwarf . AttrTrampoline ) . ( bool )
2021-04-05 18:50:59 +00:00
originIdx := ctxt . lookupAbstractOrigin ( bi , entry . Offset )
fn := & bi . Functions [ originIdx ]
fn . Name = name
fn . Entry = lowpc
fn . End = highpc
fn . offset = entry . Offset
fn . cu = cu
2021-09-16 13:38:15 +00:00
fn . trampoline = trampoline
2019-10-07 16:38:47 +00:00
if entry . Children {
2019-11-01 19:41:06 +00:00
bi . loadDebugInfoMapsInlinedCalls ( ctxt , reader , cu )
2019-10-07 16:38:47 +00:00
}
}
func subprogramEntryName ( entry * dwarf . Entry , cu * compileUnit ) ( string , bool ) {
name , ok := entry . Val ( dwarf . AttrName ) . ( string )
if ! ok {
return "" , false
}
if ! cu . isgo {
name = "C." + name
}
return name , true
}
func subprogramEntryRange ( entry * dwarf . Entry , image * Image ) ( lowpc , highpc uint64 , ok bool ) {
ok = false
if ranges , _ := image . dwarf . Ranges ( entry ) ; len ( ranges ) >= 1 {
ok = true
lowpc = ranges [ 0 ] [ 0 ] + image . StaticBase
highpc = ranges [ 0 ] [ 1 ] + image . StaticBase
}
return lowpc , highpc , ok
}
2019-11-01 19:41:06 +00:00
func ( bi * BinaryInfo ) loadDebugInfoMapsInlinedCalls ( ctxt * loadDebugInfoMapsContext , reader * reader . Reader , cu * compileUnit ) {
2019-10-07 16:38:47 +00:00
for {
entry , err := reader . Next ( )
if err != nil {
2021-11-26 16:11:29 +00:00
cu . image . setLoadError ( bi . logger , "error reading debug_info: %v" , err )
2019-10-07 16:38:47 +00:00
return
}
switch entry . Tag {
case 0 :
return
case dwarf . TagInlinedSubroutine :
originOffset , ok := entry . Val ( dwarf . AttrAbstractOrigin ) . ( dwarf . Offset )
if ! ok {
2019-11-25 17:06:30 +00:00
bi . logger . Warnf ( "reading debug_info: inlined call without origin offset at %#x" , entry . Offset )
2019-10-07 16:38:47 +00:00
reader . SkipChildren ( )
continue
}
lowpc , highpc , ok := subprogramEntryRange ( entry , cu . image )
if ! ok {
2019-11-25 17:06:30 +00:00
bi . logger . Warnf ( "reading debug_info: inlined call without address range at %#x" , entry . Offset )
2019-10-07 16:38:47 +00:00
reader . SkipChildren ( )
continue
}
callfileidx , ok1 := entry . Val ( dwarf . AttrCallFile ) . ( int64 )
callline , ok2 := entry . Val ( dwarf . AttrCallLine ) . ( int64 )
if ! ok1 || ! ok2 {
2019-11-25 17:06:30 +00:00
bi . logger . Warnf ( "reading debug_info: inlined call without CallFile/CallLine at %#x" , entry . Offset )
2019-10-07 16:38:47 +00:00
reader . SkipChildren ( )
continue
}
2021-01-29 17:23:52 +00:00
callfile , cferr := cu . filePath ( int ( callfileidx ) , entry )
if cferr != nil {
bi . logger . Warnf ( "%v" , cferr )
2019-10-21 17:43:03 +00:00
reader . SkipChildren ( )
continue
}
2019-10-07 16:38:47 +00:00
2021-04-05 18:50:59 +00:00
originIdx := ctxt . lookupAbstractOrigin ( bi , originOffset )
fn := & bi . Functions [ originIdx ]
2019-11-01 19:41:06 +00:00
fn . InlinedCalls = append ( fn . InlinedCalls , InlinedCall {
cu : cu ,
LowPC : lowpc ,
HighPC : highpc ,
2019-10-07 16:38:47 +00:00
} )
2019-11-01 19:41:06 +00:00
2021-04-05 18:50:59 +00:00
if fn . cu == nil {
fn . cu = cu
}
2019-11-01 19:41:06 +00:00
fl := fileLine { callfile , int ( callline ) }
bi . inlinedCallLines [ fl ] = append ( bi . inlinedCallLines [ fl ] , lowpc )
2022-01-24 07:56:37 +00:00
if entry . Children {
bi . loadDebugInfoMapsInlinedCalls ( ctxt , reader , cu )
}
2019-10-07 16:38:47 +00:00
}
reader . SkipChildren ( )
}
}
2019-08-08 19:25:50 +00:00
func uniq ( s [ ] string ) [ ] string {
if len ( s ) <= 0 {
return s
}
src , dst := 1 , 1
for src < len ( s ) {
if s [ src ] != s [ dst - 1 ] {
s [ dst ] = s [ src ]
dst ++
}
src ++
}
return s [ : dst ]
}
func ( bi * BinaryInfo ) expandPackagesInType ( expr ast . Expr ) {
switch e := expr . ( type ) {
case * ast . ArrayType :
bi . expandPackagesInType ( e . Elt )
case * ast . ChanType :
bi . expandPackagesInType ( e . Value )
case * ast . FuncType :
for i := range e . Params . List {
bi . expandPackagesInType ( e . Params . List [ i ] . Type )
}
if e . Results != nil {
for i := range e . Results . List {
bi . expandPackagesInType ( e . Results . List [ i ] . Type )
}
}
case * ast . MapType :
bi . expandPackagesInType ( e . Key )
bi . expandPackagesInType ( e . Value )
case * ast . ParenExpr :
bi . expandPackagesInType ( e . X )
case * ast . SelectorExpr :
switch x := e . X . ( type ) {
case * ast . Ident :
2019-11-25 17:10:18 +00:00
if len ( bi . PackageMap [ x . Name ] ) > 0 {
// There's no particular reason to expect the first entry to be the
// correct one if the package name is ambiguous, but trying all possible
// expansions of all types mentioned in the expression is complicated
// and, besides type assertions, users can always specify the type they
// want exactly, using a string.
x . Name = bi . PackageMap [ x . Name ] [ 0 ]
2019-08-08 19:25:50 +00:00
}
default :
bi . expandPackagesInType ( e . X )
}
case * ast . StarExpr :
bi . expandPackagesInType ( e . X )
default :
// nothing to do
}
}
2019-11-25 17:10:18 +00:00
// escapePackagePath returns pkg with '.' replaced with '%2e' (in all
// elements of the path except the first one) like Go does in variable and
// type names.
func escapePackagePath ( pkg string ) string {
slash := strings . Index ( pkg , "/" )
if slash < 0 {
slash = 0
}
return pkg [ : slash ] + strings . Replace ( pkg [ slash : ] , "." , "%2e" , - 1 )
}
2019-08-08 19:25:50 +00:00
// Looks up symbol (either functions or global variables) at address addr.
// Used by disassembly formatter.
func ( bi * BinaryInfo ) symLookup ( addr uint64 ) ( string , uint64 ) {
fn := bi . PCToFunc ( addr )
if fn != nil {
if fn . Entry == addr {
// only report the function name if it's the exact address because it's
// easier to read the absolute address than function_name+offset.
return fn . Name , fn . Entry
}
return "" , 0
}
2020-03-10 16:34:40 +00:00
if sym , ok := bi . SymNames [ addr ] ; ok {
return sym . Name , addr
}
2019-08-08 19:25:50 +00:00
i := sort . Search ( len ( bi . packageVars ) , func ( i int ) bool {
return bi . packageVars [ i ] . addr >= addr
} )
if i >= len ( bi . packageVars ) {
return "" , 0
}
if bi . packageVars [ i ] . addr > addr {
// report previous variable + offset if i-th variable starts after addr
i --
}
2019-08-14 15:58:27 +00:00
if i >= 0 && bi . packageVars [ i ] . addr != 0 {
2019-08-08 19:25:50 +00:00
return bi . packageVars [ i ] . name , bi . packageVars [ i ] . addr
}
return "" , 0
}
2019-12-03 13:00:30 +00:00
type PackageBuildInfo struct {
ImportPath string
DirectoryPath string
Files map [ string ] struct { }
}
// ListPackagesBuildInfo returns the list of packages used by the program along with
// the directory where each package was compiled and optionally the list of
// files constituting the package.
func ( bi * BinaryInfo ) ListPackagesBuildInfo ( includeFiles bool ) [ ] * PackageBuildInfo {
m := make ( map [ string ] * PackageBuildInfo )
2020-04-15 08:42:22 +00:00
for _ , cu := range bi . Images [ 0 ] . compileUnits {
2019-12-03 13:00:30 +00:00
if cu . image != bi . Images [ 0 ] || ! cu . isgo || cu . lineInfo == nil {
//TODO(aarzilli): what's the correct thing to do for plugins?
continue
}
ip := strings . Replace ( cu . name , "\\" , "/" , - 1 )
if _ , ok := m [ ip ] ; ! ok {
path := cu . lineInfo . FirstFile ( )
if ext := filepath . Ext ( path ) ; ext != ".go" && ext != ".s" {
continue
}
dp := filepath . Dir ( path )
m [ ip ] = & PackageBuildInfo {
ImportPath : ip ,
DirectoryPath : dp ,
Files : make ( map [ string ] struct { } ) ,
}
}
if includeFiles {
pbi := m [ ip ]
for _ , file := range cu . lineInfo . FileNames {
pbi . Files [ file . Path ] = struct { } { }
}
}
}
r := make ( [ ] * PackageBuildInfo , 0 , len ( m ) )
for _ , pbi := range m {
r = append ( r , pbi )
}
sort . Slice ( r , func ( i , j int ) bool { return r [ i ] . ImportPath < r [ j ] . ImportPath } )
return r
}
2021-01-29 17:23:52 +00:00
// cuFilePath takes a compilation unit "cu" and a file index reference
// "fileidx" and returns the corresponding file name entry from the
// DWARF line table associated with the unit; "entry" is the offset of
// the attribute where the file reference originated, for logging
// purposes. Return value is the file string and an error value; error
// will be non-nil if the file could not be recovered, perhaps due to
// malformed DWARF.
func ( cu * compileUnit ) filePath ( fileidx int , entry * dwarf . Entry ) ( string , error ) {
if cu . lineInfo == nil {
return "" , fmt . Errorf ( "reading debug_info: file reference within a compilation unit without debug_line section at %#x" , entry . Offset )
}
// File numbering is slightly different before and after DWARF 5;
// account for this here. See section 6.2.4 of the DWARF 5 spec.
if cu . Version < 5 {
fileidx --
}
if fileidx < 0 || fileidx >= len ( cu . lineInfo . FileNames ) {
return "" , fmt . Errorf ( "reading debug_info: file index (%d) out of range in compile unit file table at %#x" , fileidx , entry . Offset )
}
return cu . lineInfo . FileNames [ fileidx ] . Path , nil
}