Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf record --call-graph dwarf does not support ld.lld's default --rosegment -z noseparate-code layout #53156

Closed
Kha opened this issue Jan 12, 2022 · 6 comments
Labels

Comments

@Kha
Copy link

Kha commented Jan 12, 2022

I don't know if this is a bug in lld, perf, or libunwind, only that it doesn't happen with ld: when I link with lld, perf DWARF stack traces are incomplete. In a more complex application, this leads to stack traces being much shorter (and thus less helpful) than when linking with ld, but the following MWE demonstrates a difference in at least the _start symbol:

$ cat foo.c
#include <stdio.h>

void foo(int d) { int s = 0; for (int i = 0; i < 1000000; i++) { printf(""); s += i / d; } }
int main() { foo(100000); }

$ perf --version
perf version 5.10.89

$ clang -v foo.c && perf record --call-graph dwarf ./a.out && perf report | head -n 30
clang -v foo.c && perf record --call-graph dwarf ./a.out && perf report | head -n 30
clang version 13.0.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /nix/store/ry469fxcshgi8k6mavw5623ps4wyc2dp-clang-13.0.0/bin
Found candidate GCC installation: /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0
Found candidate GCC installation: /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib64/gcc/x86_64-unknown-linux-gnu/10.3.0
Selected GCC installation: /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib64/gcc/x86_64-unknown-linux-gnu/10.3.0
Candidate multilib: .;@m64
Selected multilib: .;@m64
 "/nix/store/ry469fxcshgi8k6mavw5623ps4wyc2dp-clang-13.0.0/bin/clang-13" -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -mrelax-all --mrelax-relocations -disable-free -disable-llvm-verifier -discard-value-names -main-file-name foo.c -mrelocation-model static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/tmp -nostdsysteminc -resource-dir /nix/store/sjyb4lh03rxfy459i4086k9q897rglgf-clang-wrapper-13.0.0/resource-root -idirafter /nix/store/12z1nc5fmif2z7wian28n55j61acbmny-glibc-2.33-59-dev/include -internal-isystem /nix/store/sjyb4lh03rxfy459i4086k9q897rglgf-clang-wrapper-13.0.0/resource-root/include -fdebug-compilation-dir=/tmp -ferror-limit 19 -fgnuc-version=4.2.1 -fcolor-diagnostics -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/foo-a74ba0.o -x c foo.c
clang -cc1 version 13.0.0 based upon LLVM 13.0.0 default target x86_64-unknown-linux-gnu
#include "..." search starts here:
#include <...> search starts here:
 /nix/store/sjyb4lh03rxfy459i4086k9q897rglgf-clang-wrapper-13.0.0/resource-root/include
 /nix/store/12z1nc5fmif2z7wian28n55j61acbmny-glibc-2.33-59-dev/include
End of search list.
 "/nix/store/sjyb4lh03rxfy459i4086k9q897rglgf-clang-wrapper-13.0.0/bin/ld" --eh-frame-hdr -m elf_x86_64 -o a.out /nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib/crt1.o /nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib/crti.o /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0/crtbegin.o -L/nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib -L/nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0 -L/nix/store/m756011mkf1i0ki78i8y6ac3gf8qphvi-gcc-10.3.0-lib/x86_64-unknown-linux-gnu/lib -L/nix/store/8prp770hvr3cgaab7wn61dq3llc07ryg-clang-13.0.0-lib/lib -L/nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib64/gcc/x86_64-unknown-linux-gnu/10.3.0 -L/nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib64/gcc/x86_64-unknown-linux-gnu/10.3.0/../../../../lib64 -L/nix/store/ry469fxcshgi8k6mavw5623ps4wyc2dp-clang-13.0.0/bin/../lib -dynamic-linker=/nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib/ld-linux-x86-64.so.2 /tmp/foo-a74ba0.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0/crtend.o /nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib/crtn.o
[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.430 MB perf.data (53 samples) ]
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 53  of event 'cycles:u'
# Event count (approx.): 47036836
#
# Children      Self  Command  Shared Object     Symbol
# ........  ........  .......  ................  .................................
#
    99.96%     0.00%  a.out    a.out             [.] _start
            |
            ---_start
               __libc_start_main
               main
               foo
               |
                --91.78%--__printf (inlined)
                          |
                           --84.62%--__vfprintf_internal
                                     |
                                     |--6.46%--_IO_new_file_xsputn (inlined)
                                     |
                                      --2.45%--__strchrnul_avx2

    99.96%     0.00%  a.out    libc-2.33.so      [.] __libc_start_main
            |
            ---__libc_start_main
               main
               
$ clang -v -fuse-ld=lld foo.c && perf record --call-graph dwarf ./a.out && perf report | head -n 30
clang version 13.0.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /nix/store/ry469fxcshgi8k6mavw5623ps4wyc2dp-clang-13.0.0/bin
Found candidate GCC installation: /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0
Found candidate GCC installation: /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib64/gcc/x86_64-unknown-linux-gnu/10.3.0
Selected GCC installation: /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib64/gcc/x86_64-unknown-linux-gnu/10.3.0
Candidate multilib: .;@m64
Selected multilib: .;@m64
 "/nix/store/ry469fxcshgi8k6mavw5623ps4wyc2dp-clang-13.0.0/bin/clang-13" -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -mrelax-all --mrelax-relocations -disable-free -disable-llvm-verifier -discard-value-names -main-file-name foo.c -mrelocation-model static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/tmp -nostdsysteminc -resource-dir /nix/store/sjyb4lh03rxfy459i4086k9q897rglgf-clang-wrapper-13.0.0/resource-root -idirafter /nix/store/12z1nc5fmif2z7wian28n55j61acbmny-glibc-2.33-59-dev/include -internal-isystem /nix/store/sjyb4lh03rxfy459i4086k9q897rglgf-clang-wrapper-13.0.0/resource-root/include -fdebug-compilation-dir=/tmp -ferror-limit 19 -fgnuc-version=4.2.1 -fcolor-diagnostics -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/foo-266589.o -x c foo.c
clang -cc1 version 13.0.0 based upon LLVM 13.0.0 default target x86_64-unknown-linux-gnu
#include "..." search starts here:
#include <...> search starts here:
 /nix/store/sjyb4lh03rxfy459i4086k9q897rglgf-clang-wrapper-13.0.0/resource-root/include
 /nix/store/12z1nc5fmif2z7wian28n55j61acbmny-glibc-2.33-59-dev/include
End of search list.
 "/nix/store/3y82ady9hh6pjyap4l42z638z25m4xxp-llvm-binutils-13.0.0/bin/ld.lld" --eh-frame-hdr -m elf_x86_64 -o a.out /nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib/crt1.o /nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib/crti.o /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0/crtbegin.o -L/nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib -L/nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0 -L/nix/store/m756011mkf1i0ki78i8y6ac3gf8qphvi-gcc-10.3.0-lib/x86_64-unknown-linux-gnu/lib -L/nix/store/8prp770hvr3cgaab7wn61dq3llc07ryg-clang-13.0.0-lib/lib -L/nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib64/gcc/x86_64-unknown-linux-gnu/10.3.0 -L/nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib64/gcc/x86_64-unknown-linux-gnu/10.3.0/../../../../lib64 -L/nix/store/ry469fxcshgi8k6mavw5623ps4wyc2dp-clang-13.0.0/bin/../lib -dynamic-linker=/nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib/ld-linux-x86-64.so.2 /tmp/foo-266589.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /nix/store/mrqrvina0lfgrvdzfyri7sw9vxy6pyms-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0/crtend.o /nix/store/vjq3q7dq8vmc13c3py97v27qwizvq7fd-glibc-2.33-59/lib/crtn.o
[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.438 MB perf.data (54 samples) ]
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 54  of event 'cycles:u'
# Event count (approx.): 47911504
#
# Children      Self  Command  Shared Object     Symbol
# ........  ........  .......  ................  .................................
#
    99.96%     0.00%  a.out    [unknown]         [.] 0xffffffffffffffff
            |
            ---0xffffffffffffffff
               __libc_start_main
               main
               foo
               |
                --82.89%--__printf (inlined)
                          |
                           --67.62%--__vfprintf_internal
                                     |
                                     |--6.38%--_IO_new_file_xsputn (inlined)
                                     |
                                      --2.41%--__strchrnul_avx2

    99.96%     0.00%  a.out    libc-2.33.so      [.] __libc_start_main
            |
            ---__libc_start_main
               main
@travisdowns
Copy link

I ran into this same issue: perf report backtraces stopped working when using dwarf unwind and it happened when we started using -fuse-ld=lld.

@travisdowns
Copy link

travisdowns commented May 24, 2022

For what it's worth I tested ld, lld, gold, and mold and only lld exhibited this behavior. For each linker the exact same .o was provided, only the -fuse-ld=XXX varied.

@MaskRay
Copy link
Member

MaskRay commented May 25, 2022

I think Linux perf does not support ld.lld's default --rosegment -z noseparate-code layout.

It seems to support ld.lld's --no-rosegment layout. I see main and foo with this command: clang -no-pie -fno-pie -fuse-ld=lld -Wl,--no-rosegment foo.c && perf record --call-graph dwarf ./a.out && perf report | head -n 30.

In addition, it seems to work with ld.lld's -z separate-code:

ld.lld => fail
ld.lld --no-rosegment => ok
ld.lld -z separate-code => ok

ld.bfd -z separate-code (default) => ok
ld.bfd -z noseparate-code => ok

It seems that for .eh_frame_hdr and .text, if their virtual address difference does not equal their file offset difference, perf will go wrong. This happens with lld when both R PT_LOAD and RX PT_LOAD are produced (the default --rosegment), and they overlap in file offsets (the default -z noseparate-code).

So I wanted to suggest that you ask on linux-perf-users and found that you had posted https://lore.kernel.org/linux-perf-users/CAOBGo4zjkcX=ZQm1uYRDe9EjYqsTCyZY-Gf1C4XqMNYGFCcF+Q@mail.gmail.com/T/#u
Can you please CC llvm@lists.linux.dev and me :) Thanks.

@captain5050
Copy link

I've been poking at this in various ways, I think the bug is actually in libunwind. Here are the mmaps that perf sees:

$ perf script -D -i perf.data
...
812805353577306 0x4d8 [0xa0]: PERF_RECORD_MMAP2 3731245/3731245: [0x200000(0x1000) @ 0 fe:01 6701626 114115094]: r--p .../a.out

0x578@perf.data [0xa0]: event: 10
.
. ... raw event: size 160 bytes
.  0000:  0a 00 00 00 02 00 a0 00 2d ef 38 00 2d ef 38 00  ........-.8.-.8.
.  0010:  00 10 20 00 00 00 00 00 00 10 00 00 00 00 00 00  .. .............
.  0020:  00 00 00 00 00 00 00 00 fe 00 00 00 01 00 00 00  ................
...
.  0090:  2d ef 38 00 2d ef 38 00 1e 4c 5f fe 3d e3 02 00  -.8.-.8..L_.=...

812805353589790 0x578 [0xa0]: PERF_RECORD_MMAP2 3731245/3731245: [0x201000(0x1000) @ 0 fe:01 6701626 114115094]: r-xp .../a.out

0x618@perf.data [0xa0]: event: 10
.
. ... raw event: size 160 bytes
.  0000:  0a 00 00 00 02 20 a0 00 2d ef 38 00 2d ef 38 00  ..... ..-.8.-.8.
.  0010:  00 20 20 00 00 00 00 00 00 10 00 00 00 00 00 00  .  .............
...
.  0090:  2d ef 38 00 2d ef 38 00 b0 5e 5f fe 3d e3 02 00  -.8.-.8..^_.=...

812805353594544 0x618 [0xa0]: PERF_RECORD_MMAP2 3731245/3731245: [0x202000(0x1000) @ 0 fe:01 6701626 114115094]: rw-p .../a.out

0x6b8@perf.data [0xa0]: event: 10
.
. ... raw event: size 160 bytes
.  0000:  0a 00 00 00 02 20 a0 00 2d ef 38 00 2d ef 38 00  ..... ..-.8.-.8.
.  0010:  00 30 20 00 00 00 00 00 00 10 00 00 00 00 00 00  .0 .............
...
...

so that's 3 1 page mmaps at 0x200000, 0x201000 and 0x202000 but notice that the offset (after the @) is always 0. The firtst mmap has .eh_frame_hdr while the 2nd has .text:

$ readelf -a -W a.out
...
Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        00000000002002a8 0002a8 00001c 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            00000000002002c4 0002c4 000020 00   A  0   0  4
  [ 3] .note.gnu.build-id NOTE            00000000002002e4 0002e4 000018 00   A  0   0  4
  [ 4] .dynsym           DYNSYM          0000000000200300 000300 0000a8 18   A  9   1  8
  [ 5] .gnu.version      VERSYM          00000000002003a8 0003a8 00000e 02   A  4   0  2
  [ 6] .gnu.version_r    VERNEED         00000000002003b8 0003b8 000020 00   A  9   1  4
  [ 7] .gnu.hash         GNU_HASH        00000000002003d8 0003d8 000020 00   A  4   0  8
  [ 8] .hash             HASH            00000000002003f8 0003f8 000040 04   A  4   0  4
  [ 9] .dynstr           STRTAB          0000000000200438 000438 00007b 00   A  0   0  1
  [10] .rela.dyn         RELA            00000000002004b8 0004b8 000048 18   A  4   0  8
  [11] .rela.plt         RELA            0000000000200500 000500 000018 18  AI  4  25  8
  [12] .rodata           PROGBITS        0000000000200518 000518 000005 00 AMS  0   0  4
  [13] .eh_frame_hdr     PROGBITS        0000000000200520 000520 00003c 00   A  0   0  4
  [14] .eh_frame         PROGBITS        0000000000200560 000560 0000f4 00   A  0   0  8
  [15] .text             PROGBITS        0000000000201660 000660 0001d1 00  AX  0   0 16
  [16] .init             PROGBITS        0000000000201834 000834 000017 00  AX  0   0  4
  [17] .fini             PROGBITS        000000000020184c 00084c 000009 00  AX  0   0  4
  [18] .plt              PROGBITS        0000000000201860 000860 000020 00  AX  0   0 16
  [19] .fini_array       FINI_ARRAY      0000000000202880 000880 000008 08  WA  0   0  8
  [20] .init_array       INIT_ARRAY      0000000000202888 000888 000008 08  WA  0   0  8
  [21] .dynamic          DYNAMIC         0000000000202890 000890 000190 10  WA  9   0  8
  [22] .got              PROGBITS        0000000000202a20 000a20 000010 00  WA  0   0  8
  [23] .data             PROGBITS        0000000000203a30 000a30 000010 00  WA  0   0  8
  [24] .tm_clone_table   PROGBITS        0000000000203a40 000a40 000000 00  WA  0   0  8
  [25] .got.plt          PROGBITS        0000000000203a40 000a40 000020 00  WA  0   0  8
  [26] .bss              NOBITS          0000000000203a60 000a60 000010 00  WA  0   0  8
  [27] .comment          PROGBITS        0000000000000000 000a60 00005f 01  MS  0   0  1
  [28] .symtab           SYMTAB          0000000000000000 000ac0 000378 18     30  23  8
  [29] .shstrtab         STRTAB          0000000000000000 000e38 00011e 00      0   0  1
  [30] .strtab           STRTAB          0000000000000000 000f56 00020e 00      0   0  1
...

The perf code tries to make the segbase value holding the .eh_frame_hdr offset relative to the code in .text creating an address in the 0x201000 range, not 0x200000. Adjusting this for just the a.out case is sufficient to get correct stack traces dynamically and statically linked. The problem is that detected the a.out case is something I've hacked together:

diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 3a9fd4d389b5..85d6f4ba31fc 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -197,7 +197,10 @@ struct dso {
                u64              file_size;
                struct list_head open_entry;
                u64              debug_frame_offset;
+               u64              eh_frame_hdr_addr;
                u64              eh_frame_hdr_offset;
+               u64              text_addr;
+               u64              text_offset;
        } data;
        /* bpf prog information */
        struct {
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 41e29fc7648a..53f7bbdee33f 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -169,30 +169,42 @@ static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
        __v;                                                    \
        })
 
-static u64 elf_section_offset(int fd, const char *name)
+static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset)
 {
        Elf *elf;
        GElf_Ehdr ehdr;
        GElf_Shdr shdr;
-       u64 offset = 0;
+       int ret = -1;
 
        elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
        if (elf == NULL)
-               return 0;
-
-       do {
-               if (gelf_getehdr(elf, &ehdr) == NULL)
-                       break;
+               return -1;
 
-               if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
-                       break;
+       if (gelf_getehdr(elf, &ehdr) == NULL)
+               goto out_err;
 
-               offset = shdr.sh_offset;
-       } while (0);
+       if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
+               goto out_err;
 
+       *address = shdr.sh_addr;
+       *offset = shdr.sh_offset;
+       ret = 0;
+out_err:
        elf_end(elf);
+       return ret;
+}
+
+#ifndef NO_LIBUNWIND_DEBUG_FRAME
+static u64 elf_section_offset(int fd, const char *name)
+{
+       u64 address, offset;
+
+       if (elf_section_address_and_offset(fd, name, &address, &offset))
+               return 0;
+
        return offset;
 }
+#endif
 
 #ifndef NO_LIBUNWIND_DEBUG_FRAME
 static int elf_is_exec(int fd, const char *name)
@@ -248,8 +260,7 @@ struct eh_frame_hdr {
 } __packed;
 
 static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
-                              u64 offset, u64 *table_data, u64 *segbase,
-                              u64 *fde_count)
+                              u64 offset, u64 *table_data, u64 *fde_count)
 {
        struct eh_frame_hdr hdr;
        u8 *enc = (u8 *) &hdr.enc;
@@ -265,35 +276,38 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
        dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
 
        *fde_count  = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
-       *segbase    = offset;
        *table_data = (enc - (u8 *) &hdr) + offset;
        return 0;
}
 
 static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
-                                    u64 *table_data, u64 *segbase,
-                                    u64 *fde_count)
+                                    u64 *table_data_offset, u64 *fde_count)
 {
-       int ret = -EINVAL, fd;
-       u64 offset = dso->data.eh_frame_hdr_offset;
+       if (dso->data.eh_frame_hdr_offset == 0) {
+               int ret, fd = dso__data_get_fd(dso, machine);
 
-       if (offset == 0) {
-               fd = dso__data_get_fd(dso, machine);
                if (fd < 0)
                        return -EINVAL;
 
                /* Check the .eh_frame section for unwinding info */
-               offset = elf_section_offset(fd, ".eh_frame_hdr");
-               dso->data.eh_frame_hdr_offset = offset;
+               ret = elf_section_address_and_offset(fd, ".eh_frame_hdr",
+                                                    &dso->data.eh_frame_hdr_addr,
+                                                    &dso->data.eh_frame_hdr_offset);
+               if (ret == 0) {
+                       ret = elf_section_address_and_offset(fd, ".text",
+                                                            &dso->data.text_addr,
+                                                            &dso->data.text_offset);
+               }
                dso__data_put_fd(dso);
+               if (ret)
+                       return ret;
        }
 
-       if (offset)
-               ret = unwind_spec_ehframe(dso, machine, offset,
-                                         table_data, segbase,
-                                         fde_count);
-
-       return ret;
+       if (dso->data.eh_frame_hdr_offset == 0)
+               return -EINVAL;
+       
+       return unwind_spec_ehframe(dso, machine, dso->data.eh_frame_hdr_offset,
+                                  table_data_offset, fde_count);
 }
 }
 
 #ifndef NO_LIBUNWIND_DEBUG_FRAME
@@ -378,7 +392,7 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
        struct unwind_info *ui = arg;
        struct map *map;
        unw_dyn_info_t di;
-       u64 table_data, segbase, fde_count;
+       u64 table_data, fde_count;
        int ret = -EINVAL;
 
        map = find_map(ip, ui);
@@ -388,18 +402,24 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
        pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
 
        /* Check the .eh_frame section for unwinding info */
-       if (!read_unwind_spec_eh_frame(map->dso, ui->machine,
-                                      &table_data, &segbase, &fde_count)) {
+       if (!read_unwind_spec_eh_frame(map->dso, ui->machine, &table_data, &fde_count)) {
                memset(&di, 0, sizeof(di));
                di.format   = UNW_INFO_FORMAT_REMOTE_TABLE;
                di.start_ip = map->start;
                di.end_ip   = map->end;
-               di.u.rti.segbase    = map->start + segbase - map->pgoff;
-               di.u.rti.table_data = map->start + table_data - map->pgoff;
-               di.u.rti.table_len  = fde_count * sizeof(struct table_entry)
-                                     / sizeof(unw_word_t);
-               ret = dwarf_search_unwind_table(as, ip, &di, pi,
-                                               need_unwind_info, arg);
+               if (map->dso->data.eh_frame_hdr_addr == map->dso->data.eh_frame_hdr_offset) {
+                       di.u.rti.segbase = map->start - map->pgoff +
+                               map->dso->data.eh_frame_hdr_offset;
+                       di.u.rti.table_data = map->start - map->pgoff +
+                               table_data;
+               } else {
+                       di.u.rti.segbase = map->dso->data.eh_frame_hdr_addr;
+                       di.u.rti.table_data = map->dso->data.eh_frame_hdr_addr - map->dso->data.eh_frame_hdr_offset
+                               + table_data;
+               }
+               di.u.rti.table_len  = fde_count * sizeof(struct table_entry) / sizeof(unw_word_t);
+               ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+               pr_info("unwind dso '%s'=%d\n", map->dso->name, ret);
        }
 
 #ifndef NO_LIBUNWIND_DEBUG_FRAME

So the question is what's the correct way to set up segbase (and the very related table_data)? Looking at libunwind there appears to be assumptions about segbase being .text relative and here but for ldd the .eh_frame_hdr is in a different section. I think libunwind can be made to work if it uses ELF section information rather then arithmetic on offsets, but fixing libunwind is a project bigger than what I wanted to take on.

@MaskRay MaskRay changed the title lld breaks perf DWARF backtraces perf record --call-graph dwarf does not support ld.lld's default --rosegment -z noseparate-code layout May 26, 2022
@captain5050
Copy link

My change as a patch:
maskray.patch.txt

@MaskRay
Copy link
Member

MaskRay commented Jun 5, 2022

Fixed for Linux 5.19 (see ClangBuiltLinux/linux#1646)

@MaskRay MaskRay closed this as completed Jun 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants