summaryrefslogtreecommitdiffstats
path: root/libdwfl
diff options
context:
space:
mode:
authorMark Wielaard <mark@klomp.org>2018-04-10 16:13:34 +0200
committerMark Wielaard <mark@klomp.org>2018-04-13 13:37:16 +0200
commit1cc2e2265df8b0f7d97d3680e9e35124ad92a4f5 (patch)
treefe93cf9e03bc3197e5a026c91773441f1d804f30 /libdwfl
parenta46d8d5f87e42b7d61cdff671ec7d3728cd1a637 (diff)
libdwfl: Handle unwind frame when the return address register isn't set.
When we have unwound the frame and then cannot set the return address we wouldn't set any error. That meant that a dwfl_thread_getframes () call could end in an error, but without any dwfl_errno set, producing the "no error" error message. If we cannot set the return address at the end of unwinding the frame that means that either the return address register is bogus (error), or that the return address is undefined (end of the call stack). This fixes the run-backtrace-native-biarch.sh testcase for me on an i386 on x86_64 setup with gcc 7.2.1 and glibc 2.17. Signed-off-by: Mark Wielaard <mark@klomp.org>
Diffstat (limited to 'libdwfl')
-rw-r--r--libdwfl/ChangeLog5
-rw-r--r--libdwfl/frame_unwind.c46
2 files changed, 35 insertions, 16 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 9776f1cb..b6262c20 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,8 @@
+2018-04-10 Mark Wielaard <mark@klomp.org>
+
+ * frame_unwind.c (unwind): If __libdwfl_frame_reg_get fails for
+ the return address either set an error or mark the pc undefined.
+
2018-03-17 Mark Wielaard <mark@klomp.org>
* libdwflP.h (struct __libdwfl_remote_mem_cache): New.
diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c
index eaea495f..8da691ee 100644
--- a/libdwfl/frame_unwind.c
+++ b/libdwfl/frame_unwind.c
@@ -632,24 +632,38 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
ra_set = true;
}
}
- if (unwound->pc_state == DWFL_FRAME_STATE_ERROR
- && __libdwfl_frame_reg_get (unwound,
- frame->fde->cie->return_address_register,
- &unwound->pc))
+ if (unwound->pc_state == DWFL_FRAME_STATE_ERROR)
{
- /* PPC32 __libc_start_main properly CFI-unwinds PC as zero. Currently
- none of the archs supported for unwinding have zero as a valid PC. */
- if (unwound->pc == 0)
- unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ if (__libdwfl_frame_reg_get (unwound,
+ frame->fde->cie->return_address_register,
+ &unwound->pc))
+ {
+ /* PPC32 __libc_start_main properly CFI-unwinds PC as zero.
+ Currently none of the archs supported for unwinding have
+ zero as a valid PC. */
+ if (unwound->pc == 0)
+ unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ else
+ {
+ unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ /* In SPARC the return address register actually contains
+ the address of the call instruction instead of the return
+ address. Therefore we add here an offset defined by the
+ backend. Most likely 0. */
+ unwound->pc += ebl_ra_offset (ebl);
+ }
+ }
else
- {
- unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
- /* In SPARC the return address register actually contains
- the address of the call instruction instead of the return
- address. Therefore we add here an offset defined by the
- backend. Most likely 0. */
- unwound->pc += ebl_ra_offset (ebl);
- }
+ {
+ /* We couldn't set the return register, either it was bogus,
+ or the return pc is undefined, maybe end of call stack. */
+ unsigned pcreg = frame->fde->cie->return_address_register;
+ if (! ebl_dwarf_to_regno (ebl, &pcreg)
+ || pcreg >= ebl_frame_nregs (ebl))
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ else
+ unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ }
}
free (frame);
}