From 4ee379569c84a9c0931c23e3fd22a4391cec7070 Mon Sep 17 00:00:00 2001 From: Viet Anh Duong Date: Wed, 15 Nov 2023 11:05:38 +0000 Subject: [PATCH] Add load /proc/kallsyms function Signed-off-by: Viet Anh Duong --- bcc/symbol.go | 33 +++++++++++++++-- bcc/symbol_utils.go | 71 ++++++++++++++++++++++++++++++++++++ examples/bcc/ksymbol/main.go | 19 ++++++++++ 3 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 bcc/symbol_utils.go create mode 100644 examples/bcc/ksymbol/main.go diff --git a/bcc/symbol.go b/bcc/symbol.go index a523332..eefaedd 100644 --- a/bcc/symbol.go +++ b/bcc/symbol.go @@ -62,9 +62,23 @@ func (s *Symbolizer) SymbolOrAddrIfUnknown(pid int, addr uintptr) string { } if module := C.GoString(symbol.module); module != "" { - return s.formatModuleName(C.GoString(symbol.module), uintptr(symbol.offset)) + return formatModuleName(C.GoString(symbol.module), uintptr(symbol.offset)) } - return s.formatAddress(addr) + return formatAddress(addr) +} + +func (s *Symbolizer) ResolveName(pid int, module, name string) (uintptr, error) { + cmodule := C.CString(module) + defer C.free(unsafe.Pointer(cmodule)) + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + var addr C.ulong + ret := C.bcc_symcache_resolve_name(s.getBCCSymbolCache(pid), cmodule, cname, &addr) + if ret < 0 { + return 0, fmt.Errorf("unable to resolve symbol name %q", name) + } + return uintptr(addr), nil } func (s *Symbolizer) ReleasePidSymCache(pid int) { @@ -86,11 +100,11 @@ func (s *Symbolizer) getBCCSymbolCache(pid int) unsafe.Pointer { return cache } -func (s *Symbolizer) formatAddress(addr uintptr) string { +func formatAddress(addr uintptr) string { return fmt.Sprintf("0x%016x", addr) } -func (s *Symbolizer) formatModuleName(module string, offset uintptr) string { +func formatModuleName(module string, offset uintptr) string { return fmt.Sprintf("[m] %s + 0x%08x", module, offset) } @@ -124,6 +138,17 @@ type bccSymbolOption struct { useSymbolType uint32 } +// Ksymname Translate a kernel name into an address. This is the reverse of +// ksym. Returns -1 when the function name is unknown. +// TODO(vietanhduong): Implement symnol cache +func Ksymname(name string) (uint64, error) { + addr, err := bccResolveName("", name, -1) + if err != nil { + return 0, err + } + return addr, nil +} + // resolveSymbolPath returns the file and offset to locate symname in module func resolveSymbolPath(module string, symname string, addr uint64, pid int) (string, uint64, error) { if pid == -1 { diff --git a/bcc/symbol_utils.go b/bcc/symbol_utils.go new file mode 100644 index 0000000..0c9304e --- /dev/null +++ b/bcc/symbol_utils.go @@ -0,0 +1,71 @@ +package bcc + +import ( + "bufio" + "fmt" + "math" + "os" + "strconv" + "strings" +) + +const KernelAddressSpace = 0x00ffffffffffffff + +type Symbol struct { + Name string + Module string + Address uint64 +} + +func (k *Symbol) String() string { + if k == nil { + return "" + } + return fmt.Sprintf("module=%s symbol=%s address=0x%016x", k.Module, k.Name, k.Address) +} + +func LoadProcKallsym() ([]*Symbol, error) { + var ret []*Symbol + LoadProcKallsymWithCallback(func(sym *Symbol) { ret = append(ret, sym) }) + return ret, nil +} + +func LoadProcKallsymWithCallback(callback func(sym *Symbol)) error { + f, err := os.Open("/proc/kallsyms") + if err != nil { + return fmt.Errorf("open /proc/kallsyms: %w", err) + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + parts := strings.Fields(scanner.Text()) + if len(parts) < 3 { // This should never happen + continue + } + sym := &Symbol{Name: parts[2], Module: "kernel"} + + if parts[1][0] == 'b' || parts[1][0] == 'B' || + parts[1][0] == 'd' || parts[1][0] == 'D' || + parts[1][0] == 'r' || parts[1][0] == 'R' { + continue + } + + sym.Address, _ = strconv.ParseUint(parts[0], 16, 0) + if sym.Address == 0 || sym.Address == math.MaxUint64 || sym.Address < KernelAddressSpace { + continue + } + + parts = append(parts, "") + if len(parts[3]) > 0 && parts[3][0] == '[' && parts[3][len(parts[3])-1] == ']' { + sym.Module = parts[3][1 : len(parts[3])-1] + } + + callback(sym) + } + + if err = scanner.Err(); err != nil { + return fmt.Errorf("scanner error: %w", err) + } + return nil +} diff --git a/examples/bcc/ksymbol/main.go b/examples/bcc/ksymbol/main.go new file mode 100644 index 0000000..8ef67fb --- /dev/null +++ b/examples/bcc/ksymbol/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "log" + "os" + + "github.com/vietanhduong/go-bpf/bcc" +) + +func main() { + ksyms, err := bcc.LoadProcKallsym() + if err != nil { + log.Printf("Failed to load Kallsyms: %v", err) + os.Exit(1) + } + for _, sym := range ksyms { + log.Println(sym.String()) + } +}