Path: blob/main/contrib/llvm-project/lldb/source/Symbol/ArmUnwindInfo.cpp
39587 views
//===-- ArmUnwindInfo.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include <vector>910#include "Utility/ARM_DWARF_Registers.h"11#include "lldb/Core/Module.h"12#include "lldb/Core/Section.h"13#include "lldb/Symbol/ArmUnwindInfo.h"14#include "lldb/Symbol/SymbolVendor.h"15#include "lldb/Symbol/UnwindPlan.h"16#include "lldb/Utility/Endian.h"1718/*19* Unwind information reader and parser for the ARM exception handling ABI20*21* Implemented based on:22* Exception Handling ABI for the ARM Architecture23* Document number: ARM IHI 0038A (current through ABI r2.09)24* Date of Issue: 25th January 2007, reissued 30th November 201225* http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf26*/2728using namespace lldb;29using namespace lldb_private;3031// Converts a prel31 value to lldb::addr_t with sign extension32static addr_t Prel31ToAddr(uint32_t prel31) {33addr_t res = prel31;34if (prel31 & (1 << 30))35res |= 0xffffffff80000000ULL;36return res;37}3839ArmUnwindInfo::ArmExidxEntry::ArmExidxEntry(uint32_t f, lldb::addr_t a,40uint32_t d)41: file_address(f), address(a), data(d) {}4243bool ArmUnwindInfo::ArmExidxEntry::operator<(const ArmExidxEntry &other) const {44return address < other.address;45}4647ArmUnwindInfo::ArmUnwindInfo(ObjectFile &objfile, SectionSP &arm_exidx,48SectionSP &arm_extab)49: m_byte_order(objfile.GetByteOrder()), m_arm_exidx_sp(arm_exidx),50m_arm_extab_sp(arm_extab) {51objfile.ReadSectionData(arm_exidx.get(), m_arm_exidx_data);52objfile.ReadSectionData(arm_extab.get(), m_arm_extab_data);5354addr_t exidx_base_addr = m_arm_exidx_sp->GetFileAddress();5556offset_t offset = 0;57while (m_arm_exidx_data.ValidOffset(offset)) {58lldb::addr_t file_addr = exidx_base_addr + offset;59lldb::addr_t addr = exidx_base_addr + (addr_t)offset +60Prel31ToAddr(m_arm_exidx_data.GetU32(&offset));61uint32_t data = m_arm_exidx_data.GetU32(&offset);62m_exidx_entries.emplace_back(file_addr, addr, data);63}6465// Sort the entries in the exidx section. The entries should be sorted inside66// the section but some old compiler isn't sorted them.67llvm::sort(m_exidx_entries);68}6970ArmUnwindInfo::~ArmUnwindInfo() = default;7172// Read a byte from the unwind instruction stream with the given offset. Custom73// function is required because have to red in order of significance within74// their containing word (most significant byte first) and in increasing word75// address order.76uint8_t ArmUnwindInfo::GetByteAtOffset(const uint32_t *data,77uint16_t offset) const {78uint32_t value = data[offset / 4];79if (m_byte_order != endian::InlHostByteOrder())80value = llvm::byteswap<uint32_t>(value);81return (value >> ((3 - (offset % 4)) * 8)) & 0xff;82}8384uint64_t ArmUnwindInfo::GetULEB128(const uint32_t *data, uint16_t &offset,85uint16_t max_offset) const {86uint64_t result = 0;87uint8_t shift = 0;88while (offset < max_offset) {89uint8_t byte = GetByteAtOffset(data, offset++);90result |= (uint64_t)(byte & 0x7f) << shift;91if ((byte & 0x80) == 0)92break;93shift += 7;94}95return result;96}9798bool ArmUnwindInfo::GetUnwindPlan(Target &target, const Address &addr,99UnwindPlan &unwind_plan) {100const uint32_t *data = (const uint32_t *)GetExceptionHandlingTableEntry(addr);101if (data == nullptr)102return false; // No unwind information for the function103104if (data[0] == 0x1)105return false; // EXIDX_CANTUNWIND106107uint16_t byte_count = 0;108uint16_t byte_offset = 0;109if (data[0] & 0x80000000) {110switch ((data[0] >> 24) & 0x0f) {111case 0:112byte_count = 4;113byte_offset = 1;114break;115case 1:116case 2:117byte_count = 4 * ((data[0] >> 16) & 0xff) + 4;118byte_offset = 2;119break;120default:121// Unhandled personality routine index122return false;123}124} else {125byte_count = 4 * ((data[1] >> 24) & 0xff) + 8;126byte_offset = 5;127}128129uint8_t vsp_reg = dwarf_sp;130int32_t vsp = 0;131std::vector<std::pair<uint32_t, int32_t>>132register_offsets; // register -> (offset from vsp_reg)133134while (byte_offset < byte_count) {135uint8_t byte1 = GetByteAtOffset(data, byte_offset++);136if ((byte1 & 0xc0) == 0x00) {137// 00xxxxxx138// vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive139vsp += ((byte1 & 0x3f) << 2) + 4;140} else if ((byte1 & 0xc0) == 0x40) {141// 01xxxxxx142// vsp = vsp – (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive143vsp -= ((byte1 & 0x3f) << 2) + 4;144} else if ((byte1 & 0xf0) == 0x80) {145if (byte_offset >= byte_count)146return false;147148uint8_t byte2 = GetByteAtOffset(data, byte_offset++);149if (byte1 == 0x80 && byte2 == 0) {150// 10000000 00000000151// Refuse to unwind (for example, out of a cleanup) (see remark a)152return false;153} else {154// 1000iiii iiiiiiii (i not all 0)155// Pop up to 12 integer registers under masks {r15-r12}, {r11-r4} (see156// remark b)157uint16_t regs = ((byte1 & 0x0f) << 8) | byte2;158for (uint8_t i = 0; i < 12; ++i) {159if (regs & (1 << i)) {160register_offsets.emplace_back(dwarf_r4 + i, vsp);161vsp += 4;162}163}164}165} else if ((byte1 & 0xff) == 0x9d) {166// 10011101167// Reserved as prefix for ARM register to register moves168return false;169} else if ((byte1 & 0xff) == 0x9f) {170// 10011111171// Reserved as prefix for Intel Wireless MMX register to register moves172return false;173} else if ((byte1 & 0xf0) == 0x90) {174// 1001nnnn (nnnn != 13,15)175// Set vsp = r[nnnn]176vsp_reg = dwarf_r0 + (byte1 & 0x0f);177} else if ((byte1 & 0xf8) == 0xa0) {178// 10100nnn179// Pop r4-r[4+nnn]180uint8_t n = byte1 & 0x7;181for (uint8_t i = 0; i <= n; ++i) {182register_offsets.emplace_back(dwarf_r4 + i, vsp);183vsp += 4;184}185} else if ((byte1 & 0xf8) == 0xa8) {186// 10101nnn187// Pop r4-r[4+nnn], r14188uint8_t n = byte1 & 0x7;189for (uint8_t i = 0; i <= n; ++i) {190register_offsets.emplace_back(dwarf_r4 + i, vsp);191vsp += 4;192}193194register_offsets.emplace_back(dwarf_lr, vsp);195vsp += 4;196} else if ((byte1 & 0xff) == 0xb0) {197// 10110000198// Finish (see remark c)199break;200} else if ((byte1 & 0xff) == 0xb1) {201if (byte_offset >= byte_count)202return false;203204uint8_t byte2 = GetByteAtOffset(data, byte_offset++);205if ((byte2 & 0xff) == 0x00) {206// 10110001 00000000207// Spare (see remark f)208return false;209} else if ((byte2 & 0xf0) == 0x00) {210// 10110001 0000iiii (i not all 0)211// Pop integer registers under mask {r3, r2, r1, r0}212for (uint8_t i = 0; i < 4; ++i) {213if (byte2 & (1 << i)) {214register_offsets.emplace_back(dwarf_r0 + i, vsp);215vsp += 4;216}217}218} else {219// 10110001 xxxxyyyy220// Spare (xxxx != 0000)221return false;222}223} else if ((byte1 & 0xff) == 0xb2) {224// 10110010 uleb128225// vsp = vsp + 0x204+ (uleb128 << 2)226uint64_t uleb128 = GetULEB128(data, byte_offset, byte_count);227vsp += 0x204 + (uleb128 << 2);228} else if ((byte1 & 0xff) == 0xb3) {229// 10110011 sssscccc230// Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if)231// by FSTMFDX (see remark d)232if (byte_offset >= byte_count)233return false;234235uint8_t byte2 = GetByteAtOffset(data, byte_offset++);236uint8_t s = (byte2 & 0xf0) >> 4;237uint8_t c = (byte2 & 0x0f) >> 0;238for (uint8_t i = 0; i <= c; ++i) {239register_offsets.emplace_back(dwarf_d0 + s + i, vsp);240vsp += 8;241}242vsp += 4;243} else if ((byte1 & 0xfc) == 0xb4) {244// 101101nn245// Spare (was Pop FPA)246return false;247} else if ((byte1 & 0xf8) == 0xb8) {248// 10111nnn249// Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by250// FSTMFDX (see remark d)251uint8_t n = byte1 & 0x07;252for (uint8_t i = 0; i <= n; ++i) {253register_offsets.emplace_back(dwarf_d8 + i, vsp);254vsp += 8;255}256vsp += 4;257} else if ((byte1 & 0xf8) == 0xc0) {258// 11000nnn (nnn != 6,7)259// Intel Wireless MMX pop wR[10]-wR[10+nnn]260261// 11000110 sssscccc262// Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc] (see remark e)263264// 11000111 00000000265// Spare266267// 11000111 0000iiii268// Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}269270// 11000111 xxxxyyyy271// Spare (xxxx != 0000)272273return false;274} else if ((byte1 & 0xff) == 0xc8) {275// 11001000 sssscccc276// Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] saved277// (as if) by FSTMFDD (see remarks d,e)278if (byte_offset >= byte_count)279return false;280281uint8_t byte2 = GetByteAtOffset(data, byte_offset++);282uint8_t s = (byte2 & 0xf0) >> 4;283uint8_t c = (byte2 & 0x0f) >> 0;284for (uint8_t i = 0; i <= c; ++i) {285register_offsets.emplace_back(dwarf_d16 + s + i, vsp);286vsp += 8;287}288} else if ((byte1 & 0xff) == 0xc9) {289// 11001001 sssscccc290// Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if)291// by FSTMFDD (see remark d)292if (byte_offset >= byte_count)293return false;294295uint8_t byte2 = GetByteAtOffset(data, byte_offset++);296uint8_t s = (byte2 & 0xf0) >> 4;297uint8_t c = (byte2 & 0x0f) >> 0;298for (uint8_t i = 0; i <= c; ++i) {299register_offsets.emplace_back(dwarf_d0 + s + i, vsp);300vsp += 8;301}302} else if ((byte1 & 0xf8) == 0xc8) {303// 11001yyy304// Spare (yyy != 000, 001)305return false;306} else if ((byte1 & 0xf8) == 0xd0) {307// 11010nnn308// Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by309// FSTMFDD (see remark d)310uint8_t n = byte1 & 0x07;311for (uint8_t i = 0; i <= n; ++i) {312register_offsets.emplace_back(dwarf_d8 + i, vsp);313vsp += 8;314}315} else if ((byte1 & 0xc0) == 0xc0) {316// 11xxxyyy Spare (xxx != 000, 001, 010)317return false;318} else {319return false;320}321}322323UnwindPlan::RowSP row = std::make_shared<UnwindPlan::Row>();324row->SetOffset(0);325row->GetCFAValue().SetIsRegisterPlusOffset(vsp_reg, vsp);326327bool have_location_for_pc = false;328for (const auto &offset : register_offsets) {329have_location_for_pc |= offset.first == dwarf_pc;330row->SetRegisterLocationToAtCFAPlusOffset(offset.first, offset.second - vsp,331true);332}333334if (!have_location_for_pc) {335UnwindPlan::Row::RegisterLocation lr_location;336if (row->GetRegisterInfo(dwarf_lr, lr_location))337row->SetRegisterInfo(dwarf_pc, lr_location);338else339row->SetRegisterLocationToRegister(dwarf_pc, dwarf_lr, false);340}341342unwind_plan.AppendRow(row);343unwind_plan.SetSourceName("ARM.exidx unwind info");344unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);345unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);346unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo);347unwind_plan.SetRegisterKind(eRegisterKindDWARF);348349return true;350}351352const uint8_t *353ArmUnwindInfo::GetExceptionHandlingTableEntry(const Address &addr) {354auto it = llvm::upper_bound(m_exidx_entries,355ArmExidxEntry{0, addr.GetFileAddress(), 0});356if (it == m_exidx_entries.begin())357return nullptr;358--it;359360if (it->data == 0x1)361return nullptr; // EXIDX_CANTUNWIND362363if (it->data & 0x80000000)364return (const uint8_t *)&it->data;365366addr_t data_file_addr = it->file_address + 4 + Prel31ToAddr(it->data);367return m_arm_extab_data.GetDataStart() +368(data_file_addr - m_arm_extab_sp->GetFileAddress());369}370371372