DWARFCallFrameInfo.cpp [plain text]
#include <list>
#include "lldb/Core/Log.h"
#include "lldb/Core/Section.h"
#include "lldb/Symbol/DWARFCallFrameInfo.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Core/Section.h"
#include "lldb/Target/Thread.h"
#include "lldb/Symbol/UnwindPlan.h"
using namespace lldb;
using namespace lldb_private;
DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile& objfile, SectionSP& section, uint32_t reg_kind, bool is_eh_frame) :
m_objfile (objfile),
m_section (section),
m_reg_kind (reg_kind), m_flags (),
m_cie_map (),
m_cfi_data (),
m_cfi_data_initialized (false),
m_fde_index (),
m_fde_index_initialized (false),
m_is_eh_frame (is_eh_frame)
{
}
DWARFCallFrameInfo::~DWARFCallFrameInfo()
{
}
bool
DWARFCallFrameInfo::GetAddressRange (Address addr, AddressRange &range)
{
FDEEntry fde_entry;
if (GetFDEEntryByAddress (addr, fde_entry) == false)
return false;
range = fde_entry.bounds;
return true;
}
bool
DWARFCallFrameInfo::GetUnwindPlan (Address addr, UnwindPlan& unwind_plan)
{
FDEEntry fde_entry;
if (GetFDEEntryByAddress (addr, fde_entry) == false)
return false;
return FDEToUnwindPlan (fde_entry.offset, addr, unwind_plan);
}
bool
DWARFCallFrameInfo::GetFDEEntryByAddress (Address addr, FDEEntry& fde_entry)
{
if (m_section.get() == NULL || m_section->IsEncrypted())
return false;
GetFDEIndex();
struct FDEEntry searchfde;
searchfde.bounds = AddressRange (addr, 1);
std::vector<FDEEntry>::const_iterator idx;
if (m_fde_index.size() == 0)
return false;
idx = std::lower_bound (m_fde_index.begin(), m_fde_index.end(), searchfde);
if (idx == m_fde_index.end())
{
--idx;
}
if (idx != m_fde_index.begin() && idx->bounds.GetBaseAddress().GetOffset() != addr.GetOffset())
{
--idx;
}
if (idx->bounds.ContainsFileAddress (addr))
{
fde_entry = *idx;
return true;
}
return false;
}
const DWARFCallFrameInfo::CIE*
DWARFCallFrameInfo::GetCIE(dw_offset_t cie_offset)
{
cie_map_t::iterator pos = m_cie_map.find(cie_offset);
if (pos != m_cie_map.end())
{
if (pos->second.get() == NULL)
pos->second = ParseCIE (cie_offset);
return pos->second.get();
}
return NULL;
}
DWARFCallFrameInfo::CIESP
DWARFCallFrameInfo::ParseCIE (const dw_offset_t cie_offset)
{
CIESP cie_sp(new CIE(cie_offset));
dw_offset_t offset = cie_offset;
if (m_cfi_data_initialized == false)
{
m_section->ReadSectionDataFromObjectFile (&m_objfile, m_cfi_data);
m_cfi_data_initialized = true;
}
const uint32_t length = m_cfi_data.GetU32(&offset);
const dw_offset_t cie_id = m_cfi_data.GetU32(&offset);
const dw_offset_t end_offset = cie_offset + length + 4;
if (length > 0 && ((!m_is_eh_frame && cie_id == 0xfffffffful) || (m_is_eh_frame && cie_id == 0ul)))
{
size_t i;
cie_sp->ptr_encoding = DW_EH_PE_absptr;
cie_sp->version = m_cfi_data.GetU8(&offset);
for (i=0; i<CFI_AUG_MAX_SIZE; ++i)
{
cie_sp->augmentation[i] = m_cfi_data.GetU8(&offset);
if (cie_sp->augmentation[i] == '\0')
{
for (size_t j = i+1; j<CFI_AUG_MAX_SIZE; ++j)
cie_sp->augmentation[j] = '\0';
break;
}
}
if (i == CFI_AUG_MAX_SIZE && cie_sp->augmentation[CFI_AUG_MAX_SIZE-1] != '\0')
{
fprintf(stderr, "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;
}
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);
if (cie_sp->augmentation[0])
{
const size_t aug_data_len = (size_t)m_cfi_data.GetULEB128(&offset);
const size_t aug_data_end = offset + aug_data_len;
const size_t aug_str_len = strlen(cie_sp->augmentation);
if (cie_sp->augmentation[0] == 'z')
{
size_t aug_str_idx = 0;
for (aug_str_idx = 1; aug_str_idx < aug_str_len; aug_str_idx++)
{
char aug = cie_sp->augmentation[aug_str_idx];
switch (aug)
{
case 'L':
m_cfi_data.GetU8(&offset);
break;
case 'P':
{
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);
}
break;
case 'R':
cie_sp->ptr_encoding = m_cfi_data.GetU8(&offset);
break;
}
}
}
else if (strcmp(cie_sp->augmentation, "eh") == 0)
{
}
offset = (uint32_t)aug_data_end;
}
if (end_offset > offset)
{
cie_sp->inst_offset = offset;
cie_sp->inst_length = end_offset - offset;
}
while (offset < end_offset)
{
uint8_t inst = m_cfi_data.GetU8(&offset);
uint8_t primary_opcode = inst & 0xC0;
uint8_t extended_opcode = inst & 0x3F;
if (extended_opcode == DW_CFA_def_cfa)
{
uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
int op_offset = (int32_t)m_cfi_data.GetULEB128(&offset);
cie_sp->initial_row.SetCFARegister (reg_num);
cie_sp->initial_row.SetCFAOffset (op_offset);
continue;
}
if (primary_opcode == DW_CFA_offset)
{
uint32_t reg_num = extended_opcode;
int op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * cie_sp->data_align;
UnwindPlan::Row::RegisterLocation reg_location;
reg_location.SetAtCFAPlusOffset(op_offset);
cie_sp->initial_row.SetRegisterInfo (reg_num, reg_location);
continue;
}
if (extended_opcode == DW_CFA_nop)
{
continue;
}
break; }
}
return cie_sp;
}
void
DWARFCallFrameInfo::GetFDEIndex ()
{
if (m_section.get() == NULL || m_section->IsEncrypted())
return;
if (m_fde_index_initialized)
return;
dw_offset_t offset = 0;
if (m_cfi_data_initialized == false)
{
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
if (log)
{
log->Printf ("Reading eh_frame information for %s", m_objfile.GetFileSpec().GetFilename().GetCString());
}
m_section->ReadSectionDataFromObjectFile (&m_objfile, m_cfi_data);
m_cfi_data_initialized = true;
}
while (m_cfi_data.ValidOffsetForDataOfSize (offset, 8))
{
const dw_offset_t current_entry = offset;
uint32_t len = m_cfi_data.GetU32 (&offset);
dw_offset_t next_entry = current_entry + len + 4;
dw_offset_t cie_id = m_cfi_data.GetU32 (&offset);
if (cie_id == 0 || cie_id == UINT32_MAX)
{
m_cie_map[current_entry] = ParseCIE (current_entry);
offset = next_entry;
continue;
}
const dw_offset_t cie_offset = current_entry + 4 - cie_id;
const CIE *cie = GetCIE (cie_offset);
if (cie)
{
const lldb::addr_t pc_rel_addr = m_section->GetFileAddress();
const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS;
const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS;
lldb::addr_t addr = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr);
lldb::addr_t length = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr);
FDEEntry fde;
fde.bounds = AddressRange (addr, length, m_objfile.GetSectionList());
fde.offset = current_entry;
m_fde_index.push_back(fde);
}
else
{
fprintf (stderr,
"error: unable to find CIE at 0x%8.8x for cie_id = 0x%8.8x for entry at 0x%8.8x.\n",
cie_offset,
cie_id,
current_entry);
}
offset = next_entry;
}
std::sort (m_fde_index.begin(), m_fde_index.end());
m_fde_index_initialized = true;
}
bool
DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t offset, Address startaddr, UnwindPlan& unwind_plan)
{
dw_offset_t current_entry = offset;
if (m_section.get() == NULL || m_section->IsEncrypted())
return false;
if (m_cfi_data_initialized == false)
{
m_section->ReadSectionDataFromObjectFile (&m_objfile, m_cfi_data);
m_cfi_data_initialized = true;
}
uint32_t length = m_cfi_data.GetU32 (&offset);
dw_offset_t cie_offset = m_cfi_data.GetU32 (&offset);
assert (cie_offset != 0 && cie_offset != UINT32_MAX);
if (m_is_eh_frame)
{
unwind_plan.SetSourceName ("eh_frame CFI");
cie_offset = current_entry + 4 - cie_offset;
}
else
{
unwind_plan.SetSourceName ("DWARF CFI");
}
const CIE *cie = GetCIE (cie_offset);
assert (cie != NULL);
const dw_offset_t end_offset = current_entry + length + 4;
const lldb::addr_t pc_rel_addr = m_section->GetFileAddress();
const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS;
const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS;
lldb::addr_t range_base = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr);
lldb::addr_t range_len = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr);
AddressRange range (range_base, m_objfile.GetAddressByteSize(), m_objfile.GetSectionList());
range.SetByteSize (range_len);
if (cie->augmentation[0] == 'z')
{
uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
offset += aug_data_len;
}
uint32_t reg_num = 0;
int32_t op_offset = 0;
uint32_t tmp_uval32;
uint32_t code_align = cie->code_align;
int32_t data_align = cie->data_align;
unwind_plan.SetPlanValidAddressRange (range);
UnwindPlan::Row row = cie->initial_row;
unwind_plan.SetRegisterKind (m_reg_kind);
UnwindPlan::Row::RegisterLocation reg_location;
while (m_cfi_data.ValidOffset(offset) && offset < end_offset)
{
uint8_t inst = m_cfi_data.GetU8(&offset);
uint8_t primary_opcode = inst & 0xC0;
uint8_t extended_opcode = inst & 0x3F;
if (primary_opcode)
{
switch (primary_opcode)
{
case DW_CFA_advance_loc : { unwind_plan.AppendRow(row);
row.SlideOffset(extended_opcode * code_align);
}
break;
case DW_CFA_offset :
{ reg_num = extended_opcode;
op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align;
reg_location.SetAtCFAPlusOffset(op_offset);
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_restore :
{ reg_num = extended_opcode;
if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0).GetRegisterInfo(reg_num, reg_location))
row.SetRegisterInfo (reg_num, reg_location);
}
break;
}
}
else
{
switch (extended_opcode)
{
case DW_CFA_nop : break;
case DW_CFA_set_loc : {
unwind_plan.AppendRow(row);
row.SetOffset(m_cfi_data.GetPointer(&offset) - startaddr.GetFileAddress());
}
break;
case DW_CFA_advance_loc1 : {
unwind_plan.AppendRow(row);
row.SlideOffset (m_cfi_data.GetU8(&offset) * code_align);
}
break;
case DW_CFA_advance_loc2 : {
unwind_plan.AppendRow(row);
row.SlideOffset (m_cfi_data.GetU16(&offset) * code_align);
}
break;
case DW_CFA_advance_loc4 : {
unwind_plan.AppendRow(row);
row.SlideOffset (m_cfi_data.GetU32(&offset) * code_align);
}
break;
case DW_CFA_offset_extended : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align;
reg_location.SetAtCFAPlusOffset(op_offset);
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_restore_extended : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0).GetRegisterInfo(reg_num, reg_location))
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_undefined : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
reg_location.SetUndefined();
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_same_value : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
reg_location.SetSame();
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_register : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
uint32_t other_reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
reg_location.SetInRegister(other_reg_num);
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_remember_state : unwind_plan.AppendRow (row);
break;
case DW_CFA_restore_state : {
row = unwind_plan.GetRowAtIndex(unwind_plan.GetRowCount() - 1);
}
break;
case DW_CFA_def_cfa : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
op_offset = (int32_t)m_cfi_data.GetULEB128(&offset);
row.SetCFARegister (reg_num);
row.SetCFAOffset (op_offset);
}
break;
case DW_CFA_def_cfa_register : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
row.SetCFARegister (reg_num);
}
break;
case DW_CFA_def_cfa_offset : {
op_offset = (int32_t)m_cfi_data.GetULEB128(&offset);
row.SetCFAOffset (op_offset);
}
break;
case DW_CFA_def_cfa_expression : {
size_t block_len = (size_t)m_cfi_data.GetULEB128(&offset);
offset += (uint32_t)block_len;
}
break;
case DW_CFA_expression : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
const uint8_t *block_data = (uint8_t *)m_cfi_data.GetData(&offset, block_len);
reg_location.SetAtDWARFExpression(block_data, block_len);
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_offset_extended_sf : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
reg_location.SetAtCFAPlusOffset(op_offset);
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_def_cfa_sf : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
row.SetCFARegister (reg_num);
row.SetCFAOffset (op_offset);
}
break;
case DW_CFA_def_cfa_offset_sf : {
op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
row.SetCFAOffset (op_offset);
}
break;
case DW_CFA_val_expression : {
reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
const uint8_t* block_data = (uint8_t*)m_cfi_data.GetData(&offset, block_len);
reg_location.SetIsDWARFExpression(block_data, block_len);
row.SetRegisterInfo (reg_num, reg_location);
}
break;
case DW_CFA_val_offset : case DW_CFA_val_offset_sf : default:
tmp_uval32 = extended_opcode;
break;
}
}
}
unwind_plan.AppendRow(row);
return true;
}