Index: include/lldb/Symbol/UnwindPlan.h =================================================================== --- include/lldb/Symbol/UnwindPlan.h +++ include/lldb/Symbol/UnwindPlan.h @@ -28,14 +28,22 @@ // The UnwindPlan object specifies how to unwind out of a function - where this // function saves the caller's register values before modifying them (for non- // volatile aka saved registers) and how to find this frame's Canonical Frame -// Address (CFA). +// Address (CFA) or Aligned Frame Address (AFA). +// CFA is a DWARF's Canonical Frame Address. // Most commonly, registers are saved on the stack, offset some bytes from the // Canonical Frame Address, or CFA, which is the starting address of this // function's stack frame (the CFA is same as the eh_frame's CFA, whatever that // may be on a given architecture). The CFA address for the stack frame does // not change during the lifetime of the function. +// AFA is an artificially introduced Aligned Frame Address. +// It is used only for stack frames with realignment (e.g. when some of the +// locals has an alignment requirement higher than the stack alignment right +// after the function call). It is used to access register values saved on the +// stack after the realignment (and so they are inaccessible through the CFA). +// AFA usually equals the stack pointer value right after the realignment. + // Internally, the UnwindPlan is structured as a vector of register locations // organized by code address in the function, showing which registers have been // saved at that point and where they are saved. It can be thought of as the @@ -61,6 +69,8 @@ same, // reg is unchanged atCFAPlusOffset, // reg = deref(CFA + offset) isCFAPlusOffset, // reg = CFA + offset + atAFAPlusOffset, // reg = deref(AFA + offset) + isAFAPlusOffset, // reg = AFA + offset inOtherRegister, // reg = other reg atDWARFExpression, // reg = deref(eval(dwarf_expr)) isDWARFExpression // reg = eval(dwarf_expr) @@ -90,6 +100,10 @@ bool IsAtCFAPlusOffset() const { return m_type == atCFAPlusOffset; } + bool IsAFAPlusOffset() const { return m_type == isAFAPlusOffset; } + + bool IsAtAFAPlusOffset() const { return m_type == atAFAPlusOffset; } + bool IsInOtherRegister() const { return m_type == inOtherRegister; } bool IsAtDWARFExpression() const { return m_type == atDWARFExpression; } @@ -106,6 +120,16 @@ m_location.offset = offset; } + void SetAtAFAPlusOffset(int32_t offset) { + m_type = atAFAPlusOffset; + m_location.offset = offset; + } + + void SetIsAFAPlusOffset(int32_t offset) { + m_type = isAFAPlusOffset; + m_location.offset = offset; + } + void SetInRegister(uint32_t reg_num) { m_type = inOtherRegister; m_location.reg_num = reg_num; @@ -120,9 +144,16 @@ RestoreType GetLocationType() const { return m_type; } int32_t GetOffset() const { - if (m_type == atCFAPlusOffset || m_type == isCFAPlusOffset) + switch(m_type) + { + case atCFAPlusOffset: + case isCFAPlusOffset: + case atAFAPlusOffset: + case isAFAPlusOffset: return m_location.offset; - return 0; + default: + return 0; + } } void GetDWARFExpr(const uint8_t **opcodes, uint16_t &len) const { @@ -169,20 +200,20 @@ } m_location; }; - class CFAValue { + class FAValue { public: enum ValueType { unspecified, // not specified - isRegisterPlusOffset, // CFA = register + offset - isRegisterDereferenced, // CFA = [reg] - isDWARFExpression // CFA = eval(dwarf_expr) + isRegisterPlusOffset, // FA = register + offset + isRegisterDereferenced, // FA = [reg] + isDWARFExpression // FA = eval(dwarf_expr) }; - CFAValue() : m_type(unspecified), m_value() {} + FAValue() : m_type(unspecified), m_value() {} - bool operator==(const CFAValue &rhs) const; + bool operator==(const FAValue &rhs) const; - bool operator!=(const CFAValue &rhs) const { return !(*this == rhs); } + bool operator!=(const FAValue &rhs) const { return !(*this == rhs); } void SetUnspecified() { m_type = unspecified; } @@ -279,7 +310,7 @@ uint16_t length; } expr; } m_value; - }; // class CFAValue + }; // class FAValue public: Row(); @@ -302,7 +333,9 @@ void SlideOffset(lldb::addr_t offset) { m_offset += offset; } - CFAValue &GetCFAValue() { return m_cfa_value; } + FAValue &GetCFAValue() { return m_cfa_value; } + + FAValue &GetAFAValue() { return m_afa_value; } bool SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num, int32_t offset, bool can_replace); @@ -329,7 +362,8 @@ typedef std::map collection; lldb::addr_t m_offset; // Offset into the function for this row - CFAValue m_cfa_value; + FAValue m_cfa_value; + FAValue m_afa_value; collection m_register_locations; }; // class Row Index: source/Plugins/Process/Utility/RegisterContextLLDB.h =================================================================== --- source/Plugins/Process/Utility/RegisterContextLLDB.h +++ source/Plugins/Process/Utility/RegisterContextLLDB.h @@ -192,9 +192,9 @@ bool ReadGPRValue(const RegisterNumber ®_num, lldb::addr_t &value); - // Get the CFA register for a given frame. - bool ReadCFAValueForRow(lldb::RegisterKind register_kind, - const UnwindPlan::RowSP &row, lldb::addr_t &value); + // Get the Frame Address register for a given frame. + bool ReadFrameAddress(lldb::RegisterKind register_kind, + UnwindPlan::Row::FAValue &fa, lldb::addr_t &address); lldb::UnwindPlanSP GetFastUnwindPlanForFrame(); @@ -225,6 +225,7 @@ int m_frame_type; // enum FrameType lldb::addr_t m_cfa; + lldb::addr_t m_afa; lldb_private::Address m_start_pc; lldb_private::Address m_current_pc; Index: source/Plugins/Process/Utility/RegisterContextLLDB.cpp =================================================================== --- source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -54,7 +54,8 @@ : RegisterContext(thread, frame_number), m_thread(thread), m_fast_unwind_plan_sp(), m_full_unwind_plan_sp(), m_fallback_unwind_plan_sp(), m_all_registers_available(false), - m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS), m_start_pc(), + m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS), + m_afa(LLDB_INVALID_ADDRESS), m_start_pc(), m_current_pc(), m_current_offset(0), m_current_offset_backed_up_one(0), m_sym_ctx(sym_ctx), m_sym_ctx_valid(false), m_frame_number(frame_number), m_registers(), m_parent_unwind(unwind_lldb) { @@ -228,7 +229,7 @@ return; } - if (!ReadCFAValueForRow(row_register_kind, active_row, m_cfa)) { + if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) { // Try the fall back unwind plan since the // full unwind plan failed. FuncUnwindersSP func_unwinders_sp; @@ -256,12 +257,14 @@ m_frame_type = eNotAValidFrame; return; } - } + } else + ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa); UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 - " using %s UnwindPlan", + " afa is 0x%" PRIx64 " using %s UnwindPlan", (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), (uint64_t)m_cfa, + (uint64_t)m_afa, m_full_unwind_plan_sp->GetSourceName().GetCString()); } @@ -379,7 +382,7 @@ RegisterKind row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0); if (row.get()) { - if (!ReadCFAValueForRow(row_register_kind, row, m_cfa)) { + if (!ReadFrameAddress(row_register_kind, row->GetCFAValue(), m_cfa)) { UnwindLogMsg("failed to get cfa value"); if (m_frame_type != eSkipFrame) // don't override eSkipFrame { @@ -388,6 +391,8 @@ return; } + ReadFrameAddress(row_register_kind, row->GetAFAValue(), m_afa); + // A couple of sanity checks.. if (m_cfa == LLDB_INVALID_ADDRESS || m_cfa == 0 || m_cfa == 1) { UnwindLogMsg("could not find a valid cfa address"); @@ -420,7 +425,8 @@ } } - UnwindLogMsg("initialized frame cfa is 0x%" PRIx64, (uint64_t)m_cfa); + UnwindLogMsg("initialized frame cfa is 0x%" PRIx64 " afa is 0x%" PRIx64, + (uint64_t)m_cfa, (uint64_t)m_afa); return; } m_frame_type = eNotAValidFrame; @@ -584,13 +590,15 @@ return; } - if (!ReadCFAValueForRow(row_register_kind, active_row, m_cfa)) { + if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) { UnwindLogMsg("failed to get cfa"); m_frame_type = eNotAValidFrame; return; } - UnwindLogMsg("m_cfa = 0x%" PRIx64, m_cfa); + ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa); + + UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa); if (CheckIfLoopingStack()) { TryFallbackUnwindPlan(); @@ -603,9 +611,10 @@ } UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 - " cfa is 0x%" PRIx64, + " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64, (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), - (uint64_t)m_cfa); + (uint64_t)m_cfa, + (uint64_t)m_afa); } bool RegisterContextLLDB::CheckIfLoopingStack() { @@ -1314,8 +1323,8 @@ unwindplan_regloc)) { can_fetch_pc_value = true; } - if (ReadCFAValueForRow(unwindplan_registerkind, active_row, - cfa_value)) { + if (ReadFrameAddress(unwindplan_registerkind, + active_row->GetCFAValue(), cfa_value)) { can_fetch_cfa = true; } } @@ -1444,6 +1453,36 @@ return UnwindLLDB::RegisterSearchResult::eRegisterFound; } + if (unwindplan_regloc.IsAFAPlusOffset()) { + if (m_afa == LLDB_INVALID_ADDRESS) + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + + int offset = unwindplan_regloc.GetOffset(); + regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; + regloc.location.inferred_value = m_afa + offset; + m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; + UnwindLogMsg("supplying caller's register %s (%d), value is AFA plus " + "offset %d [value is 0x%" PRIx64 "]", + regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset, + regloc.location.inferred_value); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + + if (unwindplan_regloc.IsAtAFAPlusOffset()) { + if (m_afa == LLDB_INVALID_ADDRESS) + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + + int offset = unwindplan_regloc.GetOffset(); + regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; + regloc.location.target_memory_location = m_afa + offset; + m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; + UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at " + "AFA plus offset %d [saved at 0x%" PRIx64 "]", + regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset, + regloc.location.target_memory_location); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + if (unwindplan_regloc.IsInOtherRegister()) { uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber(); RegisterNumber row_regnum(m_thread, unwindplan_registerkind, @@ -1558,7 +1597,6 @@ addr_t old_caller_pc_value = LLDB_INVALID_ADDRESS; addr_t new_caller_pc_value = LLDB_INVALID_ADDRESS; - addr_t old_this_frame_cfa_value = m_cfa; UnwindLLDB::RegisterLocation regloc; if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB), regloc) == @@ -1588,6 +1626,7 @@ // the value of the m_cfa ivar. Save is down below a bit in 'old_cfa'. UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp; addr_t old_cfa = m_cfa; + addr_t old_afa = m_afa; m_registers.clear(); @@ -1598,19 +1637,21 @@ if (active_row && active_row->GetCFAValue().GetValueType() != - UnwindPlan::Row::CFAValue::unspecified) { + UnwindPlan::Row::FAValue::unspecified) { addr_t new_cfa; - if (!ReadCFAValueForRow(m_fallback_unwind_plan_sp->GetRegisterKind(), - active_row, new_cfa) || + if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(), + active_row->GetCFAValue(), new_cfa) || new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) { UnwindLogMsg("failed to get cfa with fallback unwindplan"); m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; - m_cfa = old_cfa; return false; } m_cfa = new_cfa; + ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(), + active_row->GetAFAValue(), m_afa); + if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound) { @@ -1631,19 +1672,18 @@ m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; m_cfa = old_cfa; + m_afa = old_afa; return false; } - if (old_caller_pc_value != LLDB_INVALID_ADDRESS) { - if (old_caller_pc_value == new_caller_pc_value && - new_cfa == old_this_frame_cfa_value) { - UnwindLogMsg("fallback unwind plan got the same values for this frame " - "CFA and caller frame pc, not using"); - m_fallback_unwind_plan_sp.reset(); - m_full_unwind_plan_sp = original_full_unwind_plan_sp; - m_cfa = old_cfa; - return false; - } + if (old_caller_pc_value == new_caller_pc_value && + m_cfa == old_cfa && + m_afa == old_afa) { + UnwindLogMsg("fallback unwind plan got the same values for this frame " + "CFA and caller frame pc, not using"); + m_fallback_unwind_plan_sp.reset(); + m_full_unwind_plan_sp = original_full_unwind_plan_sp; + return false; } UnwindLogMsg("trying to unwind from this function with the UnwindPlan '%s' " @@ -1677,16 +1717,19 @@ if (active_row && active_row->GetCFAValue().GetValueType() != - UnwindPlan::Row::CFAValue::unspecified) { + UnwindPlan::Row::FAValue::unspecified) { addr_t new_cfa; - if (!ReadCFAValueForRow(m_fallback_unwind_plan_sp->GetRegisterKind(), - active_row, new_cfa) || + if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(), + active_row->GetCFAValue(), new_cfa) || new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) { UnwindLogMsg("failed to get cfa with fallback unwindplan"); m_fallback_unwind_plan_sp.reset(); return false; } + ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(), + active_row->GetAFAValue(), m_afa); + m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; m_fallback_unwind_plan_sp.reset(); @@ -1701,18 +1744,18 @@ return false; } -bool RegisterContextLLDB::ReadCFAValueForRow( - lldb::RegisterKind row_register_kind, const UnwindPlan::RowSP &row, - addr_t &cfa_value) { +bool RegisterContextLLDB::ReadFrameAddress( + lldb::RegisterKind row_register_kind, UnwindPlan::Row::FAValue &fa, + addr_t &address) { RegisterValue reg_value; - cfa_value = LLDB_INVALID_ADDRESS; + address = LLDB_INVALID_ADDRESS; addr_t cfa_reg_contents; - switch (row->GetCFAValue().GetValueType()) { - case UnwindPlan::Row::CFAValue::isRegisterDereferenced: { + switch (fa.GetValueType()) { + case UnwindPlan::Row::FAValue::isRegisterDereferenced: { RegisterNumber cfa_reg(m_thread, row_register_kind, - row->GetCFAValue().GetRegisterNumber()); + fa.GetRegisterNumber()); if (ReadGPRValue(cfa_reg, cfa_reg_contents)) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(cfa_reg.GetAsKind(eRegisterKindLLDB)); @@ -1721,12 +1764,12 @@ Status error = ReadRegisterValueFromMemory( reg_info, cfa_reg_contents, reg_info->byte_size, reg_value); if (error.Success()) { - cfa_value = reg_value.GetAsUInt64(); + address = reg_value.GetAsUInt64(); UnwindLogMsg( "CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64 ", CFA value is 0x%" PRIx64, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), - cfa_reg_contents, cfa_value); + cfa_reg_contents, address); return true; } else { UnwindLogMsg("Tried to deref reg %s (%d) [0x%" PRIx64 @@ -1738,9 +1781,9 @@ } break; } - case UnwindPlan::Row::CFAValue::isRegisterPlusOffset: { + case UnwindPlan::Row::FAValue::isRegisterPlusOffset: { RegisterNumber cfa_reg(m_thread, row_register_kind, - row->GetCFAValue().GetRegisterNumber()); + fa.GetRegisterNumber()); if (ReadGPRValue(cfa_reg, cfa_reg_contents)) { if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 || cfa_reg_contents == 1) { @@ -1751,35 +1794,35 @@ cfa_reg_contents = LLDB_INVALID_ADDRESS; return false; } - cfa_value = cfa_reg_contents + row->GetCFAValue().GetOffset(); + address = cfa_reg_contents + fa.GetOffset(); UnwindLogMsg( "CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64 ", offset is %d", - cfa_value, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), - cfa_reg_contents, row->GetCFAValue().GetOffset()); + address, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), + cfa_reg_contents, fa.GetOffset()); return true; } break; } - case UnwindPlan::Row::CFAValue::isDWARFExpression: { + case UnwindPlan::Row::FAValue::isDWARFExpression: { ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); - DataExtractor dwarfdata(row->GetCFAValue().GetDWARFExpressionBytes(), - row->GetCFAValue().GetDWARFExpressionLength(), + DataExtractor dwarfdata(fa.GetDWARFExpressionBytes(), + fa.GetDWARFExpressionLength(), process->GetByteOrder(), process->GetAddressByteSize()); ModuleSP opcode_ctx; DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0, - row->GetCFAValue().GetDWARFExpressionLength()); + fa.GetDWARFExpressionLength()); dwarfexpr.SetRegisterKind(row_register_kind); Value result; Status error; if (dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr, result, &error)) { - cfa_value = result.GetScalar().ULongLong(); + address = result.GetScalar().ULongLong(); UnwindLogMsg("CFA value set by DWARF expression is 0x%" PRIx64, - cfa_value); + address); return true; } UnwindLogMsg("Failed to set CFA value via DWARF expression: %s", Index: source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp =================================================================== --- source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp +++ source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp @@ -92,7 +92,7 @@ // assembly parsing instead. if (first_row->GetCFAValue().GetValueType() != - UnwindPlan::Row::CFAValue::isRegisterPlusOffset || + UnwindPlan::Row::FAValue::isRegisterPlusOffset || RegisterNumber(thread, unwind_plan.GetRegisterKind(), first_row->GetCFAValue().GetRegisterNumber()) != sp_regnum || Index: source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h =================================================================== --- source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h +++ source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h @@ -98,10 +98,15 @@ bool push_extended_pattern_p(); bool push_misc_reg_p(); bool mov_rsp_rbp_pattern_p(); + bool mov_rsp_rbx_pattern_p(); + bool mov_rbp_rsp_pattern_p(); + bool mov_rbx_rsp_pattern_p(); bool sub_rsp_pattern_p(int &amount); bool add_rsp_pattern_p(int &amount); bool lea_rsp_pattern_p(int &amount); bool lea_rbp_rsp_pattern_p(int &amount); + bool lea_rbx_rsp_pattern_p(int &amount); + bool and_rsp_pattern_p(); bool push_reg_p(int ®no); bool pop_reg_p(int ®no); bool pop_rbp_pattern_p(); @@ -157,9 +162,11 @@ uint32_t m_machine_ip_regnum; uint32_t m_machine_sp_regnum; uint32_t m_machine_fp_regnum; + uint32_t m_machine_alt_fp_regnum; uint32_t m_lldb_ip_regnum; uint32_t m_lldb_sp_regnum; uint32_t m_lldb_fp_regnum; + uint32_t m_lldb_alt_fp_regnum; typedef std::map MachineRegnumToNameAndLLDBRegnum; Index: source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp =================================================================== --- source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp +++ source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp @@ -59,6 +59,7 @@ m_machine_ip_regnum = k_machine_eip; m_machine_sp_regnum = k_machine_esp; m_machine_fp_regnum = k_machine_ebp; + m_machine_alt_fp_regnum = k_machine_ebx; m_wordsize = 4; struct lldb_reg_info reginfo; @@ -84,6 +85,7 @@ m_machine_ip_regnum = k_machine_rip; m_machine_sp_regnum = k_machine_rsp; m_machine_fp_regnum = k_machine_rbp; + m_machine_alt_fp_regnum = k_machine_rbx; m_wordsize = 8; struct lldb_reg_info reginfo; @@ -135,6 +137,8 @@ m_lldb_sp_regnum = lldb_regno; if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno)) m_lldb_fp_regnum = lldb_regno; + if (machine_regno_to_lldb_regno(m_machine_alt_fp_regnum, lldb_regno)) + m_lldb_alt_fp_regnum = lldb_regno; if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno)) m_lldb_ip_regnum = lldb_regno; @@ -160,6 +164,7 @@ m_machine_ip_regnum = k_machine_eip; m_machine_sp_regnum = k_machine_esp; m_machine_fp_regnum = k_machine_ebp; + m_machine_alt_fp_regnum = k_machine_ebx; m_wordsize = 4; struct lldb_reg_info reginfo; @@ -185,6 +190,7 @@ m_machine_ip_regnum = k_machine_rip; m_machine_sp_regnum = k_machine_rsp; m_machine_fp_regnum = k_machine_rbp; + m_machine_alt_fp_regnum = k_machine_rbx; m_wordsize = 8; struct lldb_reg_info reginfo; @@ -239,6 +245,8 @@ m_lldb_sp_regnum = lldb_regno; if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno)) m_lldb_fp_regnum = lldb_regno; + if (machine_regno_to_lldb_regno(m_machine_alt_fp_regnum, lldb_regno)) + m_lldb_alt_fp_regnum = lldb_regno; if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno)) m_lldb_ip_regnum = lldb_regno; @@ -387,6 +395,45 @@ return false; } +// movq %rsp, %rbx [0x48 0x8b 0xdc] or [0x48 0x89 0xe3] +// movl %esp, %ebx [0x8b 0xdc] or [0x89 0xe3] +bool x86AssemblyInspectionEngine::mov_rsp_rbx_pattern_p() { + uint8_t *p = m_cur_insn; + if (m_wordsize == 8 && *p == 0x48) + p++; + if (*(p) == 0x8b && *(p + 1) == 0xdc) + return true; + if (*(p) == 0x89 && *(p + 1) == 0xe3) + return true; + return false; +} + +// movq %rbp, %rsp [0x48 0x8b 0xe5] or [0x48 0x89 0xec] +// movl %ebp, %esp [0x8b 0xe5] or [0x89 0xec] +bool x86AssemblyInspectionEngine::mov_rbp_rsp_pattern_p() { + uint8_t *p = m_cur_insn; + if (m_wordsize == 8 && *p == 0x48) + p++; + if (*(p) == 0x8b && *(p + 1) == 0xe5) + return true; + if (*(p) == 0x89 && *(p + 1) == 0xec) + return true; + return false; +} + +// movq %rbx, %rsp [0x48 0x8b 0xe3] or [0x48 0x89 0xdc] +// movl %ebx, %esp [0x8b 0xe3] or [0x89 0xdc] +bool x86AssemblyInspectionEngine::mov_rbx_rsp_pattern_p() { + uint8_t *p = m_cur_insn; + if (m_wordsize == 8 && *p == 0x48) + p++; + if (*(p) == 0x8b && *(p + 1) == 0xe3) + return true; + if (*(p) == 0x89 && *(p + 1) == 0xdc) + return true; + return false; +} + // subq $0x20, %rsp bool x86AssemblyInspectionEngine::sub_rsp_pattern_p(int &amount) { uint8_t *p = m_cur_insn; @@ -476,6 +523,46 @@ return false; } +// lea -0x28(%ebx), %esp +// (32-bit and 64-bit variants, 8-bit and 32-bit displacement) +bool x86AssemblyInspectionEngine::lea_rbx_rsp_pattern_p(int &amount) { + uint8_t *p = m_cur_insn; + if (m_wordsize == 8 && *p == 0x48) + p++; + + // Check opcode + if (*p != 0x8d) + return false; + ++p; + + // 8 bit displacement + if (*p == 0x63) { + amount = (int8_t)p[1]; + return true; + } + + // 32 bit displacement + if (*p == 0xa3) { + amount = (int32_t)extract_4(p + 1); + return true; + } + + return false; +} + +// and -0xfffffff0, %esp +// (32-bit and 64-bit variants, 8-bit and 32-bit displacement) +bool x86AssemblyInspectionEngine::and_rsp_pattern_p() { + uint8_t *p = m_cur_insn; + if (m_wordsize == 8 && *p == 0x48) + p++; + + if (*p != 0x81 && *p != 0x83) + return false; + + return *++p == 0xe4; +} + // popq %rbx // popl %ebx bool x86AssemblyInspectionEngine::pop_reg_p(int ®no) { @@ -640,7 +727,8 @@ return false; addr_t current_func_text_offset = 0; - int current_sp_bytes_offset_from_cfa = 0; + int current_sp_bytes_offset_from_fa = 0; + bool is_aligned = false; UnwindPlan::Row::RegisterLocation initial_regloc; UnwindPlan::RowSP row(new UnwindPlan::Row); @@ -657,8 +745,8 @@ row->SetRegisterInfo(m_lldb_sp_regnum, initial_regloc); // saved instruction pointer can be found at CFA - wordsize. - current_sp_bytes_offset_from_cfa = m_wordsize; - initial_regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_cfa); + current_sp_bytes_offset_from_fa = m_wordsize; + initial_regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_fa); row->SetRegisterInfo(m_lldb_ip_regnum, initial_regloc); unwind_plan.AppendRow(row); @@ -682,6 +770,7 @@ UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI int prologue_completed_sp_bytes_offset_from_cfa; // The sp value before the // epilogue started executed + bool prologue_completed_is_aligned; std::vector prologue_completed_saved_registers; while (current_func_text_offset < size) { @@ -701,22 +790,59 @@ break; } - if (push_rbp_pattern_p()) { - current_sp_bytes_offset_from_cfa += m_wordsize; - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); - UnwindPlan::Row::RegisterLocation regloc; - regloc.SetAtCFAPlusOffset(-row->GetCFAValue().GetOffset()); - row->SetRegisterInfo(m_lldb_fp_regnum, regloc); - saved_registers[m_machine_fp_regnum] = true; - row_updated = true; + auto &cfa_value = row->GetCFAValue(); + auto &afa_value = row->GetAFAValue(); + auto fa_value_ptr = is_aligned ? &afa_value : &cfa_value; + + if (mov_rsp_rbp_pattern_p()) { + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetIsRegisterPlusOffset( + m_lldb_fp_regnum, fa_value_ptr->GetOffset()); + row_updated = true; + } } - else if (mov_rsp_rbp_pattern_p()) { - row->GetCFAValue().SetIsRegisterPlusOffset( - m_lldb_fp_regnum, row->GetCFAValue().GetOffset()); + else if (mov_rsp_rbx_pattern_p()) { + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetIsRegisterPlusOffset( + m_lldb_alt_fp_regnum, fa_value_ptr->GetOffset()); + row_updated = true; + } + } + + else if (and_rsp_pattern_p()) { + current_sp_bytes_offset_from_fa = 0; + afa_value.SetIsRegisterPlusOffset( + m_lldb_sp_regnum, current_sp_bytes_offset_from_fa); + fa_value_ptr = &afa_value; + is_aligned = true; row_updated = true; } + else if (mov_rbp_rsp_pattern_p()) { + if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_fp_regnum) + { + is_aligned = false; + fa_value_ptr = &cfa_value; + afa_value.SetUnspecified(); + row_updated = true; + } + if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum) + current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset(); + } + + else if (mov_rbx_rsp_pattern_p()) { + if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_alt_fp_regnum) + { + is_aligned = false; + fa_value_ptr = &cfa_value; + afa_value.SetUnspecified(); + row_updated = true; + } + if (fa_value_ptr->GetRegisterNumber() == m_lldb_alt_fp_regnum) + current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset(); + } + // This is the start() function (or a pthread equivalent), it starts with a // pushl $0x0 which puts the saved pc value of 0 on the stack. In this // case we want to pretend we didn't see a stack movement at all -- @@ -726,12 +852,12 @@ } else if (push_reg_p(machine_regno)) { - current_sp_bytes_offset_from_cfa += m_wordsize; - // the PUSH instruction has moved the stack pointer - if the CFA is set + current_sp_bytes_offset_from_fa += m_wordsize; + // the PUSH instruction has moved the stack pointer - if the FA is set // in terms of the stack pointer, we need to add a new row of // instructions. - if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) { - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); row_updated = true; } // record where non-volatile (callee-saved, spilled) registers are saved @@ -740,7 +866,10 @@ machine_regno_to_lldb_regno(machine_regno, lldb_regno) && saved_registers[machine_regno] == false) { UnwindPlan::Row::RegisterLocation regloc; - regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_cfa); + if (is_aligned) + regloc.SetAtAFAPlusOffset(-current_sp_bytes_offset_from_fa); + else + regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_fa); row->SetRegisterInfo(lldb_regno, regloc); saved_registers[machine_regno] = true; row_updated = true; @@ -748,7 +877,7 @@ } else if (pop_reg_p(machine_regno)) { - current_sp_bytes_offset_from_cfa -= m_wordsize; + current_sp_bytes_offset_from_fa -= m_wordsize; if (nonvolatile_reg_p(machine_regno) && machine_regno_to_lldb_regno(machine_regno, lldb_regno) && @@ -756,29 +885,29 @@ saved_registers[machine_regno] = false; row->RemoveRegisterInfo(lldb_regno); - if (machine_regno == (int)m_machine_fp_regnum) { - row->GetCFAValue().SetIsRegisterPlusOffset( - m_lldb_sp_regnum, row->GetCFAValue().GetOffset()); + if (lldb_regno == fa_value_ptr->GetRegisterNumber()) { + fa_value_ptr->SetIsRegisterPlusOffset( + m_lldb_sp_regnum, fa_value_ptr->GetOffset()); } in_epilogue = true; row_updated = true; } - // the POP instruction has moved the stack pointer - if the CFA is set in + // the POP instruction has moved the stack pointer - if the FA is set in // terms of the stack pointer, we need to add a new row of instructions. - if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) { - row->GetCFAValue().SetIsRegisterPlusOffset( - m_lldb_sp_regnum, current_sp_bytes_offset_from_cfa); + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetIsRegisterPlusOffset( + m_lldb_sp_regnum, current_sp_bytes_offset_from_fa); row_updated = true; } } else if (pop_misc_reg_p()) { - current_sp_bytes_offset_from_cfa -= m_wordsize; - if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) { - row->GetCFAValue().SetIsRegisterPlusOffset( - m_lldb_sp_regnum, current_sp_bytes_offset_from_cfa); + current_sp_bytes_offset_from_fa -= m_wordsize; + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetIsRegisterPlusOffset( + m_lldb_sp_regnum, current_sp_bytes_offset_from_fa); row_updated = true; } } @@ -787,24 +916,38 @@ // off the stack into rbp (restoring the caller's rbp value). It is the // opposite of ENTER, or 'push rbp, mov rsp rbp'. else if (leave_pattern_p()) { - // We're going to copy the value in rbp into rsp, so re-set the sp offset - // based on the CFAValue. Also, adjust it to recognize that we're - // popping the saved rbp value off the stack. - current_sp_bytes_offset_from_cfa = row->GetCFAValue().GetOffset(); - current_sp_bytes_offset_from_cfa -= m_wordsize; - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); - - // rbp is restored to the caller's value - saved_registers[m_machine_fp_regnum] = false; - row->RemoveRegisterInfo(m_lldb_fp_regnum); - - // cfa is now in terms of rsp again. - row->GetCFAValue().SetIsRegisterPlusOffset( - m_lldb_sp_regnum, row->GetCFAValue().GetOffset()); - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); + if (saved_registers[m_machine_fp_regnum]) { + saved_registers[m_machine_fp_regnum] = false; + row->RemoveRegisterInfo(m_lldb_fp_regnum); + + row_updated = true; + } + + if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_fp_regnum) + { + is_aligned = false; + fa_value_ptr = &cfa_value; + afa_value.SetUnspecified(); + row_updated = true; + } + + if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum) + { + fa_value_ptr->SetIsRegisterPlusOffset( + m_lldb_sp_regnum, fa_value_ptr->GetOffset()); + + current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset(); + } + + current_sp_bytes_offset_from_fa -= m_wordsize; + + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetIsRegisterPlusOffset( + m_lldb_sp_regnum, current_sp_bytes_offset_from_fa); + row_updated = true; + } in_epilogue = true; - row_updated = true; } else if (mov_reg_to_local_stack_frame_p(machine_regno, stack_offset) && @@ -816,12 +959,14 @@ UnwindPlan::Row::RegisterLocation regloc; // stack_offset for 'movq %r15, -80(%rbp)' will be 80. In the Row, we - // want to express this as the offset from the CFA. If the frame base is - // rbp (like the above instruction), the CFA offset for rbp is probably - // 16. So we want to say that the value is stored at the CFA address - + // want to express this as the offset from the FA. If the frame base is + // rbp (like the above instruction), the FA offset for rbp is probably + // 16. So we want to say that the value is stored at the FA address - // 96. - regloc.SetAtCFAPlusOffset( - -(stack_offset + row->GetCFAValue().GetOffset())); + if (is_aligned) + regloc.SetAtAFAPlusOffset(-(stack_offset + fa_value_ptr->GetOffset())); + else + regloc.SetAtCFAPlusOffset(-(stack_offset + fa_value_ptr->GetOffset())); row->SetRegisterInfo(lldb_regno, regloc); @@ -829,17 +974,17 @@ } else if (sub_rsp_pattern_p(stack_offset)) { - current_sp_bytes_offset_from_cfa += stack_offset; - if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) { - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); + current_sp_bytes_offset_from_fa += stack_offset; + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); row_updated = true; } } else if (add_rsp_pattern_p(stack_offset)) { - current_sp_bytes_offset_from_cfa -= stack_offset; - if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) { - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); + current_sp_bytes_offset_from_fa -= stack_offset; + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); row_updated = true; } in_epilogue = true; @@ -847,27 +992,48 @@ else if (push_extended_pattern_p() || push_imm_pattern_p() || push_misc_reg_p()) { - current_sp_bytes_offset_from_cfa += m_wordsize; - if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) { - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); + current_sp_bytes_offset_from_fa += m_wordsize; + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); row_updated = true; } } else if (lea_rsp_pattern_p(stack_offset)) { - current_sp_bytes_offset_from_cfa -= stack_offset; - if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) { - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); + current_sp_bytes_offset_from_fa -= stack_offset; + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); row_updated = true; } if (stack_offset > 0) in_epilogue = true; } - else if (lea_rbp_rsp_pattern_p(stack_offset) && - row->GetCFAValue().GetRegisterNumber() == m_lldb_fp_regnum) { - current_sp_bytes_offset_from_cfa = - row->GetCFAValue().GetOffset() - stack_offset; + else if (lea_rbp_rsp_pattern_p(stack_offset)) { + if (is_aligned && + cfa_value.GetRegisterNumber() == m_lldb_fp_regnum) { + is_aligned = false; + fa_value_ptr = &cfa_value; + afa_value.SetUnspecified(); + row_updated = true; + } + if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum) { + current_sp_bytes_offset_from_fa = + fa_value_ptr->GetOffset() - stack_offset; + } + } + + else if (lea_rbx_rsp_pattern_p(stack_offset)) { + if (is_aligned && + cfa_value.GetRegisterNumber() == m_lldb_alt_fp_regnum) { + is_aligned = false; + fa_value_ptr = &cfa_value; + afa_value.SetUnspecified(); + row_updated = true; + } + if (fa_value_ptr->GetRegisterNumber() == m_lldb_alt_fp_regnum) { + current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset() - stack_offset; + } } else if (ret_pattern_p() && prologue_completed_row.get()) { @@ -877,8 +1043,9 @@ UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *prologue_completed_row.get(); row.reset(newrow); - current_sp_bytes_offset_from_cfa = + current_sp_bytes_offset_from_fa = prologue_completed_sp_bytes_offset_from_cfa; + is_aligned = prologue_completed_is_aligned; saved_registers.clear(); saved_registers.resize(prologue_completed_saved_registers.size(), false); @@ -896,9 +1063,9 @@ // This is used in i386 programs to get the PIC base address for finding // global data else if (call_next_insn_pattern_p()) { - current_sp_bytes_offset_from_cfa += m_wordsize; - if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) { - row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa); + current_sp_bytes_offset_from_fa += m_wordsize; + if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { + fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); row_updated = true; } } @@ -931,7 +1098,8 @@ // track of it either way. if (in_epilogue == false) { prologue_completed_sp_bytes_offset_from_cfa = - current_sp_bytes_offset_from_cfa; + current_sp_bytes_offset_from_fa; + prologue_completed_is_aligned = is_aligned; } m_cur_insn = m_cur_insn + insn_len; Index: source/Symbol/UnwindPlan.cpp =================================================================== --- source/Symbol/UnwindPlan.cpp +++ source/Symbol/UnwindPlan.cpp @@ -29,6 +29,8 @@ case atCFAPlusOffset: case isCFAPlusOffset: + case atAFAPlusOffset: + case isAFAPlusOffset: return m_location.offset == rhs.m_location.offset; case inOtherRegister: @@ -95,6 +97,16 @@ s.PutChar(']'); } break; + case atAFAPlusOffset: + case isAFAPlusOffset: { + s.PutChar('='); + if (m_type == atAFAPlusOffset) + s.PutChar('['); + s.Printf("AFA%+d", m_location.offset); + if (m_type == atAFAPlusOffset) + s.PutChar(']'); + } break; + case inOtherRegister: { const RegisterInfo *other_reg_info = nullptr; if (unwind_plan) @@ -125,8 +137,8 @@ s.Printf("reg(%u)", reg_num); } -bool UnwindPlan::Row::CFAValue:: -operator==(const UnwindPlan::Row::CFAValue &rhs) const { +bool UnwindPlan::Row::FAValue:: +operator==(const UnwindPlan::Row::FAValue &rhs) const { if (m_type == rhs.m_type) { switch (m_type) { case unspecified: @@ -148,7 +160,7 @@ return false; } -void UnwindPlan::Row::CFAValue::Dump(Stream &s, const UnwindPlan *unwind_plan, +void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan, Thread *thread) const { switch (m_type) { case isRegisterPlusOffset: @@ -171,6 +183,7 @@ void UnwindPlan::Row::Clear() { m_cfa_value.SetUnspecified(); + m_afa_value.SetUnspecified(); m_offset = 0; m_register_locations.clear(); } @@ -183,6 +196,12 @@ s.Printf("%4" PRId64 ": CFA=", GetOffset()); m_cfa_value.Dump(s, unwind_plan, thread); + + if (!m_afa_value.IsUnspecified()) { + s.Printf(" AFA="); + m_afa_value.Dump(s, unwind_plan, thread); + } + s.Printf(" => "); for (collection::const_iterator idx = m_register_locations.begin(); idx != m_register_locations.end(); ++idx) { @@ -194,7 +213,8 @@ s.EOL(); } -UnwindPlan::Row::Row() : m_offset(0), m_cfa_value(), m_register_locations() {} +UnwindPlan::Row::Row() + : m_offset(0), m_cfa_value(), m_afa_value(), m_register_locations() {} bool UnwindPlan::Row::GetRegisterInfo( uint32_t reg_num, @@ -296,8 +316,10 @@ } bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const { - return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value && - m_register_locations == rhs.m_register_locations; + return m_offset == rhs.m_offset && + m_cfa_value == rhs.m_cfa_value && + m_afa_value == rhs.m_afa_value && + m_register_locations == rhs.m_register_locations; } void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) { @@ -399,7 +421,7 @@ // UnwindPlan. if (GetRowAtIndex(0).get() == nullptr || GetRowAtIndex(0)->GetCFAValue().GetValueType() == - Row::CFAValue::unspecified) { + Row::FAValue::unspecified) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log) { StreamString s; Index: unittests/UnwindAssembly/x86/Testx86AssemblyInspectionEngine.cpp =================================================================== --- unittests/UnwindAssembly/x86/Testx86AssemblyInspectionEngine.cpp +++ unittests/UnwindAssembly/x86/Testx86AssemblyInspectionEngine.cpp @@ -133,7 +133,7 @@ namespace lldb_private { static std::ostream &operator<<(std::ostream &OS, - const UnwindPlan::Row::CFAValue &CFA) { + const UnwindPlan::Row::FAValue &CFA) { StreamString S; CFA.Dump(S, nullptr, nullptr); return OS << S.GetData(); @@ -2368,7 +2368,7 @@ ASSERT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(data, sizeof(data), sample_range, plan)); - UnwindPlan::Row::CFAValue esp_plus_4, esp_plus_8, ebp_plus_8; + UnwindPlan::Row::FAValue esp_plus_4, esp_plus_8, ebp_plus_8; esp_plus_4.SetIsRegisterPlusOffset(k_esp, 4); esp_plus_8.SetIsRegisterPlusOffset(k_esp, 8); ebp_plus_8.SetIsRegisterPlusOffset(k_ebp, 8); @@ -2402,7 +2402,7 @@ ASSERT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(data, sizeof(data), sample_range, plan)); - UnwindPlan::Row::CFAValue rsp_plus_8, rsp_plus_16, rbp_plus_16; + UnwindPlan::Row::FAValue rsp_plus_8, rsp_plus_16, rbp_plus_16; rsp_plus_8.SetIsRegisterPlusOffset(k_rsp, 8); rsp_plus_16.SetIsRegisterPlusOffset(k_rsp, 16); rbp_plus_16.SetIsRegisterPlusOffset(k_rbp, 16); @@ -2416,6 +2416,65 @@ plan.GetRowForFunctionOffset(sizeof(data) - 1)->GetCFAValue()); } +TEST_F(Testx86AssemblyInspectionEngine, TestStackRealignMSVC_i386) { + std::unique_ptr engine = Geti386Inspector(); + + uint8_t data[] = { + 0x53, // offset 00 -- pushl %ebx + 0x8b, 0xdc, // offset 01 -- movl %esp, %ebx + 0x83, 0xec, 0x08, // offset 03 -- subl $8, %esp + 0x81, 0xe4, 0x00, 0xff, 0xff, 0xff, // offset 06 -- andl $-256, %esp + 0x83, 0xc4, 0x04, // offset 12 -- addl $4, %esp + 0x55, // offset 15 -- pushl %ebp + 0x8b, 0xec, // offset 16 -- movl %esp, %ebp + 0x81, 0xec, 0x00, 0x02, 0x00, 0x00, // offset 18 -- subl $512, %esp + 0x89, 0x7d, 0xfc, // offset 24 -- movl %edi, -4(%ebp) + 0x8b, 0xe5, // offset 27 -- movl %ebp, %esp + 0x5d, // offset 29 -- popl %ebp + 0x8b, 0xe3, // offset 30 -- movl %ebx, %esp + 0x5b, // offset 32 -- popl %ebx + 0xc3 // offset 33 -- retl + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan plan(eRegisterKindLLDB); + ASSERT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(data, sizeof(data), + sample_range, plan)); + + UnwindPlan::Row::FAValue esp_minus_4, esp_plus_0, esp_plus_4, esp_plus_8, + ebx_plus_8, ebp_plus_0; + esp_minus_4.SetIsRegisterPlusOffset(k_esp, -4); + esp_plus_0.SetIsRegisterPlusOffset(k_esp, 0); + esp_plus_4.SetIsRegisterPlusOffset(k_esp, 4); + esp_plus_8.SetIsRegisterPlusOffset(k_esp, 8); + ebx_plus_8.SetIsRegisterPlusOffset(k_ebx, 8); + ebp_plus_0.SetIsRegisterPlusOffset(k_ebp, 0); + + // Test CFA + EXPECT_EQ(esp_plus_4, plan.GetRowForFunctionOffset(0)->GetCFAValue()); + EXPECT_EQ(esp_plus_8, plan.GetRowForFunctionOffset(1)->GetCFAValue()); + for (size_t i = 3; i < 33; ++i) + EXPECT_EQ(ebx_plus_8, plan.GetRowForFunctionOffset(i)->GetCFAValue()) + << "i: " << i; + EXPECT_EQ(esp_plus_4, plan.GetRowForFunctionOffset(33)->GetCFAValue()); + + // Test AFA + EXPECT_EQ(esp_plus_0, plan.GetRowForFunctionOffset(12)->GetAFAValue()); + EXPECT_EQ(esp_minus_4, plan.GetRowForFunctionOffset(15)->GetAFAValue()); + EXPECT_EQ(esp_plus_0, plan.GetRowForFunctionOffset(16)->GetAFAValue()); + for (size_t i = 18; i < 30; ++i) + EXPECT_EQ(ebp_plus_0, plan.GetRowForFunctionOffset(i)->GetAFAValue()) + << "i: " << i; + EXPECT_EQ(esp_minus_4, plan.GetRowForFunctionOffset(30)->GetAFAValue()); + + // Test saved register + UnwindPlan::Row::RegisterLocation reg_loc; + EXPECT_TRUE( + plan.GetRowForFunctionOffset(27)->GetRegisterInfo(k_edi, reg_loc)); + EXPECT_TRUE(reg_loc.IsAtAFAPlusOffset()); + EXPECT_EQ(-4, reg_loc.GetOffset()); +} + // Give the disassembler random bytes to test that it doesn't exceed // the bounds of the array when run under clang's address sanitizer. TEST_F(Testx86AssemblyInspectionEngine, TestDisassemblyJunkBytes) {