[llvm-bugs] [Bug 48186] New: libunwind does not support rax as CFA register
via llvm-bugs
llvm-bugs at lists.llvm.org
Sun Nov 15 08:29:05 PST 2020
https://bugs.llvm.org/show_bug.cgi?id=48186
Bug ID: 48186
Summary: libunwind does not support rax as CFA register
Product: libc++abi
Version: unspecified
Hardware: PC
OS: Linux
Status: NEW
Severity: normal
Priority: P
Component: All Bugs
Assignee: unassignedbugs at nondot.org
Reporter: man2gm at gmail.com
CC: llvm-bugs at lists.llvm.org, mclow.lists at gmail.com
libunwind does not understand the
.cfi_def_cfa_register %rax
instruction.
This is because register rax has number zero.
The code in DwarfInstructions.hpp in getCFA function is the following:
https://github.com/llvm/llvm-project/blob/master/libunwind/src/DwarfInstructions.hpp#L66
The value of prolog.cfaRegister comes from:
https://github.com/llvm/llvm-project/blob/master/libunwind/src/DwarfParser.hpp#L581
If libunwind was build with assertions, assertion will be triggered.
If libunwind was build without assertions, undefined behaviour will happen due
to __builtin_unreachable, and it goes down to the branch where cfaExpression is
evaluated, then segfaults in getULEB128 (by nullptr dereference).
AFAIK, the usage of rax for CFA register is legitimate.
Surprisingly it is not used by the code generated by clang or gcc.
But it happens to be present in some manually written assembly code.
Note that both:
- gcc's builtin unwinder from libgcc;
- HP's "nongnu" libunwind;
work correctly in this case.
Example 1 is OpenSSL and the same in BoringSSL:
https://github.com/openssl/openssl/pull/9624#issuecomment-566309194
It generates asm code during build. The code of crypto/sha/asm/sha256-x86_64.s,
function sha256_block_data_order_ssse3 starts with:
sha256_block_data_order_ssse3:
.cfi_startproc
.Lssse3_shortcut:
movq %rsp,%rax
.cfi_def_cfa_register %rax
pushq %rbx
.cfi_offset %rbx,-16
pushq %rbp
.cfi_offset %rbp,-24
pushq %r12
.cfi_offset %r12,-32
pushq %r13
.cfi_offset %r13,-40
pushq %r14
.cfi_offset %r14,-48
pushq %r15
.cfi_offset %r15,-56
shlq $4,%rdx
subq $96,%rsp
leaq (%rsi,%rdx,4),%rdx
andq $-64,%rsp
movq %rdi,64+0(%rsp)
movq %rsi,64+8(%rsp)
movq %rdx,64+16(%rsp)
movq %rax,88(%rsp)
.cfi_escape 0x0f,0x06,0x77,0xd8,0x00,0x06,0x23,0x08
If we do asynchronous unwinding from somewhere between lines
.cfi_def_cfa_register and .cfi_escape, segfault or assertion will happen.
To trigger the error you need to create a program that will do asynchronous
unwinding quite frequently. This is possible by using timer signals. Note that
libunwind functions are not signal-safe (and neither gcc's or HP's libunwind
are). But it is unrelated from this issue.
Minimal reproducing example:
unwind_test.cpp:
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <cstdint>
#include <iostream>
typedef enum {
_URC_NO_REASON = 0,
_URC_OK = 0,
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
_URC_FATAL_PHASE2_ERROR = 2,
_URC_FATAL_PHASE1_ERROR = 3,
_URC_NORMAL_STOP = 4,
_URC_END_OF_STACK = 5,
_URC_HANDLER_FOUND = 6,
_URC_INSTALL_CONTEXT = 7,
_URC_CONTINUE_UNWIND = 8,
} _Unwind_Reason_Code;
typedef struct _Unwind_Context _Unwind_Context;
typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, void
*);
extern "C" _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
extern "C" void test();
[[noreturn]] void __attribute__((__noinline__)) loop()
{
while (true)
test();
}
size_t i = 0;
void handler(int, siginfo_t * info, void *)
{
if (info && info->si_overrun)
return;
++i;
if (i % 1000 == 0)
(void)write(2, ".", 1);
size_t num_frames = 0;
_Unwind_Backtrace([](struct _Unwind_Context *, void * ptr) {
++*static_cast<size_t*>(ptr); return _URC_NO_REASON; }, &num_frames);
if (i % 10000 == 0)
std::cerr << num_frames << "\n";
}
[[noreturn]] void error(const char * message)
{
std::cerr << message << "\n";
exit(1);
}
void setupTimer()
{
struct sigaction sa{};
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
if (sigemptyset(&sa.sa_mask))
error("Failed to clean signal mask for query profiler");
if (sigaddset(&sa.sa_mask, SIGPROF))
error("Failed to add signal to mask for query profiler");
if (sigaction(SIGPROF, &sa, nullptr))
error("Failed to setup signal handler for query profiler");
struct sigevent sev {};
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGPROF;
timer_t timer_id = nullptr;
if (timer_create(CLOCK_MONOTONIC, &sev, &timer_id))
error("Failed to create thread timer");
struct timespec interval{.tv_sec = 0, .tv_nsec = 50000};
struct timespec offset{.tv_sec = 0, .tv_nsec = 1};
struct itimerspec timer_spec = {.it_interval = interval, .it_value =
offset};
if (timer_settime(timer_id, 0, &timer_spec, nullptr))
error("Failed to set thread timer period");
}
int main(int, char **)
{
setupTimer();
loop();
}
test.s:
.text
.globl test
.type test, at function
.align 64
test:
.cfi_startproc
movq %rsp,%rax
.cfi_def_cfa_register %rax
movq $1000000, %rbx
.LOOP:
subq $1, %rbx
cmpq %rbx, %rbx
jne .LOOP
movq %rax,%rsp
.cfi_def_cfa_register %rsp
retq
.cfi_endproc
.size test,.-test
1. Compile with LLVM's libunwind:
Prepare the llvm-project repository, create the llvm-build directory, build
libunwind simply by
cmake ../llvm-project/libunwind && make
$ clang++ -g -O3 -pthread -ldl -lrt -fno-pic -fno-pie unwind_test.cpp test.s
-I~/work/llvm-project/libunwind/include/ -Wl,-Bstatic -L ~/work/llvm-build/lib/
-lunwind -Wl,-Bdynamic && ./a.out
a.out:
/home/milovidov/work/llvm-project/libunwind/src/DwarfInstructions.hpp:72:
static libunwind::DwarfInstructions<A, R>::pint_t
libunwind::DwarfInstructions<A, R>::getCFA(A&, const PrologInfo&, const R&)
[with A = libunwind::LocalAddressSpace; R = libunwind::Registers_x86_64;
libunwind::DwarfInstructions<A, R>::pint_t = long unsigned int;
libunwind::DwarfInstructions<A, R>::PrologInfo =
libunwind::CFI_Parser<libunwind::LocalAddressSpace>::PrologInfo]: Assertion `0
&& "getCFA(): unknown location"' failed.
Aborted (core dumped)
2. Compile with HP's "nongnu" libunwind:
sudo apt-get install libunwind-dev
$ clang++ -g -O3 -pthread -ldl -fno-pic -fno-pie unwind_test.cpp test.s -lrt
-Wl,-Bstatic -lunwind -llzma -Wl,-Bdynamic && ./a.out
Works perfectly.
3. Compile with libgcc's unwinder:
$ clang++ -g -O3 -pthread -ldl -fno-pic -fno-pie unwind_test.cpp test.s -lrt
-Wl,-Bstatic -lgcc -Wl,-Bdynamic && ./a.out
Works perfectly.
--
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20201115/c729b065/attachment.html>
More information about the llvm-bugs
mailing list