From 6f5e8c2647d98d4f7ca3a44d87f235e0c032ba6a Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 25 May 2012 01:54:06 +0000 Subject: [PATCH] Add support for function with stack frame checks added by the compiler; these functions will end in the sequence mov %rbp, %rsp ret call __stack_chk_fail instead of the usual mov, ret. The x86 assembly profiler only looked for functions ending in 'ret' and added the Unwind row describing how to set the CFA based on that -- the addition of the call insn (which is jumped to earlier in the function body) threw off that inspection. Resolves the need to "step" twice to get out of these functions when doing source-level stepping. llvm-svn: 157454 --- .../UnwindAssembly/x86/UnwindAssembly-x86.cpp | 85 +++++++++++++++------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp index c0daaac..27e3622 100644 --- a/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp +++ b/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp @@ -676,40 +676,69 @@ loopnext: } // Now look at the byte at the end of the AddressRange for a limited attempt at describing the - // epilogue. If this function is built -fomit-frame-pointer (so the CFA is defined in terms of the - // stack pointer) we'd need to profile every instruction which causes rsp to change to backtrace - // all the time. But assuming the CFA is in terms of rbp most of the time, this one additional Row - // will be sufficient. + // epilogue. We're looking for the sequence - if (m_func_bounds.GetByteSize() > 2) + // [ 0x5d ] mov %rbp, %rsp + // [ 0xc3 ] ret + // [ 0xe8 xx xx xx xx ] call __stack_chk_fail (this is sometimes the final insn in the function) + + // We want to add a Row describing how to unwind when we're stopped on the 'ret' instruction where the + // CFA is no longer defined in terms of rbp, but is now defined in terms of rsp like on function entry. + + uint64_t ret_insn_offset = LLDB_INVALID_ADDRESS; + Address end_of_fun(m_func_bounds.GetBaseAddress()); + end_of_fun.SetOffset (end_of_fun.GetOffset() + m_func_bounds.GetByteSize()); + + if (m_func_bounds.GetByteSize() > 7) { - Address last_insn (m_func_bounds.GetBaseAddress()); - last_insn.SetOffset (last_insn.GetOffset() + m_func_bounds.GetByteSize() - 1); - uint8_t bytebuf[1]; - if (target->ReadMemory (last_insn, prefer_file_cache, bytebuf, 1, error) != -1) + uint8_t bytebuf[7]; + Address last_seven_bytes(end_of_fun); + last_seven_bytes.SetOffset (last_seven_bytes.GetOffset() - 7); + if (target->ReadMemory (last_seven_bytes, prefer_file_cache, bytebuf, 7, error) != -1) { - if (bytebuf[0] == 0xc3) // ret aka retq + if (bytebuf[5] == 0x5d && bytebuf[6] == 0xc3) // mov, ret { - // Create a fresh, empty Row and RegisterLocation - don't mention any other registers - UnwindPlan::Row epi_row; - UnwindPlan::Row::RegisterLocation epi_regloc; - - // When the ret instruction is about to be executed, here's our state - epi_row.SetOffset (m_func_bounds.GetByteSize() - 1); - epi_row.SetCFARegister (m_lldb_sp_regnum); - epi_row.SetCFAOffset (m_wordsize); - - // caller's stack pointer value before the call insn is the CFA address - epi_regloc.SetIsCFAPlusOffset (0); - epi_row.SetRegisterInfo (m_lldb_sp_regnum, epi_regloc); - - // saved instruction pointer can be found at CFA - wordsize - epi_regloc.SetAtCFAPlusOffset (-m_wordsize); - epi_row.SetRegisterInfo (m_lldb_ip_regnum, epi_regloc); - - unwind_plan.AppendRow (epi_row); + ret_insn_offset = m_func_bounds.GetByteSize() - 1; + } + else if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3 && bytebuf[2] == 0xe8) // mov, ret, call + { + ret_insn_offset = m_func_bounds.GetByteSize() - 6; } } + } else if (m_func_bounds.GetByteSize() > 2) + { + uint8_t bytebuf[2]; + Address last_two_bytes(end_of_fun); + last_two_bytes.SetOffset (last_two_bytes.GetOffset() - 2); + if (target->ReadMemory (last_two_bytes, prefer_file_cache, bytebuf, 2, error) != -1) + { + if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3) // mov, ret + { + ret_insn_offset = m_func_bounds.GetByteSize() - 1; + } + } + } + + if (ret_insn_offset != LLDB_INVALID_ADDRESS) + { + // Create a fresh, empty Row and RegisterLocation - don't mention any other registers + UnwindPlan::Row epi_row; + UnwindPlan::Row::RegisterLocation epi_regloc; + + // When the ret instruction is about to be executed, here's our state + epi_row.SetOffset (ret_insn_offset); + epi_row.SetCFARegister (m_lldb_sp_regnum); + epi_row.SetCFAOffset (m_wordsize); + + // caller's stack pointer value before the call insn is the CFA address + epi_regloc.SetIsCFAPlusOffset (0); + epi_row.SetRegisterInfo (m_lldb_sp_regnum, epi_regloc); + + // saved instruction pointer can be found at CFA - wordsize + epi_regloc.SetAtCFAPlusOffset (-m_wordsize); + epi_row.SetRegisterInfo (m_lldb_ip_regnum, epi_regloc); + + unwind_plan.AppendRow (epi_row); } unwind_plan.SetSourceName ("assembly insn profiling"); -- 2.7.4