diff --git a/src/cc/BPF.cc b/src/cc/BPF.cc index 292e35177237..7a7089f0b662 100644 --- a/src/cc/BPF.cc +++ b/src/cc/BPF.cc @@ -464,7 +464,7 @@ StatusTuple BPF::check_binary_symbol(const std::string& binary_path, const std::string& symbol, uint64_t symbol_addr, bcc_symbol* output) { int res = bcc_resolve_symname(binary_path.c_str(), symbol.c_str(), - symbol_addr, output); + symbol_addr, 0, output); if (res < 0) return StatusTuple( -1, "Unable to find offset for binary %s symbol %s address %lx", diff --git a/src/cc/bcc_proc.c b/src/cc/bcc_proc.c index f4c870f2d00a..0d93d548ca4f 100644 --- a/src/cc/bcc_proc.c +++ b/src/cc/bcc_proc.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "bcc_perf_map.h" #include "bcc_proc.h" @@ -307,13 +308,57 @@ static bool match_so_flags(int flags) { return true; } -const char *bcc_procutils_which_so(const char *libname) { +static bool which_so_in_process(const char* libname, int pid, char* libpath) { + int ret, found = false; + char endline[4096], *mapname = NULL, *newline; + char mappings_file[128]; + const size_t search_len = strlen(libname) + strlen("/lib."); + char search1[search_len + 1]; + char search2[search_len + 1]; + + sprintf(mappings_file, "/proc/%ld/maps", (long)pid); + FILE *fp = fopen(mappings_file, "r"); + if (!fp) + return NULL; + + snprintf(search1, search_len + 1, "/lib%s.", libname); + snprintf(search2, search_len + 1, "/lib%s-", libname); + + do { + ret = fscanf(fp, "%*x-%*x %*s %*x %*s %*d"); + if (!fgets(endline, sizeof(endline), fp)) + break; + + mapname = endline; + newline = strchr(endline, '\n'); + if (newline) + newline[0] = '\0'; + + while (isspace(mapname[0])) mapname++; + + if (strstr(mapname, ".so") && (strstr(mapname, search1) || + strstr(mapname, search2))) { + found = true; + memcpy(libpath, mapname, strlen(mapname) + 1); + break; + } + } while (ret != EOF); + + fclose(fp); + return found; +} + +char *bcc_procutils_which_so(const char *libname, int pid) { const size_t soname_len = strlen(libname) + strlen("lib.so"); char soname[soname_len + 1]; + char libpath[4096]; int i; if (strchr(libname, '/')) - return libname; + return strdup(libname); + + if (pid && which_so_in_process(libname, pid, libpath)) + return strdup(libpath); if (lib_cache_count < 0) return NULL; @@ -327,8 +372,13 @@ const char *bcc_procutils_which_so(const char *libname) { for (i = 0; i < lib_cache_count; ++i) { if (!strncmp(lib_cache[i].libname, soname, soname_len) && - match_so_flags(lib_cache[i].flags)) - return lib_cache[i].path; + match_so_flags(lib_cache[i].flags)) { + return strdup(lib_cache[i].path); + } } return NULL; } + +void bcc_procutils_free(const char *ptr) { + free((void *)ptr); +} diff --git a/src/cc/bcc_proc.h b/src/cc/bcc_proc.h index 5e04ff29eed4..7b8bca91b10a 100644 --- a/src/cc/bcc_proc.h +++ b/src/cc/bcc_proc.h @@ -27,11 +27,12 @@ extern "C" { typedef int (*bcc_procutils_modulecb)(const char *, uint64_t, uint64_t, void *); typedef void (*bcc_procutils_ksymcb)(const char *, uint64_t, void *); -const char *bcc_procutils_which_so(const char *libname); +char *bcc_procutils_which_so(const char *libname, int pid); char *bcc_procutils_which(const char *binpath); int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback, void *payload); int bcc_procutils_each_ksym(bcc_procutils_ksymcb callback, void *payload); +void bcc_procutils_free(const char *ptr); #ifdef __cplusplus } diff --git a/src/cc/bcc_syms.cc b/src/cc/bcc_syms.cc index 8a100f3e8318..ddbb5b93c8e3 100644 --- a/src/cc/bcc_syms.cc +++ b/src/cc/bcc_syms.cc @@ -304,7 +304,7 @@ int bcc_foreach_symbol(const char *module, SYM_CB cb) { } int bcc_resolve_symname(const char *module, const char *symname, - const uint64_t addr, struct bcc_symbol *sym) { + const uint64_t addr, int pid, struct bcc_symbol *sym) { uint64_t load_addr; sym->module = NULL; @@ -315,9 +315,9 @@ int bcc_resolve_symname(const char *module, const char *symname, return -1; if (strchr(module, '/')) { - sym->module = module; + sym->module = strdup(module); } else { - sym->module = bcc_procutils_which_so(module); + sym->module = bcc_procutils_which_so(module, pid); } if (sym->module == NULL) diff --git a/src/cc/bcc_syms.h b/src/cc/bcc_syms.h index 6d8e2ada8cd1..7ce2dd72c7be 100644 --- a/src/cc/bcc_syms.h +++ b/src/cc/bcc_syms.h @@ -43,7 +43,7 @@ int bcc_resolve_global_addr(int pid, const char *module, const uint64_t address, int bcc_foreach_symbol(const char *module, SYM_CB cb); int bcc_find_symbol_addr(struct bcc_symbol *sym); int bcc_resolve_symname(const char *module, const char *symname, - const uint64_t addr, struct bcc_symbol *sym); + const uint64_t addr, int pid, struct bcc_symbol *sym); #ifdef __cplusplus } #endif diff --git a/src/cc/usdt.cc b/src/cc/usdt.cc index 4f1b00a0c73f..cd619dd90fda 100644 --- a/src/cc/usdt.cc +++ b/src/cc/usdt.cc @@ -223,8 +223,9 @@ std::string Context::resolve_bin_path(const std::string &bin_path) { if (char *which = bcc_procutils_which(bin_path.c_str())) { result = which; ::free(which); - } else if (const char *which_so = bcc_procutils_which_so(bin_path.c_str())) { + } else if (char *which_so = bcc_procutils_which_so(bin_path.c_str(), 0)) { result = which_so; + ::free(which_so); } return result; diff --git a/src/lua/bcc/libbcc.lua b/src/lua/bcc/libbcc.lua index 262844504dea..d744a4690b7b 100644 --- a/src/lua/bcc/libbcc.lua +++ b/src/lua/bcc/libbcc.lua @@ -109,7 +109,8 @@ struct bcc_symbol { }; int bcc_resolve_symname(const char *module, const char *symname, const uint64_t addr, - struct bcc_symbol *sym); + int pid, struct bcc_symbol *sym); +void bcc_procutils_free(const char *ptr); void *bcc_symcache_new(int pid); int bcc_symcache_resolve(void *symcache, uint64_t addr, struct bcc_symbol *sym); void bcc_symcache_refresh(void *resolver); diff --git a/src/lua/bcc/sym.lua b/src/lua/bcc/sym.lua index 8bd6d3bbd458..39fe017601d0 100644 --- a/src/lua/bcc/sym.lua +++ b/src/lua/bcc/sym.lua @@ -32,15 +32,20 @@ end local function check_path_symbol(module, symname, addr) local sym = SYM() - if libbcc.bcc_resolve_symname(module, symname, addr or 0x0, sym) < 0 then + local module_path + if libbcc.bcc_resolve_symname(module, symname, addr or 0x0, 0, sym) < 0 then if sym[0].module == nil then error("could not find library '%s' in the library path" % module) else + module_path = ffi.string(sym[0].module) + libbcc.bcc_procutils_free(sym[0].module) error("failed to resolve symbol '%s' in '%s'" % { - symname, ffi.string(sym[0].module)}) + symname, module_path}) end end - return ffi.string(sym[0].module), sym[0].offset + module_path = ffi.string(sym[0].module) + libbcc.bcc_procutils_free(sym[0].module) + return module_path, sym[0].offset end return { create_cache=create_cache, check_path_symbol=check_path_symbol } diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py index 454fe20d70cf..6879f9fec3dc 100644 --- a/src/python/bcc/__init__.py +++ b/src/python/bcc/__init__.py @@ -554,20 +554,28 @@ def remove_xdp(dev): @classmethod - def _check_path_symbol(cls, module, symname, addr): + def _check_path_symbol(cls, module, symname, addr, pid): sym = bcc_symbol() psym = ct.pointer(sym) + c_pid = 0 if pid == -1 else pid if lib.bcc_resolve_symname(module.encode("ascii"), - symname.encode("ascii"), addr or 0x0, psym) < 0: + symname.encode("ascii"), addr or 0x0, c_pid, psym) < 0: if not sym.module: raise Exception("could not find library %s" % module) + lib.bcc_procutils_free(sym.module) raise Exception("could not determine address of symbol %s" % symname) - return sym.module.decode(), sym.offset + module_path = ct.cast(sym.module, ct.c_char_p).value.decode() + lib.bcc_procutils_free(sym.module) + return module_path, sym.offset @staticmethod def find_library(libname): - res = lib.bcc_procutils_which_so(libname.encode("ascii")) - return res if res is None else res.decode() + res = lib.bcc_procutils_which_so(libname.encode("ascii"), 0) + if not res: + return None + libpath = ct.cast(res, ct.c_char_p).value.decode() + lib.bcc_procutils_free(res) + return libpath @staticmethod def get_tracepoints(tp_re): @@ -736,7 +744,8 @@ def attach_uprobe(self, name="", sym="", sym_re="", addr=None, Libraries can be given in the name argument without the lib prefix, or with the full path (/usr/lib/...). Binaries can be given only with the - full path (/bin/sh). + full path (/bin/sh). If a PID is given, the uprobe will attach to the + version of the library used by the process. Example: BPF(text).attach_uprobe("c", "malloc") BPF(text).attach_uprobe("/usr/bin/python", "main") @@ -753,7 +762,7 @@ def attach_uprobe(self, name="", sym="", sym_re="", addr=None, group_fd=group_fd) return - (path, addr) = BPF._check_path_symbol(name, sym, addr) + (path, addr) = BPF._check_path_symbol(name, sym, addr, pid) self._check_probe_quota(1) fn = self.load_func(fn_name, BPF.KPROBE) @@ -768,15 +777,15 @@ def attach_uprobe(self, name="", sym="", sym_re="", addr=None, self._add_uprobe(ev_name, res) return self - def detach_uprobe(self, name="", sym="", addr=None): - """detach_uprobe(name="", sym="", addr=None) + def detach_uprobe(self, name="", sym="", addr=None, pid=-1): + """detach_uprobe(name="", sym="", addr=None, pid=-1) Stop running a bpf function that is attached to symbol 'sym' in library or binary 'name'. """ name = str(name) - (path, addr) = BPF._check_path_symbol(name, sym, addr) + (path, addr) = BPF._check_path_symbol(name, sym, addr, pid) ev_name = "p_%s_0x%x" % (self._probe_repl.sub("_", path), addr) if ev_name not in self.open_uprobes: raise Exception("Uprobe %s is not attached" % ev_name) @@ -805,7 +814,7 @@ def attach_uretprobe(self, name="", sym="", sym_re="", addr=None, return name = str(name) - (path, addr) = BPF._check_path_symbol(name, sym, addr) + (path, addr) = BPF._check_path_symbol(name, sym, addr, pid) self._check_probe_quota(1) fn = self.load_func(fn_name, BPF.KPROBE) @@ -820,15 +829,15 @@ def attach_uretprobe(self, name="", sym="", sym_re="", addr=None, self._add_uprobe(ev_name, res) return self - def detach_uretprobe(self, name="", sym="", addr=None): - """detach_uretprobe(name="", sym="", addr=None) + def detach_uretprobe(self, name="", sym="", addr=None, pid=-1): + """detach_uretprobe(name="", sym="", addr=None, pid=-1) Stop running a bpf function that is attached to symbol 'sym' in library or binary 'name'. """ name = str(name) - (path, addr) = BPF._check_path_symbol(name, sym, addr) + (path, addr) = BPF._check_path_symbol(name, sym, addr, pid) ev_name = "r_%s_0x%x" % (self._probe_repl.sub("_", path), addr) if ev_name not in self.open_uprobes: raise Exception("Uretprobe %s is not attached" % ev_name) diff --git a/src/python/bcc/libbcc.py b/src/python/bcc/libbcc.py index f03a1b736ede..4b158492bf20 100644 --- a/src/python/bcc/libbcc.py +++ b/src/python/bcc/libbcc.py @@ -126,16 +126,18 @@ class bcc_symbol(ct.Structure): _fields_ = [ ('name', ct.c_char_p), ('demangle_name', ct.c_char_p), - ('module', ct.c_char_p), + ('module', ct.POINTER(ct.c_char)), ('offset', ct.c_ulonglong), ] -lib.bcc_procutils_which_so.restype = ct.c_char_p -lib.bcc_procutils_which_so.argtypes = [ct.c_char_p] +lib.bcc_procutils_which_so.restype = ct.POINTER(ct.c_char) +lib.bcc_procutils_which_so.argtypes = [ct.c_char_p, ct.c_int] +lib.bcc_procutils_free.restype = None +lib.bcc_procutils_free.argtypes = [ct.c_void_p] lib.bcc_resolve_symname.restype = ct.c_int lib.bcc_resolve_symname.argtypes = [ - ct.c_char_p, ct.c_char_p, ct.c_ulonglong, ct.POINTER(bcc_symbol)] + ct.c_char_p, ct.c_char_p, ct.c_ulonglong, ct.c_int, ct.POINTER(bcc_symbol)] _SYM_CB_TYPE = ct.CFUNCTYPE(ct.c_int, ct.c_char_p, ct.c_ulonglong) lib.bcc_foreach_symbol.restype = ct.c_int diff --git a/tests/cc/test_c_api.cc b/tests/cc/test_c_api.cc index 7a5e8dcffaa0..ee7c2f3f0087 100644 --- a/tests/cc/test_c_api.cc +++ b/tests/cc/test_c_api.cc @@ -31,10 +31,19 @@ using namespace std; TEST_CASE("shared object resolution", "[c_api]") { - const char *libm = bcc_procutils_which_so("m"); + char *libm = bcc_procutils_which_so("m", 0); REQUIRE(libm); REQUIRE(libm[0] == '/'); REQUIRE(string(libm).find("libm.so") != string::npos); + free(libm); +} + +TEST_CASE("shared object resolution using loaded libraries", "[c_api]") { + char *libelf = bcc_procutils_which_so("elf", getpid()); + REQUIRE(libelf); + REQUIRE(libelf[0] == '/'); + REQUIRE(string(libelf).find("libelf") != string::npos); + free(libelf); } TEST_CASE("binary resolution with `which`", "[c_api]") { @@ -58,10 +67,21 @@ TEST_CASE("list all kernel symbols", "[c_api]") { TEST_CASE("resolve symbol name in external library", "[c_api]") { struct bcc_symbol sym; - REQUIRE(bcc_resolve_symname("c", "malloc", 0x0, &sym) == 0); + REQUIRE(bcc_resolve_symname("c", "malloc", 0x0, 0, &sym) == 0); REQUIRE(string(sym.module).find("libc.so") != string::npos); REQUIRE(sym.module[0] == '/'); REQUIRE(sym.offset != 0); + bcc_procutils_free(sym.module); +} + +TEST_CASE("resolve symbol name in external library using loaded libraries", "[c_api]") { + struct bcc_symbol sym; + + REQUIRE(bcc_resolve_symname("bcc", "bcc_procutils_which", 0x0, getpid(), &sym) == 0); + REQUIRE(string(sym.module).find("libbcc.so") != string::npos); + REQUIRE(sym.module[0] == '/'); + REQUIRE(sym.offset != 0); + bcc_procutils_free(sym.module); } extern "C" int _a_test_function(const char *a_string) {