| 327 | //------------------------------------------------------------------------- |
| 328 | |
| 329 | static bool __fix_pcreladdr(instruction inpp, instruction outpp, context *ctxp) { |
| 330 | // Load a PC-relative address into a register |
| 331 | // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897645644.html |
| 332 | constexpr uint32_t msb = 8u; |
| 333 | constexpr uint32_t lsb = 5u; |
| 334 | constexpr uint32_t mask = 0x9f000000u; // 0b10011111000000000000000000000000 |
| 335 | constexpr uint32_t rmask = 0x0000001fu; // 0b00000000000000000000000000011111 |
| 336 | constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111 |
| 337 | constexpr uint32_t fmask = 0x00ffffffu; // 0b00000000111111111111111111111111 |
| 338 | constexpr uint32_t max_val = 0x001fffffu; // 0b00000000000111111111111111111111 |
| 339 | constexpr uint32_t op_adr = 0x10000000u; // "adr" Rd, ADDR_PCREL21 |
| 340 | constexpr uint32_t op_adrp = 0x90000000u; // "adrp" Rd, ADDR_ADRP |
| 341 | |
| 342 | const uint32_t ins = *(*inpp); |
| 343 | intptr_t current_idx; |
| 344 | switch (ins & mask) { |
| 345 | case op_adr: { |
| 346 | current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); |
| 347 | int64_t lsb_bytes = static_cast<uint32_t>(ins << 1u) >> 30u; |
| 348 | int64_t absolute_addr = reinterpret_cast<int64_t>(*inpp) + |
| 349 | (((static_cast<int32_t>(ins << msb) >> (msb + lsb - 2u)) & |
| 350 | ~3u) | lsb_bytes); |
| 351 | int64_t new_pc_offset = static_cast<int64_t>(absolute_addr - |
| 352 | reinterpret_cast<int64_t>(*outpp)); |
| 353 | bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); |
| 354 | if (!special_fix_type && llabs(new_pc_offset) > max_val) { |
| 355 | if ((reinterpret_cast<uint64_t>(*outpp + 2) & 7u) != 0u) { |
| 356 | (*outpp)[0] = A64_NOP; |
| 357 | ctxp->reset_current_ins(current_idx, ++(*outpp)); |
| 358 | } //if |
| 359 | |
| 360 | (*outpp)[0] = |
| 361 | 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 |
| 362 | (*outpp)[1] = 0x14000003u; // B #0xc |
| 363 | memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); |
| 364 | *outpp += 4; |
| 365 | } else { |
| 366 | if (special_fix_type) { |
| 367 | intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr & ~3ull); |
| 368 | if (ref_idx <= current_idx) { |
| 369 | new_pc_offset = static_cast<int64_t>(ctxp->dat[ref_idx].ins - |
| 370 | reinterpret_cast<int64_t>(*outpp)); |
| 371 | } else { |
| 372 | ctxp->insert_fix_map(ref_idx, *outpp, lsb, fmask); |
| 373 | new_pc_offset = 0; |
| 374 | } //if |
| 375 | } //if |
| 376 | |
| 377 | // the lsb_bytes will never be changed, so we can use lmask to keep it |
| 378 | (*outpp)[0] = (static_cast<uint32_t>(new_pc_offset << (lsb - 2u)) & fmask) | |
| 379 | (ins & lmask); |
| 380 | ++(*outpp); |
| 381 | } //if |
| 382 | } |
| 383 | break; |
| 384 | case op_adrp: { |
| 385 | current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); |
| 386 | int32_t lsb_bytes = static_cast<uint32_t>(ins << 1u) >> 30u; |
no outgoing calls
no test coverage detected