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. <rdar://problem/18742797> llvm-svn: 222214
This commit is contained in:
parent
9a91e4a18a
commit
e9c7ecf66e
|
@ -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<CIE> CIESP;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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: ");
|
||||
|
|
Loading…
Reference in New Issue