diff mbox series

libbpf: Fix uprobe offset calculation

Message ID 20250307140120.1261890-1-hengqi.chen@gmail.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series libbpf: Fix uprobe offset calculation | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for s390x-gcc / veristat-meta
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / GCC BPF
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-gcc / veristat-meta / x86_64-gcc veristat_meta
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / GCC BPF / GCC BPF
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-19 success Logs for s390x-gcc / veristat-kernel
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / GCC BPF / GCC BPF
bpf/vmtest-bpf-next-VM_Test-44 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / GCC BPF
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-17 / veristat-kernel
bpf/vmtest-bpf-next-VM_Test-27 fail Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-21 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-43 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-51 success Logs for x86_64-llvm-18 / veristat-meta
bpf/vmtest-bpf-next-VM_Test-18 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for aarch64-gcc / veristat-kernel
bpf/vmtest-bpf-next-VM_Test-12 success Logs for aarch64-gcc / veristat-meta
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-gcc / veristat-kernel / x86_64-gcc veristat_kernel
bpf/vmtest-bpf-next-VM_Test-50 success Logs for x86_64-llvm-18 / veristat-kernel
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / GCC BPF / GCC BPF
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-17 / veristat-meta
bpf/vmtest-bpf-next-VM_Test-9 fail Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 fail Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-45 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 fail Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-49 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-48 fail Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-46 fail Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-38 fail Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-37 fail Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-47 fail Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-17 fail Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-16 fail Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc

Commit Message

Hengqi Chen March 7, 2025, 2:01 p.m. UTC
As reported on libbpf-rs issue([0]), the current implementation
may resolve symbol to a wrong offset and thus missing uprobe
event. Calculate the symbol offset from program header instead.
See the BCC implementation (which in turn used by bpftrace) and
the spec ([1]) for references.

  [0]: https://github.com/libbpf/libbpf-rs/issues/1110
  [1]: https://refspecs.linuxfoundation.org/elf/

Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com>
---
 tools/lib/bpf/elf.c | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

Comments

Yonghong Song March 8, 2025, 6:48 a.m. UTC | #1
On 3/7/25 6:01 AM, Hengqi Chen wrote:
> As reported on libbpf-rs issue([0]), the current implementation
> may resolve symbol to a wrong offset and thus missing uprobe
> event. Calculate the symbol offset from program header instead.
> See the BCC implementation (which in turn used by bpftrace) and
> the spec ([1]) for references.
>
>    [0]: https://github.com/libbpf/libbpf-rs/issues/1110
>    [1]: https://refspecs.linuxfoundation.org/elf/
>
> Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com>

Hengqi,

There are some test failures in the CI. For example,
   https://github.com/kernel-patches/bpf/actions/runs/13725803997/job/38392284640?pr=8631

Please take a look.
Your below elf_sym_offset change matches some bcc implementation, but
maybe maybe this is only under certain condition?

Also, it would be great if you can add detailed description in commit message
about what is the problem and why a different approach is necessary to
fix the issue.

> ---
>   tools/lib/bpf/elf.c | 32 ++++++++++++++++++++++++--------
>   1 file changed, 24 insertions(+), 8 deletions(-)
>
> diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
> index 823f83ad819c..9b561c8d1eec 100644
> --- a/tools/lib/bpf/elf.c
> +++ b/tools/lib/bpf/elf.c
> @@ -260,13 +260,29 @@ static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym
>    * for shared libs) into file offset, which is what kernel is expecting
>    * for uprobe/uretprobe attachment.
>    * See Documentation/trace/uprobetracer.rst for more details. This is done
> - * by looking up symbol's containing section's header and using iter's virtual
> - * address (sh_addr) and corresponding file offset (sh_offset) to transform
> + * by looking up symbol's containing program header and using its virtual
> + * address (p_vaddr) and corresponding file offset (p_offset) to transform
>    * sym.st_value (virtual address) into desired final file offset.
>    */
> -static unsigned long elf_sym_offset(struct elf_sym *sym)
> +static unsigned long elf_sym_offset(Elf *elf, struct elf_sym *sym)
>   {
> -	return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
> +	size_t nhdrs, i;
> +	GElf_Phdr phdr;
> +
> +	if (elf_getphdrnum(elf, &nhdrs))
> +		return -1;
> +
> +	for (i = 0; i < nhdrs; i++) {
> +		if (!gelf_getphdr(elf, (int)i, &phdr))
> +			continue;
> +		if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
> +			continue;
> +		if (sym->sym.st_value >= phdr.p_vaddr &&
> +		    sym->sym.st_value < (phdr.p_vaddr + phdr.p_memsz))
> +			return sym->sym.st_value - phdr.p_vaddr + phdr.p_offset;
> +	}
> +
> +	return -1;
>   }
>   
>   /* Find offset of function name in the provided ELF object. "binary_path" is
> @@ -329,7 +345,7 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
>   
>   			if (ret > 0) {
>   				/* handle multiple matches */
> -				if (elf_sym_offset(sym) == ret) {
> +				if (elf_sym_offset(elf, sym) == ret) {
>   					/* same offset, no problem */
>   					continue;
>   				} else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {

[...]
Hengqi Chen March 11, 2025, 5:16 a.m. UTC | #2
Hi Yonghong,

On Sat, Mar 8, 2025 at 2:48 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>
>
>
> On 3/7/25 6:01 AM, Hengqi Chen wrote:
> > As reported on libbpf-rs issue([0]), the current implementation
> > may resolve symbol to a wrong offset and thus missing uprobe
> > event. Calculate the symbol offset from program header instead.
> > See the BCC implementation (which in turn used by bpftrace) and
> > the spec ([1]) for references.
> >
> >    [0]: https://github.com/libbpf/libbpf-rs/issues/1110
> >    [1]: https://refspecs.linuxfoundation.org/elf/
> >
> > Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com>
>
> Hengqi,
>
> There are some test failures in the CI. For example,
>    https://github.com/kernel-patches/bpf/actions/runs/13725803997/job/38392284640?pr=8631
>

Yes, I've received an email from BPF CI.
It seems like the uprobe multi testcase is unhappy with this change.

> Please take a look.
> Your below elf_sym_offset change matches some bcc implementation, but
> maybe maybe this is only under certain condition?
>

Remove the `phdr.p_flags & PF_X` check fix the issue. Need more investigation.

> Also, it would be great if you can add detailed description in commit message
> about what is the problem and why a different approach is necessary to
> fix the issue.
>

Will do. Thanks.

> > ---
> >   tools/lib/bpf/elf.c | 32 ++++++++++++++++++++++++--------
> >   1 file changed, 24 insertions(+), 8 deletions(-)
> >
> > diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
> > index 823f83ad819c..9b561c8d1eec 100644
> > --- a/tools/lib/bpf/elf.c
> > +++ b/tools/lib/bpf/elf.c
> > @@ -260,13 +260,29 @@ static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym
> >    * for shared libs) into file offset, which is what kernel is expecting
> >    * for uprobe/uretprobe attachment.
> >    * See Documentation/trace/uprobetracer.rst for more details. This is done
> > - * by looking up symbol's containing section's header and using iter's virtual
> > - * address (sh_addr) and corresponding file offset (sh_offset) to transform
> > + * by looking up symbol's containing program header and using its virtual
> > + * address (p_vaddr) and corresponding file offset (p_offset) to transform
> >    * sym.st_value (virtual address) into desired final file offset.
> >    */
> > -static unsigned long elf_sym_offset(struct elf_sym *sym)
> > +static unsigned long elf_sym_offset(Elf *elf, struct elf_sym *sym)
> >   {
> > -     return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
> > +     size_t nhdrs, i;
> > +     GElf_Phdr phdr;
> > +
> > +     if (elf_getphdrnum(elf, &nhdrs))
> > +             return -1;
> > +
> > +     for (i = 0; i < nhdrs; i++) {
> > +             if (!gelf_getphdr(elf, (int)i, &phdr))
> > +                     continue;
> > +             if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
> > +                     continue;
> > +             if (sym->sym.st_value >= phdr.p_vaddr &&
> > +                 sym->sym.st_value < (phdr.p_vaddr + phdr.p_memsz))
> > +                     return sym->sym.st_value - phdr.p_vaddr + phdr.p_offset;
> > +     }
> > +
> > +     return -1;
> >   }
> >
> >   /* Find offset of function name in the provided ELF object. "binary_path" is
> > @@ -329,7 +345,7 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
> >
> >                       if (ret > 0) {
> >                               /* handle multiple matches */
> > -                             if (elf_sym_offset(sym) == ret) {
> > +                             if (elf_sym_offset(elf, sym) == ret) {
> >                                       /* same offset, no problem */
> >                                       continue;
> >                               } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
>
> [...]
>
Andrii Nakryiko March 12, 2025, 6:47 p.m. UTC | #3
On Mon, Mar 10, 2025 at 10:16 PM Hengqi Chen <hengqi.chen@gmail.com> wrote:
>
> Hi Yonghong,
>
> On Sat, Mar 8, 2025 at 2:48 PM Yonghong Song <yonghong.song@linux.dev> wrote:
> >
> >
> >
> > On 3/7/25 6:01 AM, Hengqi Chen wrote:
> > > As reported on libbpf-rs issue([0]), the current implementation
> > > may resolve symbol to a wrong offset and thus missing uprobe
> > > event. Calculate the symbol offset from program header instead.
> > > See the BCC implementation (which in turn used by bpftrace) and
> > > the spec ([1]) for references.
> > >
> > >    [0]: https://github.com/libbpf/libbpf-rs/issues/1110
> > >    [1]: https://refspecs.linuxfoundation.org/elf/
> > >
> > > Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com>
> >
> > Hengqi,
> >
> > There are some test failures in the CI. For example,
> >    https://github.com/kernel-patches/bpf/actions/runs/13725803997/job/38392284640?pr=8631
> >
>
> Yes, I've received an email from BPF CI.
> It seems like the uprobe multi testcase is unhappy with this change.
>
> > Please take a look.
> > Your below elf_sym_offset change matches some bcc implementation, but
> > maybe maybe this is only under certain condition?
> >
>
> Remove the `phdr.p_flags & PF_X` check fix the issue. Need more investigation.
>
> > Also, it would be great if you can add detailed description in commit message
> > about what is the problem and why a different approach is necessary to
> > fix the issue.
> >
>
> Will do. Thanks.
>
> > > ---
> > >   tools/lib/bpf/elf.c | 32 ++++++++++++++++++++++++--------
> > >   1 file changed, 24 insertions(+), 8 deletions(-)
> > >
> > > diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
> > > index 823f83ad819c..9b561c8d1eec 100644
> > > --- a/tools/lib/bpf/elf.c
> > > +++ b/tools/lib/bpf/elf.c
> > > @@ -260,13 +260,29 @@ static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym
> > >    * for shared libs) into file offset, which is what kernel is expecting
> > >    * for uprobe/uretprobe attachment.
> > >    * See Documentation/trace/uprobetracer.rst for more details. This is done
> > > - * by looking up symbol's containing section's header and using iter's virtual
> > > - * address (sh_addr) and corresponding file offset (sh_offset) to transform
> > > + * by looking up symbol's containing program header and using its virtual
> > > + * address (p_vaddr) and corresponding file offset (p_offset) to transform
> > >    * sym.st_value (virtual address) into desired final file offset.
> > >    */
> > > -static unsigned long elf_sym_offset(struct elf_sym *sym)
> > > +static unsigned long elf_sym_offset(Elf *elf, struct elf_sym *sym)
> > >   {
> > > -     return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
> > > +     size_t nhdrs, i;
> > > +     GElf_Phdr phdr;
> > > +
> > > +     if (elf_getphdrnum(elf, &nhdrs))
> > > +             return -1;
> > > +
> > > +     for (i = 0; i < nhdrs; i++) {
> > > +             if (!gelf_getphdr(elf, (int)i, &phdr))
> > > +                     continue;
> > > +             if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
> > > +                     continue;
> > > +             if (sym->sym.st_value >= phdr.p_vaddr &&
> > > +                 sym->sym.st_value < (phdr.p_vaddr + phdr.p_memsz))
> > > +                     return sym->sym.st_value - phdr.p_vaddr + phdr.p_offset;

Hengqi,

Can you please provide an example where existing code doesn't work? I think that

sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset

and

sym->sym.st_value - phdr.p_vaddr + phdr.p_offset


Should result in the same, and we don't need to search for a matching
segment if we have an ELF symbol and its section. But maybe I'm
mistaken, so can you please elaborate a bit?

> > > +     }
> > > +
> > > +     return -1;
> > >   }
> > >
> > >   /* Find offset of function name in the provided ELF object. "binary_path" is
> > > @@ -329,7 +345,7 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
> > >
> > >                       if (ret > 0) {
> > >                               /* handle multiple matches */
> > > -                             if (elf_sym_offset(sym) == ret) {
> > > +                             if (elf_sym_offset(elf, sym) == ret) {
> > >                                       /* same offset, no problem */
> > >                                       continue;
> > >                               } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
> >
> > [...]
> >
Hengqi Chen March 13, 2025, 4:23 a.m. UTC | #4
Hi Andrii,

On Thu, Mar 13, 2025 at 2:47 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Mon, Mar 10, 2025 at 10:16 PM Hengqi Chen <hengqi.chen@gmail.com> wrote:
> >
> > Hi Yonghong,
> >
> > On Sat, Mar 8, 2025 at 2:48 PM Yonghong Song <yonghong.song@linux.dev> wrote:
> > >
> > >
> > >
> > > On 3/7/25 6:01 AM, Hengqi Chen wrote:
> > > > As reported on libbpf-rs issue([0]), the current implementation
> > > > may resolve symbol to a wrong offset and thus missing uprobe
> > > > event. Calculate the symbol offset from program header instead.
> > > > See the BCC implementation (which in turn used by bpftrace) and
> > > > the spec ([1]) for references.
> > > >
> > > >    [0]: https://github.com/libbpf/libbpf-rs/issues/1110
> > > >    [1]: https://refspecs.linuxfoundation.org/elf/
> > > >
> > > > Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com>
> > >
> > > Hengqi,
> > >
> > > There are some test failures in the CI. For example,
> > >    https://github.com/kernel-patches/bpf/actions/runs/13725803997/job/38392284640?pr=8631
> > >
> >
> > Yes, I've received an email from BPF CI.
> > It seems like the uprobe multi testcase is unhappy with this change.
> >
> > > Please take a look.
> > > Your below elf_sym_offset change matches some bcc implementation, but
> > > maybe maybe this is only under certain condition?
> > >
> >
> > Remove the `phdr.p_flags & PF_X` check fix the issue. Need more investigation.
> >
> > > Also, it would be great if you can add detailed description in commit message
> > > about what is the problem and why a different approach is necessary to
> > > fix the issue.
> > >
> >
> > Will do. Thanks.
> >
> > > > ---
> > > >   tools/lib/bpf/elf.c | 32 ++++++++++++++++++++++++--------
> > > >   1 file changed, 24 insertions(+), 8 deletions(-)
> > > >
> > > > diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
> > > > index 823f83ad819c..9b561c8d1eec 100644
> > > > --- a/tools/lib/bpf/elf.c
> > > > +++ b/tools/lib/bpf/elf.c
> > > > @@ -260,13 +260,29 @@ static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym
> > > >    * for shared libs) into file offset, which is what kernel is expecting
> > > >    * for uprobe/uretprobe attachment.
> > > >    * See Documentation/trace/uprobetracer.rst for more details. This is done
> > > > - * by looking up symbol's containing section's header and using iter's virtual
> > > > - * address (sh_addr) and corresponding file offset (sh_offset) to transform
> > > > + * by looking up symbol's containing program header and using its virtual
> > > > + * address (p_vaddr) and corresponding file offset (p_offset) to transform
> > > >    * sym.st_value (virtual address) into desired final file offset.
> > > >    */
> > > > -static unsigned long elf_sym_offset(struct elf_sym *sym)
> > > > +static unsigned long elf_sym_offset(Elf *elf, struct elf_sym *sym)
> > > >   {
> > > > -     return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
> > > > +     size_t nhdrs, i;
> > > > +     GElf_Phdr phdr;
> > > > +
> > > > +     if (elf_getphdrnum(elf, &nhdrs))
> > > > +             return -1;
> > > > +
> > > > +     for (i = 0; i < nhdrs; i++) {
> > > > +             if (!gelf_getphdr(elf, (int)i, &phdr))
> > > > +                     continue;
> > > > +             if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
> > > > +                     continue;
> > > > +             if (sym->sym.st_value >= phdr.p_vaddr &&
> > > > +                 sym->sym.st_value < (phdr.p_vaddr + phdr.p_memsz))
> > > > +                     return sym->sym.st_value - phdr.p_vaddr + phdr.p_offset;
>
> Hengqi,
>
> Can you please provide an example where existing code doesn't work? I think that
>
> sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset
>
> and
>
> sym->sym.st_value - phdr.p_vaddr + phdr.p_offset
>
>
> Should result in the same, and we don't need to search for a matching
> segment if we have an ELF symbol and its section. But maybe I'm
> mistaken, so can you please elaborate a bit?

The binary ([0]) provided in the issue shows some counterexamples.
I could't find an authoritative documentation describing this though.
A modified version ([1]) of this patch could pass the CI now.

  [0]: https://github.com/libbpf/libbpf-rs/issues/1110#issuecomment-2699221802
  [1]: https://github.com/kernel-patches/bpf/pull/8647

>
> > > > +     }
> > > > +
> > > > +     return -1;
> > > >   }
> > > >
> > > >   /* Find offset of function name in the provided ELF object. "binary_path" is
> > > > @@ -329,7 +345,7 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
> > > >
> > > >                       if (ret > 0) {
> > > >                               /* handle multiple matches */
> > > > -                             if (elf_sym_offset(sym) == ret) {
> > > > +                             if (elf_sym_offset(elf, sym) == ret) {
> > > >                                       /* same offset, no problem */
> > > >                                       continue;
> > > >                               } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
> > >
> > > [...]
> > >
Jiri Olsa March 13, 2025, 2:30 p.m. UTC | #5
On Thu, Mar 13, 2025 at 12:23:10PM +0800, Hengqi Chen wrote:

SNIP

> > > > >   tools/lib/bpf/elf.c | 32 ++++++++++++++++++++++++--------
> > > > >   1 file changed, 24 insertions(+), 8 deletions(-)
> > > > >
> > > > > diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
> > > > > index 823f83ad819c..9b561c8d1eec 100644
> > > > > --- a/tools/lib/bpf/elf.c
> > > > > +++ b/tools/lib/bpf/elf.c
> > > > > @@ -260,13 +260,29 @@ static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym
> > > > >    * for shared libs) into file offset, which is what kernel is expecting
> > > > >    * for uprobe/uretprobe attachment.
> > > > >    * See Documentation/trace/uprobetracer.rst for more details. This is done
> > > > > - * by looking up symbol's containing section's header and using iter's virtual
> > > > > - * address (sh_addr) and corresponding file offset (sh_offset) to transform
> > > > > + * by looking up symbol's containing program header and using its virtual
> > > > > + * address (p_vaddr) and corresponding file offset (p_offset) to transform
> > > > >    * sym.st_value (virtual address) into desired final file offset.
> > > > >    */
> > > > > -static unsigned long elf_sym_offset(struct elf_sym *sym)
> > > > > +static unsigned long elf_sym_offset(Elf *elf, struct elf_sym *sym)
> > > > >   {
> > > > > -     return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
> > > > > +     size_t nhdrs, i;
> > > > > +     GElf_Phdr phdr;
> > > > > +
> > > > > +     if (elf_getphdrnum(elf, &nhdrs))
> > > > > +             return -1;
> > > > > +
> > > > > +     for (i = 0; i < nhdrs; i++) {
> > > > > +             if (!gelf_getphdr(elf, (int)i, &phdr))
> > > > > +                     continue;
> > > > > +             if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
> > > > > +                     continue;
> > > > > +             if (sym->sym.st_value >= phdr.p_vaddr &&
> > > > > +                 sym->sym.st_value < (phdr.p_vaddr + phdr.p_memsz))
> > > > > +                     return sym->sym.st_value - phdr.p_vaddr + phdr.p_offset;
> >
> > Hengqi,
> >
> > Can you please provide an example where existing code doesn't work? I think that
> >
> > sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset
> >
> > and
> >
> > sym->sym.st_value - phdr.p_vaddr + phdr.p_offset
> >
> >
> > Should result in the same, and we don't need to search for a matching
> > segment if we have an ELF symbol and its section. But maybe I'm
> > mistaken, so can you please elaborate a bit?
> 
> The binary ([0]) provided in the issue shows some counterexamples.
> I could't find an authoritative documentation describing this though.
> A modified version ([1]) of this patch could pass the CI now.

yes, I tried that binary and it gives me different offsets

IIUC the symbol seems to be from .eh_frame_hdr (odd?) while the new logic
base it on offset of .text section.. I'm still not following that binary
layout completely.. will try to check on that more later today

jirka

> 
>   [0]: https://github.com/libbpf/libbpf-rs/issues/1110#issuecomment-2699221802
>   [1]: https://github.com/kernel-patches/bpf/pull/8647
> 
> >
> > > > > +     }
> > > > > +
> > > > > +     return -1;
> > > > >   }
> > > > >
> > > > >   /* Find offset of function name in the provided ELF object. "binary_path" is
> > > > > @@ -329,7 +345,7 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
> > > > >
> > > > >                       if (ret > 0) {
> > > > >                               /* handle multiple matches */
> > > > > -                             if (elf_sym_offset(sym) == ret) {
> > > > > +                             if (elf_sym_offset(elf, sym) == ret) {
> > > > >                                       /* same offset, no problem */
> > > > >                                       continue;
> > > > >                               } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
> > > >
> > > > [...]
> > > >
>
diff mbox series

Patch

diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
index 823f83ad819c..9b561c8d1eec 100644
--- a/tools/lib/bpf/elf.c
+++ b/tools/lib/bpf/elf.c
@@ -260,13 +260,29 @@  static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym
  * for shared libs) into file offset, which is what kernel is expecting
  * for uprobe/uretprobe attachment.
  * See Documentation/trace/uprobetracer.rst for more details. This is done
- * by looking up symbol's containing section's header and using iter's virtual
- * address (sh_addr) and corresponding file offset (sh_offset) to transform
+ * by looking up symbol's containing program header and using its virtual
+ * address (p_vaddr) and corresponding file offset (p_offset) to transform
  * sym.st_value (virtual address) into desired final file offset.
  */
-static unsigned long elf_sym_offset(struct elf_sym *sym)
+static unsigned long elf_sym_offset(Elf *elf, struct elf_sym *sym)
 {
-	return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
+	size_t nhdrs, i;
+	GElf_Phdr phdr;
+
+	if (elf_getphdrnum(elf, &nhdrs))
+		return -1;
+
+	for (i = 0; i < nhdrs; i++) {
+		if (!gelf_getphdr(elf, (int)i, &phdr))
+			continue;
+		if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
+			continue;
+		if (sym->sym.st_value >= phdr.p_vaddr &&
+		    sym->sym.st_value < (phdr.p_vaddr + phdr.p_memsz))
+			return sym->sym.st_value - phdr.p_vaddr + phdr.p_offset;
+	}
+
+	return -1;
 }
 
 /* Find offset of function name in the provided ELF object. "binary_path" is
@@ -329,7 +345,7 @@  long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
 
 			if (ret > 0) {
 				/* handle multiple matches */
-				if (elf_sym_offset(sym) == ret) {
+				if (elf_sym_offset(elf, sym) == ret) {
 					/* same offset, no problem */
 					continue;
 				} else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
@@ -346,7 +362,7 @@  long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
 				}
 			}
 
-			ret = elf_sym_offset(sym);
+			ret = elf_sym_offset(elf, sym);
 			last_bind = cur_bind;
 		}
 		if (ret > 0)
@@ -445,7 +461,7 @@  int elf_resolve_syms_offsets(const char *binary_path, int cnt,
 			goto out;
 
 		while ((sym = elf_sym_iter_next(&iter))) {
-			unsigned long sym_offset = elf_sym_offset(sym);
+			unsigned long sym_offset = elf_sym_offset(elf_fd.elf, sym);
 			int bind = GELF_ST_BIND(sym->sym.st_info);
 			struct symbol *found, tmp = {
 				.name = sym->name,
@@ -534,7 +550,7 @@  int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern,
 			if (err)
 				goto out;
 
-			offsets[cnt++] = elf_sym_offset(sym);
+			offsets[cnt++] = elf_sym_offset(elf_fd.elf, sym);
 		}
 
 		/* If we found anything in the first symbol section,