int Address::CompareFileAddress(const Address &a, const Address &b) { addr_t a_file_addr = a.GetFileAddress(); addr_t b_file_addr = b.GetFileAddress(); if (a_file_addr < b_file_addr) return -1; if (a_file_addr > b_file_addr) return +1; return 0; }
bool lldb_private::operator>(const Address &lhs, const Address &rhs) { ModuleSP lhs_module_sp(lhs.GetModule()); ModuleSP rhs_module_sp(rhs.GetModule()); Module *lhs_module = lhs_module_sp.get(); Module *rhs_module = rhs_module_sp.get(); if (lhs_module == rhs_module) { // Addresses are in the same module, just compare the file addresses return lhs.GetFileAddress() > rhs.GetFileAddress(); } else { // The addresses are from different modules, just use the module // pointer value to get consistent ordering return lhs_module > rhs_module; } }
bool Variable::LocationIsValidForAddress (const Address &address) { // Be sure to resolve the address to section offset prior to // calling this function. if (address.IsSectionOffset()) { SymbolContext sc; CalculateSymbolContext(&sc); if (sc.module_sp == address.GetModule()) { // Is the variable is described by a single location? if (!m_location.IsLocationList()) { // Yes it is, the location is valid. return true; } if (sc.function) { addr_t loclist_base_file_addr = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) return false; // It is a location list. We just need to tell if the location // list contains the current address when converted to a load // address return m_location.LocationListContainsAddress (loclist_base_file_addr, address.GetFileAddress()); } } } return false; }
FuncUnwindersSP UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr, SymbolContext &sc) { Initialize(); std::lock_guard<std::mutex> guard(m_mutex); // There is an UnwindTable per object file, so we can safely use file handles addr_t file_addr = addr.GetFileAddress(); iterator end = m_unwinds.end(); iterator insert_pos = end; if (!m_unwinds.empty()) { insert_pos = m_unwinds.lower_bound(file_addr); iterator pos = insert_pos; if ((pos == m_unwinds.end()) || (pos != m_unwinds.begin() && pos->second->GetFunctionStartAddress() != addr)) --pos; if (pos->second->ContainsAddress(addr)) return pos->second; } auto range_or = GetAddressRange(addr, sc); if (!range_or) return nullptr; FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, *range_or)); m_unwinds.insert(insert_pos, std::make_pair(range_or->GetBaseAddress().GetFileAddress(), func_unwinder_sp)); return func_unwinder_sp; }
int Address::CompareModulePointerAndOffset(const Address &a, const Address &b) { ModuleSP a_module_sp(a.GetModule()); ModuleSP b_module_sp(b.GetModule()); Module *a_module = a_module_sp.get(); Module *b_module = b_module_sp.get(); if (a_module < b_module) return -1; if (a_module > b_module) return +1; // Modules are the same, just compare the file address since they should // be unique addr_t a_file_addr = a.GetFileAddress(); addr_t b_file_addr = b.GetFileAddress(); if (a_file_addr < b_file_addr) return -1; if (a_file_addr > b_file_addr) return +1; return 0; }
uint32_t Module::ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) { Mutex::Locker locker (m_mutex); uint32_t resolved_flags = 0; // Clear the result symbol context in case we don't find anything sc.Clear(); // Get the section from the section/offset address. const Section *section = so_addr.GetSection(); // Make sure the section matches this module before we try and match anything if (section && section->GetModule() == this) { // If the section offset based address resolved itself, then this // is the right module. sc.module_sp = GetSP(); resolved_flags |= eSymbolContextModule; // Resolve the compile unit, function, block, line table or line // entry if requested. if (resolve_scope & eSymbolContextCompUnit || resolve_scope & eSymbolContextFunction || resolve_scope & eSymbolContextBlock || resolve_scope & eSymbolContextLineEntry ) { SymbolVendor *symbols = GetSymbolVendor (); if (symbols) resolved_flags |= symbols->ResolveSymbolContext (so_addr, resolve_scope, sc); } // Resolve the symbol if requested, but don't re-look it up if we've already found it. if (resolve_scope & eSymbolContextSymbol && !(resolved_flags & eSymbolContextSymbol)) { ObjectFile* ofile = GetObjectFile(); if (ofile) { Symtab *symtab = ofile->GetSymtab(); if (symtab) { if (so_addr.IsSectionOffset()) { sc.symbol = symtab->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); if (sc.symbol) resolved_flags |= eSymbolContextSymbol; } } } } } return resolved_flags; }
bool EmulateInstruction::SetInstruction(const Opcode &opcode, const Address &inst_addr, Target *target) { m_opcode = opcode; m_addr = LLDB_INVALID_ADDRESS; if (inst_addr.IsValid()) { if (target != nullptr) m_addr = inst_addr.GetLoadAddress(target); if (m_addr == LLDB_INVALID_ADDRESS) m_addr = inst_addr.GetFileAddress(); } return true; }
uint32_t SymbolFileSymtab::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) { if (m_obj_file->GetSymtab() == NULL) return 0; uint32_t resolved_flags = 0; if (resolve_scope & eSymbolContextSymbol) { sc.symbol = m_obj_file->GetSymtab()->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); if (sc.symbol) resolved_flags |= eSymbolContextSymbol; } return resolved_flags; }
bool DWARFCallFrameInfo::GetUnwindPlan (Address addr, UnwindPlan& unwind_plan) { FDEEntryMap::Entry fde_entry; // Make sure that the Address we're searching for is the same object file // as this DWARFCallFrameInfo, we only store File offsets in m_fde_index. ModuleSP module_sp = addr.GetModule(); if (module_sp.get() == nullptr || module_sp->GetObjectFile() == nullptr || module_sp->GetObjectFile() != &m_objfile) return false; if (GetFDEEntryByFileAddress (addr.GetFileAddress(), fde_entry) == false) return false; return FDEToUnwindPlan (fde_entry.data, addr, unwind_plan); }
bool AddressRange::ContainsFileAddress(const Address &addr) const { if (addr.GetSection() == m_base_addr.GetSection()) return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize(); addr_t file_base_addr = GetBaseAddress().GetFileAddress(); if (file_base_addr == LLDB_INVALID_ADDRESS) return false; addr_t file_addr = addr.GetFileAddress(); if (file_addr == LLDB_INVALID_ADDRESS) return false; if (file_base_addr <= file_addr) return (file_addr - file_base_addr) < GetByteSize(); return false; }
void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Status &err) override { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); const lldb::addr_t load_addr = process_address + m_offset; if (log) { log->Printf("EntitySymbol::Materialize [address = 0x%" PRIx64 ", m_symbol = %s]", (uint64_t)load_addr, m_symbol.GetName().AsCString()); } const Address sym_address = m_symbol.GetAddress(); ExecutionContextScope *exe_scope = map.GetBestExecutionContextScope(); lldb::TargetSP target_sp; if (exe_scope) target_sp = map.GetBestExecutionContextScope()->CalculateTarget(); if (!target_sp) { err.SetErrorStringWithFormat( "couldn't resolve symbol %s because there is no target", m_symbol.GetName().AsCString()); return; } lldb::addr_t resolved_address = sym_address.GetLoadAddress(target_sp.get()); if (resolved_address == LLDB_INVALID_ADDRESS) resolved_address = sym_address.GetFileAddress(); Status pointer_write_error; map.WritePointerToMemory(load_addr, resolved_address, pointer_write_error); if (!pointer_write_error.Success()) { err.SetErrorStringWithFormat( "couldn't write the address of symbol %s: %s", m_symbol.GetName().AsCString(), pointer_write_error.AsCString()); return; } }
bool Variable::DumpLocationForAddress (Stream *s, const Address &address) { // Be sure to resolve the address to section offset prior to // calling this function. if (address.IsSectionOffset()) { SymbolContext sc; CalculateSymbolContext(&sc); if (sc.module_sp == address.GetModule()) { ABI *abi = nullptr; if (m_owner_scope) { ModuleSP module_sp (m_owner_scope->CalculateSymbolContextModule()); if (module_sp) abi = ABI::FindPlugin (module_sp->GetArchitecture()).get(); } const addr_t file_addr = address.GetFileAddress(); if (sc.function) { if (sc.function->GetAddressRange().ContainsFileAddress(address)) { addr_t loclist_base_file_addr = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) return false; return m_location.DumpLocationForAddress (s, eDescriptionLevelBrief, loclist_base_file_addr, file_addr, abi); } } return m_location.DumpLocationForAddress (s, eDescriptionLevelBrief, LLDB_INVALID_ADDRESS, file_addr, abi); } } return false; }
bool DWARFCallFrameInfo::GetAddressRange (Address addr, AddressRange &range) { // Make sure that the Address we're searching for is the same object file // as this DWARFCallFrameInfo, we only store File offsets in m_fde_index. ModuleSP module_sp = addr.GetModule(); if (module_sp.get() == nullptr || module_sp->GetObjectFile() == nullptr || module_sp->GetObjectFile() != &m_objfile) return false; if (m_section_sp.get() == nullptr || m_section_sp->IsEncrypted()) return false; GetFDEIndex(); FDEEntryMap::Entry *fde_entry = m_fde_index.FindEntryThatContains (addr.GetFileAddress()); if (!fde_entry) return false; range = AddressRange(fde_entry->base, fde_entry->size, m_objfile.GetSectionList()); return true; }
FuncUnwindersSP UnwindTable::GetFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc) { FuncUnwindersSP no_unwind_found; Initialize(); // There is an UnwindTable per object file, so we can safely use file handles addr_t file_addr = addr.GetFileAddress(); iterator end = m_unwinds.end (); iterator insert_pos = end; if (!m_unwinds.empty()) { insert_pos = m_unwinds.lower_bound (file_addr); iterator pos = insert_pos; if ((pos == m_unwinds.end ()) || (pos != m_unwinds.begin() && pos->second->GetFunctionStartAddress() != addr)) --pos; if (pos->second->ContainsAddress (addr)) return pos->second; } AddressRange range; if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, false, range) || !range.GetBaseAddress().IsValid()) { // Does the eh_frame unwind info has a function bounds for this addr? if (m_eh_frame == NULL || !m_eh_frame->GetAddressRange (addr, range)) { return no_unwind_found; } } FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, m_assembly_profiler, range)); m_unwinds.insert (insert_pos, std::make_pair(range.GetBaseAddress().GetFileAddress(), func_unwinder_sp)); // StreamFile s(stdout); // Dump (s); return func_unwinder_sp; }
bool SymbolContext::GetParentOfInlinedScope (const Address &curr_frame_pc, SymbolContext &next_frame_sc, Address &next_frame_pc) const { next_frame_sc.Clear(false); next_frame_pc.Clear(); if (block) { //const addr_t curr_frame_file_addr = curr_frame_pc.GetFileAddress(); // In order to get the parent of an inlined function we first need to // see if we are in an inlined block as "this->block" could be an // inlined block, or a parent of "block" could be. So lets check if // this block or one of this blocks parents is an inlined function. Block *curr_inlined_block = block->GetContainingInlinedBlock(); if (curr_inlined_block) { // "this->block" is contained in an inline function block, so to // get the scope above the inlined block, we get the parent of the // inlined block itself Block *next_frame_block = curr_inlined_block->GetParent(); // Now calculate the symbol context of the containing block next_frame_block->CalculateSymbolContext (&next_frame_sc); // If we get here we weren't able to find the return line entry using the nesting of the blocks and // the line table. So just use the call site info from our inlined block. AddressRange range; if (curr_inlined_block->GetRangeContainingAddress (curr_frame_pc, range)) { // To see there this new frame block it, we need to look at the // call site information from const InlineFunctionInfo* curr_inlined_block_inlined_info = curr_inlined_block->GetInlinedFunctionInfo(); next_frame_pc = range.GetBaseAddress(); next_frame_sc.line_entry.range.GetBaseAddress() = next_frame_pc; next_frame_sc.line_entry.file = curr_inlined_block_inlined_info->GetCallSite().GetFile(); next_frame_sc.line_entry.line = curr_inlined_block_inlined_info->GetCallSite().GetLine(); next_frame_sc.line_entry.column = curr_inlined_block_inlined_info->GetCallSite().GetColumn(); return true; } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS)); if (log) { log->Printf ("warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64, curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress()); } #ifdef LLDB_CONFIGURATION_DEBUG else { ObjectFile *objfile = NULL; if (module_sp) { SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); if (symbol_vendor) { SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); if (symbol_file) objfile = symbol_file->GetObjectFile(); } } if (objfile) { Host::SystemLog (Host::eSystemLogWarning, "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 " in %s\n", curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress(), objfile->GetFileSpec().GetPath().c_str()); } else { Host::SystemLog (Host::eSystemLogWarning, "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 "\n", curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress()); } } #endif } } } return false; }
bool DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t dwarf_offset, Address startaddr, UnwindPlan& unwind_plan) { lldb::offset_t offset = dwarf_offset; lldb::offset_t current_entry = offset; if (m_section_sp.get() == nullptr || m_section_sp->IsEncrypted()) return false; if (m_cfi_data_initialized == false) GetCFIData(); uint32_t length = m_cfi_data.GetU32 (&offset); dw_offset_t cie_offset; bool is_64bit = (length == UINT32_MAX); if (is_64bit) { length = m_cfi_data.GetU64 (&offset); cie_offset = m_cfi_data.GetU64 (&offset); } else { cie_offset = m_cfi_data.GetU32 (&offset); } assert (cie_offset != 0 && 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 // offset if (m_is_eh_frame) { unwind_plan.SetSourceName ("eh_frame CFI"); cie_offset = current_entry + (is_64bit ? 12 : 4) - cie_offset; unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); } else { unwind_plan.SetSourceName ("DWARF CFI"); // In theory the debug_frame info should be valid at all call sites // ("asynchronous unwind info" as it is sometimes called) but in practice // gcc et al all emit call frame info for the prologue and call sites, but // not for the epilogue or all the other locations during the function reliably. unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); } unwind_plan.SetSourcedFromCompiler (eLazyBoolYes); const CIE *cie = GetCIE (cie_offset); assert (cie != nullptr); const dw_offset_t end_offset = current_entry + length + (is_64bit ? 12 : 4); const lldb::addr_t pc_rel_addr = m_section_sp->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); 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 code_align = cie->code_align; int32_t data_align = cie->data_align; unwind_plan.SetPlanValidAddressRange (range); UnwindPlan::Row *cie_initial_row = new UnwindPlan::Row; *cie_initial_row = cie->initial_row; UnwindPlan::RowSP row(cie_initial_row); unwind_plan.SetRegisterKind (m_reg_kind); unwind_plan.SetReturnAddressRegister (cie->return_addr_reg_num); std::vector<UnwindPlan::RowSP> stack; 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 (!HandleCommonDwarfOpcode(primary_opcode, extended_opcode, data_align, offset, *row)) { if (primary_opcode) { switch (primary_opcode) { case DW_CFA_advance_loc : // (Row Creation Instruction) { // 0x40 - high 2 bits are 0x1, lower 6 bits are delta // takes a single argument that represents a constant delta. The // required action is to create a new table row with a location // value that is computed by taking the current entry's location // value and adding (delta * code_align). All other // values in the new row are initially identical to the current row. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset(extended_opcode * code_align); break; } case DW_CFA_restore : { // 0xC0 - high 2 bits are 0x3, lower 6 bits are register // takes a single argument that represents a register number. The // required action is to change the rule for the indicated register // to the rule assigned it by the initial_instructions in the CIE. uint32_t reg_num = extended_opcode; // We only keep enough register locations around to // unwind what is in our thread, and these are organized // by the register index in that state, so we need to convert our // GCC register number from the EH frame info, to a register index 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_set_loc : // 0x1 (Row Creation Instruction) { // DW_CFA_set_loc takes a single argument that represents an address. // The required action is to create a new table row using the // specified address as the location. All other values in the new row // are initially identical to the current row. The new location value // should always be greater than the current one. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SetOffset(m_cfi_data.GetPointer(&offset) - startaddr.GetFileAddress()); break; } case DW_CFA_advance_loc1 : // 0x2 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU8(&offset) * code_align); break; } case DW_CFA_advance_loc2 : // 0x3 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU16(&offset) * code_align); break; } case DW_CFA_advance_loc4 : // 0x4 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU32(&offset) * code_align); break; } case DW_CFA_restore_extended : // 0x6 { // takes a single unsigned LEB128 argument that represents a register // number. This instruction is identical to DW_CFA_restore except for // the encoding and size of the register argument. uint32_t 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_remember_state : // 0xA { // These instructions define a stack of information. Encountering the // DW_CFA_remember_state instruction means to save the rules for every // register on the current row on the stack. Encountering the // DW_CFA_restore_state instruction means to pop the set of rules off // the stack and place them in the current row. (This operation is // useful for compilers that move epilogue code into the body of a // function.) stack.push_back (row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); break; } case DW_CFA_restore_state : // 0xB { // These instructions define a stack of information. Encountering the // DW_CFA_remember_state instruction means to save the rules for every // register on the current row on the stack. Encountering the // DW_CFA_restore_state instruction means to pop the set of rules off // the stack and place them in the current row. (This operation is // useful for compilers that move epilogue code into the body of a // function.) lldb::addr_t offset = row->GetOffset (); row = stack.back (); stack.pop_back (); row->SetOffset (offset); break; } case DW_CFA_val_offset : // 0x14 case DW_CFA_val_offset_sf : // 0x15 default: break; } } } } unwind_plan.AppendRow(row); return true; }
bool CompactUnwindInfo::GetCompactUnwindInfoForFunction (Target &target, Address address, FunctionInfo &unwind_info) { unwind_info.encoding = 0; unwind_info.lsda_address.Clear(); unwind_info.personality_ptr_address.Clear(); if (!IsValid (target.GetProcessSP())) return false; addr_t text_section_file_address = LLDB_INVALID_ADDRESS; SectionList *sl = m_objfile.GetSectionList (); if (sl) { SectionSP text_sect = sl->FindSectionByType (eSectionTypeCode, true); if (text_sect.get()) { text_section_file_address = text_sect->GetFileAddress(); } } if (text_section_file_address == LLDB_INVALID_ADDRESS) return false; addr_t function_offset = address.GetFileAddress() - m_objfile.GetHeaderAddress().GetFileAddress(); UnwindIndex key; key.function_offset = function_offset; std::vector<UnwindIndex>::const_iterator it; it = std::lower_bound (m_indexes.begin(), m_indexes.end(), key); if (it == m_indexes.end()) { return false; } if (it->function_offset != key.function_offset) { if (it != m_indexes.begin()) --it; } if (it->sentinal_entry == true) { return false; } auto next_it = it + 1; if (next_it != m_indexes.end()) { // initialize the function offset end range to be the start of the // next index offset. If we find an entry which is at the end of // the index table, this will establish the range end. unwind_info.valid_range_offset_end = next_it->function_offset; } offset_t second_page_offset = it->second_level; offset_t lsda_array_start = it->lsda_array_start; offset_t lsda_array_count = (it->lsda_array_end - it->lsda_array_start) / 8; offset_t offset = second_page_offset; uint32_t kind = m_unwindinfo_data.GetU32(&offset); // UNWIND_SECOND_LEVEL_REGULAR or UNWIND_SECOND_LEVEL_COMPRESSED if (kind == UNWIND_SECOND_LEVEL_REGULAR) { // struct unwind_info_regular_second_level_page_header // { // uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR // uint16_t entryPageOffset; // uint16_t entryCount; // typedef uint32_t compact_unwind_encoding_t; // struct unwind_info_regular_second_level_entry // { // uint32_t functionOffset; // compact_unwind_encoding_t encoding; uint16_t entry_page_offset = m_unwindinfo_data.GetU16(&offset); // entryPageOffset uint16_t entry_count = m_unwindinfo_data.GetU16(&offset); // entryCount offset_t entry_offset = BinarySearchRegularSecondPage (second_page_offset + entry_page_offset, entry_count, function_offset, &unwind_info.valid_range_offset_start, &unwind_info.valid_range_offset_end); if (entry_offset == LLDB_INVALID_OFFSET) { return false; } entry_offset += 4; // skip over functionOffset unwind_info.encoding = m_unwindinfo_data.GetU32(&entry_offset); // encoding if (unwind_info.encoding & UNWIND_HAS_LSDA) { SectionList *sl = m_objfile.GetSectionList (); if (sl) { uint32_t lsda_offset = GetLSDAForFunctionOffset (lsda_array_start, lsda_array_count, function_offset); addr_t objfile_header_file_address = m_objfile.GetHeaderAddress().GetFileAddress(); unwind_info.lsda_address.ResolveAddressUsingFileSections (objfile_header_file_address + lsda_offset, sl); } } if (unwind_info.encoding & UNWIND_PERSONALITY_MASK) { uint32_t personality_index = EXTRACT_BITS (unwind_info.encoding, UNWIND_PERSONALITY_MASK); if (personality_index > 0) { personality_index--; if (personality_index < m_unwind_header.personality_array_count) { offset_t offset = m_unwind_header.personality_array_offset; offset += 4 * personality_index; SectionList *sl = m_objfile.GetSectionList (); if (sl) { uint32_t personality_offset = m_unwindinfo_data.GetU32(&offset); addr_t objfile_header_file_address = m_objfile.GetHeaderAddress().GetFileAddress(); unwind_info.personality_ptr_address.ResolveAddressUsingFileSections (objfile_header_file_address + personality_offset, sl); } } } } return true; } else if (kind == UNWIND_SECOND_LEVEL_COMPRESSED) { // struct unwind_info_compressed_second_level_page_header // { // uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED // uint16_t entryPageOffset; // offset from this 2nd lvl page idx to array of entries // // (an entry has a function offset and index into the encodings) // // NB function offset from the entry in the compressed page // // must be added to the index's functionOffset value. // uint16_t entryCount; // uint16_t encodingsPageOffset; // offset from this 2nd lvl page idx to array of encodings // uint16_t encodingsCount; uint16_t entry_page_offset = m_unwindinfo_data.GetU16(&offset); // entryPageOffset uint16_t entry_count = m_unwindinfo_data.GetU16(&offset); // entryCount uint16_t encodings_page_offset = m_unwindinfo_data.GetU16(&offset); // encodingsPageOffset uint16_t encodings_count = m_unwindinfo_data.GetU16(&offset); // encodingsCount uint32_t encoding_index = BinarySearchCompressedSecondPage (second_page_offset + entry_page_offset, entry_count, function_offset, it->function_offset, &unwind_info.valid_range_offset_start, &unwind_info.valid_range_offset_end); if (encoding_index == UINT32_MAX || encoding_index >= encodings_count + m_unwind_header.common_encodings_array_count) { return false; } uint32_t encoding = 0; if (encoding_index < m_unwind_header.common_encodings_array_count) { offset = m_unwind_header.common_encodings_array_offset + (encoding_index * sizeof (uint32_t)); encoding = m_unwindinfo_data.GetU32(&offset); // encoding entry from the commonEncodingsArray } else { uint32_t page_specific_entry_index = encoding_index - m_unwind_header.common_encodings_array_count; offset = second_page_offset + encodings_page_offset + (page_specific_entry_index * sizeof (uint32_t)); encoding = m_unwindinfo_data.GetU32(&offset); // encoding entry from the page-specific encoding array } if (encoding == 0) return false; unwind_info.encoding = encoding; if (unwind_info.encoding & UNWIND_HAS_LSDA) { SectionList *sl = m_objfile.GetSectionList (); if (sl) { uint32_t lsda_offset = GetLSDAForFunctionOffset (lsda_array_start, lsda_array_count, function_offset); addr_t objfile_header_file_address = m_objfile.GetHeaderAddress().GetFileAddress(); unwind_info.lsda_address.ResolveAddressUsingFileSections (objfile_header_file_address + lsda_offset, sl); } } if (unwind_info.encoding & UNWIND_PERSONALITY_MASK) { uint32_t personality_index = EXTRACT_BITS (unwind_info.encoding, UNWIND_PERSONALITY_MASK); if (personality_index > 0) { personality_index--; if (personality_index < m_unwind_header.personality_array_count) { offset_t offset = m_unwind_header.personality_array_offset; offset += 4 * personality_index; SectionList *sl = m_objfile.GetSectionList (); if (sl) { uint32_t personality_offset = m_unwindinfo_data.GetU32(&offset); addr_t objfile_header_file_address = m_objfile.GetHeaderAddress().GetFileAddress(); unwind_info.personality_ptr_address.ResolveAddressUsingFileSections (objfile_header_file_address + personality_offset, sl); } } } } return true; } return false; }
bool DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t offset, Address startaddr, UnwindPlan& unwind_plan) { dw_offset_t current_entry = offset; if (m_section_sp.get() == NULL || m_section_sp->IsEncrypted()) return false; if (m_cfi_data_initialized == false) GetCFIData(); 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); // Translate the CIE_id from the eh_frame format, which // is relative to the FDE offset, into a __eh_frame section // offset if (m_is_eh_frame) { unwind_plan.SetSourceName ("eh_frame CFI"); cie_offset = current_entry + 4 - cie_offset; unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); } else { unwind_plan.SetSourceName ("DWARF CFI"); // In theory the debug_frame info should be valid at all call sites // ("asynchronous unwind info" as it is sometimes called) but in practice // gcc et al all emit call frame info for the prologue and call sites, but // not for the epilogue or all the other locations during the function reliably. unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); } unwind_plan.SetSourcedFromCompiler (eLazyBoolYes); 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_sp->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 code_align = cie->code_align; int32_t data_align = cie->data_align; unwind_plan.SetPlanValidAddressRange (range); UnwindPlan::Row *cie_initial_row = new UnwindPlan::Row; *cie_initial_row = cie->initial_row; UnwindPlan::RowSP row(cie_initial_row); unwind_plan.SetRegisterKind (m_reg_kind); unwind_plan.SetReturnAddressRegister (cie->return_addr_reg_num); 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 : // (Row Creation Instruction) { // 0x40 - high 2 bits are 0x1, lower 6 bits are delta // takes a single argument that represents a constant delta. The // required action is to create a new table row with a location // value that is computed by taking the current entry's location // value and adding (delta * code_align). All other // values in the new row are initially identical to the current row. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset(extended_opcode * code_align); } break; case DW_CFA_offset : { // 0x80 - high 2 bits are 0x2, lower 6 bits are register // takes two arguments: an unsigned LEB128 constant representing a // factored offset and a register number. The required action is to // change the rule for the register indicated by the register number // to be an offset(N) rule with a value of // (N = factored offset * data_align). 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 : { // 0xC0 - high 2 bits are 0x3, lower 6 bits are register // takes a single argument that represents a register number. The // required action is to change the rule for the indicated register // to the rule assigned it by the initial_instructions in the CIE. reg_num = extended_opcode; // We only keep enough register locations around to // unwind what is in our thread, and these are organized // by the register index in that state, so we need to convert our // GCC register number from the EH frame info, to a register index 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 : // 0x0 break; case DW_CFA_set_loc : // 0x1 (Row Creation Instruction) { // DW_CFA_set_loc takes a single argument that represents an address. // The required action is to create a new table row using the // specified address as the location. All other values in the new row // are initially identical to the current row. The new location value // should always be greater than the current one. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SetOffset(m_cfi_data.GetPointer(&offset) - startaddr.GetFileAddress()); } break; case DW_CFA_advance_loc1 : // 0x2 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU8(&offset) * code_align); } break; case DW_CFA_advance_loc2 : // 0x3 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU16(&offset) * code_align); } break; case DW_CFA_advance_loc4 : // 0x4 (Row Creation Instruction) { // takes a single uword argument that represents a constant delta. // This instruction is identical to DW_CFA_advance_loc except for the // encoding and size of the delta argument. unwind_plan.AppendRow(row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); row->SlideOffset (m_cfi_data.GetU32(&offset) * code_align); } break; case DW_CFA_offset_extended : // 0x5 { // takes two unsigned LEB128 arguments representing a register number // and a factored offset. This instruction is identical to DW_CFA_offset // except for the encoding and size of the register argument. 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 : // 0x6 { // takes a single unsigned LEB128 argument that represents a register // number. This instruction is identical to DW_CFA_restore except for // the encoding and size of the register argument. 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 : // 0x7 { // takes a single unsigned LEB128 argument that represents a register // number. The required action is to set the rule for the specified // register to 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 : // 0x8 { // takes a single unsigned LEB128 argument that represents a register // number. The required action is to set the rule for the specified // register to 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 : // 0x9 { // takes two unsigned LEB128 arguments representing register numbers. // The required action is to set the rule for the first register to be // the second 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 : // 0xA { // These instructions define a stack of information. Encountering the // DW_CFA_remember_state instruction means to save the rules for every // register on the current row on the stack. Encountering the // DW_CFA_restore_state instruction means to pop the set of rules off // the stack and place them in the current row. (This operation is // useful for compilers that move epilogue code into the body of a // function.) unwind_plan.AppendRow (row); UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *row.get(); row.reset (newrow); } break; case DW_CFA_restore_state : // 0xB // These instructions define a stack of information. Encountering the // DW_CFA_remember_state instruction means to save the rules for every // register on the current row on the stack. Encountering the // DW_CFA_restore_state instruction means to pop the set of rules off // the stack and place them in the current row. (This operation is // useful for compilers that move epilogue code into the body of a // function.) { row = unwind_plan.GetRowAtIndex(unwind_plan.GetRowCount() - 1); } break; case DW_CFA_def_cfa : // 0xC (CFA Definition Instruction) { // Takes two unsigned LEB128 operands representing a register // number and a (non-factored) offset. The required action // is to define the current CFA rule to use the provided // register and offset. 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 : // 0xD (CFA Definition Instruction) { // takes a single unsigned LEB128 argument representing a register // number. The required action is to define the current CFA rule to // use the provided register (but to keep the old offset). reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); row->SetCFARegister (reg_num); } break; case DW_CFA_def_cfa_offset : // 0xE (CFA Definition Instruction) { // Takes a single unsigned LEB128 operand representing a // (non-factored) offset. The required action is to define // the current CFA rule to use the provided offset (but // to keep the old register). op_offset = (int32_t)m_cfi_data.GetULEB128(&offset); row->SetCFAOffset (op_offset); } break; case DW_CFA_def_cfa_expression : // 0xF (CFA Definition Instruction) { size_t block_len = (size_t)m_cfi_data.GetULEB128(&offset); offset += (uint32_t)block_len; } break; case DW_CFA_expression : // 0x10 { // Takes two operands: an unsigned LEB128 value representing // a register number, and a DW_FORM_block value representing a DWARF // expression. The required action is to change the rule for the // register indicated by the register number to be an expression(E) // rule where E is the DWARF expression. That is, the DWARF // expression computes the address. The value of the CFA is // pushed on the DWARF evaluation stack prior to execution of // the DWARF 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 : // 0x11 { // takes two operands: an unsigned LEB128 value representing a // register number and a signed LEB128 factored offset. This // instruction is identical to DW_CFA_offset_extended except //that the second operand is signed and factored. 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 : // 0x12 (CFA Definition Instruction) { // Takes two operands: an unsigned LEB128 value representing // a register number and a signed LEB128 factored offset. // This instruction is identical to DW_CFA_def_cfa except // that the second operand is signed and factored. 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 : // 0x13 (CFA Definition Instruction) { // takes a signed LEB128 operand representing a factored // offset. This instruction is identical to DW_CFA_def_cfa_offset // except that the operand is signed and factored. op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; row->SetCFAOffset (op_offset); } break; case DW_CFA_val_expression : // 0x16 { // takes two operands: an unsigned LEB128 value representing a register // number, and a DW_FORM_block value representing a DWARF expression. // The required action is to change the rule for the register indicated // by the register number to be a val_expression(E) rule where E is the // DWARF expression. That is, the DWARF expression computes the value of // the given register. The value of the CFA is pushed on the DWARF // evaluation stack prior to execution of the DWARF 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); //#if defined(__i386__) || defined(__x86_64__) // // The EH frame info for EIP and RIP contains code that looks for traps to // // be a specific type and increments the PC. // // For i386: // // DW_CFA_val_expression where: // // eip = DW_OP_breg6(+28), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x34), // // DW_OP_deref, DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref, // // DW_OP_dup, DW_OP_lit3, DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, // // DW_OP_and, DW_OP_plus // // This basically does a: // // eip = ucontenxt.mcontext32->gpr.eip; // // if (ucontenxt.mcontext32->exc.trapno != 3 && ucontenxt.mcontext32->exc.trapno != 4) // // eip++; // // // // For x86_64: // // DW_CFA_val_expression where: // // rip = DW_OP_breg3(+48), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x90), DW_OP_deref, // // DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref_size(4), DW_OP_dup, DW_OP_lit3, // // DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, DW_OP_and, DW_OP_plus // // This basically does a: // // rip = ucontenxt.mcontext64->gpr.rip; // // if (ucontenxt.mcontext64->exc.trapno != 3 && ucontenxt.mcontext64->exc.trapno != 4) // // rip++; // // The trap comparisons and increments are not needed as it hoses up the unwound PC which // // is expected to point at least past the instruction that causes the fault/trap. So we // // take it out by trimming the expression right at the first "DW_OP_swap" opcodes // if (block_data != NULL && thread->GetPCRegNum(Thread::GCC) == reg_num) // { // if (thread->Is64Bit()) // { // if (block_len > 9 && block_data[8] == DW_OP_swap && block_data[9] == DW_OP_plus_uconst) // block_len = 8; // } // else // { // if (block_len > 8 && block_data[7] == DW_OP_swap && block_data[8] == DW_OP_plus_uconst) // block_len = 7; // } // } //#endif reg_location.SetIsDWARFExpression(block_data, block_len); row->SetRegisterInfo (reg_num, reg_location); } break; case DW_CFA_val_offset : // 0x14 case DW_CFA_val_offset_sf : // 0x15 default: break; } } } unwind_plan.AppendRow(row); return true; }