From: Ulrich Weigand Date: Sun, 24 Apr 2016 20:49:56 +0000 (+0000) Subject: Fix unwind failures when PC points beyond the end of a function X-Git-Tag: llvmorg-3.9.0-rc1~8105 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7793ba86d15f9352ef094845f3eac2f39de161f2;p=platform%2Fupstream%2Fllvm.git Fix unwind failures when PC points beyond the end of a function RegisterContextLLDB::InitializeNonZerothFrame already has code to attempt to detect and handle the case where the PC points beyond the end of a function, but there are certain cases where this doesn't work correctly. In fact, there are *two* different places where this detection is attempted, and the failure is in fact a result of an unfortunate interaction between those two separate attempts. First, the ResolveSymbolContextForAddress routine is called with the resolve_tail_call_address flag set to true. This causes the routine to internally accept a PC pointing beyond the end of a function, and still resolving the PC to that function symbol. Second, the InitializeNonZerothFrame routine itself maintains a "decr_pc_and_recompute_addr_range" flag and, if that turns out to be true, itself decrements the PC by one and searches again for a symbol at that new PC value. Both approaches correctly identify the symbol associated with the PC. However, the problem is now that later on, we also need to find the DWARF CFI record associated with the PC. This is done in the RegisterContextLLDB::GetFullUnwindPlanForFrame routine, and uses the "m_current_offset_backed_up_one" member variable. However, that variable only actually contains the PC "backed up by one" if the *second* approach above was taken. If the function was already identified via the first approach above, that member variable is *not* backed up by one but simply points to the original PC. This in turn causes GetEHFrameUnwindPlan to not correctly identify the DWARF CFI record associated with the PC. Now, in many cases, if the first method had to back up the PC by one, we *still* use the second method too, because of this piece of code: // Or if we're in the middle of the stack (and not "above" an asynchronous event like sigtramp), // and our "current" pc is the start of a function... if (m_sym_ctx_valid && GetNextFrame()->m_frame_type != eTrapHandlerFrame && GetNextFrame()->m_frame_type != eDebuggerFrame && addr_range.GetBaseAddress().IsValid() && addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection() && addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset()) { decr_pc_and_recompute_addr_range = true; } In many cases, when the PC is one beyond the end of the current function, it will indeed then be exactly at the start of the next function. But this is not always the case, e.g. if there happens to be alignment padding between the end of one function and the start of the next. In those cases, we may sucessfully look up the function symbol via ResolveSymbolContextForAddress, but *not* set decr_pc_and_recompute_addr_range, and therefore fail to find the correct DWARF CFI record. A very simple fix for this problem is to just never use the first method. Call ResolveSymbolContextForAddress with resolve_tail_call_address set to false, which will cause it to fail if the PC is beyond the end of the current function; or else, identify the next function if the PC is also at the start of the next function. In either case, we will then set the decr_pc_and_recompute_addr_range variable and back up the PC anyway, but this time also find the correct DWARF CFI. A related problem is that the ResolveSymbolContextForAddress sometimes returns a "symbol" with empty name. This turns out to be an ELF section symbol. Now, usually those get type eSymbolTypeInvalid. However, there is code in ObjectFileELF::ParseSymbols that tries to change the type of invalid symbols to eSymbolTypeCode or eSymbolTypeData if the symbol lies within the code or data section. Unfortunately, this check also hits the symbol for the code section itself, which is then marked as eSymbolTypeCode. While the size of the section symbol is 0 according to the ELF file, LLDB considers this size invalid and attempts to figure out the "correct" size. Depending on how this goes, we may end up with a symbol that overlays part of the code section, even outside areas covered by real function symbols. Therefore, if we call ResolveSymbolContextForAddress with PC pointing beyond the end of a function, we may get this bogus section symbol. This again means InitializeNonZerothFrame thinks we have a valid PC, but then we don't find any unwind info for it. The fix for this problem is me to simply always leave ELF section symbols as type eSymbolTypeInvalid. Differential Revision: http://reviews.llvm.org/D18975 llvm-svn: 267363 --- diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 7641684..c599dc3 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -2248,7 +2248,7 @@ ObjectFileELF::ParseSymbols (Symtab *symtab, } } - if (symbol_type == eSymbolTypeInvalid) + if (symbol_type == eSymbolTypeInvalid && symbol.getType() != STT_SECTION) { if (symbol_section_sp) { diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp index 6feec5b..64b1200 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -470,11 +470,13 @@ RegisterContextLLDB::InitializeNonZerothFrame() return; } - bool resolve_tail_call_address = true; // m_current_pc can be one past the address range of the function... - // This will handle the case where the saved pc does not point to - // a function/symbol because it is beyond the bounds of the correct - // function and there's no symbol there. ResolveSymbolContextForAddress - // will fail to find a symbol, back up the pc by 1 and re-search. + bool resolve_tail_call_address = false; // m_current_pc can be one past the address range of the function... + // If the saved pc does not point to a function/symbol because it is + // beyond the bounds of the correct function and there's no symbol there, + // we do *not* want ResolveSymbolContextForAddress to back up the pc by 1, + // because then we might not find the correct unwind information later. + // Instead, let ResolveSymbolContextForAddress fail, and handle the case + // via decr_pc_and_recompute_addr_range below. const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; uint32_t resolved_scope = pc_module_sp->ResolveSymbolContextForAddress (m_current_pc, resolve_scope,