Documentation ¶
Overview ¶
Package bininspect provides tools to inspect a Go binary.
Index ¶
- Constants
- Variables
- func FindGoVersion(elfFile *safeelf.File) (ddversion.GoVersion, error)
- func FindReturnLocations(elfFile *safeelf.File, sym safeelf.Symbol, functionOffset uint64) ([]uint64, error)
- func GetAllSymbolsByName(elfFile *safeelf.File, filter symbolFilter) (map[string]safeelf.Symbol, error)
- func GetAllSymbolsInSetByName(elfFile *safeelf.File, symbolSet common.StringSet) (map[string]safeelf.Symbol, error)
- func GetAnySymbolWithInfix(elfFile *safeelf.File, infix string, minLength int, maxLength int) (*safeelf.Symbol, error)
- func GetAnySymbolWithInfixPCLNTAB(elfFile *safeelf.File, infix string, minLength int, maxLength int) (*safeelf.Symbol, error)
- func GetPCLNTABSymbolParser(f *safeelf.File, symbolFilter symbolFilter) (map[string]*safeelf.Symbol, error)
- func HasDwarfInfo(elfFile *safeelf.File) (*dwarf.Data, bool)
- func SymbolToOffset(f *safeelf.File, symbol safeelf.Symbol) (uint32, error)
- type FieldIdentifier
- type FunctionConfiguration
- type FunctionMetadata
- type GoABI
- type GoArch
- type GoroutineIDMetadata
- type ParameterLookupFunction
- type ParameterMetadata
- type ParameterPiece
- type Result
- type StructLookupFunction
Constants ¶
const ( // WriteGoTLSFunc is the name of the function that writes to the TLS connection. WriteGoTLSFunc = "crypto/tls.(*Conn).Write" // ReadGoTLSFunc is the name of the function that reads from the TLS connection. ReadGoTLSFunc = "crypto/tls.(*Conn).Read" // CloseGoTLSFunc is the name of the function that closes the TLS connection. CloseGoTLSFunc = "crypto/tls.(*Conn).Close" )
Variables ¶
var ( // ErrMissingPCLNTABSection is returned when the pclntab section is missing. ErrMissingPCLNTABSection = errors.New("failed to find pclntab section") // ErrUnsupportedPCLNTABVersion is returned when the pclntab version is not supported. ErrUnsupportedPCLNTABVersion = errors.New("unsupported pclntab version") // ErrFailedToFindAllSymbols is returned when not all symbols were found. ErrFailedToFindAllSymbols = errors.New("failed to find all symbols") )
var ErrNilElf = errors.New("got nil elf file")
ErrNilElf is returned when getting a nil pointer to an elf file.
var ErrUnsupportedArch = errors.New("got unsupported arch")
ErrUnsupportedArch is returned when an architecture given as a parameter is not supported.
var StructOffsetLimitListenerConnNetConn = FieldIdentifier{
StructName: "golang.org/x/net/netutil.limitListenerConn",
FieldName: "Conn",
}
StructOffsetLimitListenerConnNetConn is the offset of the `net.Conn` field within `netutil/limitListenerConn`.
var StructOffsetNetConnFd = FieldIdentifier{
StructName: "net.conn",
FieldName: "fd",
}
StructOffsetNetConnFd is the offset of the `fd` field within `net.conn`.
var StructOffsetNetFdPfd = FieldIdentifier{
StructName: "net.netFD",
FieldName: "pfd",
}
StructOffsetNetFdPfd is the offset of the `pdf` field within `net.netFD`.
var StructOffsetPollFdSysfd = FieldIdentifier{
StructName: "internal/poll.FD",
FieldName: "Sysfd",
}
StructOffsetPollFdSysfd is the offset of the `sysfd` field within `internal/poll.FD`.
var StructOffsetTCPConn = FieldIdentifier{
StructName: "net.TCPConn",
FieldName: "conn",
}
StructOffsetTCPConn is the offset of the `conn` field within `net.TCPConn`.
var StructOffsetTLSConn = FieldIdentifier{
StructName: "crypto/tls.Conn",
FieldName: "conn",
}
StructOffsetTLSConn is the offset of the `conn` field within `crypto/tls.Conn`.
Functions ¶
func FindGoVersion ¶
FindGoVersion attempts to determine the Go version from the embedded string inserted in the binary by the linker. The implementation is available in src/cmd/go/internal/version/version.go: https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/cmd/go/internal/version/version.go The main logic was pulled out to a sub-package, `binversion`
func FindReturnLocations ¶
func FindReturnLocations(elfFile *safeelf.File, sym safeelf.Symbol, functionOffset uint64) ([]uint64, error)
FindReturnLocations returns the offsets of all the returns of the given func (sym) with the given offset. Note that this may not behave well with panics or defer statements. See the following links for more context: - https://github.com/go-delve/delve/pull/2704/files#diff-fb7b7a020e32bf8bf477c052ac2d2857e7e587478be6039aebc7135c658417b2R769 - https://github.com/go-delve/delve/blob/75bbbbb60cecda0d65c63de7ae8cb8b8412d6fc3/pkg/proc/breakpoints.go#L86-L95 - https://github.com/go-delve/delve/blob/75bbbbb60cecda0d65c63de7ae8cb8b8412d6fc3/pkg/proc/breakpoints.go#L374
func GetAllSymbolsByName ¶
func GetAllSymbolsByName(elfFile *safeelf.File, filter symbolFilter) (map[string]safeelf.Symbol, error)
GetAllSymbolsByName returns all filtered symbols in the given elf file, mapped by the symbol names. In case of a missing symbol, an error is returned.
func GetAllSymbolsInSetByName ¶
func GetAllSymbolsInSetByName(elfFile *safeelf.File, symbolSet common.StringSet) (map[string]safeelf.Symbol, error)
GetAllSymbolsInSetByName returns all symbols (from the symbolSet) in the given elf file, mapped by the symbol names. In case of a missing symbol, an error is returned.
func GetAnySymbolWithInfix ¶
func GetAnySymbolWithInfix(elfFile *safeelf.File, infix string, minLength int, maxLength int) (*safeelf.Symbol, error)
GetAnySymbolWithInfix returns any one symbol with the given infix and the specified maximum length from the ELF file.
func GetAnySymbolWithInfixPCLNTAB ¶
func GetAnySymbolWithInfixPCLNTAB(elfFile *safeelf.File, infix string, minLength int, maxLength int) (*safeelf.Symbol, error)
GetAnySymbolWithInfixPCLNTAB returns any one symbol with the given infix and the specified maximum length from the pclntab section in ELF file.
func GetPCLNTABSymbolParser ¶
func GetPCLNTABSymbolParser(f *safeelf.File, symbolFilter symbolFilter) (map[string]*safeelf.Symbol, error)
GetPCLNTABSymbolParser returns the matching symbols from the pclntab section.
func HasDwarfInfo ¶
HasDwarfInfo attempts to parse the DWARF data and look for any records. If it cannot be parsed or if there are no DWARF info records, then it assumes that the binary has been stripped.
Types ¶
type FieldIdentifier ¶
type FieldIdentifier struct { // Fully-qualified name of the struct StructName string // Name of the field in the struct FieldName string }
FieldIdentifier represents a field in a struct and can be used as a map key.
type FunctionConfiguration ¶
type FunctionConfiguration struct { IncludeReturnLocations bool ParamLookupFunction ParameterLookupFunction }
FunctionConfiguration contains info for the function analyzing process when scanning a binary.
type FunctionMetadata ¶
type FunctionMetadata struct { // The virtual address for the function's entrypoint for non-PIE's, // or offset to the file load address for PIE's. // // This should really probably be the location of the end of the prologue // (which might help with parameter locations being half-spilled), // but so far using the first PC position in the function has worked // for the functions we're tracing. // See: // - https://github.com/go-delve/delve/pull/2704#issuecomment-944374511 // (which implies that the instructions in the prologue // might get executed multiple times over the course of a single function call, // though I'm not sure under what circumstances this might be true) EntryLocation uint64 // A list of parameter metadata structs // used for extracting the function arguments in the eBPF probe. // // When optimizations are enabled, and depending on the Go version, // it has been observed that these parameter locations are sometimes missing pieces // (either due to them being eliminated on purpose // (as in the case of an unused slice length/capacity/data) being omitted) // or due to (spooky behavior in the compiler?)) // or missing entire parameters (again due to (spooky behavior in the compiler?)). // This is unfortunate; I don't think there is a clean way to work around this // (since the data is just missing). // The best solution I have found is to: // - manually handle cases where entire parameters are missing // if it's known that they only occur on a specific Go version // (Go 1.16.* is one that I have found to be troublesome) // and insert the appropriate parameter locations if known. // - if all of the parameters are missing, // then I have found that manually re-implementing // the stack/register allocation algorithm for function arguments/returns // (depending on the ABI, and using the sizes of the values from the type metadata) // can work to still obtain the parameters at runtime. // This is probably much more brittle // than getting the locations directly from the debug information. // // If the outer result's IncludesDebugSymbols is false, // then this slice always will be nil/empty. Parameters []ParameterMetadata // A list of locations for each return instruction in the compiled function machine code. // Each element is the virtual address for the function's entrypoint for non-PIE's, // or offset to the file load address for PIE's. // Only given if `IncludesReturn` is true. // // Note that this may not behave well with panics or defer statements. ReturnLocations []uint64 }
FunctionMetadata contains the results of the inspection process that that can be used to attach an eBPF uprobe to a single function.
type GoABI ¶
type GoABI string
GoABI is the type of ABI used by the Go compiler when generating a binary
type GoArch ¶
type GoArch string
GoArch only includes supported architectures, and the underlying values are compatible with GOARCH values.
func GetArchitecture ¶
GetArchitecture returns the `runtime.GOARCH`-compatible names of the architecture. Only returns a value for supported architectures.
func (*GoArch) PointerSize ¶
PointerSize gets the size, in bytes, of pointers on the architecture
type GoroutineIDMetadata ¶
type GoroutineIDMetadata struct { // The offset of the `goid` field within `runtime.g` GoroutineIDOffset uint64 // Whether the pointer to the current `runtime.g` value is in a register. // If true, then `RuntimeGRegister` is given. // Otherwise, `RuntimeGTLSAddrOffset` is given RuntimeGInRegister bool // The register that the pointer to the current `runtime.g` is in. RuntimeGRegister int // The offset of the `runtime.g` value within thread-local-storage. RuntimeGTLSAddrOffset uint64 }
GoroutineIDMetadata contains information that can be used to reliably determine the ID of the currently-running goroutine from an eBPF uprobe.
type ParameterLookupFunction ¶
type ParameterLookupFunction func(delve.GoVersion, string) ([]ParameterMetadata, error)
ParameterLookupFunction represents a function that returns a list of parameter metadata (for example, parameter size) for a specific golang function. It selects the relevant parameters metadata by the given go version & architecture.
type ParameterMetadata ¶
type ParameterMetadata struct { // Total size in bytes. TotalSize int64 // Kind of variable. Kind reflect.Kind // Pieces that make up the location of this parameter at runtime Pieces []ParameterPiece }
ParameterMetadata contains information about a single parameter (both traditional input parameters and return "parameters"). This includes both data about the type of the parameter (and its total size, in bytes) as well as the position of the parameter (or its pieces) at runtime, which will either be on the stack or in registers.
See the note on FunctionMetadata.Parameters for limitations of the position data included within this struct.
Note that while Go only ever passes parameters entirely in the stack or entirely in registers (depending on the ABI used and the version of the Go compiler), it has been observed that the position metadata emitted by the compiler at the function's entrypoint indicates partially-spilled parameters that have some pieces still in registers and some pieces that have been moved into their reserved space on the stack. It's a little unclear whether the location metadata is *correct* in these cases (i.e. when attaching an eBPF uprobe at the entrypoint of the function, has the first instruction to spill the registers actually been executed?), but so far, taking the locations at face value and interpreting them directly (specifically handling mixed stack/register locations) has been successful in getting the expected values from eBPF.
TODO: look into cases where a middle piece of a parameter has been eliminated
(such as the length of a slice), and make sure result is expected/handled well. Then, document such cases.
type ParameterPiece ¶
type ParameterPiece struct { // Size of this piece in bytes Size int64 // True if this piece is contained in a register. InReg bool // Offset from the stackpointer. // Only given if the piece resides on the stack. StackOffset int64 // Register number of the piece. // Only given if the piece resides in registers. Register int }
ParameterPiece represents a single piece of a parameter that either exists entirely on the stack or in a register. This might be the full parameter, or might be just part of it (especially in the case of slices/interfaces/strings that are made up of multiple word-sized parts).
type Result ¶
type Result struct { Arch GoArch ABI GoABI GoVersion goversion.GoVersion Functions map[string]FunctionMetadata StructOffsets map[FieldIdentifier]uint64 GoroutineIDMetadata GoroutineIDMetadata }
Result is the result of the binary inspection process.
func InspectNewProcessBinary ¶
func InspectNewProcessBinary(elfFile *safeelf.File, functions map[string]FunctionConfiguration, structs map[FieldIdentifier]StructLookupFunction) (*Result, error)
InspectNewProcessBinary process the given elf File, and returns the offsets of the given functions and structs.
func InspectWithDWARF ¶
func InspectWithDWARF(elfFile *safeelf.File, functions []string, structFields []FieldIdentifier) (*Result, error)
InspectWithDWARF returns the offsets of the given functions and fields in the given elf file. It also returns some additional relevant metadata about the given file. It is using the DWARF debug data to obtain information, and therefore should be run on elf files that contain debug data, like our test binaries.