Skip to content

Commit c44fd84

Browse files
d-e-s-oanakryiko
authored andcommitted
libbpf: Add support for attaching uprobes to shared objects in APKs
This change adds support for attaching uprobes to shared objects located in APKs, which is relevant for Android systems where various libraries may reside in APKs. To make that happen, we extend the syntax for the "binary path" argument to attach to with that supported by various Android tools: <archive>!/<binary-in-archive> For example: /system/app/test-app/test-app.apk!/lib/arm64-v8a/libc++_shared.so APKs need to be specified via full path, i.e., we do not attempt to resolve mere file names by searching system directories. We cannot currently test this functionality end-to-end in an automated fashion, because it relies on an Android system being present, but there is no support for that in CI. I have tested the functionality manually, by creating a libbpf program containing a uretprobe, attaching it to a function inside a shared object inside an APK, and verifying the sanity of the returned values. Signed-off-by: Daniel Müller <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 434fdce commit c44fd84

File tree

1 file changed

+84
-7
lines changed

1 file changed

+84
-7
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "libbpf_internal.h"
5454
#include "hashmap.h"
5555
#include "bpf_gen_internal.h"
56+
#include "zip.h"
5657

5758
#ifndef BPF_FS_MAGIC
5859
#define BPF_FS_MAGIC 0xcafe4a11
@@ -10701,6 +10702,68 @@ static long elf_find_func_offset_from_file(const char *binary_path, const char *
1070110702
return ret;
1070210703
}
1070310704

10705+
/* Find offset of function name in archive specified by path. Currently
10706+
* supported are .zip files that do not compress their contents, as used on
10707+
* Android in the form of APKs, for example. "file_name" is the name of the ELF
10708+
* file inside the archive. "func_name" matches symbol name or name@@LIB for
10709+
* library functions.
10710+
*
10711+
* An overview of the APK format specifically provided here:
10712+
* https://en.wikipedia.org/w/index.php?title=Apk_(file_format)&oldid=1139099120#Package_contents
10713+
*/
10714+
static long elf_find_func_offset_from_archive(const char *archive_path, const char *file_name,
10715+
const char *func_name)
10716+
{
10717+
struct zip_archive *archive;
10718+
struct zip_entry entry;
10719+
long ret;
10720+
Elf *elf;
10721+
10722+
archive = zip_archive_open(archive_path);
10723+
if (IS_ERR(archive)) {
10724+
ret = PTR_ERR(archive);
10725+
pr_warn("zip: failed to open %s: %ld\n", archive_path, ret);
10726+
return ret;
10727+
}
10728+
10729+
ret = zip_archive_find_entry(archive, file_name, &entry);
10730+
if (ret) {
10731+
pr_warn("zip: could not find archive member %s in %s: %ld\n", file_name,
10732+
archive_path, ret);
10733+
goto out;
10734+
}
10735+
pr_debug("zip: found entry for %s in %s at 0x%lx\n", file_name, archive_path,
10736+
(unsigned long)entry.data_offset);
10737+
10738+
if (entry.compression) {
10739+
pr_warn("zip: entry %s of %s is compressed and cannot be handled\n", file_name,
10740+
archive_path);
10741+
ret = -LIBBPF_ERRNO__FORMAT;
10742+
goto out;
10743+
}
10744+
10745+
elf = elf_memory((void *)entry.data, entry.data_length);
10746+
if (!elf) {
10747+
pr_warn("elf: could not read elf file %s from %s: %s\n", file_name, archive_path,
10748+
elf_errmsg(-1));
10749+
ret = -LIBBPF_ERRNO__LIBELF;
10750+
goto out;
10751+
}
10752+
10753+
ret = elf_find_func_offset(elf, file_name, func_name);
10754+
if (ret > 0) {
10755+
pr_debug("elf: symbol address match for %s of %s in %s: 0x%x + 0x%lx = 0x%lx\n",
10756+
func_name, file_name, archive_path, entry.data_offset, ret,
10757+
ret + entry.data_offset);
10758+
ret += entry.data_offset;
10759+
}
10760+
elf_end(elf);
10761+
10762+
out:
10763+
zip_archive_close(archive);
10764+
return ret;
10765+
}
10766+
1070410767
static const char *arch_specific_lib_paths(void)
1070510768
{
1070610769
/*
@@ -10786,9 +10849,10 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
1078610849
const char *binary_path, size_t func_offset,
1078710850
const struct bpf_uprobe_opts *opts)
1078810851
{
10789-
DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
10852+
const char *archive_path = NULL, *archive_sep = NULL;
1079010853
char errmsg[STRERR_BUFSIZE], *legacy_probe = NULL;
10791-
char full_binary_path[PATH_MAX];
10854+
DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
10855+
char full_path[PATH_MAX];
1079210856
struct bpf_link *link;
1079310857
size_t ref_ctr_off;
1079410858
int pfd, err;
@@ -10805,21 +10869,34 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
1080510869
if (!binary_path)
1080610870
return libbpf_err_ptr(-EINVAL);
1080710871

10808-
if (!strchr(binary_path, '/')) {
10809-
err = resolve_full_path(binary_path, full_binary_path,
10810-
sizeof(full_binary_path));
10872+
/* Check if "binary_path" refers to an archive. */
10873+
archive_sep = strstr(binary_path, "!/");
10874+
if (archive_sep) {
10875+
full_path[0] = '\0';
10876+
libbpf_strlcpy(full_path, binary_path,
10877+
min(sizeof(full_path), (size_t)(archive_sep - binary_path + 1)));
10878+
archive_path = full_path;
10879+
binary_path = archive_sep + 2;
10880+
} else if (!strchr(binary_path, '/')) {
10881+
err = resolve_full_path(binary_path, full_path, sizeof(full_path));
1081110882
if (err) {
1081210883
pr_warn("prog '%s': failed to resolve full path for '%s': %d\n",
1081310884
prog->name, binary_path, err);
1081410885
return libbpf_err_ptr(err);
1081510886
}
10816-
binary_path = full_binary_path;
10887+
binary_path = full_path;
1081710888
}
1081810889
func_name = OPTS_GET(opts, func_name, NULL);
1081910890
if (func_name) {
1082010891
long sym_off;
1082110892

10822-
sym_off = elf_find_func_offset_from_file(binary_path, func_name);
10893+
if (archive_path) {
10894+
sym_off = elf_find_func_offset_from_archive(archive_path, binary_path,
10895+
func_name);
10896+
binary_path = archive_path;
10897+
} else {
10898+
sym_off = elf_find_func_offset_from_file(binary_path, func_name);
10899+
}
1082310900
if (sym_off < 0)
1082410901
return libbpf_err_ptr(sym_off);
1082510902
func_offset += sym_off;

0 commit comments

Comments
 (0)