Two cases of DWARF unwind info and instructions mismatch on x86-64
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Hi. So last few months I was working on new backtrace library (http://github.com/gperftools/aw-backtrace). As part of that I was exercising various tests and found what looks like 2 different cases where LLVM produces CFI unwind info slightly out of line with actual instructions.
Perhaps the simplest way to diagnose it is with http://github.com/gperftools/unwind-check which I also just released.
I have spot checked (as of about a week ago) that main branch of LLVM still has both bugs. And they're seemingly present even as back as llvm-17 (I haven't checked beyond that).
Note, I am filing this as one ticket, even though I think it does looks like 2 distinct bugs, because I think communication is a little easier this way. Feel free to split/redirect/etc as you see fit.
So quoting from my tool on Debian-shipped libllvm which is built with itself. I've seen the same issues with locally compiled llvm and clang, or when clang or rust compiles something else, e.g. sqlite3.
Posting just singular findings per issue from `$ ./bazel-bin/unwind-check --only_mismatch /usr/lib/x86_64-linux-gnu/libLLVM-21.so`. There are 80 total for this .so file (and I haven't inspected them all, but I have inspected many, and all I inspected are one of the 2).
With tool reporting which functions/fdes are wrong, it should be trivial-ish to check manually. I.e. you don't have to trust me. Either matching e.g. readelf -W -wf output to asm, or GDB single stepping through that pair and observing how, indeed, backtrace is broken just in that window.
First case is what looks like `add $8, %rsp` instruction pulled before the test/jcc without matching unwind info update. I.e. we see add $8, %rsp which lacks update of CFA offset after it. Instead CFA offset is updated after cmp/jz instructions.
```
MISMATCH basic_set_opt_lp [0x81c56e0 - 0x81c57bf)
0x81c575e (build-llvm/tools/clang/stage2-bins/polly/lib/External/isl/isl_lp.c:0): declared CFA is rsp+64, but the code leaves rsp at CFA-56
0x81c574c mov %eax, %ebp cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c574e mov %r12, %rdi cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5751 call 0x000000000827EFF0 cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5756 cmp $-0x01, %ebp cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5759 jz 0x00000000081C5776 cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c575b mov %r14, %rdi cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
-> 0x81c575e add $0x08, %rsp cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5762 cmp $0x02, %ebp cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5765 jnz 0x00000000081C579D cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5767 pop %rbx cfa=rsp+56 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5768 pop %r12 cfa=rsp+48 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5765 (build-llvm/tools/clang/stage2-bins/polly/lib/External/isl/isl_lp.c:236): declared CFA is rsp+56, but the code leaves rsp at CFA-64 [and at 2 more addresses]
0x81c5751 call 0x000000000827EFF0 cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5756 cmp $-0x01, %ebp cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5759 jz 0x00000000081C5776 cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c575b mov %r14, %rdi cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c575e add $0x08, %rsp cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5762 cmp $0x02, %ebp cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
-> 0x81c5765 jnz 0x00000000081C579D cfa=rsp+64 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5767 pop %rbx cfa=rsp+56 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c5768 pop %r12 cfa=rsp+48 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c576a pop %r13 cfa=rsp+40 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x81c576c pop %r14 cfa=rsp+32 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
```
the second set of bugs look like pushing some register as function argument while loading something into it. One example is here. Effect of push %rbp is different CFA offset, but CFI isn't updated for one instruction.
```
MISMATCH llvm::jitlink::MachOLinkGraphBuilder::createStandardGraphSymbol(llvm::jitlink::MachOLinkGraphBuilder::NormalizedSymbol&, llvm::jitlink::Block&, unsigned long, bool, bool, bool) [0x659b560 - 0x659b779)
0x659b5ca (build-llvm/tools/clang/stage2-bins/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp:592): declared CFA is rsp+136, but the code leaves rsp at CFA-144
0x659b5b9 movzx %r8b, %ebp cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5bd mov %rdx, %rsi cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5c0 mov %r13, %rdx cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5c3 mov %rax, %r8 cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5c6 mov %r12, %r9 cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5c9 push %rbx cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
-> 0x659b5ca push %rbp cfa=rsp+136 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5cb mov $0x1FFFFFFFFFFFFFF, %rbp cfa=rsp+136 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5d5 push %r11 cfa=rsp+144 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5d7 push %r10 cfa=rsp+152 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5d9 call 0x000000000658AF40 cfa=rsp+160 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5cb (build-llvm/tools/clang/stage2-bins/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp:592): declared CFA is rsp+144, but the code leaves rsp at CFA-136
0x659b5bd mov %rdx, %rsi cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5c0 mov %r13, %rdx cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5c3 mov %rax, %r8 cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5c6 mov %r12, %r9 cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5c9 push %rbx cfa=rsp+128 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5ca push %rbp cfa=rsp+136 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
-> 0x659b5cb mov $0x1FFFFFFFFFFFFFF, %rbp cfa=rsp+136 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5d5 push %r11 cfa=rsp+144 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5d7 push %r10 cfa=rsp+152 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5d9 call 0x000000000658AF40 cfa=rsp+160 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
0x659b5de add $0x20, %rsp cfa=rsp+160 rbx=[CFA-56] rbp=[CFA-16] r12=[CFA-48] r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] ra=[CFA-8]
```
Feel free to run the tool yourself. Or just ask me how can I help you to diagnose it in any other way.
Contributor guide
Research direction
Start by running bazel-bin/unwind-check --only_mismatch against the reported libLLVM-21.so and compare its findings with readelf -W -wf output or GDB single-stepping. Inspect the examples in isl_lp.c and llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp:592. Done means identifying why the generated DWARF CFI differs from the x86-64 instructions in both reported cases and confirming the mismatches are resolved.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100