| 1 | //===-- ArmUnwindInfo.cpp -------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include <vector> |
| 10 | |
| 11 | #include "Utility/ARM_DWARF_Registers.h" |
| 12 | #include "lldb/Core/Module.h" |
| 13 | #include "lldb/Core/Section.h" |
| 14 | #include "lldb/Symbol/ArmUnwindInfo.h" |
| 15 | #include "lldb/Symbol/SymbolVendor.h" |
| 16 | #include "lldb/Symbol/UnwindPlan.h" |
| 17 | #include "lldb/Utility/Endian.h" |
| 18 | |
| 19 | /* |
| 20 | * Unwind information reader and parser for the ARM exception handling ABI |
| 21 | * |
| 22 | * Implemented based on: |
| 23 | * Exception Handling ABI for the ARM Architecture |
| 24 | * Document number: ARM IHI 0038A (current through ABI r2.09) |
| 25 | * Date of Issue: 25th January 2007, reissued 30th November 2012 |
| 26 | * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf |
| 27 | */ |
| 28 | |
| 29 | using namespace lldb; |
| 30 | using namespace lldb_private; |
| 31 | |
| 32 | // Converts a prel31 value to lldb::addr_t with sign extension |
| 33 | static addr_t Prel31ToAddr(uint32_t prel31) { |
| 34 | addr_t res = prel31; |
| 35 | if (prel31 & (1 << 30)) |
| 36 | res |= 0xffffffff80000000ULL; |
| 37 | return res; |
| 38 | } |
| 39 | |
| 40 | ArmUnwindInfo::ArmExidxEntry::ArmExidxEntry(uint32_t f, lldb::addr_t a, |
| 41 | uint32_t d) |
| 42 | : file_address(f), address(a), data(d) {} |
| 43 | |
| 44 | bool ArmUnwindInfo::ArmExidxEntry::operator<(const ArmExidxEntry &other) const { |
| 45 | return address < other.address; |
| 46 | } |
| 47 | |
| 48 | ArmUnwindInfo::ArmUnwindInfo(ObjectFile &objfile, SectionSP &arm_exidx, |
| 49 | SectionSP &arm_extab) |
| 50 | : m_byte_order(objfile.GetByteOrder()), m_arm_exidx_sp(arm_exidx), |
| 51 | m_arm_extab_sp(arm_extab) { |
| 52 | objfile.ReadSectionData(section: arm_exidx.get(), section_data&: m_arm_exidx_data); |
| 53 | objfile.ReadSectionData(section: arm_extab.get(), section_data&: m_arm_extab_data); |
| 54 | |
| 55 | addr_t exidx_base_addr = m_arm_exidx_sp->GetFileAddress(); |
| 56 | |
| 57 | offset_t offset = 0; |
| 58 | while (m_arm_exidx_data.ValidOffset(offset)) { |
| 59 | lldb::addr_t file_addr = exidx_base_addr + offset; |
| 60 | lldb::addr_t addr = exidx_base_addr + (addr_t)offset + |
| 61 | Prel31ToAddr(prel31: m_arm_exidx_data.GetU32(offset_ptr: &offset)); |
| 62 | uint32_t data = m_arm_exidx_data.GetU32(offset_ptr: &offset); |
| 63 | m_exidx_entries.emplace_back(args&: file_addr, args&: addr, args&: data); |
| 64 | } |
| 65 | |
| 66 | // Sort the entries in the exidx section. The entries should be sorted inside |
| 67 | // the section but some old compiler isn't sorted them. |
| 68 | llvm::sort(C&: m_exidx_entries); |
| 69 | } |
| 70 | |
| 71 | ArmUnwindInfo::~ArmUnwindInfo() = default; |
| 72 | |
| 73 | // Read a byte from the unwind instruction stream with the given offset. Custom |
| 74 | // function is required because have to red in order of significance within |
| 75 | // their containing word (most significant byte first) and in increasing word |
| 76 | // address order. |
| 77 | uint8_t ArmUnwindInfo::GetByteAtOffset(const uint32_t *data, |
| 78 | uint16_t offset) const { |
| 79 | uint32_t value = data[offset / 4]; |
| 80 | if (m_byte_order != endian::InlHostByteOrder()) |
| 81 | value = llvm::byteswap<uint32_t>(V: value); |
| 82 | return (value >> ((3 - (offset % 4)) * 8)) & 0xff; |
| 83 | } |
| 84 | |
| 85 | uint64_t ArmUnwindInfo::GetULEB128(const uint32_t *data, uint16_t &offset, |
| 86 | uint16_t max_offset) const { |
| 87 | uint64_t result = 0; |
| 88 | uint8_t shift = 0; |
| 89 | while (offset < max_offset) { |
| 90 | uint8_t byte = GetByteAtOffset(data, offset: offset++); |
| 91 | result |= (uint64_t)(byte & 0x7f) << shift; |
| 92 | if ((byte & 0x80) == 0) |
| 93 | break; |
| 94 | shift += 7; |
| 95 | } |
| 96 | return result; |
| 97 | } |
| 98 | |
| 99 | bool ArmUnwindInfo::GetUnwindPlan(Target &target, const Address &addr, |
| 100 | UnwindPlan &unwind_plan) { |
| 101 | const uint32_t *data = (const uint32_t *)GetExceptionHandlingTableEntry(addr); |
| 102 | if (data == nullptr) |
| 103 | return false; // No unwind information for the function |
| 104 | |
| 105 | if (data[0] == 0x1) |
| 106 | return false; // EXIDX_CANTUNWIND |
| 107 | |
| 108 | uint16_t byte_count = 0; |
| 109 | uint16_t byte_offset = 0; |
| 110 | if (data[0] & 0x80000000) { |
| 111 | switch ((data[0] >> 24) & 0x0f) { |
| 112 | case 0: |
| 113 | byte_count = 4; |
| 114 | byte_offset = 1; |
| 115 | break; |
| 116 | case 1: |
| 117 | case 2: |
| 118 | byte_count = 4 * ((data[0] >> 16) & 0xff) + 4; |
| 119 | byte_offset = 2; |
| 120 | break; |
| 121 | default: |
| 122 | // Unhandled personality routine index |
| 123 | return false; |
| 124 | } |
| 125 | } else { |
| 126 | byte_count = 4 * ((data[1] >> 24) & 0xff) + 8; |
| 127 | byte_offset = 5; |
| 128 | } |
| 129 | |
| 130 | uint8_t vsp_reg = dwarf_sp; |
| 131 | int32_t vsp = 0; |
| 132 | std::vector<std::pair<uint32_t, int32_t>> |
| 133 | register_offsets; // register -> (offset from vsp_reg) |
| 134 | |
| 135 | while (byte_offset < byte_count) { |
| 136 | uint8_t byte1 = GetByteAtOffset(data, offset: byte_offset++); |
| 137 | if ((byte1 & 0xc0) == 0x00) { |
| 138 | // 00xxxxxx |
| 139 | // vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive |
| 140 | vsp += ((byte1 & 0x3f) << 2) + 4; |
| 141 | } else if ((byte1 & 0xc0) == 0x40) { |
| 142 | // 01xxxxxx |
| 143 | // vsp = vsp – (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive |
| 144 | vsp -= ((byte1 & 0x3f) << 2) + 4; |
| 145 | } else if ((byte1 & 0xf0) == 0x80) { |
| 146 | if (byte_offset >= byte_count) |
| 147 | return false; |
| 148 | |
| 149 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
| 150 | if (byte1 == 0x80 && byte2 == 0) { |
| 151 | // 10000000 00000000 |
| 152 | // Refuse to unwind (for example, out of a cleanup) (see remark a) |
| 153 | return false; |
| 154 | } else { |
| 155 | // 1000iiii iiiiiiii (i not all 0) |
| 156 | // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4} (see |
| 157 | // remark b) |
| 158 | uint16_t regs = ((byte1 & 0x0f) << 8) | byte2; |
| 159 | for (uint8_t i = 0; i < 12; ++i) { |
| 160 | if (regs & (1 << i)) { |
| 161 | register_offsets.emplace_back(args: dwarf_r4 + i, args&: vsp); |
| 162 | vsp += 4; |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | } else if ((byte1 & 0xff) == 0x9d) { |
| 167 | // 10011101 |
| 168 | // Reserved as prefix for ARM register to register moves |
| 169 | return false; |
| 170 | } else if ((byte1 & 0xff) == 0x9f) { |
| 171 | // 10011111 |
| 172 | // Reserved as prefix for Intel Wireless MMX register to register moves |
| 173 | return false; |
| 174 | } else if ((byte1 & 0xf0) == 0x90) { |
| 175 | // 1001nnnn (nnnn != 13,15) |
| 176 | // Set vsp = r[nnnn] |
| 177 | vsp_reg = dwarf_r0 + (byte1 & 0x0f); |
| 178 | } else if ((byte1 & 0xf8) == 0xa0) { |
| 179 | // 10100nnn |
| 180 | // Pop r4-r[4+nnn] |
| 181 | uint8_t n = byte1 & 0x7; |
| 182 | for (uint8_t i = 0; i <= n; ++i) { |
| 183 | register_offsets.emplace_back(args: dwarf_r4 + i, args&: vsp); |
| 184 | vsp += 4; |
| 185 | } |
| 186 | } else if ((byte1 & 0xf8) == 0xa8) { |
| 187 | // 10101nnn |
| 188 | // Pop r4-r[4+nnn], r14 |
| 189 | uint8_t n = byte1 & 0x7; |
| 190 | for (uint8_t i = 0; i <= n; ++i) { |
| 191 | register_offsets.emplace_back(args: dwarf_r4 + i, args&: vsp); |
| 192 | vsp += 4; |
| 193 | } |
| 194 | |
| 195 | register_offsets.emplace_back(args: dwarf_lr, args&: vsp); |
| 196 | vsp += 4; |
| 197 | } else if ((byte1 & 0xff) == 0xb0) { |
| 198 | // 10110000 |
| 199 | // Finish (see remark c) |
| 200 | break; |
| 201 | } else if ((byte1 & 0xff) == 0xb1) { |
| 202 | if (byte_offset >= byte_count) |
| 203 | return false; |
| 204 | |
| 205 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
| 206 | if ((byte2 & 0xff) == 0x00) { |
| 207 | // 10110001 00000000 |
| 208 | // Spare (see remark f) |
| 209 | return false; |
| 210 | } else if ((byte2 & 0xf0) == 0x00) { |
| 211 | // 10110001 0000iiii (i not all 0) |
| 212 | // Pop integer registers under mask {r3, r2, r1, r0} |
| 213 | for (uint8_t i = 0; i < 4; ++i) { |
| 214 | if (byte2 & (1 << i)) { |
| 215 | register_offsets.emplace_back(args: dwarf_r0 + i, args&: vsp); |
| 216 | vsp += 4; |
| 217 | } |
| 218 | } |
| 219 | } else { |
| 220 | // 10110001 xxxxyyyy |
| 221 | // Spare (xxxx != 0000) |
| 222 | return false; |
| 223 | } |
| 224 | } else if ((byte1 & 0xff) == 0xb2) { |
| 225 | // 10110010 uleb128 |
| 226 | // vsp = vsp + 0x204+ (uleb128 << 2) |
| 227 | uint64_t uleb128 = GetULEB128(data, offset&: byte_offset, max_offset: byte_count); |
| 228 | vsp += 0x204 + (uleb128 << 2); |
| 229 | } else if ((byte1 & 0xff) == 0xb3) { |
| 230 | // 10110011 sssscccc |
| 231 | // Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) |
| 232 | // by FSTMFDX (see remark d) |
| 233 | if (byte_offset >= byte_count) |
| 234 | return false; |
| 235 | |
| 236 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
| 237 | uint8_t s = (byte2 & 0xf0) >> 4; |
| 238 | uint8_t c = (byte2 & 0x0f) >> 0; |
| 239 | for (uint8_t i = 0; i <= c; ++i) { |
| 240 | register_offsets.emplace_back(args: dwarf_d0 + s + i, args&: vsp); |
| 241 | vsp += 8; |
| 242 | } |
| 243 | vsp += 4; |
| 244 | } else if ((byte1 & 0xfc) == 0xb4) { |
| 245 | // 101101nn |
| 246 | // Spare (was Pop FPA) |
| 247 | return false; |
| 248 | } else if ((byte1 & 0xf8) == 0xb8) { |
| 249 | // 10111nnn |
| 250 | // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by |
| 251 | // FSTMFDX (see remark d) |
| 252 | uint8_t n = byte1 & 0x07; |
| 253 | for (uint8_t i = 0; i <= n; ++i) { |
| 254 | register_offsets.emplace_back(args: dwarf_d8 + i, args&: vsp); |
| 255 | vsp += 8; |
| 256 | } |
| 257 | vsp += 4; |
| 258 | } else if ((byte1 & 0xf8) == 0xc0) { |
| 259 | // 11000nnn (nnn != 6,7) |
| 260 | // Intel Wireless MMX pop wR[10]-wR[10+nnn] |
| 261 | |
| 262 | // 11000110 sssscccc |
| 263 | // Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc] (see remark e) |
| 264 | |
| 265 | // 11000111 00000000 |
| 266 | // Spare |
| 267 | |
| 268 | // 11000111 0000iiii |
| 269 | // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0} |
| 270 | |
| 271 | // 11000111 xxxxyyyy |
| 272 | // Spare (xxxx != 0000) |
| 273 | |
| 274 | return false; |
| 275 | } else if ((byte1 & 0xff) == 0xc8) { |
| 276 | // 11001000 sssscccc |
| 277 | // Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] saved |
| 278 | // (as if) by FSTMFDD (see remarks d,e) |
| 279 | if (byte_offset >= byte_count) |
| 280 | return false; |
| 281 | |
| 282 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
| 283 | uint8_t s = (byte2 & 0xf0) >> 4; |
| 284 | uint8_t c = (byte2 & 0x0f) >> 0; |
| 285 | for (uint8_t i = 0; i <= c; ++i) { |
| 286 | register_offsets.emplace_back(args: dwarf_d16 + s + i, args&: vsp); |
| 287 | vsp += 8; |
| 288 | } |
| 289 | } else if ((byte1 & 0xff) == 0xc9) { |
| 290 | // 11001001 sssscccc |
| 291 | // Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) |
| 292 | // by FSTMFDD (see remark d) |
| 293 | if (byte_offset >= byte_count) |
| 294 | return false; |
| 295 | |
| 296 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
| 297 | uint8_t s = (byte2 & 0xf0) >> 4; |
| 298 | uint8_t c = (byte2 & 0x0f) >> 0; |
| 299 | for (uint8_t i = 0; i <= c; ++i) { |
| 300 | register_offsets.emplace_back(args: dwarf_d0 + s + i, args&: vsp); |
| 301 | vsp += 8; |
| 302 | } |
| 303 | } else if ((byte1 & 0xf8) == 0xc8) { |
| 304 | // 11001yyy |
| 305 | // Spare (yyy != 000, 001) |
| 306 | return false; |
| 307 | } else if ((byte1 & 0xf8) == 0xd0) { |
| 308 | // 11010nnn |
| 309 | // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by |
| 310 | // FSTMFDD (see remark d) |
| 311 | uint8_t n = byte1 & 0x07; |
| 312 | for (uint8_t i = 0; i <= n; ++i) { |
| 313 | register_offsets.emplace_back(args: dwarf_d8 + i, args&: vsp); |
| 314 | vsp += 8; |
| 315 | } |
| 316 | } else if ((byte1 & 0xc0) == 0xc0) { |
| 317 | // 11xxxyyy Spare (xxx != 000, 001, 010) |
| 318 | return false; |
| 319 | } else { |
| 320 | return false; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | UnwindPlan::Row row; |
| 325 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: vsp_reg, offset: vsp); |
| 326 | |
| 327 | bool have_location_for_pc = false; |
| 328 | for (const auto &offset : register_offsets) { |
| 329 | have_location_for_pc |= offset.first == dwarf_pc; |
| 330 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: offset.first, offset: offset.second - vsp, |
| 331 | can_replace: true); |
| 332 | } |
| 333 | |
| 334 | if (!have_location_for_pc) { |
| 335 | UnwindPlan::Row::AbstractRegisterLocation lr_location; |
| 336 | if (row.GetRegisterInfo(reg_num: dwarf_lr, register_location&: lr_location)) |
| 337 | row.SetRegisterInfo(reg_num: dwarf_pc, register_location: lr_location); |
| 338 | else |
| 339 | row.SetRegisterLocationToRegister(reg_num: dwarf_pc, other_reg_num: dwarf_lr, can_replace: false); |
| 340 | } |
| 341 | |
| 342 | unwind_plan.AppendRow(row); |
| 343 | unwind_plan.SetSourceName("ARM.exidx unwind info" ); |
| 344 | unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); |
| 345 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
| 346 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
| 347 | unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
| 348 | |
| 349 | return true; |
| 350 | } |
| 351 | |
| 352 | const uint8_t * |
| 353 | ArmUnwindInfo::GetExceptionHandlingTableEntry(const Address &addr) { |
| 354 | auto it = llvm::upper_bound(Range&: m_exidx_entries, |
| 355 | Value: ArmExidxEntry{0, addr.GetFileAddress(), 0}); |
| 356 | if (it == m_exidx_entries.begin()) |
| 357 | return nullptr; |
| 358 | --it; |
| 359 | |
| 360 | if (it->data == 0x1) |
| 361 | return nullptr; // EXIDX_CANTUNWIND |
| 362 | |
| 363 | if (it->data & 0x80000000) |
| 364 | return (const uint8_t *)&it->data; |
| 365 | |
| 366 | addr_t data_file_addr = it->file_address + 4 + Prel31ToAddr(prel31: it->data); |
| 367 | return m_arm_extab_data.GetDataStart() + |
| 368 | (data_file_addr - m_arm_extab_sp->GetFileAddress()); |
| 369 | } |
| 370 | |