diff options
author | Jan Kratochvil <jan.kratochvil@redhat.com> | 2023-04-01 15:42:57 +0200 |
---|---|---|
committer | Jan Kratochvil <jan.kratochvil@redhat.com> | 2023-04-01 15:42:57 +0200 |
commit | 3026cdbdde0e1937f811b52ba18fe3fbb1419ef9 (patch) | |
tree | 8ba5b6f1f0445eb216851da8382eda66a1c6a6c2 /gdb/arm-tdep.c | |
parent | a3424b707737ede8c5823cb27c896b12fe94471c (diff) | |
download | gdb-3026cdbdde0e1937f811b52ba18fe3fbb1419ef9.zip gdb-3026cdbdde0e1937f811b52ba18fe3fbb1419ef9.tar.gz gdb-3026cdbdde0e1937f811b52ba18fe3fbb1419ef9.tar.bz2 |
gdb/arm: Fix backtrace for pthread_cond_timedwait
GDB expected PC should point right after the SVC instruction when the
syscall is active. But some active syscalls keep PC pointing to the SVC
instruction itself.
This leads to a broken backtrace like:
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
#0 0xb6f8681c in pthread_cond_timedwait@@GLIBC_2.4 () from /lib/arm-linux-gnueabihf/libpthread.so.0
#1 0xb6e21f80 in ?? ()
The reason is that .ARM.exidx unwinder gives up if PC does not point
right after the SVC (syscall) instruction. I did not investigate why but
some syscalls will point PC to the SVC instruction itself. This happens
for the "futex" syscall used by pthread_cond_timedwait.
That normally does not matter as ARM prologue unwinder gets called
instead of the .ARM.exidx one. Unfortunately some glibc calls have more
complicated prologue where the GDB unwinder fails to properly determine
the return address (that is in fact an orthogonal GDB bug). I expect it
is due to the "vpush" there in this case but I did not investigate it more:
Dump of assembler code for function pthread_cond_timedwait@@GLIBC_2.4:
0xb6f8757c <+0>: push {r4, r5, r6, r7, r8, r9, r10, r11, lr}
0xb6f87580 <+4>: mov r10, r2
0xb6f87584 <+8>: vpush {d8}
Regression tested on armv7l kernel 5.15.32-v7l+ (Raspbian 11).
Approved-By: Luis Machado <luis.machado@arm.com>
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r-- | gdb/arm-tdep.c | 42 |
1 files changed, 25 insertions, 17 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 803596d..492a71f 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -3115,26 +3115,34 @@ arm_exidx_unwind_sniffer (const struct frame_unwind *self, && get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME) exc_valid = 1; - /* We also assume exception information is valid if we're currently - blocked in a system call. The system library is supposed to - ensure this, so that e.g. pthread cancellation works. */ - if (arm_frame_is_thumb (this_frame)) + /* Some syscalls keep PC pointing to the SVC instruction itself. */ + for (int shift = 0; shift <= 1 && !exc_valid; ++shift) { - ULONGEST insn; + /* We also assume exception information is valid if we're currently + blocked in a system call. The system library is supposed to + ensure this, so that e.g. pthread cancellation works. */ + if (arm_frame_is_thumb (this_frame)) + { + ULONGEST insn; - if (safe_read_memory_unsigned_integer (get_frame_pc (this_frame) - 2, - 2, byte_order_for_code, &insn) - && (insn & 0xff00) == 0xdf00 /* svc */) - exc_valid = 1; - } - else - { - ULONGEST insn; + if (safe_read_memory_unsigned_integer ((get_frame_pc (this_frame) + - (shift ? 2 : 0)), + 2, byte_order_for_code, + &insn) + && (insn & 0xff00) == 0xdf00 /* svc */) + exc_valid = 1; + } + else + { + ULONGEST insn; - if (safe_read_memory_unsigned_integer (get_frame_pc (this_frame) - 4, - 4, byte_order_for_code, &insn) - && (insn & 0x0f000000) == 0x0f000000 /* svc */) - exc_valid = 1; + if (safe_read_memory_unsigned_integer ((get_frame_pc (this_frame) + - (shift ? 4 : 0)), + 4, byte_order_for_code, + &insn) + && (insn & 0x0f000000) == 0x0f000000 /* svc */) + exc_valid = 1; + } } /* Bail out if we don't know that exception information is valid. */ |