Skip to content

Commit

Permalink
Recognize zipped libraries when parsing maps.
Browse files Browse the repository at this point in the history
This commit recognizes *.zip and *.apk files mmapped with executable
flag set and changes name of associated module to the corresponding
zipped entry path following the "{zip path}!/{zip entry name}" format.
File offset of the module is updated as well to reflect offset within
the zipped file data instead of the offset within the archive.
  • Loading branch information
michalgr committed Feb 17, 2023
1 parent bef3d74 commit 7546bb5
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 14 deletions.
66 changes: 58 additions & 8 deletions src/cc/bcc_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
*/

#include "bcc_proc.h"

#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
Expand All @@ -29,9 +31,9 @@
#include <sys/types.h>
#include <unistd.h>

#include "bcc_perf_map.h"
#include "bcc_proc.h"
#include "bcc_elf.h"
#include "bcc_perf_map.h"
#include "bcc_zip.h"

#ifdef __x86_64__
// https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt
Expand Down Expand Up @@ -121,12 +123,49 @@ static char *_procutils_memfd_path(const int pid, const uint64_t inum) {
return path;
}

static int _procfs_might_be_zip_path(const char *path) {
return strstr(path, ".zip") || strstr(path, ".apk");
}

static char *_procfs_find_zip_entry(const char *path, int pid,
uint32_t *offset) {
char ns_relative_path[PATH_MAX];
int rc = snprintf(ns_relative_path, sizeof(ns_relative_path),
"/proc/%d/root%s", pid, path);
if (rc < 0 || rc >= sizeof(ns_relative_path)) {
return NULL;
}

struct bcc_zip_archive *archive = bcc_zip_archive_open(ns_relative_path);
if (archive == NULL) {
return NULL;
}

struct bcc_zip_entry entry;
if (bcc_zip_archive_find_entry_at_offset(archive, *offset, &entry) ||
entry.compression) {
bcc_zip_archive_close(archive);
return NULL;
}

char *result = malloc(strlen(path) + entry.name_length + 3);
if (result == NULL) {
bcc_zip_archive_close(archive);
return NULL;
}

sprintf(result, "%s!/%.*s", path, entry.name_length, entry.name);
*offset -= entry.data_offset;
bcc_zip_archive_close(archive);
return result;
}

// return: 0 -> callback returned < 0, stopped iterating
// -1 -> callback never indicated to stop
int _procfs_maps_each_module(FILE *procmap, int pid,
bcc_procutils_modulecb callback, void *payload) {
char buf[PATH_MAX + 1], perm[5];
char *name;
char *name, *resolved_name;
mod_info mod;
uint8_t enter_ns;
while (true) {
Expand All @@ -148,14 +187,25 @@ int _procfs_maps_each_module(FILE *procmap, int pid,
if (!bcc_mapping_is_file_backed(name))
continue;

resolved_name = NULL;
if (strstr(name, "/memfd:")) {
char *memfd_name = _procutils_memfd_path(pid, mod.inode);
if (memfd_name != NULL) {
strcpy(buf, memfd_name);
free(memfd_name);
mod.name = buf;
resolved_name = _procutils_memfd_path(pid, mod.inode);
if (resolved_name != NULL) {
enter_ns = 0;
}
} else if (_procfs_might_be_zip_path(mod.name)) {
uint32_t zip_entry_offset = mod.file_offset;
resolved_name = _procfs_find_zip_entry(mod.name, pid, &zip_entry_offset);
if (resolved_name != NULL) {
mod.file_offset = zip_entry_offset;
}
}

if (resolved_name != NULL) {
strncpy(buf, resolved_name, PATH_MAX);
buf[PATH_MAX] = 0;
free(resolved_name);
mod.name = buf;
}

if (callback(&mod, enter_ns, payload) < 0)
Expand Down
38 changes: 32 additions & 6 deletions tests/cc/test_c_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,19 @@ TEST_CASE("resolve symbol name in external library using loaded libraries", "[c_
bcc_procutils_free(sym.module);
}

namespace {

static std::string zipped_lib_path() {
return CMAKE_CURRENT_BINARY_DIR "/archive.zip!/libdebuginfo_test_lib.so";
}

} // namespace

TEST_CASE("resolve symbol name in external zipped library", "[c_api]") {
struct bcc_symbol sym;
std::string lib_path =
CMAKE_CURRENT_BINARY_DIR "/archive.zip!/libdebuginfo_test_lib.so";

REQUIRE(bcc_resolve_symname(lib_path.c_str(), "symbol", 0x0, 0, nullptr,
&sym) == 0);
REQUIRE(sym.module == lib_path);
REQUIRE(bcc_resolve_symname(zipped_lib_path().c_str(), "symbol", 0x0, 0,
nullptr, &sym) == 0);
REQUIRE(sym.module == zipped_lib_path());
REQUIRE(sym.offset != 0);
bcc_procutils_free(sym.module);
}
Expand Down Expand Up @@ -781,6 +786,27 @@ TEST_CASE("searching for modules in /proc/[pid]/maps", "[c_api][!mayfail]") {
}

fclose(dummy_maps);

SECTION("seach for lib in zip") {
std::string line =
"7f151476e000-7f1514779000 r-xp 00001000 00:1b "
"72809479 " CMAKE_CURRENT_BINARY_DIR "/archive.zip\n";
dummy_maps = fmemopen(nullptr, line.size(), "w+");
REQUIRE(fwrite(line.c_str(), line.size(), 1, dummy_maps) == 1);
fseek(dummy_maps, 0, SEEK_SET);

struct mod_search search;
memset(&search, 0, sizeof(struct mod_search));
std::string zip_entry_path = zipped_lib_path();
search.name = zip_entry_path.c_str();
int res = _procfs_maps_each_module(dummy_maps, getpid(),
_bcc_syms_find_module, &search);
REQUIRE(res == 0);
REQUIRE(search.start == 0x7f151476e000);
REQUIRE(search.file_offset < 0x1000);

fclose(dummy_maps);
}
}

TEST_CASE("resolve global addr in libc in this process", "[c_api][!mayfail]") {
Expand Down

0 comments on commit 7546bb5

Please sign in to comment.