lists.linaro.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2024
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
List overview
Download
Linux-kselftest-mirror
May 2023
----- 2024 -----
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
linux-kselftest-mirror@lists.linaro.org
148 participants
182 discussions
Start a n
N
ew thread
[PATCH v2] libbpf: Improve version handling when attaching uprobe
by Espen Grindhaug
This change fixes the handling of versions in elf_find_func_offset. In the previous implementation, we incorrectly assumed that the version information would be present in the string found in the string table. We now look up the correct version string in the version symbol table before constructing the full name and then comparing. This patch adds support for both name@version and name@@version to match output of the various elf parsers. Signed-off-by: Espen Grindhaug <espen.grindhaug(a)gmail.com> --- Changes since v1: - Added test --- tools/lib/bpf/libbpf.c | 148 +++++++++++++++--- .../selftests/bpf/prog_tests/attach_probe.c | 37 +++++ .../selftests/bpf/progs/test_attach_probe.c | 15 ++ 3 files changed, 181 insertions(+), 19 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 49cd304ae3bc..ef5e11ce6241 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -10620,31 +10620,94 @@ static int perf_event_uprobe_open_legacy(const char *probe_name, bool retprobe, } /* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */ -static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn) +static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn, size_t *idx) { while ((scn = elf_nextscn(elf, scn)) != NULL) { GElf_Shdr sh; if (!gelf_getshdr(scn, &sh)) continue; - if (sh.sh_type == sh_type) + if (sh.sh_type == sh_type) { + if (idx) + *idx = sh.sh_link; return scn; + } + } + return NULL; +} + +static Elf_Data *elf_find_data_by_type(Elf *elf, int sh_type, size_t *idx) +{ + Elf_Scn *scn = elf_find_next_scn_by_type(elf, sh_type, NULL, idx); + + if (scn) + return elf_getdata(scn, NULL); + + return NULL; +} + +static Elf64_Verdef *elf_verdef_by_offset(Elf_Data *data, size_t offset) +{ + if (offset + sizeof(Elf64_Verdef) > data->d_size) + return NULL; + + return (Elf64_Verdef *)((char *) data->d_buf + offset); +} + +static Elf64_Versym *elf_versym_by_idx(Elf_Data *data, size_t idx) +{ + if (idx >= data->d_size / sizeof(Elf64_Versym)) + return NULL; + + return (Elf64_Versym *)(data->d_buf + idx * sizeof(Elf64_Versym)); +} + +static Elf64_Verdaux *elf_verdaux_by_offset(Elf_Data *data, size_t offset) +{ + if (offset + sizeof(Elf64_Verdaux) > data->d_size) + return NULL; + + return (Elf64_Verdaux *)((char *) data->d_buf + offset); +} + +#define ELF_VERSYM_HIDDEN 0x8000 +#define ELF_VERSYM_IDX_MASK 0x7fff + +static Elf64_Verdaux *elf_get_verdaux_by_versym(Elf_Data *verdef_data, Elf64_Versym *versym) +{ + size_t offset = 0; + + while (offset + sizeof(Elf64_Verdef) <= verdef_data->d_size) { + Elf64_Verdef *verdef = elf_verdef_by_offset(verdef_data, offset); + + if (!verdef) + break; + + if (verdef->vd_ndx == (*versym & ELF_VERSYM_IDX_MASK)) + return elf_verdaux_by_offset(verdef_data, offset + verdef->vd_aux); + + if (verdef->vd_next == 0) + break; + + offset += verdef->vd_next; } return NULL; } /* Find offset of function name in the provided ELF object. "binary_path" is * the path to the ELF binary represented by "elf", and only used for error - * reporting matters. "name" matches symbol name or name@@LIB for library - * functions. + * reporting matters. "name" matches symbol name, name@LIB or name@@LIB for + * library functions. */ static long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name) { int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB }; bool is_shared_lib, is_name_qualified; long ret = -ENOENT; - size_t name_len; GElf_Ehdr ehdr; + Elf_Data *versym_data = NULL; + Elf_Data *verdef_data = NULL; + size_t verdef_stridx = 0; if (!gelf_getehdr(elf, &ehdr)) { pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1)); @@ -10654,9 +10717,12 @@ static long elf_find_func_offset(Elf *elf, const char *binary_path, const char * /* for shared lib case, we do not need to calculate relative offset */ is_shared_lib = ehdr.e_type == ET_DYN; - name_len = strlen(name); - /* Does name specify "@@LIB"? */ - is_name_qualified = strstr(name, "@@") != NULL; + /* Does name specify "@@LIB" or "@LIB"? */ + is_name_qualified = strstr(name, "@") != NULL; + + /* Extract version definition and version symbol table */ + versym_data = elf_find_data_by_type(elf, SHT_GNU_versym, NULL); + verdef_data = elf_find_data_by_type(elf, SHT_GNU_verdef, &verdef_stridx); /* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if * a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically @@ -10671,10 +10737,10 @@ static long elf_find_func_offset(Elf *elf, const char *binary_path, const char * const char *sname; GElf_Shdr sh; - scn = elf_find_next_scn_by_type(elf, sh_types[i], NULL); + scn = elf_find_next_scn_by_type(elf, sh_types[i], NULL, NULL); if (!scn) { pr_debug("elf: failed to find symbol table ELF sections in '%s'\n", - binary_path); + binary_path); continue; } if (!gelf_getshdr(scn, &sh)) @@ -10705,16 +10771,60 @@ static long elf_find_func_offset(Elf *elf, const char *binary_path, const char * if (!sname) continue; - curr_bind = GELF_ST_BIND(sym.st_info); + if (is_name_qualified) { + Elf64_Versym *versym; + Elf64_Verdaux *verdaux; + int res; + char full_name[256]; - /* User can specify func, func@@LIB or func@@LIB_VERSION. */ - if (strncmp(sname, name, name_len) != 0) - continue; - /* ...but we don't want a search for "foo" to match 'foo2" also, so any - * additional characters in sname should be of the form "@@LIB". - */ - if (!is_name_qualified && sname[name_len] != '\0' && sname[name_len] != '@') - continue; + /* check that name at least starts with sname before building + * the full name + */ + if (strncmp(name, sname, strlen(sname)) != 0) + continue; + + if (!versym_data || !verdef_data) { + pr_warn("elf: failed to find version definition or version symbol table in '%s'\n", + binary_path); + break; + } + + versym = elf_versym_by_idx(versym_data, idx); + if (!versym) { + pr_warn("elf: failed to lookup versym for '%s' in '%s'\n", + sname, binary_path); + continue; + } + + verdaux = elf_get_verdaux_by_versym(verdef_data, versym); + if (!verdaux) { + pr_warn("elf: failed to lookup verdaux for '%s' in '%s'\n", + sname, binary_path); + continue; + } + + res = snprintf(full_name, sizeof(full_name), + (*versym & ELF_VERSYM_HIDDEN) ? "%s@%s" : + "%s@@%s", + sname, + elf_strptr(elf, verdef_stridx, + verdaux->vda_name)); + + if (res < 0 || res >= sizeof(full_name)) { + pr_warn("elf: failed to build full name for '%s' in '%s'\n", + sname, binary_path); + continue; + } + + if (strcmp(full_name, name) != 0) + continue; + } else { + /* If name is not qualified, we want to match the symbol name */ + if (strcmp(sname, name) != 0) + continue; + } + + curr_bind = GELF_ST_BIND(sym.st_info); if (ret >= 0) { /* handle multiple matches */ diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c index 7175af39134f..c3f33f7e9d12 100644 --- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -192,6 +192,41 @@ static void test_uprobe_lib(struct test_attach_probe *skel) ASSERT_EQ(skel->bss->uretprobe_byname2_res, 8, "check_uretprobe_byname2_res"); } +static void test_uprobe_lib_with_versions(struct test_attach_probe *skel) +{ + DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); + char absolute_path[256]; + + /* test attach with a versioned name. + * realpath has two implementations in libc, only the default version will be used. + */ + uprobe_opts.func_name = "realpath@@GLIBC_2.3"; + uprobe_opts.retprobe = false; + skel->links.handle_uprobe_byversionedname_a = + bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe_byversionedname_a, + 0 /* this pid */, + "libc.so.6", + 0, &uprobe_opts); + if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byversionedname_a, "attach_handle_uprobe_byversionedname_a")) + return; + + uprobe_opts.func_name = "realpath(a)GLIBC_2.2.5"; + uprobe_opts.retprobe = false; + skel->links.handle_uprobe_byversionedname_b = + bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe_byversionedname_b, + 0 /* this pid */, + "libc.so.6", + 0, &uprobe_opts); + if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byversionedname_b, "attach_handle_uprobe_byversionedname_b")) + return; + + /* trigger & validate probes */ + realpath("/", absolute_path); + + ASSERT_EQ(skel->bss->uprobe_byversionedname_a_res, 13, "check_uprobe_byversionedname_a_res"); + ASSERT_NEQ(skel->bss->uprobe_byversionedname_b_res, 14, "check_uprobe_byversionedname_b_res"); +} + static void test_uprobe_ref_ctr(struct test_attach_probe *skel) { DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); @@ -316,6 +351,8 @@ void test_attach_probe(void) test_kprobe_sleepable(); if (test__start_subtest("uprobe-lib")) test_uprobe_lib(skel); + if (test__start_subtest("uprobe-lib-with-versions")) + test_uprobe_lib_with_versions(skel); if (test__start_subtest("uprobe-sleepable")) test_uprobe_sleepable(skel); if (test__start_subtest("uprobe-ref_ctr")) diff --git a/tools/testing/selftests/bpf/progs/test_attach_probe.c b/tools/testing/selftests/bpf/progs/test_attach_probe.c index 68466a6ad18c..079b58901ff8 100644 --- a/tools/testing/selftests/bpf/progs/test_attach_probe.c +++ b/tools/testing/selftests/bpf/progs/test_attach_probe.c @@ -17,6 +17,8 @@ int uprobe_byname3_sleepable_res = 0; int uprobe_byname3_res = 0; int uretprobe_byname3_sleepable_res = 0; int uretprobe_byname3_res = 0; +int uprobe_byversionedname_a_res = 0; +int uprobe_byversionedname_b_res = 0; void *user_ptr = 0; SEC("ksyscall/nanosleep") @@ -121,5 +123,18 @@ int handle_uretprobe_byname3(struct pt_regs *ctx) return 0; } +SEC("uprobe") +int BPF_UPROBE(handle_uprobe_byversionedname_a, const char *a, char *b) +{ + uprobe_byversionedname_a_res = 13; + return 0; +} + +SEC("uprobe") +int BPF_UPROBE(handle_uprobe_byversionedname_b, const char *a, char *b) +{ + uprobe_byversionedname_b_res = 14; + return 0; +} char _license[] SEC("license") = "GPL"; -- 2.34.1
1 year
3
8
0
0
[PATCH v2 1/3] kunit: tool: add subscripts for type annotations where appropriate
by Daniel Latypov
E.g. for subprocess.Popen, it can be opened in `text=True` mode where it returns strings, or `text=False` where it returns bytes. To differentiate, you can annotate types as `Popen[str]` or `Popen[bytes]`. This patch should add subscripts in all the places we were missing them. Reported-by: Johannes Berg <johannes.berg(a)intel.com> Link:
https://lore.kernel.org/linux-kselftest/20230315105055.9b2be0153625.I7a2cb9…
Signed-off-by: Daniel Latypov <dlatypov(a)google.com> --- Note: this is unchanged, just added a 3rd patch to this series. --- tools/testing/kunit/kunit_kernel.py | 6 +++--- tools/testing/kunit/kunit_printer.py | 2 +- tools/testing/kunit/run_checks.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index 53e90c335834..e6fc8fcb071a 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -92,7 +92,7 @@ class LinuxSourceTreeOperations: if stderr: # likely only due to build warnings print(stderr.decode()) - def start(self, params: List[str], build_dir: str) -> subprocess.Popen: + def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]: raise RuntimeError('not implemented!') @@ -112,7 +112,7 @@ class LinuxSourceTreeOperationsQemu(LinuxSourceTreeOperations): kconfig.merge_in_entries(base_kunitconfig) return kconfig - def start(self, params: List[str], build_dir: str) -> subprocess.Popen: + def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]: kernel_path = os.path.join(build_dir, self._kernel_path) qemu_command = ['qemu-system-' + self._qemu_arch, '-nodefaults', @@ -141,7 +141,7 @@ class LinuxSourceTreeOperationsUml(LinuxSourceTreeOperations): kconfig.merge_in_entries(base_kunitconfig) return kconfig - def start(self, params: List[str], build_dir: str) -> subprocess.Popen: + def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]: """Runs the Linux UML binary. Must be named 'linux'.""" linux_bin = os.path.join(build_dir, 'linux') params.extend(['mem=1G', 'console=tty', 'kunit_shutdown=halt']) diff --git a/tools/testing/kunit/kunit_printer.py b/tools/testing/kunit/kunit_printer.py index 5f1cc55ecdf5..015adf87dc2c 100644 --- a/tools/testing/kunit/kunit_printer.py +++ b/tools/testing/kunit/kunit_printer.py @@ -15,7 +15,7 @@ _RESET = '\033[0;0m' class Printer: """Wraps a file object, providing utilities for coloring output, etc.""" - def __init__(self, output: typing.IO): + def __init__(self, output: typing.IO[str]): self._output = output self._use_color = output.isatty() diff --git a/tools/testing/kunit/run_checks.py b/tools/testing/kunit/run_checks.py index 066e6f938f6d..61cece1684df 100755 --- a/tools/testing/kunit/run_checks.py +++ b/tools/testing/kunit/run_checks.py @@ -37,7 +37,7 @@ def main(argv: Sequence[str]) -> None: if argv: raise RuntimeError('This script takes no arguments') - future_to_name: Dict[futures.Future, str] = {} + future_to_name: Dict[futures.Future[None], str] = {} executor = futures.ThreadPoolExecutor(max_workers=len(commands)) for name, argv in commands.items(): if name in necessary_deps and shutil.which(necessary_deps[name]) is None: base-commit: 2c6a96dad5797e57b4cf04101d6c8d5c7a571603 -- 2.40.0.rc1.284.g88254d51c5-goog
1 year
3
9
0
0
← Newer
1
...
16
17
18
19
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Results per page:
10
25
50
100
200