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
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;
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
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 ()
{
}
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:
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
// 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;
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;
}
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;
+}
{
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: ");