diff --git a/src/cc/BPF.cc b/src/cc/BPF.cc index 4a7ca2ccea11..f2d6ab6de3a2 100644 --- a/src/cc/BPF.cc +++ b/src/cc/BPF.cc @@ -462,7 +462,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..99b24b6f67f0 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" @@ -196,6 +197,8 @@ static struct ld_lib { int flags; } * lib_cache; +static char libpath[4096]; + static int read_cache1(const char *ld_map) { struct ld_cache1 *ldcache = (struct ld_cache1 *)ld_map; const char *ldstrings = @@ -307,7 +310,47 @@ 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) { + 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)); + break; + } + } while (ret != EOF); + + fclose(fp); + return found; +} + +const 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]; int i; @@ -315,6 +358,9 @@ const char *bcc_procutils_which_so(const char *libname) { if (strchr(libname, '/')) return libname; + if (pid && which_so_in_process(libname, pid)) + return libpath; + if (lib_cache_count < 0) return NULL; @@ -327,8 +373,9 @@ 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)) + match_so_flags(lib_cache[i].flags)) { return lib_cache[i].path; + } } return NULL; } diff --git a/src/cc/bcc_proc.h b/src/cc/bcc_proc.h index 5e04ff29eed4..e7607f9783a5 100644 --- a/src/cc/bcc_proc.h +++ b/src/cc/bcc_proc.h @@ -27,7 +27,7 @@ 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); +const 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); diff --git a/src/cc/bcc_syms.cc b/src/cc/bcc_syms.cc index 8a100f3e8318..2dc575bad1e1 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; @@ -317,7 +317,7 @@ int bcc_resolve_symname(const char *module, const char *symname, if (strchr(module, '/')) { sym->module = 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..095e1ce192e5 100644 --- a/src/cc/usdt.cc +++ b/src/cc/usdt.cc @@ -223,7 +223,7 @@ 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 (const char *which_so = bcc_procutils_which_so(bin_path.c_str(), 0)) { result = which_so; } diff --git a/src/lua/bcc/libbcc.lua b/src/lua/bcc/libbcc.lua index 262844504dea..1f4cf4efea83 100644 --- a/src/lua/bcc/libbcc.lua +++ b/src/lua/bcc/libbcc.lua @@ -109,7 +109,7 @@ 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_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..023e4f3b2731 100644 --- a/src/lua/bcc/sym.lua +++ b/src/lua/bcc/sym.lua @@ -32,7 +32,7 @@ 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 + 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 diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py index 0304da180b2e..e1363adcf09b 100644 --- a/src/python/bcc/__init__.py +++ b/src/python/bcc/__init__.py @@ -554,11 +554,12 @@ 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) raise Exception("could not determine address of symbol %s" % symname) @@ -566,7 +567,7 @@ def _check_path_symbol(cls, module, symname, addr): @staticmethod def find_library(libname): - res = lib.bcc_procutils_which_so(libname.encode("ascii")) + res = lib.bcc_procutils_which_so(libname.encode("ascii"), 0) return res if res is None else res.decode() @staticmethod @@ -736,7 +737,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 +755,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) @@ -776,7 +778,7 @@ def detach_uprobe(self, name="", sym="", addr=None): """ name = str(name) - (path, addr) = BPF._check_path_symbol(name, sym, addr) + (path, addr) = BPF._check_path_symbol(name, sym, addr, -1) 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 +807,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) @@ -828,7 +830,7 @@ def detach_uretprobe(self, name="", sym="", addr=None): """ name = str(name) - (path, addr) = BPF._check_path_symbol(name, sym, addr) + (path, addr) = BPF._check_path_symbol(name, sym, addr, -1) 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..2ba98671ce28 100644 --- a/src/python/bcc/libbcc.py +++ b/src/python/bcc/libbcc.py @@ -131,11 +131,11 @@ class bcc_symbol(ct.Structure): ] 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.argtypes = [ct.c_char_p, ct.c_int] 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 af0bd3acf31a..276301bedd1e 100644 --- a/tests/cc/test_c_api.cc +++ b/tests/cc/test_c_api.cc @@ -30,12 +30,19 @@ using namespace std; TEST_CASE("shared object resolution", "[c_api]") { - const char *libm = bcc_procutils_which_so("m"); + const char *libm = bcc_procutils_which_so("m", 0); REQUIRE(libm); REQUIRE(libm[0] == '/'); REQUIRE(string(libm).find("libm.so") != string::npos); } +TEST_CASE("shared object resolution using loaded libraries", "[c_api]") { + const char *libelf = bcc_procutils_which_so("elf", getpid()); + REQUIRE(libelf); + REQUIRE(libelf[0] == '/'); + REQUIRE(string(libelf).find("libelf") != string::npos); +} + TEST_CASE("binary resolution with `which`", "[c_api]") { char *ld = bcc_procutils_which("ld"); REQUIRE(ld); @@ -57,12 +64,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); } +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); +} + extern "C" int _a_test_function(const char *a_string) { int i; for (i = 0; a_string[i]; ++i)