1dda28197Spatrick //===-- ArmUnwindInfo.cpp -------------------------------------------------===// 2061da546Spatrick // 3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information. 5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6061da546Spatrick // 7061da546Spatrick //===----------------------------------------------------------------------===// 8061da546Spatrick 9061da546Spatrick #include <vector> 10061da546Spatrick 11061da546Spatrick #include "Utility/ARM_DWARF_Registers.h" 12061da546Spatrick #include "lldb/Core/Module.h" 13061da546Spatrick #include "lldb/Core/Section.h" 14061da546Spatrick #include "lldb/Symbol/ArmUnwindInfo.h" 15061da546Spatrick #include "lldb/Symbol/SymbolVendor.h" 16061da546Spatrick #include "lldb/Symbol/UnwindPlan.h" 17061da546Spatrick #include "lldb/Utility/Endian.h" 18061da546Spatrick 19061da546Spatrick /* 20061da546Spatrick * Unwind information reader and parser for the ARM exception handling ABI 21061da546Spatrick * 22061da546Spatrick * Implemented based on: 23061da546Spatrick * Exception Handling ABI for the ARM Architecture 24061da546Spatrick * Document number: ARM IHI 0038A (current through ABI r2.09) 25061da546Spatrick * Date of Issue: 25th January 2007, reissued 30th November 2012 26061da546Spatrick * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf 27061da546Spatrick */ 28061da546Spatrick 29061da546Spatrick using namespace lldb; 30061da546Spatrick using namespace lldb_private; 31061da546Spatrick 32dda28197Spatrick // Converts a prel31 value to lldb::addr_t with sign extension 33061da546Spatrick static addr_t Prel31ToAddr(uint32_t prel31) { 34061da546Spatrick addr_t res = prel31; 35061da546Spatrick if (prel31 & (1 << 30)) 36061da546Spatrick res |= 0xffffffff80000000ULL; 37061da546Spatrick return res; 38061da546Spatrick } 39061da546Spatrick 40061da546Spatrick ArmUnwindInfo::ArmExidxEntry::ArmExidxEntry(uint32_t f, lldb::addr_t a, 41061da546Spatrick uint32_t d) 42061da546Spatrick : file_address(f), address(a), data(d) {} 43061da546Spatrick 44061da546Spatrick bool ArmUnwindInfo::ArmExidxEntry::operator<(const ArmExidxEntry &other) const { 45061da546Spatrick return address < other.address; 46061da546Spatrick } 47061da546Spatrick 48061da546Spatrick ArmUnwindInfo::ArmUnwindInfo(ObjectFile &objfile, SectionSP &arm_exidx, 49061da546Spatrick SectionSP &arm_extab) 50061da546Spatrick : m_byte_order(objfile.GetByteOrder()), m_arm_exidx_sp(arm_exidx), 51061da546Spatrick m_arm_extab_sp(arm_extab) { 52061da546Spatrick objfile.ReadSectionData(arm_exidx.get(), m_arm_exidx_data); 53061da546Spatrick objfile.ReadSectionData(arm_extab.get(), m_arm_extab_data); 54061da546Spatrick 55061da546Spatrick addr_t exidx_base_addr = m_arm_exidx_sp->GetFileAddress(); 56061da546Spatrick 57061da546Spatrick offset_t offset = 0; 58061da546Spatrick while (m_arm_exidx_data.ValidOffset(offset)) { 59061da546Spatrick lldb::addr_t file_addr = exidx_base_addr + offset; 60061da546Spatrick lldb::addr_t addr = exidx_base_addr + (addr_t)offset + 61061da546Spatrick Prel31ToAddr(m_arm_exidx_data.GetU32(&offset)); 62061da546Spatrick uint32_t data = m_arm_exidx_data.GetU32(&offset); 63061da546Spatrick m_exidx_entries.emplace_back(file_addr, addr, data); 64061da546Spatrick } 65061da546Spatrick 66061da546Spatrick // Sort the entries in the exidx section. The entries should be sorted inside 67061da546Spatrick // the section but some old compiler isn't sorted them. 68061da546Spatrick llvm::sort(m_exidx_entries.begin(), m_exidx_entries.end()); 69061da546Spatrick } 70061da546Spatrick 71*be691f3bSpatrick ArmUnwindInfo::~ArmUnwindInfo() = default; 72061da546Spatrick 73061da546Spatrick // Read a byte from the unwind instruction stream with the given offset. Custom 74061da546Spatrick // function is required because have to red in order of significance within 75061da546Spatrick // their containing word (most significant byte first) and in increasing word 76061da546Spatrick // address order. 77061da546Spatrick uint8_t ArmUnwindInfo::GetByteAtOffset(const uint32_t *data, 78061da546Spatrick uint16_t offset) const { 79061da546Spatrick uint32_t value = data[offset / 4]; 80061da546Spatrick if (m_byte_order != endian::InlHostByteOrder()) 81061da546Spatrick value = llvm::ByteSwap_32(value); 82061da546Spatrick return (value >> ((3 - (offset % 4)) * 8)) & 0xff; 83061da546Spatrick } 84061da546Spatrick 85061da546Spatrick uint64_t ArmUnwindInfo::GetULEB128(const uint32_t *data, uint16_t &offset, 86061da546Spatrick uint16_t max_offset) const { 87061da546Spatrick uint64_t result = 0; 88061da546Spatrick uint8_t shift = 0; 89061da546Spatrick while (offset < max_offset) { 90061da546Spatrick uint8_t byte = GetByteAtOffset(data, offset++); 91061da546Spatrick result |= (uint64_t)(byte & 0x7f) << shift; 92061da546Spatrick if ((byte & 0x80) == 0) 93061da546Spatrick break; 94061da546Spatrick shift += 7; 95061da546Spatrick } 96061da546Spatrick return result; 97061da546Spatrick } 98061da546Spatrick 99061da546Spatrick bool ArmUnwindInfo::GetUnwindPlan(Target &target, const Address &addr, 100061da546Spatrick UnwindPlan &unwind_plan) { 101061da546Spatrick const uint32_t *data = (const uint32_t *)GetExceptionHandlingTableEntry(addr); 102061da546Spatrick if (data == nullptr) 103061da546Spatrick return false; // No unwind information for the function 104061da546Spatrick 105061da546Spatrick if (data[0] == 0x1) 106061da546Spatrick return false; // EXIDX_CANTUNWIND 107061da546Spatrick 108061da546Spatrick uint16_t byte_count = 0; 109061da546Spatrick uint16_t byte_offset = 0; 110061da546Spatrick if (data[0] & 0x80000000) { 111061da546Spatrick switch ((data[0] >> 24) & 0x0f) { 112061da546Spatrick case 0: 113061da546Spatrick byte_count = 4; 114061da546Spatrick byte_offset = 1; 115061da546Spatrick break; 116061da546Spatrick case 1: 117061da546Spatrick case 2: 118061da546Spatrick byte_count = 4 * ((data[0] >> 16) & 0xff) + 4; 119061da546Spatrick byte_offset = 2; 120061da546Spatrick break; 121061da546Spatrick default: 122061da546Spatrick // Unhandled personality routine index 123061da546Spatrick return false; 124061da546Spatrick } 125061da546Spatrick } else { 126061da546Spatrick byte_count = 4 * ((data[1] >> 24) & 0xff) + 8; 127061da546Spatrick byte_offset = 5; 128061da546Spatrick } 129061da546Spatrick 130061da546Spatrick uint8_t vsp_reg = dwarf_sp; 131061da546Spatrick int32_t vsp = 0; 132061da546Spatrick std::vector<std::pair<uint32_t, int32_t>> 133061da546Spatrick register_offsets; // register -> (offset from vsp_reg) 134061da546Spatrick 135061da546Spatrick while (byte_offset < byte_count) { 136061da546Spatrick uint8_t byte1 = GetByteAtOffset(data, byte_offset++); 137061da546Spatrick if ((byte1 & 0xc0) == 0x00) { 138061da546Spatrick // 00xxxxxx 139061da546Spatrick // vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive 140061da546Spatrick vsp += ((byte1 & 0x3f) << 2) + 4; 141061da546Spatrick } else if ((byte1 & 0xc0) == 0x40) { 142061da546Spatrick // 01xxxxxx 143061da546Spatrick // vsp = vsp – (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive 144061da546Spatrick vsp -= ((byte1 & 0x3f) << 2) + 4; 145061da546Spatrick } else if ((byte1 & 0xf0) == 0x80) { 146061da546Spatrick if (byte_offset >= byte_count) 147061da546Spatrick return false; 148061da546Spatrick 149061da546Spatrick uint8_t byte2 = GetByteAtOffset(data, byte_offset++); 150061da546Spatrick if (byte1 == 0x80 && byte2 == 0) { 151061da546Spatrick // 10000000 00000000 152061da546Spatrick // Refuse to unwind (for example, out of a cleanup) (see remark a) 153061da546Spatrick return false; 154061da546Spatrick } else { 155061da546Spatrick // 1000iiii iiiiiiii (i not all 0) 156061da546Spatrick // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4} (see 157061da546Spatrick // remark b) 158061da546Spatrick uint16_t regs = ((byte1 & 0x0f) << 8) | byte2; 159061da546Spatrick for (uint8_t i = 0; i < 12; ++i) { 160061da546Spatrick if (regs & (1 << i)) { 161061da546Spatrick register_offsets.emplace_back(dwarf_r4 + i, vsp); 162061da546Spatrick vsp += 4; 163061da546Spatrick } 164061da546Spatrick } 165061da546Spatrick } 166061da546Spatrick } else if ((byte1 & 0xff) == 0x9d) { 167061da546Spatrick // 10011101 168061da546Spatrick // Reserved as prefix for ARM register to register moves 169061da546Spatrick return false; 170061da546Spatrick } else if ((byte1 & 0xff) == 0x9f) { 171061da546Spatrick // 10011111 172061da546Spatrick // Reserved as prefix for Intel Wireless MMX register to register moves 173061da546Spatrick return false; 174061da546Spatrick } else if ((byte1 & 0xf0) == 0x90) { 175061da546Spatrick // 1001nnnn (nnnn != 13,15) 176061da546Spatrick // Set vsp = r[nnnn] 177061da546Spatrick vsp_reg = dwarf_r0 + (byte1 & 0x0f); 178061da546Spatrick } else if ((byte1 & 0xf8) == 0xa0) { 179061da546Spatrick // 10100nnn 180061da546Spatrick // Pop r4-r[4+nnn] 181061da546Spatrick uint8_t n = byte1 & 0x7; 182061da546Spatrick for (uint8_t i = 0; i <= n; ++i) { 183061da546Spatrick register_offsets.emplace_back(dwarf_r4 + i, vsp); 184061da546Spatrick vsp += 4; 185061da546Spatrick } 186061da546Spatrick } else if ((byte1 & 0xf8) == 0xa8) { 187061da546Spatrick // 10101nnn 188061da546Spatrick // Pop r4-r[4+nnn], r14 189061da546Spatrick uint8_t n = byte1 & 0x7; 190061da546Spatrick for (uint8_t i = 0; i <= n; ++i) { 191061da546Spatrick register_offsets.emplace_back(dwarf_r4 + i, vsp); 192061da546Spatrick vsp += 4; 193061da546Spatrick } 194061da546Spatrick 195061da546Spatrick register_offsets.emplace_back(dwarf_lr, vsp); 196061da546Spatrick vsp += 4; 197061da546Spatrick } else if ((byte1 & 0xff) == 0xb0) { 198061da546Spatrick // 10110000 199061da546Spatrick // Finish (see remark c) 200061da546Spatrick break; 201061da546Spatrick } else if ((byte1 & 0xff) == 0xb1) { 202061da546Spatrick if (byte_offset >= byte_count) 203061da546Spatrick return false; 204061da546Spatrick 205061da546Spatrick uint8_t byte2 = GetByteAtOffset(data, byte_offset++); 206061da546Spatrick if ((byte2 & 0xff) == 0x00) { 207061da546Spatrick // 10110001 00000000 208061da546Spatrick // Spare (see remark f) 209061da546Spatrick return false; 210061da546Spatrick } else if ((byte2 & 0xf0) == 0x00) { 211061da546Spatrick // 10110001 0000iiii (i not all 0) 212061da546Spatrick // Pop integer registers under mask {r3, r2, r1, r0} 213061da546Spatrick for (uint8_t i = 0; i < 4; ++i) { 214061da546Spatrick if (byte2 & (1 << i)) { 215061da546Spatrick register_offsets.emplace_back(dwarf_r0 + i, vsp); 216061da546Spatrick vsp += 4; 217061da546Spatrick } 218061da546Spatrick } 219061da546Spatrick } else { 220061da546Spatrick // 10110001 xxxxyyyy 221061da546Spatrick // Spare (xxxx != 0000) 222061da546Spatrick return false; 223061da546Spatrick } 224061da546Spatrick } else if ((byte1 & 0xff) == 0xb2) { 225061da546Spatrick // 10110010 uleb128 226061da546Spatrick // vsp = vsp + 0x204+ (uleb128 << 2) 227061da546Spatrick uint64_t uleb128 = GetULEB128(data, byte_offset, byte_count); 228061da546Spatrick vsp += 0x204 + (uleb128 << 2); 229061da546Spatrick } else if ((byte1 & 0xff) == 0xb3) { 230061da546Spatrick // 10110011 sssscccc 231061da546Spatrick // Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) 232061da546Spatrick // by FSTMFDX (see remark d) 233061da546Spatrick if (byte_offset >= byte_count) 234061da546Spatrick return false; 235061da546Spatrick 236061da546Spatrick uint8_t byte2 = GetByteAtOffset(data, byte_offset++); 237061da546Spatrick uint8_t s = (byte2 & 0xf0) >> 4; 238061da546Spatrick uint8_t c = (byte2 & 0x0f) >> 0; 239061da546Spatrick for (uint8_t i = 0; i <= c; ++i) { 240061da546Spatrick register_offsets.emplace_back(dwarf_d0 + s + i, vsp); 241061da546Spatrick vsp += 8; 242061da546Spatrick } 243061da546Spatrick vsp += 4; 244061da546Spatrick } else if ((byte1 & 0xfc) == 0xb4) { 245061da546Spatrick // 101101nn 246061da546Spatrick // Spare (was Pop FPA) 247061da546Spatrick return false; 248061da546Spatrick } else if ((byte1 & 0xf8) == 0xb8) { 249061da546Spatrick // 10111nnn 250061da546Spatrick // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by 251061da546Spatrick // FSTMFDX (see remark d) 252061da546Spatrick uint8_t n = byte1 & 0x07; 253061da546Spatrick for (uint8_t i = 0; i <= n; ++i) { 254061da546Spatrick register_offsets.emplace_back(dwarf_d8 + i, vsp); 255061da546Spatrick vsp += 8; 256061da546Spatrick } 257061da546Spatrick vsp += 4; 258061da546Spatrick } else if ((byte1 & 0xf8) == 0xc0) { 259061da546Spatrick // 11000nnn (nnn != 6,7) 260061da546Spatrick // Intel Wireless MMX pop wR[10]-wR[10+nnn] 261061da546Spatrick 262061da546Spatrick // 11000110 sssscccc 263061da546Spatrick // Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc] (see remark e) 264061da546Spatrick 265061da546Spatrick // 11000111 00000000 266061da546Spatrick // Spare 267061da546Spatrick 268061da546Spatrick // 11000111 0000iiii 269061da546Spatrick // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0} 270061da546Spatrick 271061da546Spatrick // 11000111 xxxxyyyy 272061da546Spatrick // Spare (xxxx != 0000) 273061da546Spatrick 274061da546Spatrick return false; 275061da546Spatrick } else if ((byte1 & 0xff) == 0xc8) { 276061da546Spatrick // 11001000 sssscccc 277061da546Spatrick // Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] saved 278061da546Spatrick // (as if) by FSTMFDD (see remarks d,e) 279061da546Spatrick if (byte_offset >= byte_count) 280061da546Spatrick return false; 281061da546Spatrick 282061da546Spatrick uint8_t byte2 = GetByteAtOffset(data, byte_offset++); 283061da546Spatrick uint8_t s = (byte2 & 0xf0) >> 4; 284061da546Spatrick uint8_t c = (byte2 & 0x0f) >> 0; 285061da546Spatrick for (uint8_t i = 0; i <= c; ++i) { 286061da546Spatrick register_offsets.emplace_back(dwarf_d16 + s + i, vsp); 287061da546Spatrick vsp += 8; 288061da546Spatrick } 289061da546Spatrick } else if ((byte1 & 0xff) == 0xc9) { 290061da546Spatrick // 11001001 sssscccc 291061da546Spatrick // Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) 292061da546Spatrick // by FSTMFDD (see remark d) 293061da546Spatrick if (byte_offset >= byte_count) 294061da546Spatrick return false; 295061da546Spatrick 296061da546Spatrick uint8_t byte2 = GetByteAtOffset(data, byte_offset++); 297061da546Spatrick uint8_t s = (byte2 & 0xf0) >> 4; 298061da546Spatrick uint8_t c = (byte2 & 0x0f) >> 0; 299061da546Spatrick for (uint8_t i = 0; i <= c; ++i) { 300061da546Spatrick register_offsets.emplace_back(dwarf_d0 + s + i, vsp); 301061da546Spatrick vsp += 8; 302061da546Spatrick } 303061da546Spatrick } else if ((byte1 & 0xf8) == 0xc8) { 304061da546Spatrick // 11001yyy 305061da546Spatrick // Spare (yyy != 000, 001) 306061da546Spatrick return false; 307061da546Spatrick } else if ((byte1 & 0xf8) == 0xd0) { 308061da546Spatrick // 11010nnn 309061da546Spatrick // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by 310061da546Spatrick // FSTMFDD (see remark d) 311061da546Spatrick uint8_t n = byte1 & 0x07; 312061da546Spatrick for (uint8_t i = 0; i <= n; ++i) { 313061da546Spatrick register_offsets.emplace_back(dwarf_d8 + i, vsp); 314061da546Spatrick vsp += 8; 315061da546Spatrick } 316061da546Spatrick } else if ((byte1 & 0xc0) == 0xc0) { 317061da546Spatrick // 11xxxyyy Spare (xxx != 000, 001, 010) 318061da546Spatrick return false; 319061da546Spatrick } else { 320061da546Spatrick return false; 321061da546Spatrick } 322061da546Spatrick } 323061da546Spatrick 324061da546Spatrick UnwindPlan::RowSP row = std::make_shared<UnwindPlan::Row>(); 325061da546Spatrick row->SetOffset(0); 326061da546Spatrick row->GetCFAValue().SetIsRegisterPlusOffset(vsp_reg, vsp); 327061da546Spatrick 328061da546Spatrick bool have_location_for_pc = false; 329061da546Spatrick for (const auto &offset : register_offsets) { 330061da546Spatrick have_location_for_pc |= offset.first == dwarf_pc; 331061da546Spatrick row->SetRegisterLocationToAtCFAPlusOffset(offset.first, offset.second - vsp, 332061da546Spatrick true); 333061da546Spatrick } 334061da546Spatrick 335061da546Spatrick if (!have_location_for_pc) { 336061da546Spatrick UnwindPlan::Row::RegisterLocation lr_location; 337061da546Spatrick if (row->GetRegisterInfo(dwarf_lr, lr_location)) 338061da546Spatrick row->SetRegisterInfo(dwarf_pc, lr_location); 339061da546Spatrick else 340061da546Spatrick row->SetRegisterLocationToRegister(dwarf_pc, dwarf_lr, false); 341061da546Spatrick } 342061da546Spatrick 343061da546Spatrick unwind_plan.AppendRow(row); 344061da546Spatrick unwind_plan.SetSourceName("ARM.exidx unwind info"); 345061da546Spatrick unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); 346061da546Spatrick unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); 347061da546Spatrick unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); 348061da546Spatrick unwind_plan.SetRegisterKind(eRegisterKindDWARF); 349061da546Spatrick 350061da546Spatrick return true; 351061da546Spatrick } 352061da546Spatrick 353061da546Spatrick const uint8_t * 354061da546Spatrick ArmUnwindInfo::GetExceptionHandlingTableEntry(const Address &addr) { 355061da546Spatrick auto it = std::upper_bound(m_exidx_entries.begin(), m_exidx_entries.end(), 356061da546Spatrick ArmExidxEntry{0, addr.GetFileAddress(), 0}); 357061da546Spatrick if (it == m_exidx_entries.begin()) 358061da546Spatrick return nullptr; 359061da546Spatrick --it; 360061da546Spatrick 361061da546Spatrick if (it->data == 0x1) 362061da546Spatrick return nullptr; // EXIDX_CANTUNWIND 363061da546Spatrick 364061da546Spatrick if (it->data & 0x80000000) 365061da546Spatrick return (const uint8_t *)&it->data; 366061da546Spatrick 367061da546Spatrick addr_t data_file_addr = it->file_address + 4 + Prel31ToAddr(it->data); 368061da546Spatrick return m_arm_extab_data.GetDataStart() + 369061da546Spatrick (data_file_addr - m_arm_extab_sp->GetFileAddress()); 370061da546Spatrick } 371