Skip to content

Commit

Permalink
Add debug_frame section support
Browse files Browse the repository at this point in the history
Summary:
This is a beefed-up version of D33504, which adds support for dwarf 4
debug_frame section format.

The main difference here is that the decision whether to use eh_frame or
debug_frame is done on a per-function basis instead of per-object file.
This is necessary because one module can contain both sections (for
example, the start files added by the linker will typically pull in
eh_frame), but we want to be able to access both, for maximum
information.

I also add unit test for parsing various CFI formats (eh_frame,
debug_frame v3 and debug_frame v4).

Reviewers: jasonmolenda, clayborg

Subscribers: mgorny, aprantl, abidh, lldb-commits, tatyana-krasnukha

Differential Revision: https://reviews.llvm.org/D34613

llvm-svn: 306397
  • Loading branch information
labath committed Jun 27, 2017
1 parent 7caff0e commit cdda23e
Show file tree
Hide file tree
Showing 10 changed files with 544 additions and 105 deletions.
10 changes: 9 additions & 1 deletion lldb/include/lldb/Symbol/DWARFCallFrameInfo.h
Expand Up @@ -37,7 +37,7 @@ class DWARFCallFrameInfo {
DWARFCallFrameInfo(ObjectFile &objfile, lldb::SectionSP &section,
lldb::RegisterKind reg_kind, bool is_eh_frame);

~DWARFCallFrameInfo();
~DWARFCallFrameInfo() = default;

// Locate an AddressRange that includes the provided Address in this
// object's eh_frame/debug_info
Expand Down Expand Up @@ -74,12 +74,20 @@ class DWARFCallFrameInfo {

private:
enum { CFI_AUG_MAX_SIZE = 8, CFI_HEADER_SIZE = 8 };
enum CFIVersion {
CFI_VERSION1 = 1, // DWARF v.2
CFI_VERSION3 = 3, // DWARF v.3
CFI_VERSION4 = 4 // DWARF v.4, v.5
};

struct CIE {
dw_offset_t cie_offset;
uint8_t version;
char augmentation[CFI_AUG_MAX_SIZE]; // This is typically empty or very
// short.
uint8_t address_size = sizeof(uint32_t); // The size of a target address.
uint8_t segment_size = 0; // The size of a segment selector.

uint32_t code_align;
int32_t data_align;
uint32_t return_addr_reg_num;
Expand Down
19 changes: 15 additions & 4 deletions lldb/include/lldb/Symbol/FuncUnwinders.h
Expand Up @@ -99,6 +99,13 @@ class FuncUnwinders {
Thread &thread,
int current_offset);

lldb::UnwindPlanSP GetDebugFrameUnwindPlan(Target &target,
int current_offset);

lldb::UnwindPlanSP GetDebugFrameAugmentedUnwindPlan(Target &target,
Thread &thread,
int current_offset);

lldb::UnwindPlanSP GetCompactUnwindUnwindPlan(Target &target,
int current_offset);

Expand Down Expand Up @@ -126,10 +133,12 @@ class FuncUnwinders {

lldb::UnwindPlanSP m_unwind_plan_assembly_sp;
lldb::UnwindPlanSP m_unwind_plan_eh_frame_sp;
lldb::UnwindPlanSP m_unwind_plan_eh_frame_augmented_sp; // augmented by
// assembly inspection
// so it's valid
// everywhere
lldb::UnwindPlanSP m_unwind_plan_debug_frame_sp;

// augmented by assembly inspection so it's valid everywhere
lldb::UnwindPlanSP m_unwind_plan_eh_frame_augmented_sp;
lldb::UnwindPlanSP m_unwind_plan_debug_frame_augmented_sp;

std::vector<lldb::UnwindPlanSP> m_unwind_plan_compact_unwind;
lldb::UnwindPlanSP m_unwind_plan_arm_unwind_sp;
lldb::UnwindPlanSP m_unwind_plan_fast_sp;
Expand All @@ -139,7 +148,9 @@ class FuncUnwinders {
// Fetching the UnwindPlans can be expensive - if we've already attempted
// to get one & failed, don't try again.
bool m_tried_unwind_plan_assembly : 1, m_tried_unwind_plan_eh_frame : 1,
m_tried_unwind_plan_debug_frame : 1,
m_tried_unwind_plan_eh_frame_augmented : 1,
m_tried_unwind_plan_debug_frame_augmented : 1,
m_tried_unwind_plan_compact_unwind : 1,
m_tried_unwind_plan_arm_unwind : 1, m_tried_unwind_fast : 1,
m_tried_unwind_arch_default : 1,
Expand Down
4 changes: 4 additions & 0 deletions lldb/include/lldb/Symbol/UnwindTable.h
Expand Up @@ -27,6 +27,7 @@ class UnwindTable {
~UnwindTable();

lldb_private::DWARFCallFrameInfo *GetEHFrameInfo();
lldb_private::DWARFCallFrameInfo *GetDebugFrameInfo();

lldb_private::CompactUnwindInfo *GetCompactUnwindInfo();

Expand Down Expand Up @@ -58,6 +59,8 @@ class UnwindTable {
void Dump(Stream &s);

void Initialize();
llvm::Optional<AddressRange> GetAddressRange(const Address &addr,
SymbolContext &sc);

typedef std::map<lldb::addr_t, lldb::FuncUnwindersSP> collection;
typedef collection::iterator iterator;
Expand All @@ -70,6 +73,7 @@ class UnwindTable {
std::mutex m_mutex;

std::unique_ptr<DWARFCallFrameInfo> m_eh_frame_up;
std::unique_ptr<DWARFCallFrameInfo> m_debug_frame_up;
std::unique_ptr<CompactUnwindInfo> m_compact_unwind_up;
std::unique_ptr<ArmUnwindInfo> m_arm_unwind_up;

Expand Down
17 changes: 17 additions & 0 deletions lldb/source/Commands/CommandObjectTarget.cpp
Expand Up @@ -3426,6 +3426,23 @@ class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed {
result.GetOutputStream().Printf("\n");
}

if (UnwindPlanSP plan_sp =
func_unwinders_sp->GetDebugFrameUnwindPlan(*target, 0)) {
result.GetOutputStream().Printf("debug_frame UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}

if (UnwindPlanSP plan_sp =
func_unwinders_sp->GetDebugFrameAugmentedUnwindPlan(*target,
*thread, 0)) {
result.GetOutputStream().Printf("debug_frame augmented UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}

UnwindPlanSP arm_unwind_sp =
func_unwinders_sp->GetArmUnwindUnwindPlan(*target, 0);
if (arm_unwind_sp) {
Expand Down
63 changes: 48 additions & 15 deletions lldb/source/Symbol/DWARFCallFrameInfo.cpp
Expand Up @@ -161,8 +161,6 @@ DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile &objfile,
m_fde_index(), m_fde_index_initialized(false),
m_is_eh_frame(is_eh_frame) {}

DWARFCallFrameInfo::~DWARFCallFrameInfo() {}

bool DWARFCallFrameInfo::GetUnwindPlan(Address addr, UnwindPlan &unwind_plan) {
FDEEntryMap::Entry fde_entry;

Expand Down Expand Up @@ -276,6 +274,12 @@ DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) {
// cie.cieID = cieID;
cie_sp->ptr_encoding = DW_EH_PE_absptr; // default
cie_sp->version = m_cfi_data.GetU8(&offset);
if (cie_sp->version > CFI_VERSION4) {
Host::SystemLog(Host::eSystemLogError,
"CIE parse error: CFI version %d is not supported\n",
cie_sp->version);
return nullptr;
}

for (i = 0; i < CFI_AUG_MAX_SIZE; ++i) {
cie_sp->augmentation[i] = m_cfi_data.GetU8(&offset);
Expand All @@ -294,11 +298,23 @@ DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) {
"CIE parse error: CIE augmentation string was too large "
"for the fixed sized buffer of %d bytes.\n",
CFI_AUG_MAX_SIZE);
return cie_sp;
return nullptr;
}

// m_cfi_data uses address size from target architecture of the process
// may ignore these fields?
if (!m_is_eh_frame && cie_sp->version >= CFI_VERSION4) {
cie_sp->address_size = m_cfi_data.GetU8(&offset);
cie_sp->segment_size = m_cfi_data.GetU8(&offset);
}

cie_sp->code_align = (uint32_t)m_cfi_data.GetULEB128(&offset);
cie_sp->data_align = (int32_t)m_cfi_data.GetSLEB128(&offset);
cie_sp->return_addr_reg_num = m_cfi_data.GetU8(&offset);

cie_sp->return_addr_reg_num =
!m_is_eh_frame && cie_sp->version >= CFI_VERSION3
? static_cast<uint32_t>(m_cfi_data.GetULEB128(&offset))
: m_cfi_data.GetU8(&offset);

if (cie_sp->augmentation[0]) {
// Get the length of the eh_frame augmentation data
Expand Down Expand Up @@ -461,24 +477,40 @@ void DWARFCallFrameInfo::GetFDEIndex() {
m_fde_index_initialized = true;
return;
}

// An FDE entry contains CIE_pointer in debug_frame in same place as cie_id
// in eh_frame. CIE_pointer is an offset into the .debug_frame section.
// So, variable cie_offset should be equal to cie_id for debug_frame.
// FDE entries with cie_id == 0 shouldn't be ignored for it.
if ((cie_id == 0 && m_is_eh_frame) || cie_id == UINT32_MAX || len == 0) {
auto cie_sp = ParseCIE(current_entry);
if (!cie_sp) {
// Cannot parse, the reason is already logged
m_fde_index.Clear();
m_fde_index_initialized = true;
return;
}

m_cie_map[current_entry] = std::move(cie_sp);
offset = next_entry;
continue;
}

if (!m_is_eh_frame)
cie_offset = cie_id;

if (cie_offset > m_cfi_data.GetByteSize()) {
Host::SystemLog(
Host::eSystemLogError,
"error: Invalid cie offset of 0x%x found in cie/fde at 0x%x\n",
cie_offset, current_entry);
Host::SystemLog(Host::eSystemLogError,
"error: Invalid cie offset of 0x%x "
"found in cie/fde at 0x%x\n",
cie_offset, current_entry);
// Don't trust anything in this eh_frame section if we find blatantly
// invalid data.
m_fde_index.Clear();
m_fde_index_initialized = true;
return;
}

if (cie_id == 0 || cie_id == UINT32_MAX || len == 0) {
m_cie_map[current_entry] = ParseCIE(current_entry);
offset = next_entry;
continue;
}

const CIE *cie = GetCIE(cie_offset);
if (cie) {
const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress();
Expand Down Expand Up @@ -531,7 +563,8 @@ bool DWARFCallFrameInfo::FDEToUnwindPlan(dw_offset_t dwarf_offset,
cie_offset = m_cfi_data.GetU32(&offset);
}

assert(cie_offset != 0 && cie_offset != UINT32_MAX);
// FDE entries with zeroth cie_offset may occur for debug_frame.
assert(!(m_is_eh_frame && 0 == cie_offset) && cie_offset != UINT32_MAX);

// Translate the CIE_id from the eh_frame format, which
// is relative to the FDE offset, into a __eh_frame section
Expand Down

0 comments on commit cdda23e

Please sign in to comment.