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:
Jason Molenda 2014-11-18 02:27:42 +00:00
parent 9a91e4a18a
commit e9c7ecf66e
6 changed files with 166 additions and 5 deletions

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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: ");