diff --git a/src/x86_64/Gstep.c b/src/x86_64/Gstep.c index 84b3728..29bd56a 100644 --- a/src/x86_64/Gstep.c +++ b/src/x86_64/Gstep.c @@ -27,6 +27,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" #include +#include /* Recognise PLT entries such as: 3bdf0: ff 25 e2 49 13 00 jmpq *0x1349e2(%rip) @@ -52,6 +53,18 @@ is_plt_entry (struct dwarf_cursor *c) return ret; } +static int +is_vsyscall (struct dwarf_cursor *c) +{ +#if defined(VSYSCALL_START) && defined(VSYSCALL_END) + return c->ip >= VSYSCALL_START && c->ip < VSYSCALL_END; +#else + /* Linux 3.16 removes `VSYSCALL_START` and `VSYSCALL_END`. Assume + * that no-one is using vsyscall on newer systems. */ + return 0; +#endif +} + PROTECTED int unw_step (unw_cursor_t *cursor) { @@ -106,7 +119,6 @@ unw_step (unw_cursor_t *cursor) code. */ unw_word_t prev_ip = c->dwarf.ip, prev_cfa = c->dwarf.cfa; - struct dwarf_loc rbp_loc, rsp_loc, rip_loc; /* We could get here because of missing/bad unwind information. Validate all addresses before dereferencing. */ @@ -133,6 +145,40 @@ unw_step (unw_cursor_t *cursor) c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); c->dwarf.cfa += 8; } + else if (is_vsyscall (&c->dwarf)) + { + int frameless; + unw_word_t rbp; + Debug (2, "in vsyscall region\n"); + ret = dwarf_get (&c->dwarf, c->dwarf.loc[RBP], &rbp); + frameless = (ret < 0) || (rbp != c->dwarf.cfa); + Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) frameless = %d\n", + (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]), + rbp, c->dwarf.cfa, frameless); + if (frameless) + { + c->frame_info.cfa_reg_offset = 8; + c->frame_info.cfa_reg_rsp = -1; + c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; + c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); + c->dwarf.cfa += 8; + } + else + { + c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; + c->frame_info.cfa_reg_rsp = 0; + c->frame_info.cfa_reg_offset = 16; + c->frame_info.rbp_cfa_offset = -16; + c->dwarf.cfa += 16; + + /* Mark all registers unsaved */ + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; + c->dwarf.loc[RBP] = DWARF_LOC(rbp, 0); + c->dwarf.loc[RSP] = DWARF_NULL_LOC; + c->dwarf.loc[RIP] = DWARF_LOC (rbp + 8, 0); + } + } else if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP])) { for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) @@ -140,62 +186,67 @@ unw_step (unw_cursor_t *cursor) } else { + int frameless; unw_word_t rbp; ret = dwarf_get (&c->dwarf, c->dwarf.loc[RBP], &rbp); - if (ret < 0) + if (is_vsyscall (&c->dwarf)) { - Debug (2, "returning %d [RBP=0x%lx]\n", ret, - DWARF_GET_LOC (c->dwarf.loc[RBP])); - return ret; - } - + Debug (2, "in vsyscall region\n"); + /* The vsyscalls don't use the stack so we can use a stricter check. */ + frameless = (ret < 0) || (rbp != c->dwarf.cfa); + } + else + { + /* Heuristic to determine incorrect guess. For RBP to be a + valid frame it needs to be above current CFA, but don't + let it go more than a little. Note that we can't deduce + anything about new RBP (rbp1) since it may not be a frame + pointer in the frame above. Just check we get the value. */ + frameless = (ret < 0) || (rbp < c->dwarf.cfa) || ((rbp - c->dwarf.cfa) > 0x4000); + } if (!rbp) { - /* Looks like we may have reached the end of the call-chain. */ - rbp_loc = DWARF_NULL_LOC; - rsp_loc = DWARF_NULL_LOC; - rip_loc = DWARF_NULL_LOC; + /* Looks like we may have reached the end of the call-chain. */ + /* Mark all registers unsaved */ + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; } + else if (frameless) + { + Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) (frameless)\n", + (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]), + rbp, c->dwarf.cfa); + + c->frame_info.cfa_reg_offset = 8; + c->frame_info.cfa_reg_rsp = -1; + c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; + c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); + c->dwarf.cfa += 8; + } else - { - unw_word_t rbp1 = 0; - rbp_loc = DWARF_LOC(rbp, 0); - rsp_loc = DWARF_NULL_LOC; - rip_loc = DWARF_LOC (rbp + 8, 0); - ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1); - Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) -> 0x%lx\n", - (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]), - rbp, c->dwarf.cfa, rbp1); - - /* Heuristic to determine incorrect guess. For RBP to be a - valid frame it needs to be above current CFA, but don't - let it go more than a little. Note that we can't deduce - anything about new RBP (rbp1) since it may not be a frame - pointer in the frame above. Just check we get the value. */ - if (ret < 0 - || rbp < c->dwarf.cfa - || (rbp - c->dwarf.cfa) > 0x4000) - { - rip_loc = DWARF_NULL_LOC; - rbp_loc = DWARF_NULL_LOC; - } - - c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; - c->frame_info.cfa_reg_rsp = 0; - c->frame_info.cfa_reg_offset = 16; - c->frame_info.rbp_cfa_offset = -16; - c->dwarf.cfa += 16; - } - - /* Mark all registers unsaved */ - for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) - c->dwarf.loc[i] = DWARF_NULL_LOC; - - c->dwarf.loc[RBP] = rbp_loc; - c->dwarf.loc[RSP] = rsp_loc; - c->dwarf.loc[RIP] = rip_loc; - c->dwarf.use_prev_instr = 1; + { + unw_word_t rbp1 = 0; + struct dwarf_loc rbp_loc = DWARF_LOC(rbp, 0); + ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1); + Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) -> 0x%lx\n", + (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]), + rbp, c->dwarf.cfa, rbp1); + + c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; + c->frame_info.cfa_reg_rsp = 0; + c->frame_info.cfa_reg_offset = 16; + c->frame_info.rbp_cfa_offset = -16; + c->dwarf.cfa += 16; + c->dwarf.use_prev_instr = 1; + /* Mark all registers unsaved */ + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; + + c->dwarf.loc[RBP] = rbp_loc; + c->dwarf.loc[RSP] = DWARF_NULL_LOC; + c->dwarf.loc[RIP] = DWARF_LOC (rbp + 8, 0); + } } c->dwarf.ret_addr_column = RIP;