From e9c7ecf66ef6f62c79e0b1bdb0a66b2fe3192b0c Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 18 Nov 2014 02:27:42 +0000 Subject: [PATCH] Read the LSDA and Personality Routine function address out of the eh_frame data. These two pieces of information are used in the process of exception handler unwinding on SysV ABI systems. This patch reads the data from the eh_frame section (DWARFCallFrameInfo.cpp), allows for it to be saved & read out of a given UnwindPlan (UnwindPlan.h, UnwindPlan.cpp) - as well as printing the information in the UnwindPlan::Dump method - and adds methods to the FuncUnwinders object so that higher levels can query if a given function has an LSDA / personality routine defined. It's only lightly tested, but seems to be working correctly as long as your have this information in eh_frame. Does not address getting this information from compact unwind yet on Darwin systems. llvm-svn: 222214 --- lldb/include/lldb/Symbol/DWARFCallFrameInfo.h | 8 +++++- lldb/include/lldb/Symbol/FuncUnwinders.h | 14 +++++++++ lldb/include/lldb/Symbol/UnwindPlan.h | 37 +++++++++++++++++++++++- lldb/source/Symbol/DWARFCallFrameInfo.cpp | 41 +++++++++++++++++++++++++-- lldb/source/Symbol/FuncUnwinders.cpp | 33 +++++++++++++++++++++ lldb/source/Symbol/UnwindPlan.cpp | 38 +++++++++++++++++++++++++ 6 files changed, 166 insertions(+), 5 deletions(-) diff --git a/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h b/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h index e67a5a2..27d1a52 100644 --- a/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h +++ b/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h @@ -91,11 +91,17 @@ private: dw_offset_t inst_offset; // offset of CIE instructions in mCFIData uint32_t inst_length; // length of CIE instructions in mCFIData uint8_t ptr_encoding; + uint8_t lsda_addr_encoding; // The encoding of the LSDA address in the FDE augmentation data + lldb::addr_t personality_loc; // (file) address of the pointer to the personality routine lldb_private::UnwindPlan::Row initial_row; CIE(dw_offset_t offset) : cie_offset(offset), version (-1), code_align (0), data_align (0), return_addr_reg_num (LLDB_INVALID_REGNUM), inst_offset (0), - inst_length (0), ptr_encoding (0), initial_row() {} + inst_length (0), ptr_encoding (0), + lsda_addr_encoding (DW_EH_PE_omit), personality_loc (LLDB_INVALID_ADDRESS), + initial_row () + { + } }; typedef std::shared_ptr CIESP; diff --git a/lldb/include/lldb/Symbol/FuncUnwinders.h b/lldb/include/lldb/Symbol/FuncUnwinders.h index 8e29e4e..a3df7fb 100644 --- a/lldb/include/lldb/Symbol/FuncUnwinders.h +++ b/lldb/include/lldb/Symbol/FuncUnwinders.h @@ -68,6 +68,20 @@ public: return m_range.ContainsFileAddress (addr); } + // A function may have a Language Specific Data Area specified -- a block of data in + // the object file which is used in the processing of an exception throw / catch. + // If any of the UnwindPlans have the address of the LSDA region for this function, + // this will return it. + Address + GetLSDAAddress () const; + + // A function may have a Personality Routine associated with it -- used in the + // processing of throwing an exception. If any of the UnwindPlans have the + // address of the personality routine, this will return it. Read the target-pointer + // at this address to get the personality function address. + Address + GetPersonalityRoutinePtrAddress () const; + private: lldb::UnwindAssemblySP diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h index ba8e532..ab846e1 100644 --- a/lldb/include/lldb/Symbol/UnwindPlan.h +++ b/lldb/include/lldb/Symbol/UnwindPlan.h @@ -379,7 +379,9 @@ public: m_return_addr_register (LLDB_INVALID_REGNUM), m_source_name (), m_plan_is_sourced_from_compiler (eLazyBoolCalculate), - m_plan_is_valid_at_all_instruction_locations (eLazyBoolCalculate) + m_plan_is_valid_at_all_instruction_locations (eLazyBoolCalculate), + m_lsda_address (), + m_personality_func_addr () { } @@ -505,11 +507,39 @@ public: m_plan_valid_address_range.Clear(); m_register_kind = lldb::eRegisterKindDWARF; m_source_name.Clear(); + m_plan_is_sourced_from_compiler = eLazyBoolCalculate; + m_plan_is_valid_at_all_instruction_locations = eLazyBoolCalculate; + m_lsda_address.Clear(); + m_personality_func_addr.Clear(); } const RegisterInfo * GetRegisterInfo (Thread* thread, uint32_t reg_num) const; + Address + GetLSDAAddress () const + { + return m_lsda_address; + } + + void + SetLSDAAddress (Address lsda_addr) + { + m_lsda_address = lsda_addr; + } + + Address + GetPersonalityFunctionPtr () const + { + return m_personality_func_addr; + } + + void + SetPersonalityFunctionPtr (Address presonality_func_ptr) + { + m_personality_func_addr = presonality_func_ptr; + } + private: @@ -523,6 +553,11 @@ private: lldb_private::ConstString m_source_name; // for logging, where this UnwindPlan originated from lldb_private::LazyBool m_plan_is_sourced_from_compiler; lldb_private::LazyBool m_plan_is_valid_at_all_instruction_locations; + + Address m_lsda_address; // Where the language specific data area exists in the module - used + // in exception handling. + Address m_personality_func_addr; // The address of a pointer to the personality function - used in + // exception handling. }; // class UnwindPlan } // namespace lldb_private diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp index a9da631..78d2623 100644 --- a/lldb/source/Symbol/DWARFCallFrameInfo.cpp +++ b/lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -218,20 +218,27 @@ DWARFCallFrameInfo::ParseCIE (const dw_offset_t cie_offset) // FDE, which is the address of a language-specific // data area (LSDA). The size of the LSDA pointer is // specified by the pointer encoding used. - m_cfi_data.GetU8(&offset); + cie_sp->lsda_addr_encoding = m_cfi_data.GetU8(&offset); break; case 'P': // Indicates the presence of two arguments in the - // Augmentation Data of the cie_sp-> The first argument + // Augmentation Data of the CIE. The first argument // is 1-byte and represents the pointer encoding // used for the second argument, which is the // address of a personality routine handler. The // size of the personality routine pointer is // specified by the pointer encoding used. + // + // The address of the personality function will + // be stored at this location. Pre-execution, it + // will be all zero's so don't read it until we're + // trying to do an unwind & the reloc has been + // resolved. { uint8_t arg_ptr_encoding = m_cfi_data.GetU8(&offset); - m_cfi_data.GetGNUEHPointer(&offset, arg_ptr_encoding, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS); + const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress(); + cie_sp->personality_loc = m_cfi_data.GetGNUEHPointer(&offset, arg_ptr_encoding, pc_rel_addr, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS); } break; @@ -449,11 +456,39 @@ DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t dwarf_offset, Address startaddr AddressRange range (range_base, m_objfile.GetAddressByteSize(), m_objfile.GetSectionList()); range.SetByteSize (range_len); + addr_t lsda_data_file_address = LLDB_INVALID_ADDRESS; + if (cie->augmentation[0] == 'z') { uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset); + if (aug_data_len != 0 && cie->lsda_addr_encoding != DW_EH_PE_omit) + { + offset_t saved_offset = offset; + lsda_data_file_address = m_cfi_data.GetGNUEHPointer(&offset, cie->lsda_addr_encoding, pc_rel_addr, text_addr, data_addr); + if (offset - saved_offset != aug_data_len) + { + // There is more in the augmentation region than we know how to process; + // don't read anything. + lsda_data_file_address = LLDB_INVALID_ADDRESS; + } + offset = saved_offset; + } offset += aug_data_len; } + Address lsda_data; + Address personality_function_ptr; + + if (lsda_data_file_address != LLDB_INVALID_ADDRESS && cie->personality_loc != LLDB_INVALID_ADDRESS) + { + m_objfile.GetModule()->ResolveFileAddress (lsda_data_file_address, lsda_data); + m_objfile.GetModule()->ResolveFileAddress (cie->personality_loc, personality_function_ptr); + } + + if (lsda_data.IsValid() && personality_function_ptr.IsValid()) + { + unwind_plan.SetLSDAAddress (lsda_data); + unwind_plan.SetPersonalityFunctionPtr (personality_function_ptr); + } uint32_t reg_num = 0; int32_t op_offset = 0; diff --git a/lldb/source/Symbol/FuncUnwinders.cpp b/lldb/source/Symbol/FuncUnwinders.cpp index bee84cb..2c47979 100644 --- a/lldb/source/Symbol/FuncUnwinders.cpp +++ b/lldb/source/Symbol/FuncUnwinders.cpp @@ -209,3 +209,36 @@ FuncUnwinders::GetUnwindAssemblyProfiler () } return assembly_profiler_sp; } + +Address +FuncUnwinders::GetLSDAAddress () const +{ + Address lsda_addr; + if (m_unwind_plan_non_call_site_sp->GetLSDAAddress().IsValid()) + { + lsda_addr = m_unwind_plan_non_call_site_sp->GetLSDAAddress().IsValid(); + } + else if (m_unwind_plan_call_site_sp->GetLSDAAddress().IsValid()) + { + lsda_addr = m_unwind_plan_non_call_site_sp->GetLSDAAddress().IsValid(); + } + + return lsda_addr; +} + + +Address +FuncUnwinders::GetPersonalityRoutinePtrAddress () const +{ + Address personality_addr; + if (m_unwind_plan_non_call_site_sp->GetPersonalityFunctionPtr().IsValid()) + { + personality_addr = m_unwind_plan_non_call_site_sp->GetPersonalityFunctionPtr().IsValid(); + } + else if (m_unwind_plan_call_site_sp->GetPersonalityFunctionPtr().IsValid()) + { + personality_addr = m_unwind_plan_non_call_site_sp->GetPersonalityFunctionPtr().IsValid(); + } + + return personality_addr; +} diff --git a/lldb/source/Symbol/UnwindPlan.cpp b/lldb/source/Symbol/UnwindPlan.cpp index 5680540..d32955e 100644 --- a/lldb/source/Symbol/UnwindPlan.cpp +++ b/lldb/source/Symbol/UnwindPlan.cpp @@ -468,6 +468,44 @@ UnwindPlan::Dump (Stream& s, Thread *thread, lldb::addr_t base_addr) const { s.Printf ("This UnwindPlan originally sourced from %s\n", m_source_name.GetCString()); } + if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) + { + TargetSP target_sp(thread->CalculateTarget()); + addr_t lsda_load_addr = m_lsda_address.GetLoadAddress (target_sp.get()); + addr_t personality_func_load_addr = m_personality_func_addr.GetLoadAddress (target_sp.get()); + + if (lsda_load_addr != LLDB_INVALID_ADDRESS && personality_func_load_addr != LLDB_INVALID_ADDRESS) + { + s.Printf("LSDA address 0x%" PRIx64 ", personality routine is at address 0x%" PRIx64 "\n", + lsda_load_addr, personality_func_load_addr); + } + } + s.Printf ("This UnwindPlan is sourced from the compiler: "); + switch (m_plan_is_sourced_from_compiler) + { + case eLazyBoolYes: + s.Printf ("yes.\n"); + break; + case eLazyBoolNo: + s.Printf ("no.\n"); + break; + case eLazyBoolCalculate: + s.Printf ("not specified.\n"); + break; + } + s.Printf ("This UnwindPlan is valid at all instruction locations: "); + switch (m_plan_is_valid_at_all_instruction_locations) + { + case eLazyBoolYes: + s.Printf ("yes.\n"); + break; + case eLazyBoolNo: + s.Printf ("no.\n"); + break; + case eLazyBoolCalculate: + s.Printf ("not specified.\n"); + break; + } if (m_plan_valid_address_range.GetBaseAddress().IsValid() && m_plan_valid_address_range.GetByteSize() > 0) { s.PutCString ("Address range of this UnwindPlan: "); -- 2.7.4