summaryrefslogtreecommitdiff
path: root/gdb/arm-tdep.c
diff options
context:
space:
mode:
authorJan Kratochvil <jan.kratochvil@redhat.com>2023-04-01 15:42:57 +0200
committerJan Kratochvil <jan.kratochvil@redhat.com>2023-04-01 15:42:57 +0200
commit3026cdbdde0e1937f811b52ba18fe3fbb1419ef9 (patch)
tree8ba5b6f1f0445eb216851da8382eda66a1c6a6c2 /gdb/arm-tdep.c
parenta3424b707737ede8c5823cb27c896b12fe94471c (diff)
downloadbinutils-gdb-3026cdbdde0e1937f811b52ba18fe3fbb1419ef9.tar.gz
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.c42
1 files changed, 25 insertions, 17 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 803596d0fe6..492a71fa987 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. */