|  | //===-- ArmUnwindInfo.cpp -------------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include "Utility/ARM_DWARF_Registers.h" | 
|  | #include "lldb/Core/Module.h" | 
|  | #include "lldb/Core/Section.h" | 
|  | #include "lldb/Symbol/ArmUnwindInfo.h" | 
|  | #include "lldb/Symbol/SymbolVendor.h" | 
|  | #include "lldb/Symbol/UnwindPlan.h" | 
|  | #include "lldb/Utility/Endian.h" | 
|  |  | 
|  | /* | 
|  | * Unwind information reader and parser for the ARM exception handling ABI | 
|  | * | 
|  | * Implemented based on: | 
|  | *     Exception Handling ABI for the ARM Architecture | 
|  | *     Document number: ARM IHI 0038A (current through ABI r2.09) | 
|  | *     Date of Issue: 25th January 2007, reissued 30th November 2012 | 
|  | *     http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf | 
|  | */ | 
|  |  | 
|  | using namespace lldb; | 
|  | using namespace lldb_private; | 
|  |  | 
|  | // Converts a prel31 value to lldb::addr_t with sign extension | 
|  | static addr_t Prel31ToAddr(uint32_t prel31) { | 
|  | addr_t res = prel31; | 
|  | if (prel31 & (1 << 30)) | 
|  | res |= 0xffffffff80000000ULL; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | ArmUnwindInfo::ArmExidxEntry::ArmExidxEntry(uint32_t f, lldb::addr_t a, | 
|  | uint32_t d) | 
|  | : file_address(f), address(a), data(d) {} | 
|  |  | 
|  | bool ArmUnwindInfo::ArmExidxEntry::operator<(const ArmExidxEntry &other) const { | 
|  | return address < other.address; | 
|  | } | 
|  |  | 
|  | ArmUnwindInfo::ArmUnwindInfo(ObjectFile &objfile, SectionSP &arm_exidx, | 
|  | SectionSP &arm_extab) | 
|  | : m_byte_order(objfile.GetByteOrder()), m_arm_exidx_sp(arm_exidx), | 
|  | m_arm_extab_sp(arm_extab) { | 
|  | objfile.ReadSectionData(arm_exidx.get(), m_arm_exidx_data); | 
|  | objfile.ReadSectionData(arm_extab.get(), m_arm_extab_data); | 
|  |  | 
|  | addr_t exidx_base_addr = m_arm_exidx_sp->GetFileAddress(); | 
|  |  | 
|  | offset_t offset = 0; | 
|  | while (m_arm_exidx_data.ValidOffset(offset)) { | 
|  | lldb::addr_t file_addr = exidx_base_addr + offset; | 
|  | lldb::addr_t addr = exidx_base_addr + (addr_t)offset + | 
|  | Prel31ToAddr(m_arm_exidx_data.GetU32(&offset)); | 
|  | uint32_t data = m_arm_exidx_data.GetU32(&offset); | 
|  | m_exidx_entries.emplace_back(file_addr, addr, data); | 
|  | } | 
|  |  | 
|  | // Sort the entries in the exidx section. The entries should be sorted inside | 
|  | // the section but some old compiler isn't sorted them. | 
|  | llvm::sort(m_exidx_entries); | 
|  | } | 
|  |  | 
|  | ArmUnwindInfo::~ArmUnwindInfo() = default; | 
|  |  | 
|  | // Read a byte from the unwind instruction stream with the given offset. Custom | 
|  | // function is required because have to red in order of significance within | 
|  | // their containing word (most significant byte first) and in increasing word | 
|  | // address order. | 
|  | uint8_t ArmUnwindInfo::GetByteAtOffset(const uint32_t *data, | 
|  | uint16_t offset) const { | 
|  | uint32_t value = data[offset / 4]; | 
|  | if (m_byte_order != endian::InlHostByteOrder()) | 
|  | value = llvm::byteswap<uint32_t>(value); | 
|  | return (value >> ((3 - (offset % 4)) * 8)) & 0xff; | 
|  | } | 
|  |  | 
|  | uint64_t ArmUnwindInfo::GetULEB128(const uint32_t *data, uint16_t &offset, | 
|  | uint16_t max_offset) const { | 
|  | uint64_t result = 0; | 
|  | uint8_t shift = 0; | 
|  | while (offset < max_offset) { | 
|  | uint8_t byte = GetByteAtOffset(data, offset++); | 
|  | result |= (uint64_t)(byte & 0x7f) << shift; | 
|  | if ((byte & 0x80) == 0) | 
|  | break; | 
|  | shift += 7; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | bool ArmUnwindInfo::GetUnwindPlan(Target &target, const Address &addr, | 
|  | UnwindPlan &unwind_plan) { | 
|  | const uint32_t *data = (const uint32_t *)GetExceptionHandlingTableEntry(addr); | 
|  | if (data == nullptr) | 
|  | return false; // No unwind information for the function | 
|  |  | 
|  | if (data[0] == 0x1) | 
|  | return false; // EXIDX_CANTUNWIND | 
|  |  | 
|  | uint16_t byte_count = 0; | 
|  | uint16_t byte_offset = 0; | 
|  | if (data[0] & 0x80000000) { | 
|  | switch ((data[0] >> 24) & 0x0f) { | 
|  | case 0: | 
|  | byte_count = 4; | 
|  | byte_offset = 1; | 
|  | break; | 
|  | case 1: | 
|  | case 2: | 
|  | byte_count = 4 * ((data[0] >> 16) & 0xff) + 4; | 
|  | byte_offset = 2; | 
|  | break; | 
|  | default: | 
|  | // Unhandled personality routine index | 
|  | return false; | 
|  | } | 
|  | } else { | 
|  | byte_count = 4 * ((data[1] >> 24) & 0xff) + 8; | 
|  | byte_offset = 5; | 
|  | } | 
|  |  | 
|  | uint8_t vsp_reg = dwarf_sp; | 
|  | int32_t vsp = 0; | 
|  | std::vector<std::pair<uint32_t, int32_t>> | 
|  | register_offsets; // register -> (offset from vsp_reg) | 
|  |  | 
|  | while (byte_offset < byte_count) { | 
|  | uint8_t byte1 = GetByteAtOffset(data, byte_offset++); | 
|  | if ((byte1 & 0xc0) == 0x00) { | 
|  | // 00xxxxxx | 
|  | // vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive | 
|  | vsp += ((byte1 & 0x3f) << 2) + 4; | 
|  | } else if ((byte1 & 0xc0) == 0x40) { | 
|  | // 01xxxxxx | 
|  | // vsp = vsp – (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive | 
|  | vsp -= ((byte1 & 0x3f) << 2) + 4; | 
|  | } else if ((byte1 & 0xf0) == 0x80) { | 
|  | if (byte_offset >= byte_count) | 
|  | return false; | 
|  |  | 
|  | uint8_t byte2 = GetByteAtOffset(data, byte_offset++); | 
|  | if (byte1 == 0x80 && byte2 == 0) { | 
|  | // 10000000 00000000 | 
|  | // Refuse to unwind (for example, out of a cleanup) (see remark a) | 
|  | return false; | 
|  | } else { | 
|  | // 1000iiii iiiiiiii (i not all 0) | 
|  | // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4} (see | 
|  | // remark b) | 
|  | uint16_t regs = ((byte1 & 0x0f) << 8) | byte2; | 
|  | for (uint8_t i = 0; i < 12; ++i) { | 
|  | if (regs & (1 << i)) { | 
|  | register_offsets.emplace_back(dwarf_r4 + i, vsp); | 
|  | vsp += 4; | 
|  | } | 
|  | } | 
|  | } | 
|  | } else if ((byte1 & 0xff) == 0x9d) { | 
|  | // 10011101 | 
|  | // Reserved as prefix for ARM register to register moves | 
|  | return false; | 
|  | } else if ((byte1 & 0xff) == 0x9f) { | 
|  | // 10011111 | 
|  | // Reserved as prefix for Intel Wireless MMX register to register moves | 
|  | return false; | 
|  | } else if ((byte1 & 0xf0) == 0x90) { | 
|  | // 1001nnnn (nnnn != 13,15) | 
|  | // Set vsp = r[nnnn] | 
|  | vsp_reg = dwarf_r0 + (byte1 & 0x0f); | 
|  | } else if ((byte1 & 0xf8) == 0xa0) { | 
|  | // 10100nnn | 
|  | // Pop r4-r[4+nnn] | 
|  | uint8_t n = byte1 & 0x7; | 
|  | for (uint8_t i = 0; i <= n; ++i) { | 
|  | register_offsets.emplace_back(dwarf_r4 + i, vsp); | 
|  | vsp += 4; | 
|  | } | 
|  | } else if ((byte1 & 0xf8) == 0xa8) { | 
|  | // 10101nnn | 
|  | // Pop r4-r[4+nnn], r14 | 
|  | uint8_t n = byte1 & 0x7; | 
|  | for (uint8_t i = 0; i <= n; ++i) { | 
|  | register_offsets.emplace_back(dwarf_r4 + i, vsp); | 
|  | vsp += 4; | 
|  | } | 
|  |  | 
|  | register_offsets.emplace_back(dwarf_lr, vsp); | 
|  | vsp += 4; | 
|  | } else if ((byte1 & 0xff) == 0xb0) { | 
|  | // 10110000 | 
|  | // Finish (see remark c) | 
|  | break; | 
|  | } else if ((byte1 & 0xff) == 0xb1) { | 
|  | if (byte_offset >= byte_count) | 
|  | return false; | 
|  |  | 
|  | uint8_t byte2 = GetByteAtOffset(data, byte_offset++); | 
|  | if ((byte2 & 0xff) == 0x00) { | 
|  | // 10110001 00000000 | 
|  | // Spare (see remark f) | 
|  | return false; | 
|  | } else if ((byte2 & 0xf0) == 0x00) { | 
|  | // 10110001 0000iiii (i not all 0) | 
|  | // Pop integer registers under mask {r3, r2, r1, r0} | 
|  | for (uint8_t i = 0; i < 4; ++i) { | 
|  | if (byte2 & (1 << i)) { | 
|  | register_offsets.emplace_back(dwarf_r0 + i, vsp); | 
|  | vsp += 4; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // 10110001 xxxxyyyy | 
|  | // Spare (xxxx != 0000) | 
|  | return false; | 
|  | } | 
|  | } else if ((byte1 & 0xff) == 0xb2) { | 
|  | // 10110010 uleb128 | 
|  | // vsp = vsp + 0x204+ (uleb128 << 2) | 
|  | uint64_t uleb128 = GetULEB128(data, byte_offset, byte_count); | 
|  | vsp += 0x204 + (uleb128 << 2); | 
|  | } else if ((byte1 & 0xff) == 0xb3) { | 
|  | // 10110011 sssscccc | 
|  | // Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) | 
|  | // by FSTMFDX (see remark d) | 
|  | if (byte_offset >= byte_count) | 
|  | return false; | 
|  |  | 
|  | uint8_t byte2 = GetByteAtOffset(data, byte_offset++); | 
|  | uint8_t s = (byte2 & 0xf0) >> 4; | 
|  | uint8_t c = (byte2 & 0x0f) >> 0; | 
|  | for (uint8_t i = 0; i <= c; ++i) { | 
|  | register_offsets.emplace_back(dwarf_d0 + s + i, vsp); | 
|  | vsp += 8; | 
|  | } | 
|  | vsp += 4; | 
|  | } else if ((byte1 & 0xfc) == 0xb4) { | 
|  | // 101101nn | 
|  | // Spare (was Pop FPA) | 
|  | return false; | 
|  | } else if ((byte1 & 0xf8) == 0xb8) { | 
|  | // 10111nnn | 
|  | // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by | 
|  | // FSTMFDX (see remark d) | 
|  | uint8_t n = byte1 & 0x07; | 
|  | for (uint8_t i = 0; i <= n; ++i) { | 
|  | register_offsets.emplace_back(dwarf_d8 + i, vsp); | 
|  | vsp += 8; | 
|  | } | 
|  | vsp += 4; | 
|  | } else if ((byte1 & 0xf8) == 0xc0) { | 
|  | // 11000nnn (nnn != 6,7) | 
|  | // Intel Wireless MMX pop wR[10]-wR[10+nnn] | 
|  |  | 
|  | // 11000110 sssscccc | 
|  | // Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc] (see remark e) | 
|  |  | 
|  | // 11000111 00000000 | 
|  | // Spare | 
|  |  | 
|  | // 11000111 0000iiii | 
|  | // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0} | 
|  |  | 
|  | // 11000111 xxxxyyyy | 
|  | // Spare (xxxx != 0000) | 
|  |  | 
|  | return false; | 
|  | } else if ((byte1 & 0xff) == 0xc8) { | 
|  | // 11001000 sssscccc | 
|  | // Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] saved | 
|  | // (as if) by FSTMFDD (see remarks d,e) | 
|  | if (byte_offset >= byte_count) | 
|  | return false; | 
|  |  | 
|  | uint8_t byte2 = GetByteAtOffset(data, byte_offset++); | 
|  | uint8_t s = (byte2 & 0xf0) >> 4; | 
|  | uint8_t c = (byte2 & 0x0f) >> 0; | 
|  | for (uint8_t i = 0; i <= c; ++i) { | 
|  | register_offsets.emplace_back(dwarf_d16 + s + i, vsp); | 
|  | vsp += 8; | 
|  | } | 
|  | } else if ((byte1 & 0xff) == 0xc9) { | 
|  | // 11001001 sssscccc | 
|  | // Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) | 
|  | // by FSTMFDD (see remark d) | 
|  | if (byte_offset >= byte_count) | 
|  | return false; | 
|  |  | 
|  | uint8_t byte2 = GetByteAtOffset(data, byte_offset++); | 
|  | uint8_t s = (byte2 & 0xf0) >> 4; | 
|  | uint8_t c = (byte2 & 0x0f) >> 0; | 
|  | for (uint8_t i = 0; i <= c; ++i) { | 
|  | register_offsets.emplace_back(dwarf_d0 + s + i, vsp); | 
|  | vsp += 8; | 
|  | } | 
|  | } else if ((byte1 & 0xf8) == 0xc8) { | 
|  | // 11001yyy | 
|  | // Spare (yyy != 000, 001) | 
|  | return false; | 
|  | } else if ((byte1 & 0xf8) == 0xd0) { | 
|  | // 11010nnn | 
|  | // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by | 
|  | // FSTMFDD (see remark d) | 
|  | uint8_t n = byte1 & 0x07; | 
|  | for (uint8_t i = 0; i <= n; ++i) { | 
|  | register_offsets.emplace_back(dwarf_d8 + i, vsp); | 
|  | vsp += 8; | 
|  | } | 
|  | } else if ((byte1 & 0xc0) == 0xc0) { | 
|  | // 11xxxyyy Spare (xxx != 000, 001, 010) | 
|  | return false; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | UnwindPlan::Row row; | 
|  | row.GetCFAValue().SetIsRegisterPlusOffset(vsp_reg, vsp); | 
|  |  | 
|  | bool have_location_for_pc = false; | 
|  | for (const auto &offset : register_offsets) { | 
|  | have_location_for_pc |= offset.first == dwarf_pc; | 
|  | row.SetRegisterLocationToAtCFAPlusOffset(offset.first, offset.second - vsp, | 
|  | true); | 
|  | } | 
|  |  | 
|  | if (!have_location_for_pc) { | 
|  | UnwindPlan::Row::AbstractRegisterLocation lr_location; | 
|  | if (row.GetRegisterInfo(dwarf_lr, lr_location)) | 
|  | row.SetRegisterInfo(dwarf_pc, lr_location); | 
|  | else | 
|  | row.SetRegisterLocationToRegister(dwarf_pc, dwarf_lr, false); | 
|  | } | 
|  |  | 
|  | unwind_plan.AppendRow(row); | 
|  | unwind_plan.SetSourceName("ARM.exidx unwind info"); | 
|  | unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); | 
|  | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); | 
|  | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); | 
|  | unwind_plan.SetRegisterKind(eRegisterKindDWARF); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const uint8_t * | 
|  | ArmUnwindInfo::GetExceptionHandlingTableEntry(const Address &addr) { | 
|  | auto it = llvm::upper_bound(m_exidx_entries, | 
|  | ArmExidxEntry{0, addr.GetFileAddress(), 0}); | 
|  | if (it == m_exidx_entries.begin()) | 
|  | return nullptr; | 
|  | --it; | 
|  |  | 
|  | if (it->data == 0x1) | 
|  | return nullptr; // EXIDX_CANTUNWIND | 
|  |  | 
|  | if (it->data & 0x80000000) | 
|  | return (const uint8_t *)&it->data; | 
|  |  | 
|  | addr_t data_file_addr = it->file_address + 4 + Prel31ToAddr(it->data); | 
|  | return m_arm_extab_data.GetDataStart() + | 
|  | (data_file_addr - m_arm_extab_sp->GetFileAddress()); | 
|  | } |