Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 9 additions & 21 deletions interpreter/nodev8/v8.go
Original file line number Diff line number Diff line change
Expand Up @@ -1884,7 +1884,7 @@ func (d *v8Data) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID, _ libpf.Add
func (d *v8Data) Unload(_ interpreter.EbpfHandler) {
}

func (d *v8Data) readIntrospectionData(ef *pfelf.File, syms libpf.SymbolFinder) error {
func (d *v8Data) readIntrospectionData(ef *pfelf.File) error {
// Read the variables from the pfelf.File so we avoid failures if the process
// exists during extraction of the introspection data.
rm := ef.GetRemoteMemory()
Expand Down Expand Up @@ -1921,7 +1921,7 @@ func (d *v8Data) readIntrospectionData(ef *pfelf.File, syms libpf.SymbolFinder)
if memberVal.Kind() == reflect.Bool {
s = "v8dbg_parent_" + className + "__" + memberName
}
addr, err := syms.LookupSymbolAddress(libpf.SymbolName(s))
addr, err := ef.LookupSymbolAddress(libpf.SymbolName(s))
if err != nil {
log.Debugf("V8: %s = not found", s)
if classType.Name == "FrameType" {
Expand Down Expand Up @@ -2146,18 +2146,14 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr

var vers [3]uint32
for i, sym := range []string{"major", "minor", "build"} {
var addr libpf.SymbolValue
var raw [4]byte
// Resolve and read "v8::internal::Versions::XXXXXX_E"
var val []byte
sym = fmt.Sprintf("_ZN2v88internal7Version6%s_E", sym)
addr, err = ef.LookupSymbolAddress(libpf.SymbolName(sym))
if err == nil {
_, err = ef.ReadVirtualMemory(raw[:], int64(addr))
}
_, val, err = ef.SymbolData(libpf.SymbolName(sym), 4)
if err != nil {
return nil, fmt.Errorf("symbol '%s': %v", sym, err)
return nil, fmt.Errorf("unable to read '%s': %v", sym, err)
}
vers[i] = npsr.Uint32(raw[:], 0)
vers[i] = npsr.Uint32(val, 0)
}

version := vers[0]*0x1000000 + vers[1]*0x10000 + vers[2]
Expand All @@ -2167,19 +2163,11 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr
vers[0], vers[1], vers[2])
}

var syms libpf.SymbolFinder
syms, err = ef.ReadDynamicSymbols()
if err != nil {
// Dynamic section does not exists for core dumps. Use the pfelf as
// symbol finder then.
syms = ef
}

d := &v8Data{
version: version,
}

addr, err := syms.LookupSymbolAddress("_ZN2v88internal8Snapshot19DefaultSnapshotBlobEv")
addr, err := ef.LookupSymbolAddress("_ZN2v88internal8Snapshot19DefaultSnapshotBlobEv")
if err == nil {
// If there is a big stack delta soon after v8::internal::Snapshot::DefaultSnapshotBlob()
// assume it is the V8 snapshot data.
Expand All @@ -2192,7 +2180,7 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr
}
}

sym, err := syms.LookupSymbol("_ZN2v88internal11interpreter9Bytecodes14kBytecodeSizesE")
sym, err := ef.LookupSymbol("_ZN2v88internal11interpreter9Bytecodes14kBytecodeSizesE")
if err == nil && sym.Size%3 == 0 && sym.Size < 3*256 {
// Symbol v8::internal::interpreter::Bytecodes::kBytecodeSizes:
// static const uint8_t Bytecodes::kBytecodeSizes[3][kBytecodeCount];
Expand All @@ -2215,7 +2203,7 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr
}

// load introspection data
if err = d.readIntrospectionData(ef, syms); err != nil {
if err = d.readIntrospectionData(ef); err != nil {
return nil, err
}

Expand Down
12 changes: 4 additions & 8 deletions interpreter/php/opcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,18 +311,14 @@ func getOpcacheJITInfo(ef *pfelf.File) (dasmBuf, dasmSize libpf.Address, err err
// Note: zend_jit_unprotect was chosen because it immediately calls mprotect with
// dasm_buf as the first parameter, which should be in a register for both x86-64
// and ARM64.
zendJit, err := ef.LookupSymbolAddress("zend_jit_unprotect")
if err != nil {
return 0, 0, err
}

// We should only need 64 bytes, since this should be early in the instruction sequence.
code := make([]byte, 64)
if _, err = ef.ReadVirtualMemory(code, int64(zendJit)); err != nil {
return 0, 0, err
addr, code, err := ef.SymbolData("zend_jit_unprotect", 64)
if err != nil {
return 0, 0, fmt.Errorf("unable to read 'zend_jit_unprotect': %w", err)
}

dasmBufPtr, dasmSizePtr, err := retrieveJITBufferPtrWrapper(code, zendJit)
dasmBufPtr, dasmSizePtr, err := retrieveJITBufferPtrWrapper(code, addr)
if err != nil {
return 0, 0, fmt.Errorf("failed to extract DASM pointers: %w", err)
}
Expand Down
27 changes: 8 additions & 19 deletions interpreter/php/php.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,21 +202,16 @@ func recoverExecuteExJumpLabelAddress(ef *pfelf.File) (libpf.SymbolValue, error)
// executor function, has been such at least since PHP7.0. This is guaranteed
// to be the vm executor function in PHP JIT'd code, since the JIT is (currently)
// inoperable with overridden execute_ex's
executeExAddr, err := ef.LookupSymbolAddress("execute_ex")
if err != nil {
return libpf.SymbolValueInvalid,
fmt.Errorf("could not find execute_ex: %w", err)
}

// The address we care about varies from being 47 bytes in to about 107 bytes in,
// so we'll read 128 bytes. This might need to be adjusted up in future.
code := make([]byte, 128)
if _, err = ef.ReadVirtualMemory(code, int64(executeExAddr)); err != nil {
// so we'll cap at 128 bytes. This might need to be adjusted up in future.
addr, code, err := ef.SymbolData("execute_ex", 128)
if err != nil {
return libpf.SymbolValueInvalid,
fmt.Errorf("could not read from executeExAddr: %w", err)
fmt.Errorf("unable to read 'execute_ex': %w", err)
}

returnAddress, err := retrieveExecuteExJumpLabelAddressWrapper(code, executeExAddr)
returnAddress, err := retrieveExecuteExJumpLabelAddressWrapper(code, addr)
if err != nil {
return libpf.SymbolValueInvalid,
fmt.Errorf("reading the return address from execute_ex failed (%w)",
Expand All @@ -233,23 +228,17 @@ func determineVMKind(ef *pfelf.File) (uint, error) {

// This is a publicly exposed function in PHP that returns the VM type
// This has been implemented in PHP since at least 7.2
vmKindAddr, err := ef.LookupSymbolAddress("zend_vm_kind")
if err != nil {
return 0, fmt.Errorf("zend_vm_kind not found: %w", err)
}

// We should only need around 32 bytes here, since this function should be
// really short (e.g a mov and a ret).
code := make([]byte, 32)
if _, err = ef.ReadVirtualMemory(code, int64(vmKindAddr)); err != nil {
return 0, fmt.Errorf("could not read from zend_vm_kind: %w", err)
_, code, err := ef.SymbolData("zend_vm_kind", 64)
if err != nil {
return 0, fmt.Errorf("unable to read 'zend_vm_kind': %w", err)
}

vmKind, err := retrieveZendVMKindWrapper(code)
if err != nil {
return 0, fmt.Errorf("an error occurred decoding zend_vm_kind: %w", err)
}

return vmKind, nil
}

Expand Down
24 changes: 8 additions & 16 deletions interpreter/python/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -656,28 +656,20 @@ func (d *pythonData) readIntrospectionData(ef *pfelf.File, symbol libpf.SymbolNa

// decodeStub will resolve a given symbol, extract the code for it, and analyze
// the code to resolve specified argument parameter to the first jump/call.
func decodeStub(
ef *pfelf.File,
memoryBase libpf.SymbolValue,
symbolName libpf.SymbolName,
) (libpf.SymbolValue, error) {
codeAddress, err := ef.LookupSymbolAddress(symbolName)
func decodeStub(ef *pfelf.File, memoryBase libpf.SymbolValue,
symbolName libpf.SymbolName) (libpf.SymbolValue, error) {
// Read and decode the code for the symbol
addr, code, err := ef.SymbolData(symbolName, 64)
if err != nil {
return libpf.SymbolValueInvalid, fmt.Errorf("lookup %s failed: %v",
return libpf.SymbolValueInvalid, fmt.Errorf("unable to read '%s': %v",
symbolName, err)
}

code := make([]byte, 64)
if _, err = ef.ReadVirtualMemory(code, int64(codeAddress)); err != nil {
return libpf.SymbolValueInvalid, fmt.Errorf("reading %s 0x%x code failed: %v",
symbolName, codeAddress, err)
}
value, err := decodeStubArgumentWrapper(code, codeAddress, memoryBase)
value, err := decodeStubArgumentWrapper(code, addr, memoryBase)

// Sanity check the value range and alignment
if err != nil || value%4 != 0 {
return libpf.SymbolValueInvalid, fmt.Errorf("decode stub %s 0x%x %s failed (0x%x): %v",
symbolName, codeAddress, hex.Dump(code), value, err)
symbolName, addr, hex.Dump(code), value, err)
}
// If base symbol (_PyRuntime) is not provided, accept any found value.
if memoryBase == 0 && value != 0 {
Expand All @@ -688,7 +680,7 @@ func decodeStub(
return value, nil
}
return libpf.SymbolValueInvalid, fmt.Errorf("decode stub %s 0x%x %s failed (0x%x)",
symbolName, codeAddress, hex.Dump(code), value)
symbolName, addr, hex.Dump(code), value)
}

func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpreter.Data, error) {
Expand Down
16 changes: 7 additions & 9 deletions interpreter/ruby/ruby.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"unsafe"
Expand Down Expand Up @@ -784,19 +785,16 @@ func (r *rubyInstance) GetAndResetMetrics() ([]metrics.Metric, error) {
// determineRubyVersion looks for the symbol ruby_version and extracts version
// information from its value.
func determineRubyVersion(ef *pfelf.File) (uint32, error) {
sym, err := ef.LookupSymbol("ruby_version")
_, memory, err := ef.SymbolData("ruby_version", 64)
if err != nil {
return 0, fmt.Errorf("symbol ruby_version not found: %v", err)
return 0, fmt.Errorf("unable to read 'ruby_version': %v", err)
}

memory := make([]byte, 5)
if _, err := ef.ReadVirtualMemory(memory, int64(sym.Address)); err != nil {
return 0, fmt.Errorf("failed to read process memory at 0x%x:%v",
sym.Address, err)
versionString := strings.TrimRight(unsafe.String(unsafe.SliceData(memory), len(memory)), "\x00")
matches := rubyVersionRegex.FindStringSubmatch(versionString)
if len(matches) < 3 {
return 0, fmt.Errorf("failed to parse version string: '%s'", versionString)
}

matches := rubyVersionRegex.FindStringSubmatch(string(memory))

major, _ := strconv.Atoi(matches[1])
minor, _ := strconv.Atoi(matches[2])
release, _ := strconv.Atoi(matches[3])
Expand Down
Loading