diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 19:50:13 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 20:34:34 -0500 |
| commit | e093c21ed880ac3eb72119be15093ee04f8ce299 (patch) | |
| tree | 9f720ebdc0ae415734b1199ed341668c69710a94 /arch/armv7/arch_armv7.cpp | |
| parent | 0609276712622908254065546102381466033141 (diff) | |
Move architecture modules into the API repo
Diffstat (limited to 'arch/armv7/arch_armv7.cpp')
| -rw-r--r-- | arch/armv7/arch_armv7.cpp | 3241 |
1 files changed, 3241 insertions, 0 deletions
diff --git a/arch/armv7/arch_armv7.cpp b/arch/armv7/arch_armv7.cpp new file mode 100644 index 00000000..aa983518 --- /dev/null +++ b/arch/armv7/arch_armv7.cpp @@ -0,0 +1,3241 @@ +#define _CRT_SECURE_NO_WARNINGS +#define NOMINMAX + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <exception> + +#include "binaryninjaapi.h" +#include "lowlevelilinstruction.h" +#include "arch_armv7.h" +#include "il.h" + +using namespace BinaryNinja; +using namespace armv7; +using namespace std; + + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif + +// #define DEBUG_COFF LogDebug + +#define DISASM_SUCCESS 0 +#define FAILED_TO_DISASSEMBLE_OPERAND 1 +#define FAILED_TO_DISASSEMBLE_REGISTER 2 + +#define COALESCE_MAX_INSTRS 100 + +#define HANDLE_CASE(orig, opposite) case orig: case opposite: return (candidate == orig) || (candidate == opposite) + +static bool IsRelatedCondition(Condition orig, Condition candidate) +{ + switch (orig) + { + HANDLE_CASE(COND_EQ, COND_NE); + HANDLE_CASE(COND_CS, COND_CC); + HANDLE_CASE(COND_MI, COND_PL); + HANDLE_CASE(COND_VS, COND_VC); + HANDLE_CASE(COND_HI, COND_LS); + HANDLE_CASE(COND_GE, COND_LT); + HANDLE_CASE(COND_GT, COND_LE); + default: return false; + } +} + +static bool CanCoalesceAfterInstruction(Instruction& instr) +{ + switch (instr.operation) + { + case ARMV7_BX: + case ARMV7_B: + return false; + + case ARMV7_ADC: + case ARMV7_ADD: + case ARMV7_AND: + case ARMV7_ASR: + case ARMV7_BIC: + case ARMV7_EOR: + case ARMV7_LDR: + case ARMV7_LSL: + case ARMV7_LSR: + case ARMV7_MOV: + case ARMV7_MVN: + case ARMV7_ORR: + case ARMV7_ROR: + case ARMV7_RRX: + case ARMV7_RSB: + case ARMV7_RSC: + case ARMV7_SUB: + case ARMV7_SBC: + case ARMV7_MOVW: + case ARMV7_MOVT: + case ARMV7_LDRT: + case ARMV7_LDRH: + case ARMV7_LDRHT: + case ARMV7_LDRB: + case ARMV7_LDRBT: + case ARMV7_LDRSH: + case ARMV7_LDRSHT: + case ARMV7_LDRSB: + case ARMV7_LDRSBT: + case ARMV7_LDRD: + case ARMV7_ADR: + case ARMV7_UBFX: + case ARMV7_UXTAB: + case ARMV7_UXTB: + case ARMV7_UXTH: + case ARMV7_MUL: + case ARMV7_SDIV: + case ARMV7_UDIV: + case ARMV7_SBFX: + case ARMV7_SXTB: + case ARMV7_SXTH: + case ARMV7_BFC: + case ARMV7_BFI: + case ARMV7_CLZ: + if (instr.operands[0].cls == REG && instr.operands[0].reg == REG_PC) + return false; + return true; + + default: + return true; + } +} + +enum MachoArmRelocationType : uint32_t +{ + ARM_RELOC_VANILLA = 0, + ARM_RELOC_PAIR = 1, + ARM_RELOC_SECTDIFF = 2, + ARM_RELOC_LOCAL_SECTDIFF = 3, + ARM_RELOC_PB_LA_PTR = 4, + ARM_RELOC_BR24 = 5, + ARM_THUMB_RELOC_BR22 = 6, + ARM_THUMB_32BIT_BRANCH = 7, + ARM_RELOC_HALF = 8, + ARM_RELOC_HALF_SECTDIFF = 9, + MACHO_MAX_ARM_RELOCATION +}; + +enum ElfArmRelocationType : uint32_t +{ + R_ARM_NONE = 0, + R_ARM_PC24 = 1, + R_ARM_ABS32 = 2, + R_ARM_REL32 = 3, + R_ARM_LDR_PC_G0 = 4, + R_ARM_ABS16 = 5, + R_ARM_ABS12 = 6, + R_ARM_THM_ABS5 = 7, + R_ARM_ABS8 = 8, + R_ARM_SBREL32 = 9, + R_ARM_THM_CALL = 10, + R_ARM_THM_PC8 = 11, + R_ARM_BREL_ADJ = 12, + R_ARM_TLS_DESC = 13, + R_ARM_THM_SWI8 = 14, + R_ARM_XPC25 = 15, + R_ARM_THM_XPC22 = 16, + R_ARM_TLS_DTPMOD32 = 17, + R_ARM_TLS_DTPOFF32 = 18, + R_ARM_TLS_TPOFF32 = 19, + R_ARM_COPY = 20, + R_ARM_GLOB_DAT = 21, + R_ARM_JUMP_SLOT = 22, + R_ARM_RELATIVE = 23, + R_ARM_GOTOFF32 = 24, + R_ARM_BASE_PREL = 25, + R_ARM_GOT_BREL = 26, + R_ARM_PLT32 = 27, + R_ARM_CALL = 28, + R_ARM_JUMP24 = 29, + R_ARM_THM_JUMP24 = 30, + R_ARM_BASE_ABS = 31, + R_ARM_ALU_PCREL_7_0 = 32, + R_ARM_ALU_PCREL_15_8 = 33, + R_ARM_ALU_PCREL_23_15 = 34, + R_ARM_LDR_SBREL_11_0_NC = 35, + R_ARM_ALU_SBREL_19_12_NC = 36, + R_ARM_ALU_SBREL_27_20_CK = 37, + R_ARM_TARGET1 = 38, + R_ARM_SBREL31 = 39, + R_ARM_V4BX = 40, + R_ARM_TARGET2 = 41, + R_ARM_PREL31 = 42, + R_ARM_MOVW_ABS_NC = 43, + R_ARM_MOVT_ABS = 44, + R_ARM_MOVW_PREL_NC = 45, + R_ARM_MOVT_PREL = 46, + R_ARM_THM_MOVW_ABS_NC = 47, + R_ARM_THM_MOVT_ABS = 48, + R_ARM_THM_MOVW_PREL_NC = 49, + R_ARM_THM_MOVT_PREL = 50, + R_ARM_THM_JUMP19 = 51, + R_ARM_THM_JUMP6 = 52, + R_ARM_THM_ALU_PREL_11_0 = 53, + R_ARM_THM_PC12 = 54, + R_ARM_ABS32_NOI = 55, + R_ARM_REL32_NOI = 56, + R_ARM_ALU_PC_G0_NC = 57, + R_ARM_ALU_PC_G0 = 58, + R_ARM_ALU_PC_G1_NC = 59, + R_ARM_ALU_PC_G1 = 60, + R_ARM_ALU_PC_G2 = 61, + R_ARM_LDR_PC_G1 = 62, + R_ARM_LDR_PC_G2 = 63, + R_ARM_LDRS_PC_G0 = 64, + R_ARM_LDRS_PC_G1 = 65, + R_ARM_LDRS_PC_G2 = 66, + R_ARM_LDC_PC_G0 = 67, + R_ARM_LDC_PC_G1 = 68, + R_ARM_LDC_PC_G2 = 69, + R_ARM_ALU_SB_G0_NC = 70, + R_ARM_ALU_SB_G0 = 71, + R_ARM_ALU_SB_G1_NC = 72, + R_ARM_ALU_SB_G1 = 73, + R_ARM_ALU_SB_G2 = 74, + R_ARM_LDR_SB_G0 = 75, + R_ARM_LDR_SB_G1 = 76, + R_ARM_LDR_SB_G2 = 77, + R_ARM_LDRS_SB_G0 = 78, + R_ARM_LDRS_SB_G1 = 79, + R_ARM_LDRS_SB_G2 = 80, + R_ARM_LDC_SB_G0 = 81, + R_ARM_LDC_SB_G1 = 82, + R_ARM_LDC_SB_G2 = 83, + R_ARM_MOVW_BREL_NC = 84, + R_ARM_MOVT_BREL = 85, + R_ARM_MOVW_BREL = 86, + R_ARM_THM_MOVW_BREL_NC = 87, + R_ARM_THM_MOVT_BREL = 88, + R_ARM_THM_MOVW_BREL = 89, + R_ARM_TLS_GOTDESC = 90, + R_ARM_TLS_CALL = 91, + R_ARM_TLS_DESCSEQ = 92, + R_ARM_THM_TLS_CALL = 93, + R_ARM_PLT32_ABS = 94, + R_ARM_GOT_ABS = 95, + R_ARM_GOT_PREL = 96, + R_ARM_GOT_BREL12 = 97, + R_ARM_GOTOFF12 = 98, + R_ARM_GOTRELAX = 99, + R_ARM_GNU_VTENTRY = 100, + R_ARM_GNU_VTINHERIT = 101, + R_ARM_THM_JUMP11 = 102, + R_ARM_THM_JUMP8 = 103, + R_ARM_TLS_GD32 = 104, + R_ARM_TLS_LDM32 = 105, + R_ARM_TLS_LDO32 = 106, + R_ARM_TLS_IE32 = 107, + R_ARM_TLS_LE32 = 108, + R_ARM_TLS_LDO12 = 109, + R_ARM_TLS_LE12 = 110, + R_ARM_TLS_IE12GP = 111, + R_ARM_ME_TOO = 128, + R_ARM_THM_TLS_DESCSEQ16 = 129, + R_ARM_THM_TLS_DESCSEQ32 = 130, + R_ARM_THM_GOT_BREL12 = 131, + R_ARM_THM_ALU_ABS_G0_NC = 132, + R_ARM_THM_ALU_ABS_G1_NC = 133, + R_ARM_THM_ALU_ABS_G2_NC = 134, + R_ARM_THM_ALU_ABS_G3 = 135, + R_ARM_IRELATIVE = 160, + R_ARM_RXPC25 = 249, + R_ARM_RSBREL32 = 250, + R_ARM_THM_RPC22 = 251, + R_ARM_RREL32 = 252, + R_ARM_RABS32 = 253, + R_ARM_RPC24 = 254, + R_ARM_RBASE = 255 +}; + +enum PeArmRelocationType : uint32_t +{ + PE_IMAGE_REL_ARM_ABSOLUTE = 0x0000, // The relocation is ignored. + PE_IMAGE_REL_ARM_ADDR32 = 0x0001, // The 32-bit VA of the target. + PE_IMAGE_REL_ARM_ADDR32NB = 0x0002, // The 32-bit RVA of the target. + PE_IMAGE_REL_ARM_BRANCH24 = 0x0003, // The 24-bit relative displacement to the target. + PE_IMAGE_REL_ARM_BRANCH11 = 0x0004, // The reference to a subroutine call. The reference consists of two 16-bit instructions with 11-bit offsets. + PE_IMAGE_REL_ARM_BLX24 = 0x0008, // The most significant 24 or 25 bits of the signed 26-bit relative displacement of the target. Applied to an unconditional BL instruction in ARM mode. The BL is transformed to a BLX during relocation if the target is in Thumb mode. + PE_IMAGE_REL_ARM_BLX11 = 0x0009, // The most significant 21 or 22 bits of the signed 23-bit relative displacement of the target. Applied to a contiguous 16-bit B+BL pair in Thumb mode prior to ARMv7. The BL is transformed to a BLX during relocation if the target is in ARM mode. + PE_IMAGE_REL_ARM_REL32 = 0x000A, // TODO: description + PE_IMAGE_REL_ARM_SECTION = 0x000E, // The 16-bit section index of the section that contains the target. This is used to support debugging information. + PE_IMAGE_REL_ARM_SECREL = 0x000F, // The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage. + PE_IMAGE_REL_ARM_MOV32 = 0x0010, // The 32-bit VA of the target. This relocation is applied using a MOVW instruction for the low 16 bits followed by a MOVT for the high 16 bits. + PE_IMAGE_REL_THUMB_MOV32 = 0x0011, // The 32-bit VA of the target. This relocation is applied using a MOVW instruction for the low 16 bits followed by a MOVT for the high 16 bits. + PE_IMAGE_REL_THUMB_BRANCH20 = 0x0012, // The instruction is fixed up with the 21-bit relative displacement to the 2-byte aligned target. The least significant bit of the displacement is always zero and is not stored. This relocation corresponds to a Thumb-2 32-bit conditional B instruction. + PE_IMAGE_REL_THUMB_UNUSED = 0x0013, // Unused + PE_IMAGE_REL_THUMB_BRANCH24 = 0x0014, // The instruction is fixed up with the 25-bit relative displacement to the 2-byte aligned target. The least significant bit of the displacement is zero and is not stored.This relocation corresponds to a Thumb-2 B instruction. + PE_IMAGE_REL_THUMB_BLX23 = 0x0015, // The instruction is fixed up with the 25-bit relative displacement to the 4-byte aligned target. The low 2 bits of the displacement are zero and are not stored. This relocation corresponds to a Thumb-2 BLX instruction. + PE_IMAGE_REL_ARM_PAIR = 0x0016, // The relocation is valid only when it immediately follows a ARM_REFHI or THUMB_REFHI. Its SymbolTableIndex contains a displacement and not an index into the symbol table. + MAX_ARM_PE_RELOCATION +}; + +enum PeRelocationType : uint32_t +{ + PE_IMAGE_REL_BASED_ABSOLUTE = 0, // The base relocation is skipped. This type can be used to pad a block. + PE_IMAGE_REL_BASED_HIGH = 1, // The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word. + PE_IMAGE_REL_BASED_LOW = 2, // The base relocation adds the low 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the low half of a 32-bit word. + PE_IMAGE_REL_BASED_HIGHLOW = 3, // The base relocation applies all 32 bits of the difference to the 32-bit field at offset. + PE_IMAGE_REL_BASED_HIGHADJ = 4, // The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word. The low 16 bits of the 32-bit value are stored in the 16-bit word that follows this base relocation. This means that this base relocation occupies two slots. + PE_IMAGE_REL_BASED_MIPS_JMPADDR = 5, // The relocation interpretation is dependent on the machine type. When the machine type is MIPS, the base relocation applies to a MIPS jump instruction. + PE_IMAGE_REL_BASED_ARM_MOV32 = 5, // This relocation is meaningful only when the machine type is ARM or Thumb. The base relocation applies the 32-bit address of a symbol across a consecutive MOVW/MOVT instruction pair. + PE_IMAGE_REL_BASED_RISCV_HIGH20 = 5, // This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the high 20 bits of a 32-bit absolute address. + PE_IMAGE_REL_BASE_RESERVED = 6, // Reserved, must be zero. + PE_IMAGE_REL_BASED_THUMB_MOV32 = 7, // This relocation is meaningful only when the machine type is Thumb. The base relocation applies the 32-bit address of a symbol to a consecutive MOVW/MOVT instruction pair. + PE_IMAGE_REL_BASED_RISCV_LOW12I = 7, // This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V I-type instruction format. + PE_IMAGE_REL_BASED_RISCV_LOW12S = 8, // This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V S-type instruction format. + PE_IMAGE_REL_BASED_MIPS_JMPADDR16 = 9, // The relocation is only meaningful when the machine type is MIPS. The base relocation applies to a MIPS16 jump instruction. + PE_IMAGE_REL_BASED_DIR64 = 10, // The base relocation applies the difference to the 64-bit field at offset. + MAX_PE_RELOCATION +}; + + +static const char* GetRelocationString(PeRelocationType relocType) +{ + static const char* relocTable[] = + { + "PE_IMAGE_REL_BASED_ABSOLUTE", + "PE_IMAGE_REL_BASED_HIGH", + "PE_IMAGE_REL_BASED_LOW", + "PE_IMAGE_REL_BASED_HIGHLOW", + "PE_IMAGE_REL_BASED_HIGHADJ", + "PE_IMAGE_REL_BASED_MIPS_JMPADDR", + "PE_IMAGE_REL_BASED_ARM_MOV32", + "PE_IMAGE_REL_BASED_RISCV_HIGH20", + "PE_IMAGE_REL_BASE_RESERVED", + "PE_IMAGE_REL_BASED_THUMB_MOV32", + "PE_IMAGE_REL_BASED_RISCV_LOW12I", + "PE_IMAGE_REL_BASED_RISCV_LOW12S", + "PE_IMAGE_REL_BASED_MIPS_JMPADDR16", + "PE_IMAGE_REL_BASED_DIR64" + }; + + if (relocType < MAX_PE_RELOCATION) + return relocTable[relocType]; + return "Unknown relocation"; +} + +static const char* GetRelocationString(MachoArmRelocationType rel) +{ + static const char* relocTable[] = + { + "ARM_RELOC_VANILLA", + "ARM_RELOC_PAIR", + "ARM_RELOC_SECTDIFF", + "ARM_RELOC_LOCAL_SECTDIFF", + "ARM_RELOC_PB_LA_PTR", + "ARM_RELOC_BR24", + "ARM_THUMB_RELOC_BR22", + "ARM_THUMB_32BIT_BRANCH", + "ARM_RELOC_HALF", + "ARM_RELOC_HALF_SECTDIFF" + }; + if (rel < MACHO_MAX_ARM_RELOCATION) + { + return relocTable[rel]; + } + return "Unknown ARM relocation"; +} + + +static const char* GetRelocationString(ElfArmRelocationType rel) +{ + static map<ElfArmRelocationType, const char*> relocTable = + { + {R_ARM_NONE, "R_ARM_NONE"}, + {R_ARM_PC24, "R_ARM_PC24"}, + {R_ARM_ABS32, "R_ARM_ABS32"}, + {R_ARM_REL32, "R_ARM_REL32"}, + {R_ARM_LDR_PC_G0, "R_ARM_LDR_PC_G0"}, + {R_ARM_ABS16, "R_ARM_ABS16"}, + {R_ARM_ABS12, "R_ARM_ABS12"}, + {R_ARM_THM_ABS5, "R_ARM_THM_ABS5"}, + {R_ARM_ABS8, "R_ARM_ABS8"}, + {R_ARM_SBREL32, "R_ARM_SBREL32"}, + {R_ARM_THM_CALL, "R_ARM_THM_CALL"}, + {R_ARM_THM_PC8, "R_ARM_THM_PC8"}, + {R_ARM_BREL_ADJ, "R_ARM_BREL_ADJ"}, + {R_ARM_TLS_DESC, "R_ARM_TLS_DESC"}, + {R_ARM_THM_SWI8, "R_ARM_THM_SWI8"}, + {R_ARM_XPC25, "R_ARM_XPC25"}, + {R_ARM_THM_XPC22, "R_ARM_THM_XPC22"}, + {R_ARM_TLS_DTPMOD32, "R_ARM_TLS_DTPMOD32"}, + {R_ARM_TLS_DTPOFF32, "R_ARM_TLS_DTPOFF32"}, + {R_ARM_TLS_TPOFF32, "R_ARM_TLS_TPOFF32"}, + {R_ARM_COPY, "R_ARM_COPY"}, + {R_ARM_GLOB_DAT, "R_ARM_GLOB_DAT"}, + {R_ARM_JUMP_SLOT, "R_ARM_JUMP_SLOT"}, + {R_ARM_RELATIVE, "R_ARM_RELATIVE"}, + {R_ARM_GOTOFF32, "R_ARM_GOTOFF32"}, + {R_ARM_BASE_PREL, "R_ARM_BASE_PREL"}, + {R_ARM_GOT_BREL, "R_ARM_GOT_BREL"}, + {R_ARM_PLT32, "R_ARM_PLT32"}, + {R_ARM_CALL, "R_ARM_CALL"}, + {R_ARM_JUMP24, "R_ARM_JUMP24"}, + {R_ARM_THM_JUMP24, "R_ARM_THM_JUMP24"}, + {R_ARM_BASE_ABS, "R_ARM_BASE_ABS"}, + {R_ARM_ALU_PCREL_7_0, "R_ARM_ALU_PCREL_7_0"}, + {R_ARM_ALU_PCREL_15_8, "R_ARM_ALU_PCREL_15_8"}, + {R_ARM_ALU_PCREL_23_15, "R_ARM_ALU_PCREL_23_15"}, + {R_ARM_LDR_SBREL_11_0_NC, "R_ARM_LDR_SBREL_11_0_NC"}, + {R_ARM_ALU_SBREL_19_12_NC, "R_ARM_ALU_SBREL_19_12_NC"}, + {R_ARM_ALU_SBREL_27_20_CK, "R_ARM_ALU_SBREL_27_20_CK"}, + {R_ARM_TARGET1, "R_ARM_TARGET1"}, + {R_ARM_SBREL31, "R_ARM_SBREL31"}, + {R_ARM_V4BX, "R_ARM_V4BX"}, + {R_ARM_TARGET2, "R_ARM_TARGET2"}, + {R_ARM_PREL31, "R_ARM_PREL31"}, + {R_ARM_MOVW_ABS_NC, "R_ARM_MOVW_ABS_NC"}, + {R_ARM_MOVT_ABS, "R_ARM_MOVT_ABS"}, + {R_ARM_MOVW_PREL_NC, "R_ARM_MOVW_PREL_NC"}, + {R_ARM_MOVT_PREL, "R_ARM_MOVT_PREL"}, + {R_ARM_THM_MOVW_ABS_NC, "R_ARM_THM_MOVW_ABS_NC"}, + {R_ARM_THM_MOVT_ABS, "R_ARM_THM_MOVT_ABS"}, + {R_ARM_THM_MOVW_PREL_NC, "R_ARM_THM_MOVW_PREL_NC"}, + {R_ARM_THM_MOVT_PREL, "R_ARM_THM_MOVT_PREL"}, + {R_ARM_THM_JUMP19, "R_ARM_THM_JUMP19"}, + {R_ARM_THM_JUMP6, "R_ARM_THM_JUMP6"}, + {R_ARM_THM_ALU_PREL_11_0, "R_ARM_THM_ALU_PREL_11_0"}, + {R_ARM_THM_PC12, "R_ARM_THM_PC12"}, + {R_ARM_ABS32_NOI, "R_ARM_ABS32_NOI"}, + {R_ARM_REL32_NOI, "R_ARM_REL32_NOI"}, + {R_ARM_ALU_PC_G0_NC, "R_ARM_ALU_PC_G0_NC"}, + {R_ARM_ALU_PC_G0, "R_ARM_ALU_PC_G0"}, + {R_ARM_ALU_PC_G1_NC, "R_ARM_ALU_PC_G1_NC"}, + {R_ARM_ALU_PC_G1, "R_ARM_ALU_PC_G1"}, + {R_ARM_ALU_PC_G2, "R_ARM_ALU_PC_G2"}, + {R_ARM_LDR_PC_G1, "R_ARM_LDR_PC_G1"}, + {R_ARM_LDR_PC_G2, "R_ARM_LDR_PC_G2"}, + {R_ARM_LDRS_PC_G0, "R_ARM_LDRS_PC_G0"}, + {R_ARM_LDRS_PC_G1, "R_ARM_LDRS_PC_G1"}, + {R_ARM_LDRS_PC_G2, "R_ARM_LDRS_PC_G2"}, + {R_ARM_LDC_PC_G0, "R_ARM_LDC_PC_G0"}, + {R_ARM_LDC_PC_G1, "R_ARM_LDC_PC_G1"}, + {R_ARM_LDC_PC_G2, "R_ARM_LDC_PC_G2"}, + {R_ARM_ALU_SB_G0_NC, "R_ARM_ALU_SB_G0_NC"}, + {R_ARM_ALU_SB_G0, "R_ARM_ALU_SB_G0"}, + {R_ARM_ALU_SB_G1_NC, "R_ARM_ALU_SB_G1_NC"}, + {R_ARM_ALU_SB_G1, "R_ARM_ALU_SB_G1"}, + {R_ARM_ALU_SB_G2, "R_ARM_ALU_SB_G2"}, + {R_ARM_LDR_SB_G0, "R_ARM_LDR_SB_G0"}, + {R_ARM_LDR_SB_G1, "R_ARM_LDR_SB_G1"}, + {R_ARM_LDR_SB_G2, "R_ARM_LDR_SB_G2"}, + {R_ARM_LDRS_SB_G0, "R_ARM_LDRS_SB_G0"}, + {R_ARM_LDRS_SB_G1, "R_ARM_LDRS_SB_G1"}, + {R_ARM_LDRS_SB_G2, "R_ARM_LDRS_SB_G2"}, + {R_ARM_LDC_SB_G0, "R_ARM_LDC_SB_G0"}, + {R_ARM_LDC_SB_G1, "R_ARM_LDC_SB_G1"}, + {R_ARM_LDC_SB_G2, "R_ARM_LDC_SB_G2"}, + {R_ARM_MOVW_BREL_NC, "R_ARM_MOVW_BREL_NC"}, + {R_ARM_MOVT_BREL, "R_ARM_MOVT_BREL"}, + {R_ARM_MOVW_BREL, "R_ARM_MOVW_BREL"}, + {R_ARM_THM_MOVW_BREL_NC, "R_ARM_THM_MOVW_BREL_NC"}, + {R_ARM_THM_MOVT_BREL, "R_ARM_THM_MOVT_BREL"}, + {R_ARM_THM_MOVW_BREL, "R_ARM_THM_MOVW_BREL"}, + {R_ARM_TLS_GOTDESC, "R_ARM_TLS_GOTDESC"}, + {R_ARM_TLS_CALL, "R_ARM_TLS_CALL"}, + {R_ARM_TLS_DESCSEQ, "R_ARM_TLS_DESCSEQ"}, + {R_ARM_THM_TLS_CALL, "R_ARM_THM_TLS_CALL"}, + {R_ARM_PLT32_ABS, "R_ARM_PLT32_ABS"}, + {R_ARM_GOT_ABS, "R_ARM_GOT_ABS"}, + {R_ARM_GOT_PREL, "R_ARM_GOT_PREL"}, + {R_ARM_GOT_BREL12, "R_ARM_GOT_BREL12"}, + {R_ARM_GOTOFF12, "R_ARM_GOTOFF12"}, + {R_ARM_GOTRELAX, "R_ARM_GOTRELAX"}, + {R_ARM_GNU_VTENTRY, "R_ARM_GNU_VTENTRY"}, + {R_ARM_GNU_VTINHERIT, "R_ARM_GNU_VTINHERIT"}, + {R_ARM_THM_JUMP11, "R_ARM_THM_JUMP11"}, + {R_ARM_THM_JUMP8, "R_ARM_THM_JUMP8"}, + {R_ARM_TLS_GD32, "R_ARM_TLS_GD32"}, + {R_ARM_TLS_LDM32, "R_ARM_TLS_LDM32"}, + {R_ARM_TLS_LDO32, "R_ARM_TLS_LDO32"}, + {R_ARM_TLS_IE32, "R_ARM_TLS_IE32"}, + {R_ARM_TLS_LE32, "R_ARM_TLS_LE32"}, + {R_ARM_TLS_LDO12, "R_ARM_TLS_LDO12"}, + {R_ARM_TLS_LE12, "R_ARM_TLS_LE12"}, + {R_ARM_TLS_IE12GP, "R_ARM_TLS_IE12GP"}, + {R_ARM_ME_TOO, "R_ARM_ME_TOO"}, + {R_ARM_THM_TLS_DESCSEQ16, "R_ARM_THM_TLS_DESCSEQ16"}, + {R_ARM_THM_TLS_DESCSEQ32, "R_ARM_THM_TLS_DESCSEQ32"}, + {R_ARM_THM_GOT_BREL12, "R_ARM_THM_GOT_BREL12"}, + {R_ARM_THM_ALU_ABS_G0_NC, "R_ARM_THM_ALU_ABS_G0_NC"}, + {R_ARM_THM_ALU_ABS_G1_NC, "R_ARM_THM_ALU_ABS_G1_NC"}, + {R_ARM_THM_ALU_ABS_G2_NC, "R_ARM_THM_ALU_ABS_G2_NC"}, + {R_ARM_THM_ALU_ABS_G3, "R_ARM_THM_ALU_ABS_G3"}, + {R_ARM_IRELATIVE, "R_ARM_IRELATIVE"}, + {R_ARM_RXPC25, "R_ARM_RXPC25"}, + {R_ARM_RSBREL32, "R_ARM_RSBREL32"}, + {R_ARM_THM_RPC22, "R_ARM_THM_RPC22"}, + {R_ARM_RREL32, "R_ARM_RREL32"}, + {R_ARM_RABS32, "R_ARM_RABS32"}, + {R_ARM_RPC24, "R_ARM_RPC24"}, + {R_ARM_RBASE, "R_ARM_RBASE"} + }; + if (relocTable.count(rel)) + return relocTable.at(rel); + return "Unknown ARM relocation"; +} + + +static const char* GetRelocationString(PeArmRelocationType rel) +{ + static const char* relocTable[] = + { + "IMAGE_REL_ARM_ABSOLUTE", + "IMAGE_REL_ARM_ADDR32", + "IMAGE_REL_ARM_ADDR32NB", + "IMAGE_REL_ARM_BRANCH24", + "IMAGE_REL_ARM_BRANCH11", + "IMAGE_REL_ARM_SECTION", + "IMAGE_REL_ARM_SECREL", + "IMAGE_REL_ARM_MOV32", + "IMAGE_REL_THUMB_MOV32", + "IMAGE_REL_THUMB_BRANCH20", + "IMAGE_REL_THUMB_UNUSED", + "IMAGE_REL_THUMB_BRANCH24", + "IMAGE_REL_THUMB_BLX23", + "IMAGE_REL_ARM_PAIR" + }; + if (rel < MAX_ARM_PE_RELOCATION) + { + if (rel >= PE_IMAGE_REL_ARM_SECTION) + { + rel = (PeArmRelocationType) ((int)rel - PE_IMAGE_REL_ARM_SECTION + PE_IMAGE_REL_ARM_BRANCH11 + 1); + } + return relocTable[rel]; + } + return "Unknown ARM relocation"; +} + +static bool IsELFDataRelocation(ElfArmRelocationType reloc) +{ + map<ElfArmRelocationType, bool> isDataMap = + { + {R_ARM_NONE, false}, + {R_ARM_PC24, false}, + {R_ARM_ABS32, true}, + {R_ARM_REL32, true}, + {R_ARM_LDR_PC_G0, false}, + {R_ARM_ABS16, true}, + {R_ARM_ABS12, false}, + {R_ARM_THM_ABS5, false}, + {R_ARM_ABS8, true}, + {R_ARM_SBREL32, true}, + {R_ARM_THM_CALL, false}, + {R_ARM_THM_PC8, false}, + {R_ARM_BREL_ADJ, true}, + {R_ARM_TLS_DESC, true}, + {R_ARM_THM_SWI8, false}, + {R_ARM_XPC25, false}, + {R_ARM_THM_XPC22, false}, + {R_ARM_TLS_DTPMOD32, true}, + {R_ARM_TLS_DTPOFF32, true}, + {R_ARM_TLS_TPOFF32, true}, + {R_ARM_COPY, true}, + {R_ARM_GLOB_DAT, true}, + {R_ARM_JUMP_SLOT, true}, + {R_ARM_RELATIVE, true}, + {R_ARM_GOTOFF32, true}, + {R_ARM_BASE_PREL, true}, + {R_ARM_GOT_BREL, true}, + {R_ARM_PLT32, false}, + {R_ARM_CALL, false}, + {R_ARM_JUMP24, false}, + {R_ARM_THM_JUMP24, false}, + {R_ARM_BASE_ABS, true}, + {R_ARM_ALU_PCREL_7_0, false}, + {R_ARM_ALU_PCREL_15_8, false}, + {R_ARM_ALU_PCREL_23_15, false}, + {R_ARM_LDR_SBREL_11_0_NC, false}, + {R_ARM_ALU_SBREL_19_12_NC, false}, + {R_ARM_ALU_SBREL_27_20_CK, false}, + {R_ARM_TARGET1, false}, + {R_ARM_SBREL31, true}, + {R_ARM_V4BX, false}, + {R_ARM_TARGET2, false}, + {R_ARM_PREL31, true}, + {R_ARM_MOVW_ABS_NC, false}, + {R_ARM_MOVT_ABS, false}, + {R_ARM_MOVW_PREL_NC, false}, + {R_ARM_MOVT_PREL, false}, + {R_ARM_THM_MOVW_ABS_NC, false}, + {R_ARM_THM_MOVT_ABS, false}, + {R_ARM_THM_MOVW_PREL_NC, false}, + {R_ARM_THM_MOVT_PREL, false}, + {R_ARM_THM_JUMP19, false}, + {R_ARM_THM_JUMP6, false}, + {R_ARM_THM_ALU_PREL_11_0, false}, + {R_ARM_THM_PC12, false}, + {R_ARM_ABS32_NOI, true}, + {R_ARM_REL32_NOI, true}, + {R_ARM_ALU_PC_G0_NC, false}, + {R_ARM_ALU_PC_G0, false}, + {R_ARM_ALU_PC_G1_NC, false}, + {R_ARM_ALU_PC_G1, false}, + {R_ARM_ALU_PC_G2, false}, + {R_ARM_LDR_PC_G1, false}, + {R_ARM_LDR_PC_G2, false}, + {R_ARM_LDRS_PC_G0, false}, + {R_ARM_LDRS_PC_G1, false}, + {R_ARM_LDRS_PC_G2, false}, + {R_ARM_LDC_PC_G0, false}, + {R_ARM_LDC_PC_G1, false}, + {R_ARM_LDC_PC_G2, false}, + {R_ARM_ALU_SB_G0_NC, false}, + {R_ARM_ALU_SB_G0, false}, + {R_ARM_ALU_SB_G1_NC, false}, + {R_ARM_ALU_SB_G1, false}, + {R_ARM_ALU_SB_G2, false}, + {R_ARM_LDR_SB_G0, false}, + {R_ARM_LDR_SB_G1, false}, + {R_ARM_LDR_SB_G2, false}, + {R_ARM_LDRS_SB_G0, false}, + {R_ARM_LDRS_SB_G1, false}, + {R_ARM_LDRS_SB_G2, false}, + {R_ARM_LDC_SB_G0, false}, + {R_ARM_LDC_SB_G1, false}, + {R_ARM_LDC_SB_G2, false}, + {R_ARM_MOVW_BREL_NC, false}, + {R_ARM_MOVT_BREL, false}, + {R_ARM_MOVW_BREL, false}, + {R_ARM_THM_MOVW_BREL_NC, false}, + {R_ARM_THM_MOVT_BREL, false}, + {R_ARM_THM_MOVW_BREL, false}, + {R_ARM_TLS_GOTDESC, true}, + {R_ARM_TLS_CALL, false}, + {R_ARM_TLS_DESCSEQ, false}, + {R_ARM_THM_TLS_CALL, false}, + {R_ARM_PLT32_ABS, true}, + {R_ARM_GOT_ABS, true}, + {R_ARM_GOT_PREL, true}, + {R_ARM_GOT_BREL12, false}, + {R_ARM_GOTOFF12, false}, + {R_ARM_GOTRELAX, false}, + {R_ARM_GNU_VTENTRY, true}, + {R_ARM_GNU_VTINHERIT, true}, + {R_ARM_THM_JUMP11, false}, + {R_ARM_THM_JUMP8, false}, + {R_ARM_TLS_GD32, true}, + {R_ARM_TLS_LDM32, true}, + {R_ARM_TLS_LDO32, true}, + {R_ARM_TLS_IE32, true}, + {R_ARM_TLS_LE32, false}, + {R_ARM_TLS_LDO12, false}, + {R_ARM_TLS_LE12, false}, + {R_ARM_TLS_IE12GP, false}, + {R_ARM_ME_TOO, false}, + {R_ARM_THM_TLS_DESCSEQ16, false}, + {R_ARM_THM_TLS_DESCSEQ32, false}, + {R_ARM_THM_GOT_BREL12, false}, + {R_ARM_THM_ALU_ABS_G0_NC, false}, + {R_ARM_THM_ALU_ABS_G1_NC, false}, + {R_ARM_THM_ALU_ABS_G2_NC, false}, + {R_ARM_THM_ALU_ABS_G3, false}, + {R_ARM_IRELATIVE, false}, + {R_ARM_RXPC25, false}, + {R_ARM_RSBREL32, false}, + {R_ARM_THM_RPC22, false}, + {R_ARM_RREL32, false}, + {R_ARM_RABS32, false}, + {R_ARM_RPC24, false}, + {R_ARM_RBASE, false} + }; + if (!isDataMap.count(reloc)) + return false; + return isDataMap.at(reloc); +} + +static BNRegisterInfo RegisterInfo(uint32_t fullWidthReg, size_t offset, size_t size, bool zeroExtend = false) +{ + BNRegisterInfo result; + result.fullWidthRegister = fullWidthReg; + result.offset = offset; + result.size = size; + result.extend = zeroExtend ? ZeroExtendToFullWidth : NoExtend; + return result; +} + +class Armv7Architecture: public ArmCommonArchitecture +{ +protected: + virtual std::string GetAssemblerTriple() override + { + if(m_endian == BigEndian) + return "armv7eb-none-none"; + + return "armv7-none-none"; + } + + virtual bool Disassemble(const uint8_t* data, uint64_t addr, size_t maxLen, Instruction& result) + { + (void)addr; + (void)maxLen; + memset(&result, 0, sizeof(result)); + if (armv7_decompose(*(uint32_t*)data, &result, (uint32_t)addr, (uint32_t)(m_endian == BigEndian)) != 0) + return false; + return true; + } + + void SetInstructionInfoForInstruction(uint64_t addr, const Instruction& instr, InstructionInfo& result) + { + result.length = 4; + + switch (instr.operation) + { + case ARMV7_BL: + if (UNCONDITIONAL(instr.cond) && (instr.operands[0].cls == LABEL)) + result.AddBranch(CallDestination, instr.operands[0].imm, this); + break; + case ARMV7_BLX: + result.archTransitionByTargetAddr = true; + if (UNCONDITIONAL(instr.cond)) + { + if (instr.operands[0].cls == LABEL) + result.AddBranch(CallDestination, instr.operands[0].imm, m_thumbArch); + else if (instr.operands[0].cls == REG && instr.operands[0].reg == REG_LR) + result.AddBranch(FunctionReturn); // initially indicate "blx lr" as a return since this is common and conservative; subsequent analysis determines if it's a function call + } + break; + case ARMV7_BX: + if (UNCONDITIONAL(instr.cond)) + { + if (instr.operands[0].cls == REG && instr.operands[0].reg == REG_LR) + result.AddBranch(FunctionReturn); + else + { + result.AddBranch(UnresolvedBranch); + result.archTransitionByTargetAddr = true; + } + } + else if (instr.operands[0].cls == REG && instr.operands[0].reg == REG_LR) + result.AddBranch(FalseBranch, addr + 4, this); + break; + case ARMV7_B: + if (UNCONDITIONAL(instr.cond)) + result.AddBranch(UnconditionalBranch, instr.operands[0].imm, this); + else + { + result.AddBranch(TrueBranch, instr.operands[0].imm, this); + result.AddBranch(FalseBranch, addr + 4, this); + } + break; + case ARMV7_POP: + //if pop with PC in the register list treat as a return + if (instr.operands[0].cls == REG_LIST && ((instr.operands[0].reg & REG_LIST_PC) == REG_LIST_PC)) + { + result.AddBranch(FunctionReturn); + if (!UNCONDITIONAL(instr.cond)) + result.AddBranch(FalseBranch, addr + 4, this); + } + break; + case ARMV7_LDM: + case ARMV7_LDMDA: + case ARMV7_LDMDB: + case ARMV7_LDMIA: // defaults to ARMV7_LDM + case ARMV7_LDMIB: + //if this is an unconditional load multiple with PC in the register list treat as a return + if (UNCONDITIONAL(instr.cond)) + { + if (instr.operands[1].cls == REG_LIST && ((RegisterList)instr.operands[1].reg == REG_LIST_PC)) + { + result.archTransitionByTargetAddr = true; + result.AddBranch(UnresolvedBranch); + } + else if (instr.operands[1].cls == REG_LIST && ((instr.operands[1].reg & REG_LIST_PC) == REG_LIST_PC)) + result.AddBranch(FunctionReturn); + } + break; + case ARMV7_ADC: + case ARMV7_ADD: + case ARMV7_AND: + case ARMV7_ASR: + case ARMV7_BIC: + case ARMV7_EOR: + case ARMV7_LDR: + case ARMV7_LSL: + case ARMV7_LSR: + case ARMV7_MOV: + case ARMV7_MVN: + case ARMV7_ORR: + case ARMV7_ROR: + case ARMV7_RRX: + case ARMV7_RSB: + case ARMV7_RSC: + case ARMV7_SUB: + case ARMV7_SBC: + if (instr.operands[0].cls == REG && instr.operands[0].reg == REG_PC) + { + result.archTransitionByTargetAddr = true; + result.AddBranch(UnresolvedBranch); + if (!UNCONDITIONAL(instr.cond)) + result.AddBranch(FalseBranch, addr + 4, this); + } + break; + case ARMV7_MOVW: + case ARMV7_MOVT: + case ARMV7_LDRT: + case ARMV7_LDRH: + case ARMV7_LDRHT: + case ARMV7_LDRB: + case ARMV7_LDRBT: + case ARMV7_LDRSH: + case ARMV7_LDRSHT: + case ARMV7_LDRSB: + case ARMV7_LDRSBT: + case ARMV7_LDRD: + case ARMV7_ADR: + case ARMV7_UBFX: + case ARMV7_UXTAB: + case ARMV7_UXTB: + case ARMV7_UXTH: + case ARMV7_MUL: + case ARMV7_SDIV: + case ARMV7_UDIV: + case ARMV7_SBFX: + case ARMV7_SXTB: + case ARMV7_SXTH: + case ARMV7_BFC: + case ARMV7_BFI: + case ARMV7_CLZ: + if (instr.operands[0].cls == REG && instr.operands[0].reg == REG_PC) + { + result.AddBranch(UnresolvedBranch); + if (!UNCONDITIONAL(instr.cond)) + result.AddBranch(FalseBranch, addr + 4, this); + } + break; + case ARMV7_SVC: + if (instr.operands[0].cls == IMM && instr.operands[0].imm == 0) + result.AddBranch(SystemCall); + break; + case ARMV7_UDF: + result.AddBranch(ExceptionBranch); + break; + default: + break; + } + } + + uint32_t tokenize_shift(const InstructionOperand& op, vector<InstructionTextToken>& result) + { + char operand[64] = {0}; + if (op.shift != SHIFT_NONE) + { + const char* shiftStr = get_shift(op.shift); + if (shiftStr == NULL) + return FAILED_TO_DISASSEMBLE_OPERAND; + + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, shiftStr); + snprintf(operand, sizeof(operand), "%#x", (uint32_t)op.imm); + result.emplace_back(TextToken, " #"); + result.emplace_back(IntegerToken, operand, op.imm); + } + return DISASM_SUCCESS; + } + + void tokenize_shifted_immediate(const InstructionOperand& op, vector<InstructionTextToken>& result) + { + char operand[64] = {0}; + const char* sign = ""; + switch (op.cls) + { + case FIMM16: + case FIMM32: + { + snprintf(operand, sizeof(operand), "%f", op.immf); + result.emplace_back(TextToken, "#"); + result.emplace_back(FloatingPointToken, operand); + break; + } + case FIMM64: + { + snprintf(operand, sizeof(operand), "%e", op.immd); + result.emplace_back(TextToken, "#"); + result.emplace_back(FloatingPointToken, operand); + break; + } + case IMM: + snprintf(operand, sizeof(operand), "%s%#x", sign, (uint32_t)op.imm); + result.emplace_back(TextToken, "#"); + result.emplace_back(IntegerToken, operand, op.imm); + break; + case IMM64: + snprintf(operand, sizeof(operand), "%s%#" PRIx64, sign, op.imm64); + result.emplace_back(TextToken, "#"); + result.emplace_back(IntegerToken, operand, op.imm64); + break; + case LABEL: + snprintf(operand, sizeof(operand), "%#x", op.imm); + result.emplace_back(PossibleAddressToken, operand, op.imm); + break; + default: + return; + } + + tokenize_shift(op, result); + } + + uint32_t tokenize_shifted_register( + const InstructionOperand& op, + vector<InstructionTextToken>& result) + { + const char* reg = NULL; + reg = GetRegisterName((enum Register)op.reg).c_str(); + if (reg == NULL) + return FAILED_TO_DISASSEMBLE_REGISTER; + + + result.emplace_back(RegisterToken, reg); + tokenize_shift(op, result); + return DISASM_SUCCESS; + } + + + bool GetCoalescedLowLevelIL(const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il, Instruction& instr) + { + size_t remaining = len / 4; + if (remaining > COALESCE_MAX_INSTRS) + remaining = COALESCE_MAX_INSTRS; + + Condition cond = instr.cond; + + Instruction coalesced[COALESCE_MAX_INSTRS]; + bool liftInstruction[COALESCE_MAX_INSTRS]; + size_t disassembled = 1; + + coalesced[0] = instr; + liftInstruction[0] = true; + + auto setsFlags = [](const Instruction& instr) + { + if (instr.setsFlags) + return true; + + switch(instr.operation) + { + case ARMV7_CMP: + case ARMV7_CMN: + case ARMV7_TST: + return true; + + case ARMV7_BL: + case ARMV7_BLX: + return true; + + default: + return false; + } + }; + + for (bool condValid[2] = {true, true}; (disassembled < remaining) && (condValid[0] || condValid[1]); disassembled++) + { + size_t consumed = disassembled * 4; + auto& newInstr = coalesced[disassembled]; + + if (!Disassemble(data + consumed, addr + consumed, len - consumed, newInstr)) + break; + if (UNCONDITIONAL(newInstr.cond)) + break; + if (!IsRelatedCondition(newInstr.cond, cond)) + break; + + liftInstruction[disassembled] = condValid[newInstr.cond != cond]; + if (!CanCoalesceAfterInstruction(newInstr)) + condValid[newInstr.cond != cond] = false; + + if (setsFlags(instr)) + { + condValid[0] = true; + condValid[1] = true; + } + } + + if (disassembled == 1) + { + len = 4; + return GetLowLevelILForArmInstruction(this, addr, il, instr, GetAddressSize()); + } + + + LowLevelILLabel doneLabel; + LowLevelILLabel condLabels[2]; + + BNLowLevelILLabel* doneLabelExisting = il.GetLabelForAddress(this, addr + (disassembled * 4)); + BNLowLevelILLabel* doneLabelToUse = doneLabelExisting ? doneLabelExisting : &doneLabel; + + for (size_t blockStart = 0; blockStart < disassembled;) + { + auto& beginInstr = coalesced[blockStart]; + size_t stateIdx = (beginInstr.cond != cond); + + // determine how many instructions to lift this iteration. + // generally, this will be set to `disassembled`, but in the + // event that cmp/cmn/tst instructions are used in the conditional + // block, they each require re-evaluation of the condition on the side + // that executed the flag setting instructions + size_t nextFlagSet = blockStart; + for (; nextFlagSet < disassembled; nextFlagSet++) + { + if (!liftInstruction[nextFlagSet]) + continue; // skip unreachable instructions + + if (setsFlags(coalesced[nextFlagSet])) + break; + } + + // figure out where the next block start for the *other* condition in the sequence is + size_t otherCondNext = blockStart + 1; + for (; otherCondNext < disassembled ; otherCondNext++) + { + if (!liftInstruction[otherCondNext]) + continue; // skip unreachable instructions + + if (coalesced[otherCondNext].cond != beginInstr.cond) + break; + } + + bool hasOtherPath = (otherCondNext < disassembled); + + il.SetCurrentAddress(this, addr + (blockStart * 4)); + il.AddInstruction(il.If(GetCondition(il, beginInstr.cond), condLabels[stateIdx], + hasOtherPath ? condLabels[1 - stateIdx] : *doneLabelToUse)); + + auto liftInstructions = [&](Condition liftCond) + { + size_t stateIdx = (liftCond != cond); + + il.MarkLabel(condLabels[stateIdx]); + condLabels[stateIdx] = LowLevelILLabel(); + + bool exhausted = true; + for (size_t i = nextFlagSet + 1; (i < disassembled) && exhausted; i++) + if (coalesced[i].cond == liftCond) + exhausted = false; + + size_t liftIdx = blockStart; + for (; (liftIdx <= nextFlagSet) && (liftIdx < disassembled); liftIdx++) + { + if (!liftInstruction[liftIdx]) + continue; // skip unreachable instructions + + auto& curInstr = coalesced[liftIdx]; + if (curInstr.cond != liftCond) + continue; + + uint64_t instrAddr = addr + (liftIdx * 4); + + il.SetCurrentAddress(this, instrAddr); + + curInstr.cond = COND_NONE; + GetLowLevelILForArmInstruction(this, instrAddr, il, curInstr, GetAddressSize()); + curInstr.cond = liftCond; + } + + // CASE 1: last instr was a flag-setting instruction, do nothing, next lifting fixes it + if ((nextFlagSet < disassembled) && (coalesced[nextFlagSet].cond == liftCond)) + return; + + // CASE 2: no further instructions with this cond exist: goto done + else if (exhausted) + il.AddInstruction(il.Goto(*doneLabelToUse)); + + // CASE 3: last instr was not a flag-setting instruction, goto next block of this cond (or end) + else + il.AddInstruction(il.Goto(condLabels[stateIdx])); + }; + + bool liftAfter = false; + if (hasOtherPath && (otherCondNext <= nextFlagSet)) + { + // if we have two different cases to lift, and one of them contains a flag-setting + // instruction, make sure the condition with the the flag-setting instruction is + // lifted last. this lets us avoid an unnecessary LLIL_GOTO to the next if statement + if ((nextFlagSet < disassembled) && (coalesced[nextFlagSet].cond == beginInstr.cond)) + liftInstructions(coalesced[otherCondNext].cond); + else + liftAfter = true; + } + + liftInstructions(beginInstr.cond); + + if (liftAfter) + liftInstructions(coalesced[otherCondNext].cond); + + blockStart = nextFlagSet + 1; + } + + if (!doneLabelExisting) + il.MarkLabel(doneLabel); + + len = disassembled * 4; + return (doneLabelExisting == nullptr); + } + +public: + Armv7Architecture(const char* arch, BNEndianness endian) + : ArmCommonArchitecture(arch, endian) + { + } + + virtual size_t GetInstructionAlignment() const override + { + return 4; + } + + virtual size_t GetMaxInstructionLength() const override + { + return 4; + } + + virtual bool GetInstructionInfo(const uint8_t* data, uint64_t addr, size_t maxLen, InstructionInfo& result) override + { + if (maxLen < 4) + return false; + + Instruction instr; + if (!Disassemble(data, addr, maxLen, instr)) + return false; + + SetInstructionInfoForInstruction(addr, instr, result); + return true; + } + + static inline void GetImmToken(const InstructionOperand& op, vector<InstructionTextToken>& result) + { + char operand[32]; + snprintf(operand, sizeof(operand), "%#x", (uint32_t)op.imm); + result.emplace_back(TextToken, " #"); + result.emplace_back(IntegerToken, operand, op.imm); + } + + static inline void GetSignedImmToken(const InstructionOperand& op, vector<InstructionTextToken>& result) + { + char operand[32]; + const char* neg[2] = {"-", ""}; + snprintf(operand, sizeof(operand), "%s%#x", neg[op.flags.add == 1], (uint32_t)op.imm); + result.emplace_back(TextToken, " #"); + result.emplace_back(IntegerToken, operand, op.imm); + } + + virtual bool GetInstructionText(const uint8_t* data, uint64_t addr, size_t& len, vector<InstructionTextToken>& result) override + { + Instruction instr; + char padding[9]; + + const char* neg[2] = {"-", ""}; + const char* wb[2] = {"", "!"}; + const char* crt[2] = {"", " ^"}; + bool first = true; + char tmpOperand[256]; + + if (!Disassemble(data, addr, len, instr)) + return false; + len = 4; + memset(padding, 0x20, sizeof(padding)); + + const char* operation = get_full_operation(tmpOperand, sizeof(tmpOperand), &instr); + if (operation == NULL) + return false; + size_t operationLen = strlen(operation); + if (operationLen < 8) + { + padding[8-operationLen] = '\0'; + } + else + padding[1] = '\0'; + + result.emplace_back(InstructionToken, operation); + result.emplace_back(TextToken, padding); + + try + { + for (size_t i = 0; i < MAX_OPERANDS; i++) + { + if (instr.operands[i].cls == NONE) + return true; + + if (i != 0) + result.emplace_back(OperandSeparatorToken, ", "); + + switch (instr.operands[i].cls) + { + case FIMM16: + case FIMM32: + case FIMM64: + case IMM: + case IMM64: + case LABEL: + tokenize_shifted_immediate(instr.operands[i], result); + break; + case REG: + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].reg)); + result.emplace_back(TextToken, wb[instr.operands[i].flags.wb]); + if (instr.operands[i].shift == SHIFT_NONE) + { + if (instr.operands[i].flags.hasElements == 1) + { + result.emplace_back(TextToken, "["); + snprintf(tmpOperand, sizeof(tmpOperand), "%d", instr.operands[i].imm); + result.emplace_back(IntegerToken, tmpOperand, instr.operands[i].imm); + result.emplace_back(TextToken, "]"); + } + } + else if (instr.operands[i].flags.offsetRegUsed == 1) + { + //Register shifted by register + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, get_shift(instr.operands[i].shift)); + result.emplace_back(TextToken, " "); + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + } + else + { + //Register shifted by constant + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, get_shift(instr.operands[i].shift)); + if (instr.operands[i].shift != SHIFT_RRX) + { + result.emplace_back(TextToken, " "); + GetImmToken(instr.operands[i], result); + } + } + break; + case REG_LIST: + case REG_LIST_SINGLE: + case REG_LIST_DOUBLE: + { + result.emplace_back(TextToken, "{"); + first = true; + uint32_t base = 0; + if (instr.operands[i].cls == REG_LIST_SINGLE) + base = REG_S0; + else if (instr.operands[i].cls == REG_LIST_DOUBLE) + base = REG_D0; + + for (int32_t j = 0; j < 16; j++) + { + if (((instr.operands[i].reg >> j) & 1) == 1) + { + if (!first) + result.emplace_back(TextToken, ", "); + result.emplace_back(RegisterToken, GetRegisterName((enum Register)(j + base))); + first = false; + } + } + result.emplace_back(TextToken, "}"); + result.emplace_back(TextToken, crt[instr.operands[i].flags.wb]); + } + break; + case REG_SPEC: + result.emplace_back(TextToken, get_spec_register_name(instr.operands[i].regs)); + break; + case REG_BANKED: + result.emplace_back(TextToken, get_banked_register_name(instr.operands[i].regb)); + break; + case REG_COPROCP: + result.emplace_back(TextToken, get_coproc_register_p_name(instr.operands[i].regp)); + break; + case REG_COPROCC: + result.emplace_back(TextToken, get_coproc_register_c_name(instr.operands[i].regc)); + break; + case IFLAGS: + result.emplace_back(TextToken, get_iflag(instr.operands[i].iflag)); + break; + case ENDIAN_SPEC: + result.emplace_back(TextToken, get_endian(instr.operands[i].endian)); + break; + case DSB_OPTION: + result.emplace_back(TextToken, get_dsb_option(instr.operands[i].dsbOpt)); + break; + case MEM_ALIGNED: + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].reg)); + if (instr.operands[i].imm != 0) + { + result.emplace_back(TextToken, ":0x"); + snprintf(tmpOperand, sizeof(tmpOperand), ":%#x", instr.operands[i].imm); + result.emplace_back(IntegerToken, tmpOperand, instr.operands[i].imm); + } + result.emplace_back(EndMemoryOperandToken, "]"); + result.emplace_back(TextToken, wb[instr.operands[i].flags.wb]); + break; + case MEM_OPTION: + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].reg)); + result.emplace_back(EndMemoryOperandToken, "], {"); + GetImmToken(instr.operands[i], result); + result.emplace_back(TextToken, "}"); + break; + case MEM_PRE_IDX: + result.emplace_back(BeginMemoryOperandToken, "["); + if (instr.operands[i].flags.offsetRegUsed == 1) + { + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].reg)); + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, neg[instr.operands[i].flags.add == 1]); + if (instr.operands[i].imm == 0) + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + else if (instr.operands[i].shift == SHIFT_RRX) + { + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, get_shift(instr.operands[i].shift)); + } + else + { + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, get_shift(instr.operands[i].shift)); + result.emplace_back(TextToken, " "); + GetImmToken(instr.operands[i], result); + } + } + else + { + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].reg)); + result.emplace_back(TextToken, ", "); + GetSignedImmToken(instr.operands[i], result); + } + result.emplace_back(EndMemoryOperandToken, "]!"); + break; + break; + case MEM_POST_IDX: + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].reg)); + result.emplace_back(EndMemoryOperandToken, "], "); + if (instr.operands[i].flags.offsetRegUsed == 1) + { + result.emplace_back(TextToken, neg[instr.operands[i].flags.add == 1]); + if (instr.operands[i].imm == 0) + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + else if (instr.operands[i].shift == SHIFT_RRX) + { + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, get_shift(instr.operands[i].shift)); + } + else + { + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, get_shift(instr.operands[i].shift)); + result.emplace_back(TextToken, " "); + GetImmToken(instr.operands[i], result); + } + } + else + { + GetSignedImmToken(instr.operands[i], result); + } + break; + case MEM_IMM: + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].reg)); + switch (instr.operands[i].shift) + { + case SHIFT_NONE: + if (instr.operands[i].flags.offsetRegUsed == 1) + { + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, neg[instr.operands[i].flags.add == 1]); + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + } + else if (instr.operands[i].imm != 0)// || instr.operands[i].flags.add == 0) + { + result.emplace_back(TextToken, ", "); + GetSignedImmToken(instr.operands[i], result); + } + break; + case SHIFT_RRX: + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, neg[instr.operands[i].flags.add == 1]); + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, get_shift(instr.operands[i].shift)); + break; + default: + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, neg[instr.operands[i].flags.add == 1]); + result.emplace_back(RegisterToken, GetRegisterName(instr.operands[i].offset)); + result.emplace_back(TextToken, ", "); + result.emplace_back(TextToken, get_shift(instr.operands[i].shift)); + result.emplace_back(TextToken, " "); + GetImmToken(instr.operands[i], result); + } + result.emplace_back(EndMemoryOperandToken, "]"); + break; + default: + LogError("operandClass %d\n", instr.operands[i].cls); + return false; + } + } + } + catch (exception&) + { + LogWarn("Failed to disassemble instruction with encoding: %" PRIx32 "\n", *(uint32_t*)data); + } + return true; + } + + + virtual string GetIntrinsicName(uint32_t intrinsic) override + { + switch (intrinsic) + { + case ARMV7_INTRIN_COPROC_GETONEWORD: + return "Coproc_GetOneWord"; + case ARMV7_INTRIN_COPROC_GETTWOWORDS: + return "Coproc_GetTwoWords"; + case ARMV7_INTRIN_COPROC_SENDONEWORD: + return "Coproc_SendOneWord"; + case ARMV7_INTRIN_COPROC_SENDTWOWORDS: + return "Coproc_SendTwoWords"; + case ARMV7_INTRIN_EXCLUSIVE_MONITORS_PASS: + return "ExclusiveMonitorsPass"; + case ARMV7_INTRIN_SET_EXCLUSIVE_MONITORS: + return "SetExclusiveMonitors"; + default: + return ""; + } + } + + virtual vector<uint32_t> GetAllIntrinsics() override + { + return vector<uint32_t> { + ARMV7_INTRIN_COPROC_GETONEWORD, + ARMV7_INTRIN_COPROC_GETTWOWORDS, + ARMV7_INTRIN_COPROC_SENDONEWORD, + ARMV7_INTRIN_COPROC_SENDTWOWORDS, + ARMV7_INTRIN_EXCLUSIVE_MONITORS_PASS, + ARMV7_INTRIN_SET_EXCLUSIVE_MONITORS, + }; + } + + virtual vector<NameAndType> GetIntrinsicInputs(uint32_t intrinsic) override + { + switch (intrinsic) + { + case ARMV7_INTRIN_COPROC_GETONEWORD: + return { + NameAndType("cp", Type::IntegerType(1, false)), + NameAndType(Type::IntegerType(1, false)), + NameAndType("n", Type::IntegerType(1, false)), + NameAndType("m", Type::IntegerType(1, false)), + NameAndType(Type::IntegerType(1, false)), + }; + case ARMV7_INTRIN_COPROC_GETTWOWORDS: + return { + NameAndType("cp", Type::IntegerType(1, false)), + NameAndType(Type::IntegerType(1, false)), + NameAndType("m", Type::IntegerType(1, false)), + }; + case ARMV7_INTRIN_COPROC_SENDONEWORD: + return { + NameAndType(Type::IntegerType(4, false)), + NameAndType("cp", Type::IntegerType(1, false)), + NameAndType(Type::IntegerType(1, false)), + NameAndType("n", Type::IntegerType(1, false)), + NameAndType("m", Type::IntegerType(1, false)), + NameAndType(Type::IntegerType(1, false)), + }; + case ARMV7_INTRIN_COPROC_SENDTWOWORDS: + return { + NameAndType(Type::IntegerType(4, false)), + NameAndType(Type::IntegerType(4, false)), + NameAndType("cp", Type::IntegerType(1, false)), + NameAndType(Type::IntegerType(1, false)), + NameAndType("m", Type::IntegerType(1, false)), + }; + case ARMV7_INTRIN_EXCLUSIVE_MONITORS_PASS: + case ARMV7_INTRIN_SET_EXCLUSIVE_MONITORS: + return { + NameAndType("address", Type::PointerType(4, Confidence(Type::VoidType(), 0), Confidence(false), Confidence(false), PointerReferenceType)), + NameAndType("size", Type::IntegerType(1, false)), + }; + default: + return vector<NameAndType>(); + } + } + + virtual vector<Confidence<Ref<Type>>> GetIntrinsicOutputs(uint32_t intrinsic) override + { + switch (intrinsic) + { + case ARMV7_INTRIN_COPROC_GETONEWORD: + return { Type::IntegerType(4, false) }; + case ARMV7_INTRIN_COPROC_GETTWOWORDS: + return { Type::IntegerType(4, false), Type::IntegerType(4, false) }; + case ARMV7_INTRIN_EXCLUSIVE_MONITORS_PASS: + return { Type::BoolType() }; + default: + return vector<Confidence<Ref<Type>>>(); + } + } + + virtual bool IsNeverBranchPatchAvailable(const uint8_t* data, uint64_t addr, size_t len) override + { + Instruction instr; + if (!Disassemble(data, addr, len, instr)) + return false; + + return (instr.operation == ARMV7_B && CONDITIONAL(instr.cond)); + } + + virtual bool IsAlwaysBranchPatchAvailable(const uint8_t* data, uint64_t addr, size_t len) override + { + Instruction instr; + if (!Disassemble(data, addr, len, instr)) + return false; + + return (instr.operation == ARMV7_B && CONDITIONAL(instr.cond)); + } + + virtual bool IsInvertBranchPatchAvailable(const uint8_t* data, uint64_t addr, size_t len) override + { + Instruction instr; + if (!Disassemble(data, addr, len, instr)) + return false; + + return (instr.operation == ARMV7_B && CONDITIONAL(instr.cond)); + } + + virtual bool ConvertToNop(uint8_t* data, uint64_t, size_t len) override + { + uint32_t nop = 0xe1a00000; + if (len < sizeof(nop)) + return false; + for (size_t i = 0; i < len/sizeof(nop); i++) + ((uint32_t*)data)[i] = nop; + return true; + } + + virtual bool AlwaysBranch(uint8_t* data, uint64_t addr, size_t len) override + { + (void)addr; + if (len < 4) + return false; + + uint32_t *value = (uint32_t*)data; + *value = (*value & 0x0fffffff) | (COND_NONE << 28); + return true; + } + + virtual bool InvertBranch(uint8_t* data, uint64_t addr, size_t len) override + { + (void)addr; + if (len < sizeof(uint32_t)) + return false; + uint32_t *value = (uint32_t*)data; + Condition cond = COND_NONE; + switch (*value >> 28) + { + case COND_EQ: cond = COND_NE; break; + case COND_NE: cond = COND_EQ; break; + case COND_CS: cond = COND_CC; break; + case COND_CC: cond = COND_CS; break; + case COND_MI: cond = COND_PL; break; + case COND_PL: cond = COND_MI; break; + case COND_VS: cond = COND_VC; break; + case COND_VC: cond = COND_VS; break; + case COND_HI: cond = COND_LS; break; + case COND_LS: cond = COND_HI; break; + case COND_GE: cond = COND_LT; break; + case COND_LT: cond = COND_GE; break; + case COND_GT: cond = COND_LE; break; + case COND_LE: cond = COND_GT; break; + } + *value = (*value & 0x0fffffff) | (cond << 28); + return true; + } + + virtual bool IsSkipAndReturnZeroPatchAvailable(const uint8_t* data, uint64_t addr, size_t len) override + { + Instruction instr; + if (!Disassemble(data, addr, len, instr)) + return false; + + return (instr.operation == ARMV7_BL) || (instr.operation == ARMV7_BLX); + } + + virtual bool IsSkipAndReturnValuePatchAvailable(const uint8_t* data, uint64_t addr, size_t len) override + { + Instruction instr; + if (!Disassemble(data, addr, len, instr)) + return false; + + return (instr.operation == ARMV7_BL) || (instr.operation == ARMV7_BLX); + } + + virtual bool SkipAndReturnValue(uint8_t* data, uint64_t addr, size_t len, uint64_t value) override + { + (void)addr; + //Return value is put in R0. The largest value that we can put into a single integer is 12 bits + if (value > 0xfff || len > 4) + return false; + + uint32_t movValueR0 = 0xe3a00000; + uint32_t *inst = (uint32_t*)data; + *inst = movValueR0 | (value & 0xfff); + return true; + } + + virtual bool GetInstructionLowLevelIL(const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il) override + { + Instruction instr; + if (!Disassemble(data, addr, len, instr)) + { + il.AddInstruction(il.Undefined()); + return false; + } + + if (!UNCONDITIONAL(instr.cond)) + return GetCoalescedLowLevelIL(data, addr, len, il, instr); + else + { + if ((instr.operation == ARMV7_MOV) && (instr.operands[0].cls == REG) && (instr.operands[0].reg == REG_LR) && (instr.operands[1].cls == REG) && (instr.operands[1].reg == REG_PC)) + { + Instruction branchInstr; + if (Disassemble(data + 4, addr + 4, len - 4, branchInstr) && UNCONDITIONAL(branchInstr.cond) && + (((branchInstr.operands[0].cls == REG) && (branchInstr.operands[0].reg == REG_PC)) || branchInstr.operation == ARMV7_BX)) + { + switch (branchInstr.operation) + { + case ARMV7_ADC: + case ARMV7_ADD: + case ARMV7_AND: + case ARMV7_ASR: + case ARMV7_BIC: + case ARMV7_EOR: + case ARMV7_LDR: + case ARMV7_LSL: + case ARMV7_LSR: + case ARMV7_MOV: + case ARMV7_MVN: + case ARMV7_ORR: + case ARMV7_ROR: + case ARMV7_RRX: + case ARMV7_RSB: + case ARMV7_RSC: + case ARMV7_SUB: + case ARMV7_SBC: + case ARMV7_BX: + { + len = 8; + il.SetCurrentAddress(this, addr + 4); + size_t nextInstr = il.GetInstructionCount(); + GetLowLevelILForArmInstruction(this, addr + 4, il, branchInstr, GetAddressSize()); + for (; nextInstr < il.GetInstructionCount(); nextInstr++) + { + if (auto tgtInstr = il.GetInstruction(nextInstr); tgtInstr.operation == LLIL_JUMP) + { + il.ReplaceExpr(tgtInstr.exprIndex, il.Call(tgtInstr.GetDestExpr<LLIL_JUMP>().exprIndex)); + return true; + } + } + break; + } + default: + break; + }; + } + } + + len = 4; + return GetLowLevelILForArmInstruction(this, addr, il, instr, GetAddressSize()); + } + } +}; + +ArmCommonArchitecture::ArmCommonArchitecture(const char* name, BNEndianness endian): Architecture(name), m_endian(endian) +{ +} + +void ArmCommonArchitecture::SetArmAndThumbArchitectures(Architecture* arm, Architecture* thumb) +{ + m_armArch = arm; + m_thumbArch = thumb; +} + +size_t ArmCommonArchitecture::GetAddressSize() const +{ + return 4; +} + +BNEndianness ArmCommonArchitecture::GetEndianness() const +{ + return m_endian; +} + +Ref<Architecture> ArmCommonArchitecture::GetAssociatedArchitectureByAddress(uint64_t& addr) +{ + if (addr & 1) + { + addr &= ~1LL; + return m_thumbArch; + } + return m_armArch; +} + +string ArmCommonArchitecture::GetFlagName(uint32_t flag) +{ + char result[32]; + switch (flag) + { + case IL_FLAG_N: + return "n"; + case IL_FLAG_Z: + return "z"; + case IL_FLAG_C: + return "c"; + case IL_FLAG_V: + return "v"; + case IL_FLAG_Q: + return "q"; + default: + snprintf(result, sizeof(result), "flag%" PRIu32, flag); + return result; + } +} + +string ArmCommonArchitecture::GetFlagWriteTypeName(uint32_t flags) +{ + switch (flags) + { + case IL_FLAGWRITE_ALL: return "*"; + case IL_FLAGWRITE_NZ: return "nz"; + case IL_FLAGWRITE_CNZ: return "cnz"; + default: + return ""; + } +} + +BNFlagRole ArmCommonArchitecture::GetFlagRole(uint32_t flag, uint32_t) +{ + switch (flag) + { + case IL_FLAG_N: + return NegativeSignFlagRole; + case IL_FLAG_Z: + return ZeroFlagRole; + case IL_FLAG_C: + return CarryFlagRole; + case IL_FLAG_V: + return OverflowFlagRole; + default: + return SpecialFlagRole; + } +} + +vector<uint32_t> ArmCommonArchitecture::GetFlagsWrittenByFlagWriteType(uint32_t flags) +{ + switch (flags) + { + case IL_FLAGWRITE_ALL: + return vector<uint32_t> { IL_FLAG_N, IL_FLAG_Z, IL_FLAG_C, IL_FLAG_V }; + case IL_FLAGWRITE_NZ: + return vector<uint32_t> { IL_FLAG_N, IL_FLAG_Z }; + case IL_FLAGWRITE_CNZ: + return vector<uint32_t> { IL_FLAG_C, IL_FLAG_N, IL_FLAG_Z }; + default: + return vector<uint32_t> {}; + } +} + +vector<uint32_t> ArmCommonArchitecture::GetFlagsRequiredForFlagCondition(BNLowLevelILFlagCondition cond, uint32_t) +{ + switch (cond) + { + case LLFC_E: + case LLFC_NE: + return vector<uint32_t>{ IL_FLAG_Z }; + case LLFC_SLT: + case LLFC_SGE: + return vector<uint32_t>{ IL_FLAG_N, IL_FLAG_V }; + case LLFC_ULT: + case LLFC_UGE: + return vector<uint32_t>{ IL_FLAG_C }; + case LLFC_SLE: + case LLFC_SGT: + return vector<uint32_t>{ IL_FLAG_Z, IL_FLAG_N, IL_FLAG_V }; + case LLFC_ULE: + case LLFC_UGT: + return vector<uint32_t>{ IL_FLAG_C, IL_FLAG_Z }; + case LLFC_NEG: + case LLFC_POS: + return vector<uint32_t>{ IL_FLAG_N }; + case LLFC_O: + case LLFC_NO: + return vector<uint32_t>{ IL_FLAG_V }; + default: + return vector<uint32_t>(); + } +} + +size_t ArmCommonArchitecture::GetFlagWriteLowLevelIL(BNLowLevelILOperation op, size_t size, uint32_t flagWriteType, + uint32_t flag, BNRegisterOrConstant* operands, size_t operandCount, LowLevelILFunction& il) +{ + switch (op) + { + case LLIL_SBB: + switch (flag) + { + case IL_FLAG_C: + // Copied from arm64 + // r u< a || (r == a && flag_c) + return il.Or(0, + il.CompareUnsignedLessThan(size, + il.GetExprForRegisterOrConstantOperation(op, size, operands, operandCount), + il.GetExprForRegisterOrConstant(operands[0], size)), + il.And(0, + il.CompareEqual(size, + il.GetExprForRegisterOrConstantOperation(op, size, operands, operandCount), + il.GetExprForRegisterOrConstant(operands[0], size)), + il.Flag(IL_FLAG_C))); + case IL_FLAG_V: + return il.CompareEqual(0, + il.CompareSignedLessThan(size, + il.GetExprForRegisterOrConstantOperation(op, size, operands, operandCount), + il.GetExprForRegisterOrConstant(operands[0], size)), + il.CompareEqual(size, + il.GetExprForRegisterOrConstant(operands[0], size), + il.Const(size, 0))); + } + break; + case LLIL_LSR: + switch (flag) + { + case IL_FLAG_C: + /* + * The last bit spilled out of the register by the shift lands in the carry flag. + * For example, `((u32)1) >> 1` sets the carry flag, `((u32)2) >> 1` clears it. + * We can simplify this to a bit test: `x & (1 << (shift_amt - 1))` + */ + return il.TestBit(0, + il.GetExprForRegisterOrConstant(operands[0], size), + il.Sub(size, il.GetExprForRegisterOrConstant(operands[1], size), il.Const(size, 1))); + } + break; + case LLIL_LSL: + switch (flag) + { + case IL_FLAG_C: + /* + * Just like the carry flag for LSR, this is the last bit spilled out of the register. + * Also equivalent to a bit test, just indexing from the most significant bit rather + * than the least. + */ + return il.TestBit(0, + il.GetExprForRegisterOrConstant(operands[0], size), + il.Sub(size, il.Const(size, 8 * size), il.GetExprForRegisterOrConstant(operands[1], size))); + } + default: + break; + } + + BNFlagRole role = GetFlagRole(flag, GetSemanticClassForFlagWriteType(flagWriteType)); + return GetDefaultFlagWriteLowLevelIL(op, size, role, operands, operandCount, il); +} + +string ArmCommonArchitecture::GetRegisterName(uint32_t reg) +{ + if (reg >= REG_R0 && reg < REG_INVALID) + { + return get_register_name((enum Register)reg); + } + + if (reg == FAKEREG_SYSCALL_INFO) + { + return "syscall_info"; + } + + LogError("Unknown Register: %x - Please report this as a bug.\n", reg); + return "unknown"; +} + +vector<uint32_t> ArmCommonArchitecture::GetFullWidthRegisters() +{ + return vector<uint32_t>{ + REG_R0, REG_R1, REG_R2, REG_R3, REG_R4, REG_R5, REG_R6, REG_R7, + REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, + REG_Q0, REG_Q1, REG_Q2, REG_Q3, REG_Q4, REG_Q5, REG_Q6, REG_Q7, + REG_Q8, REG_Q9, REG_Q10, REG_Q11, REG_Q12, REG_Q13, REG_Q14, REG_Q15, + }; +} + +vector<uint32_t> ArmCommonArchitecture::GetAllRegisters() +{ + return vector<uint32_t>{ + REG_R0, REG_R1, REG_R2, REG_R3, REG_R4, REG_R5, REG_R6, REG_R7, + REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, + REG_S0, REG_S1, REG_S2, REG_S3, REG_S4, REG_S5, REG_S6, REG_S7, + REG_S8, REG_S9, REG_S10, REG_S11, REG_S12, REG_S13, REG_S14, REG_S15, + REG_S16, REG_S17, REG_S18, REG_S19, REG_S20, REG_S21, REG_S22, REG_S23, + REG_S24, REG_S25, REG_S26, REG_S27, REG_S28, REG_S29, REG_S30, REG_S31, + REG_D0, REG_D1, REG_D2, REG_D3, REG_D4, REG_D5, REG_D6, REG_D7, + REG_D8, REG_D9, REG_D10, REG_D11, REG_D12, REG_D13, REG_D14, REG_D15, + REG_D16, REG_D17, REG_D18, REG_D19, REG_D20, REG_D21, REG_D22, REG_D23, + REG_D24, REG_D25, REG_D26, REG_D27, REG_D28, REG_D29, REG_D30, REG_D31, + REG_Q0, REG_Q1, REG_Q2, REG_Q3, REG_Q4, REG_Q5, REG_Q6, REG_Q7, + REG_Q8, REG_Q9, REG_Q10, REG_Q11, REG_Q12, REG_Q13, REG_Q14, REG_Q15, + + /* special registers */ + REGS_APSR, REGS_APSR_G, REGS_APSR_NZCVQ, REGS_APSR_NZCVQG, + REGS_CPSR, REGS_CPSR_C, REGS_CPSR_X, REGS_CPSR_XC, + REGS_CPSR_S, REGS_CPSR_SC, REGS_CPSR_SX, REGS_CPSR_SXC, + REGS_CPSR_F, REGS_CPSR_FC, REGS_CPSR_FX, REGS_CPSR_FXC, + REGS_CPSR_FS, REGS_CPSR_FSC, REGS_CPSR_FSX, REGS_CPSR_FSXC, + REGS_SPSR, REGS_SPSR_C, REGS_SPSR_X, REGS_SPSR_XC, + REGS_SPSR_S, REGS_SPSR_SC, REGS_SPSR_SX, REGS_SPSR_SXC, + REGS_SPSR_F, REGS_SPSR_FC, REGS_SPSR_FX, REGS_SPSR_FXC, + REGS_SPSR_FS, REGS_SPSR_FSC, REGS_SPSR_FSX, REGS_SPSR_FSXC, + REGS_APSR_NZCV, REGS_FPSID, REGS_FPSCR, REGS_MVFR2, + REGS_MVFR1, REGS_MVFR0, REGS_FPEXC, REGS_FPINST, + REGS_FPINST2, REGS_MSP, REGS_PSP, REGS_PRIMASK, + REGS_BASEPRI, REGS_FAULTMASK, REGS_CONTROL, + + /* fake registers */ + FAKEREG_SYSCALL_INFO + }; +} + +vector<uint32_t> ArmCommonArchitecture::GetAllFlags() +{ + return vector<uint32_t>{ + IL_FLAG_N, IL_FLAG_Z, IL_FLAG_C, IL_FLAG_V, IL_FLAG_Q + }; +} + +vector<uint32_t> ArmCommonArchitecture::GetAllFlagWriteTypes() +{ + return vector<uint32_t>{ + IL_FLAGWRITE_ALL, + IL_FLAGWRITE_NZ, + IL_FLAGWRITE_CNZ + }; +} + +BNRegisterInfo ArmCommonArchitecture::GetRegisterInfo(uint32_t reg) +{ + switch (reg) + { + case REG_R0: + case REG_R1: + case REG_R2: + case REG_R3: + case REG_R4: + case REG_R5: + case REG_R6: + case REG_R7: + case REG_R8: + case REG_R9: + case REG_R10: + case REG_R11: + case REG_R12: + case REG_R13: + case REG_R14: + case REG_R15: + return RegisterInfo(reg, 0, 4); + case REG_S0: + case REG_S1: + case REG_S2: + case REG_S3: + case REG_S4: + case REG_S5: + case REG_S6: + case REG_S7: + case REG_S8: + case REG_S9: + case REG_S10: + case REG_S11: + case REG_S12: + case REG_S13: + case REG_S14: + case REG_S15: + case REG_S16: + case REG_S17: + case REG_S18: + case REG_S19: + case REG_S20: + case REG_S21: + case REG_S22: + case REG_S23: + case REG_S24: + case REG_S25: + case REG_S26: + case REG_S27: + case REG_S28: + case REG_S29: + case REG_S30: + case REG_S31: + return RegisterInfo(REG_Q0+((reg-REG_S0)/4), ((reg-REG_S0)%4) * 4, 4); + case REG_D0: + case REG_D1: + case REG_D2: + case REG_D3: + case REG_D4: + case REG_D5: + case REG_D6: + case REG_D7: + case REG_D8: + case REG_D9: + case REG_D10: + case REG_D11: + case REG_D12: + case REG_D13: + case REG_D14: + case REG_D15: + case REG_D16: + case REG_D17: + case REG_D18: + case REG_D19: + case REG_D20: + case REG_D21: + case REG_D22: + case REG_D23: + case REG_D24: + case REG_D25: + case REG_D26: + case REG_D27: + case REG_D28: + case REG_D29: + case REG_D30: + case REG_D31: + return RegisterInfo(REG_Q0+((reg-REG_D0)/2), ((reg-REG_D0)%2) * 8, 8); + case REG_Q0: + case REG_Q1: + case REG_Q2: + case REG_Q3: + case REG_Q4: + case REG_Q5: + case REG_Q6: + case REG_Q7: + case REG_Q8: + case REG_Q9: + case REG_Q10: + case REG_Q11: + case REG_Q12: + case REG_Q13: + case REG_Q14: + case REG_Q15: + return RegisterInfo(reg, 0, 16); + case REGS_APSR: + case REGS_APSR_G: + case REGS_APSR_NZCVQ: + case REGS_APSR_NZCVQG: + case REGS_CPSR: + case REGS_CPSR_C: + case REGS_CPSR_X: + case REGS_CPSR_XC: + case REGS_CPSR_S: + case REGS_CPSR_SC: + case REGS_CPSR_SX: + case REGS_CPSR_SXC: + case REGS_CPSR_F: + case REGS_CPSR_FC: + case REGS_CPSR_FX: + case REGS_CPSR_FXC: + case REGS_CPSR_FS: + case REGS_CPSR_FSC: + case REGS_CPSR_FSX: + case REGS_CPSR_FSXC: + case REGS_SPSR: + case REGS_SPSR_C: + case REGS_SPSR_X: + case REGS_SPSR_XC: + case REGS_SPSR_S: + case REGS_SPSR_SC: + case REGS_SPSR_SX: + case REGS_SPSR_SXC: + case REGS_SPSR_F: + case REGS_SPSR_FC: + case REGS_SPSR_FX: + case REGS_SPSR_FXC: + case REGS_SPSR_FS: + case REGS_SPSR_FSC: + case REGS_SPSR_FSX: + case REGS_SPSR_FSXC: + case REGS_APSR_NZCV: + case REGS_FPSID: + case REGS_FPSCR: + case REGS_MVFR2: + case REGS_MVFR1: + case REGS_MVFR0: + case REGS_FPEXC: + case REGS_FPINST: + case REGS_FPINST2: + case REGS_MSP: + case REGS_PSP: + case REGS_PRIMASK: + case REGS_BASEPRI: + case REGS_FAULTMASK: + case REGS_CONTROL: + return RegisterInfo(reg, 0, 4); + case FAKEREG_SYSCALL_INFO: + return RegisterInfo(reg, 0, 4); + } + return RegisterInfo(0, 0, 0); +} + +uint32_t ArmCommonArchitecture::GetStackPointerRegister() +{ + return REG_SP; +} + +uint32_t ArmCommonArchitecture::GetLinkRegister() +{ + return REG_LR; +} + +bool ArmCommonArchitecture::CanAssemble() +{ + return true; +} + +bool ArmCommonArchitecture::Assemble(const string& code, uint64_t addr, DataBuffer& result, string& errors) +{ + (void)addr; + + char *instrBytes=NULL, *err=NULL; + int instrBytesLen=0, errLen=0; + + int assembleResult; + + string triple = GetAssemblerTriple(); + LogDebug("%s() retrieves and uses triple %s\n", __func__, triple.c_str()); + + BNLlvmServicesInit(); + + errors.clear(); + assembleResult = BNLlvmServicesAssemble(code.c_str(), LLVM_SVCS_DIALECT_UNSPEC, + triple.c_str(), LLVM_SVCS_CM_DEFAULT, LLVM_SVCS_RM_STATIC, + &instrBytes, &instrBytesLen, &err, &errLen); + + if(assembleResult || errLen) { + errors = err; + BNLlvmServicesAssembleFree(instrBytes, err); + return false; + } + + result.Clear(); + result.Append(instrBytes, instrBytesLen); + BNLlvmServicesAssembleFree(instrBytes, err); + return true; +} + +class ArmCallingConvention: public CallingConvention +{ +public: + ArmCallingConvention(Architecture* arch): CallingConvention(arch, "cdecl") + { + } + + virtual vector<uint32_t> GetIntegerArgumentRegisters() override + { + return vector<uint32_t>{ REG_R0, REG_R1, REG_R2, REG_R3 }; + } + + virtual vector<uint32_t> GetCallerSavedRegisters() override + { + return vector<uint32_t>{ REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_LR }; + } + + virtual vector<uint32_t> GetCalleeSavedRegisters() override + { + return vector<uint32_t>{ REG_R4, REG_R5, REG_R6, REG_R7, REG_R8, REG_R10, REG_R11 }; + } + + virtual uint32_t GetIntegerReturnValueRegister() override + { + return REG_R0; + } + + virtual uint32_t GetHighIntegerReturnValueRegister() override + { + return REG_R1; + } +}; + + +class LinuxArmv7SystemCallConvention: public CallingConvention +{ +public: + LinuxArmv7SystemCallConvention(Architecture* arch): CallingConvention(arch, "linux-syscall") + { + } + + virtual vector<uint32_t> GetIntegerArgumentRegisters() override + { + return vector<uint32_t>{ REG_R7, REG_R0, REG_R1, REG_R2, REG_R3, REG_R4, REG_R5, REG_R6 }; + } + + virtual vector<uint32_t> GetCallerSavedRegisters() override + { + return vector<uint32_t>{ REG_R0 }; + } + + virtual vector<uint32_t> GetCalleeSavedRegisters() override + { + return vector<uint32_t>{ REG_R4, REG_R5, REG_R6, REG_R7, REG_R8, REG_R10, REG_R11 }; + } + + virtual uint32_t GetIntegerReturnValueRegister() override + { + return REG_R0; + } + + virtual bool IsEligibleForHeuristics() override + { + return false; + } +}; + + +class Thumb2ImportedFunctionRecognizer: public FunctionRecognizer +{ +public: + virtual bool RecognizeLowLevelIL(BinaryView* data, Function* func, LowLevelILFunction* il) override + { + // Detection for inline veneers for thumb -> arm transitions + if (il->GetInstructionCount() == 1) + { + LowLevelILInstruction instr = il->GetInstruction(0); + if ((instr.operation == LLIL_JUMP) || (instr.operation == LLIL_TAILCALL)) + { + LowLevelILInstruction operand = instr.GetDestExpr(); + if (operand.operation == LLIL_CONST_PTR) + { + uint64_t entry = operand.GetConstant(); + if (entry == (func->GetStart() + 4)) + { + Ref<Function> entryFunc = data->GetRecentAnalysisFunctionForAddress(entry); + Ref<Symbol> sym = data->GetSymbolByAddress(entry); + if (!entryFunc || !sym || (sym->GetType() != ImportedFunctionSymbol)) + return false; + + Confidence<Ref<Type>> type = entryFunc->GetType(); + data->DefineImportedFunction(sym, func, type); + return true; + } + } + } + } + + return false; + } +}; + + +uint32_t bswap32(uint32_t x) +{ + return ((x & 0xff000000) >> 24) | + ((x & 0x00ff0000) >> 8) | + ((x & 0x0000ff00) << 8) | + ((x & 0x000000ff) << 24); +} + +class ArmElfRelocationHandler: public RelocationHandler +{ +public: + virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override + { + (void)view; + BNRelocationInfo info = reloc->GetInfo(); + if (len < info.size) + return false; + Ref<Symbol> sym = reloc->GetSymbol(); + uint32_t target = (uint32_t)reloc->GetTarget(); + uint32_t* dest32 = (uint32_t*)dest; + + auto swap = [&arch](uint32_t x) { return (arch->GetEndianness() == LittleEndian)? x : bswap32(x); }; + switch (info.nativeType) + { + case R_ARM_COPY: + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + case R_ARM_BASE_PREL: + case R_ARM_GOT_BREL: + dest32[0] = swap(target); + break; + case R_ARM_RELATIVE: + case R_ARM_ABS32: + dest32[0] = swap(swap(dest32[0]) + target); + break; + case R_ARM_REL32: + dest32[0] = swap((uint32_t)((target + (info.implicitAddend ? swap(dest32[0]) : info.addend)) - reloc->GetAddress())); + break; + case R_ARM_CALL: + { + if (target & 1) + { + LogError("Unsupported relocation R_ARM_CALL to thumb target"); + break; + } + struct _bl { + int32_t imm:24; + uint32_t group1:4; + uint32_t cond:4; + }; + _bl* bl = (_bl*) dest32; + int64_t newTarget = (target + (info.implicitAddend ? ((bl->imm << 2) + 8) : info.addend)) - reloc->GetAddress(); + if ((newTarget - 8) > 0x3ffffff) + { + LogError("Unsupported relocation R_ARM_CALL @ 0x%" PRIx64 " with target greater than 0x3ffffff: 0x%" PRIx64, reloc->GetAddress(), newTarget - 8); + break; + } + bl->imm = (newTarget - 8) >> 2; + break; + } + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + { + // TODO: not portable + #pragma pack(push, 1) + union _thumb32_bl_hw1 { + uint16_t word; + struct { + uint16_t offHi:10; // 21-12 + uint16_t sign:1; // 31-24 + uint16_t group:5; + }; + }; + + union _thumb32_bl_hw2 { + uint16_t word; + struct { + uint16_t offLo:11; //b11-1 + uint16_t j2:1; //b18 + uint16_t thumb:1; + uint16_t j1:1; //b19 + uint16_t i2:1; //b22 + uint16_t i1:1; //b23 + }; + }; + #pragma pack(pop) + + _thumb32_bl_hw1* bl_hw1 = (_thumb32_bl_hw1*)dest; + _thumb32_bl_hw2* bl_hw2 = (_thumb32_bl_hw2*)(dest + 2); + int32_t curTarget = (bl_hw2->offLo << 1) | (bl_hw1->offHi << 12) | (bl_hw1->sign ? (0xffc << 20) : 0); + int32_t newTarget = (int32_t)((target + (info.implicitAddend ? curTarget : info.addend)) - reloc->GetAddress()); + + bl_hw1->sign = newTarget < 0 ? 1 : 0; + bl_hw1->offHi = newTarget >> 12; + bl_hw2->offLo = newTarget >> 1; + + // TODO: I/J bit handling conflicts with at least one Thumb2 supplement + // bl_hw2->i1 = bl_hw1->sign ^ ((newTarget >> 23) & 1); + // bl_hw2->i2 = bl_hw1->sign ^ ((newTarget >> 22) & 1); + // bl_hw2->j1 = (newTarget >> 19) & 1; + // bl_hw2->j2 = (newTarget >> 18) & 1; + break; + } + case R_ARM_PREL31: + { + // if (sym) + // { + // LogError("%lx sym: %s dest32[0] %lx target: %lx", reloc->GetAddress(), sym->GetFullName().c_str(), dest32[0], target); + // } + // else + // { + // LogError("%lx sym: null dest32[0] %lx target: %lx", reloc->GetAddress(), dest32[0], target); + // } + dest32[0] = (info.implicitAddend ? dest32[0] : (uint32_t)info.addend) + (target & ~1) - (uint32_t)reloc->GetAddress(); + break; + } + case R_ARM_JUMP24: + { + if (target & 1) + { + LogError("Unsupported relocation R_ARM_JUMP24 to thumb target"); + break; + } + struct _b { + int32_t imm:24; + uint32_t group1:4; + uint32_t cond:4; + }; + _b* b = (_b*) dest32; + int64_t newTarget = (target + (info.implicitAddend ? ((b->imm << 2) + 8) : info.addend)) - reloc->GetAddress(); + if ((newTarget - 8) > 0x3ffffff) + { + LogError("Unsupported relocation R_ARM_JUMP24 0x%" PRIx64 " with target greater than 0x3ffffff: 0x%" PRIx64, reloc->GetAddress(), newTarget - 8); + break; + } + b->imm = (newTarget - 8) >> 2; + break; + } + case R_ARM_MOVW_ABS_NC: + { + struct _mov { + uint32_t imm12:12; + uint32_t rd:4; + uint32_t imm4:4; + uint32_t group2:8; + uint32_t cond:4; + }; + _mov* mov = (_mov*)dest32; + int64_t newTarget = (target + (info.implicitAddend ? (mov->imm4 << 12 | mov->imm12) : info.addend)); + mov->imm12 = newTarget & 0xfff; + mov->imm4 = (newTarget >> 12) & 0xf; + break; + } + case R_ARM_MOVT_ABS: + { + struct _mov { + uint32_t imm12:12; + uint32_t rd:4; + uint32_t imm4:4; + uint32_t group2:8; + uint32_t cond:4; + }; + _mov* mov = (_mov*)dest32; + int64_t newTarget = (target + (info.implicitAddend ? (mov->imm4 << 12 | mov->imm12) : info.addend)); + mov->imm12 = (newTarget >> 16) & 0xfff; + mov->imm4 = (newTarget >> 28) & 0xf; + break; + } + case R_ARM_TLS_DTPMOD32: + /* Default to module index 0. */ + dest32[0] = 0; + break; + case R_ARM_TLS_DTPOFF32: + { + if (sym) + dest32[0] = sym->GetAddress(); + break; + } + default: + return RelocationHandler::ApplyRelocation(view, arch, reloc, dest, len); + } + return false; + } + + virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override + { + (void)view; + (void)arch; + set<uint64_t> relocTypes; + for (auto& reloc: result) + { + reloc.type = StandardRelocationType; + reloc.size = 4; + reloc.pcRelative = false; + reloc.dataRelocation = IsELFDataRelocation((ElfArmRelocationType)reloc.nativeType); + switch (reloc.nativeType) + { + case R_ARM_NONE: + reloc.type = IgnoredRelocation; + reloc.pcRelative = true; + break; + case R_ARM_PREL31: + case R_ARM_RELATIVE: + reloc.pcRelative = true; + break; + case R_ARM_ABS32: + case R_ARM_BASE_PREL: + case R_ARM_GOT_BREL: + break; + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + reloc.pcRelative = true; + break; + case R_ARM_COPY: + reloc.type = ELFCopyRelocationType; + break; + case R_ARM_GLOB_DAT: + reloc.type = ELFGlobalRelocationType; + break; + case R_ARM_JUMP_SLOT: + reloc.type = ELFJumpSlotRelocationType; + break; + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + break; + case R_ARM_REL32: + reloc.pcRelative = true; + break; + case R_ARM_IRELATIVE: + reloc.baseRelative = true; + reloc.type = ELFJumpSlotRelocationType; + break; + case R_ARM_TLS_DTPMOD32: + /* Prevent higher level behavior based on associated symbol + (we'll do that for the corresponding R_ARM_TLS_DTPOFF32). */ + reloc.symbolIndex = 0; + break; + case R_ARM_TLS_DTPOFF32: + break; + case R_ARM_SBREL31: + case R_ARM_PC24: + case R_ARM_LDR_PC_G0: + case R_ARM_ABS16: + case R_ARM_ABS12: + case R_ARM_ABS8: + case R_ARM_SBREL32: + case R_ARM_BREL_ADJ: + case R_ARM_TLS_DESC: + case R_ARM_XPC25: + case R_ARM_TLS_TPOFF32: + case R_ARM_GOTOFF32: + case R_ARM_PLT32: + case R_ARM_BASE_ABS: + case R_ARM_ALU_PCREL_7_0: + case R_ARM_ALU_PCREL_15_8: + case R_ARM_ALU_PCREL_23_15: + case R_ARM_LDR_SBREL_11_0_NC: + case R_ARM_ALU_SBREL_19_12_NC: + case R_ARM_ALU_SBREL_27_20_CK: + case R_ARM_TARGET1: + case R_ARM_V4BX: + case R_ARM_TARGET2: + case R_ARM_MOVW_PREL_NC: + case R_ARM_MOVT_PREL: + case R_ARM_ABS32_NOI: + case R_ARM_REL32_NOI: + case R_ARM_ALU_PC_G0_NC: + case R_ARM_ALU_PC_G0: + case R_ARM_ALU_PC_G1_NC: + case R_ARM_ALU_PC_G1: + case R_ARM_ALU_PC_G2: + case R_ARM_LDR_PC_G1: + case R_ARM_LDR_PC_G2: + case R_ARM_LDRS_PC_G0: + case R_ARM_LDRS_PC_G1: + case R_ARM_LDRS_PC_G2: + case R_ARM_LDC_PC_G0: + case R_ARM_LDC_PC_G1: + case R_ARM_LDC_PC_G2: + case R_ARM_ALU_SB_G0_NC: + case R_ARM_ALU_SB_G0: + case R_ARM_ALU_SB_G1_NC: + case R_ARM_ALU_SB_G1: + case R_ARM_ALU_SB_G2: + case R_ARM_LDR_SB_G0: + case R_ARM_LDR_SB_G1: + case R_ARM_LDR_SB_G2: + case R_ARM_LDRS_SB_G0: + case R_ARM_LDRS_SB_G1: + case R_ARM_LDRS_SB_G2: + case R_ARM_LDC_SB_G0: + case R_ARM_LDC_SB_G1: + case R_ARM_LDC_SB_G2: + case R_ARM_MOVW_BREL_NC: + case R_ARM_MOVT_BREL: + case R_ARM_MOVW_BREL: + + case R_ARM_THM_ABS5: + case R_ARM_THM_PC8: + case R_ARM_THM_SWI8: + case R_ARM_THM_XPC22: + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVW_PREL_NC: + case R_ARM_THM_MOVT_PREL: + case R_ARM_THM_JUMP19: + case R_ARM_THM_JUMP6: + case R_ARM_THM_ALU_PREL_11_0: + case R_ARM_THM_PC12: + case R_ARM_THM_MOVW_BREL_NC: + case R_ARM_THM_MOVT_BREL: + case R_ARM_THM_MOVW_BREL: + case R_ARM_THM_JUMP11: + case R_ARM_THM_JUMP8: + case R_ARM_THM_TLS_DESCSEQ16: + case R_ARM_THM_TLS_DESCSEQ32: + case R_ARM_THM_RPC22: + + case R_ARM_TLS_GOTDESC: + case R_ARM_TLS_CALL: + case R_ARM_TLS_DESCSEQ: + case R_ARM_THM_TLS_CALL: + case R_ARM_PLT32_ABS: + case R_ARM_GOT_ABS: + case R_ARM_GOT_PREL: + case R_ARM_GOT_BREL12: + case R_ARM_GOTOFF12: + case R_ARM_GOTRELAX: + case R_ARM_GNU_VTENTRY: + case R_ARM_GNU_VTINHERIT: + case R_ARM_TLS_GD32: + case R_ARM_TLS_LDM32: + case R_ARM_TLS_LDO32: + case R_ARM_TLS_LE32: + case R_ARM_TLS_LDO12: + case R_ARM_TLS_LE12: + case R_ARM_TLS_IE12GP: + case R_ARM_ME_TOO: + case R_ARM_RXPC25: + case R_ARM_RSBREL32: + case R_ARM_RREL32: + case R_ARM_RABS32: + case R_ARM_RPC24: + case R_ARM_RBASE: + default: + reloc.type = UnhandledRelocation; + relocTypes.insert(reloc.nativeType); + break; + } + } + for (auto& reloc : relocTypes) + LogWarn("Unsupported ELF relocation: %s", GetRelocationString((ElfArmRelocationType)reloc)); + return true; + } +}; + + +class ArmMachORelocationHandler: public RelocationHandler +{ +public: + virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, + uint8_t* dest, size_t len) override + { + auto info = reloc->GetInfo(); + if (info.nativeType == BINARYNINJA_MANUAL_RELOCATION) + { // Magic number defined in MachOView.cpp for tagged pointers + *(uint32_t*)dest = (uint32_t)info.target; + } + + return true; + } + + virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override + { + (void)view; + (void)arch; + set<uint64_t> relocTypes; + for (auto& reloc: result) + { + reloc.type = UnhandledRelocation; + relocTypes.insert(reloc.nativeType); + } + for (auto& reloc : relocTypes) + LogWarn("Unsupported Mach-O relocation %s", GetRelocationString((MachoArmRelocationType)reloc)); + return false; + }; +}; + + +class ArmPERelocationHandler: public RelocationHandler +{ +public: + virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override + { + // Note: info.base contains preferred base address and the base where the image is actually loaded + (void)view; + (void)arch; + (void)len; + uint64_t* data64 = (uint64_t*)dest; + uint32_t* data32 = (uint32_t*)dest; + uint16_t* data16 = (uint16_t*)dest; + auto info = reloc->GetInfo(); + if (info.size == 8) + { + data64[0] += info.base; + } + else if (info.size == 4) + { + data32[0] += (uint32_t)info.base; + } + else if (info.size == 2) + { + if (info.nativeType == PE_IMAGE_REL_BASED_HIGH) + { + data16[0] = data16[0] + (uint16_t)(info.base >> 16); + } + else if (info.nativeType == PE_IMAGE_REL_BASED_LOW) + { + data16[0] = data16[0] + (uint16_t)(info.base & 0xffff); + } + } + return true; + } + + virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override + { + (void)view; + (void)arch; + set<uint64_t> relocTypes; + for (auto& reloc: result) + { + switch (reloc.nativeType) + { + case PE_IMAGE_REL_BASED_ABSOLUTE: + reloc.type = IgnoredRelocation; + break; + case PE_IMAGE_REL_BASED_HIGHLOW: + reloc.size = 4; + break; + case PE_IMAGE_REL_BASED_DIR64: + reloc.size = 8; + break; + case PE_IMAGE_REL_BASED_HIGH: + reloc.size = 2; + break; + case PE_IMAGE_REL_BASED_LOW: + reloc.size = 2; + break; + default: + // By default, PE relocations are correct when not rebased. + // Upon rebasing, support would need to be added to correctly process the relocation + reloc.type = UnhandledRelocation; + relocTypes.insert(reloc.nativeType); + } + } + for (auto& reloc : relocTypes) + LogWarn("Unsupported PE relocation %s", GetRelocationString((PeRelocationType)reloc)); + return false; + } + + virtual size_t GetOperandForExternalRelocation(const uint8_t* data, uint64_t addr, size_t length, + Ref<LowLevelILFunction> il, Ref<Relocation> relocation) override + { + (void)data; + (void)addr; + (void)length; + (void)il; + (void)relocation; + return BN_AUTOCOERCE_EXTERN_PTR; + } +}; + +class ArmCOFFRelocationHandler: public RelocationHandler +{ +public: + virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override + { + // Note: info.base contains preferred base address and the base where the image is actually loaded + (void)view; + (void)arch; + (void)len; + BNRelocationInfo info = reloc->GetInfo(); + uint64_t target = info.target; + // uint64_t pc = info.pcRelative ? reloc->GetAddress() : 0; + uint64_t base = info.base; + if (! info.baseRelative) + target -= base; + uint64_t address = info.address; + uint32_t* dest32 = (uint32_t*)dest; + uint16_t* dest16 = (uint16_t*)dest; + // (void)pc; + // (void)base; + (void)dest16; + + Ref<Architecture> associatedArch = arch->GetAssociatedArchitectureByAddress(address); + +#ifdef DEBUG_COFF + DEBUG_COFF("COFF ARCH %s: arch: %s (%s @ %#" PRIx64 ") %s relocation at %#" PRIx64 " len: %zu info.size: %zu addend: %zu pc rel: %s base rel: %s target: %#" PRIx64 " base: %#" PRIx64, + __func__, + arch->GetName().c_str(), + associatedArch ? associatedArch->GetName().c_str() : "<none>", + info.address, + GetRelocationString((PeArmRelocationType)info.nativeType), + reloc->GetAddress(), + len, + info.size, + info.addend, + info.pcRelative ? "yes" : "no", + info.baseRelative ? "yes" : "no", + info.target, + info.base + ); +#endif /* DEBUG_COFF */ + + if (len < info.size) + { + return false; + } + + //auto swap = [&arch](uint32_t x) { return (arch->GetEndianness() == LittleEndian)? x : bswap32(x); }; + switch (info.nativeType) + { + case PE_IMAGE_REL_THUMB_MOV32: + { + enum _mov_type : uint16_t + { + MOVW = 0b100100, + MOVT = 0b101100, + }; + #pragma pack(push,1) + union _mov + { + uint32_t word; + struct { + uint32_t imm4:4; + uint32_t bits_hi_4_6:3; + uint32_t is_movt_flag:1; + uint32_t bits_hi_8_9:2; + uint32_t imm1:1; + uint32_t bits_hi_11_15:5; + + uint32_t imm8:8; + uint32_t rd:4; + uint32_t imm3:3; + uint32_t bit_lo_15:1; + }; + struct { + // MOVW + // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // 1 1 1 1 0 i 1 0 0 1 0 0 imm4 0 imm3 Rd imm8 + // MOVT + // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // 1 1 1 1 0 i 1 0 1 1 0 0 imm4 0 imm3 Rd imm8 + uint32_t _imm4:4; + uint32_t group2_4:6; // MOVW: 0b100100 (0x24) MOVT: 0b101100 (0x2c) + uint32_t _imm1:1; + uint32_t group2_11:5; + + uint32_t _imm8:8; + uint32_t _rd:4; + uint32_t _imm3:3; + uint32_t group1_15:1; + }; + }; + struct _target + { + + uint16_t imm8:8; + uint16_t imm3:3; + uint16_t imm1:1; + uint16_t imm4:4; + }; + #pragma pack(pop) + _mov* movw = (_mov*)dest32; + if (movw->is_movt_flag != 0) //movw->group2_4 != MOVW) + { + LogWarn("Expected MOVW in 0x%08" PRIx32 " (0x%" PRIx16 ") at 0x%" PRIx64 " but found 0x%" PRIx32 " (0x%" PRIx32 ") movt_flag: %d", + movw->word, MOVW, address, movw->group2_4, *dest32, movw->is_movt_flag); + } + _mov* movt = (_mov*)dest32 + 1; + if (movt->is_movt_flag != 1) // movt->group2_4 != MOVT) + { + LogWarn("Expected MOVT in 0x%08" PRIx32 " (0x%" PRIx16 ") at 0x%" PRIx64 " but found 0x%" PRIx32 " (0x%" PRIx32 ") movt_flag: %d", + movt->word, MOVT, address + 4, movt->group2_4, *(dest32 + 1), movt->is_movt_flag); + } + + _target *targetHiLo = (_target*)⌖ + + // This could be done more efficiently with shifts, ands, and ors, but that's the compiler's job + movw->imm8 = targetHiLo[0].imm8; + movw->imm3 = targetHiLo[0].imm3; + movw->imm1 = targetHiLo[0].imm1; + movw->imm4 = targetHiLo[0].imm4; + + movt->imm8 = targetHiLo[1].imm8; + movt->imm3 = targetHiLo[1].imm3; + movt->imm1 = targetHiLo[1].imm1; + movt->imm4 = targetHiLo[1].imm4; +#ifdef DEBUG_COFF + DEBUG_COFF( + "COFF arm %s: address: 0x%" PRIx64 " %s %s/%s target: 0x%" PRIx64 + ", base: 0x%" PRIx64 + ", addend: %zu", + __func__, + address, + GetRelocationString((PeArmRelocationType) info.nativeType), + movw->is_movt_flag ? "MOVT" : "MOVW", + movt->is_movt_flag ? "MOVT" : "MOVW", + target, + info.base, info.addend + ); +#endif /* DEBUG_COFF */ + break; + } + case PE_IMAGE_REL_THUMB_BRANCH20: + case PE_IMAGE_REL_THUMB_BRANCH24: + case PE_IMAGE_REL_THUMB_BLX23: + { + // Adapted from R_ARM_THM_CALL & R_ARM_THM_JUMP24 cases of ArmElfRelocationHandler::ApplyRelocation + // TODO: not portable + // ^^^^^^^^^^^^ I believe this is because the bit-field structs will break on big-endian hosts? + #pragma pack(push, 1) + // Unions cover all of b (Encoding T4), bl (Encoding T1), and blx (Encoding T2) + // conditional b (Encoding T3) only uses the low 6 bits of offHi, upper 4 are cond + union _thumb32_bl_hw1 { + uint16_t word; + union { + struct { + uint16_t offHi:10; // 21-12 + uint16_t sign:1; // 31-24 + uint16_t group:5; + }; + struct { + uint16_t offHi:6; // 17-12 + uint16_t cond:4; + uint16_t sign:1; // 31-24 + uint16_t group:5; + } b_cond; + }; + }; + + union _thumb32_bl_hw2 { + uint16_t word; + struct { + uint16_t offLo:11; //b11-1 10-0 + uint16_t j2:1; //b12 11 + uint16_t not_blx:1; //b13 12 + uint16_t j1:1; //b14 13 + uint16_t branch_and_link:1; //b15 14 (i2) + uint16_t i1:1; //b16 15 + }; + struct { + uint16_t offLo:11; //b11-1 10-0 + uint16_t j2:1; //b12 11 + uint16_t not_conditional:1; //b13 12 + uint16_t j1:1; //b14 13 + uint16_t branch_and_link:1; //b15 14 (i2) + uint16_t i1:1; //b16 15 + } b_cond; + + }; + #pragma pack(pop) + + _thumb32_bl_hw1* bl_hw1 = (_thumb32_bl_hw1*)dest16; + _thumb32_bl_hw2* bl_hw2 = (_thumb32_bl_hw2*)(dest16 + 1); + +#ifdef DEBUG_COFF + uint32_t old_value = *dest32; + uint16_t old_value1 = bl_hw1->word; + uint16_t old_value2 = bl_hw2->word; +#endif /* DEBUG_COFF */ + + int32_t curTarget = (bl_hw2->offLo << 1) | (bl_hw1->offHi << 12) | (bl_hw1->sign ? (0xffc << 20) : 0); + int32_t newTarget = (int32_t)((target + (info.implicitAddend ? curTarget : info.addend)) - address); + bl_hw1->sign = newTarget < 0 ? 1 : 0; + + if (!bl_hw2->branch_and_link && !bl_hw2->b_cond.not_conditional) + // In practice, this probably makes no difference, but it is correct for conditional b instructions + bl_hw1->b_cond.offHi = (newTarget >> 12) & ((1 << 6) - 1); + else + bl_hw1->offHi = (newTarget >> 12) & ((1 << 10) - 1); + bl_hw2->offLo = (newTarget >> 1) & ((1 << 11) - 1); + +#ifdef DEBUG_COFF + bool is_conditional_branch = !bl_hw2->branch_and_link && !bl_hw2->b_cond.not_conditional; + DEBUG_COFF( + "COFF thumb2 %s: %sbranch%s, target: 0x%" PRIx64 + ", curTarget: 0x%" PRIx32 + ", newTarget: 0x%" PRIx32 + ", actual new target: 0x%" PRIx32 + ", address: 0x%" PRIx64 + ", base: 0x%" PRIx64 + ", old/new value: 0x%" PRIx32 "/0x%" PRIx32 ":0x%" PRIx16 " 0x%" PRIx16 + " sizeof(%zu %zu)" + , + __func__, + is_conditional_branch ? "conditional " : + bl_hw2->branch_and_link ? "linking " : "", + (bl_hw2->branch_and_link && !bl_hw2->not_blx) ? " and exchange" : "", + target, curTarget, newTarget, + (uint32_t) ((uint32_t) address + newTarget), + address, info.base, old_value, *dest32, old_value1, old_value2, + sizeof(*bl_hw1), sizeof(*bl_hw2) + ); +#endif /* DEBUG_COFF */ + + break; + } + case PE_IMAGE_REL_THUMB_UNUSED: + break; + + case PE_IMAGE_REL_ARM_ABSOLUTE: + break; + case PE_IMAGE_REL_ARM_BRANCH11: + case PE_IMAGE_REL_ARM_BLX11: + // obsolete: only < ARMv7 + break; + case PE_IMAGE_REL_ARM_BRANCH24: + case PE_IMAGE_REL_ARM_BLX24: + { + struct _arm_b_bl_blx { + union { + uint32_t word; + struct { + uint32_t imm24:24; + uint32_t bit_24_blx_H:1; + uint32_t bits_25_27:3; + uint32_t cond:4; + }; + }; + }; + union _target { + int32_t word; + struct { + uint32_t lo_bit:1; + uint32_t H_bit:1; + uint32_t imm24:24; + uint32_t unused:6; + }; + }; + + _arm_b_bl_blx* bl = (_arm_b_bl_blx*)dest; + int32_t curTarget = bl->imm24 << 2; + + if (bl->cond == 0xf) + { + // BLX is unconditional, and incorporates one more bit into the target address, + // to allow for 2-byte aligned thumb target offsets. + // TODO: determine whether the target address should have its low bit set after relocation + curTarget |= bl->bit_24_blx_H << 1; + } + + _target newTarget; + newTarget.word = (int32_t)((target + (info.implicitAddend ? curTarget : info.addend)) - address); + bl->imm24 = newTarget.imm24; + if (bl->cond == 0xf) + { + bl->bit_24_blx_H = newTarget.H_bit; + } +#ifdef DEBUG_COFF + bool is_conditional_branch = bl->cond != 0xff; + DEBUG_COFF( + "COFF arm %s: address: 0x%" PRIx64 " %s %sbranch, target: 0x%" PRIx64 + ", curTarget: 0x%" PRIx32 + ", newTarget: 0x%" PRIx32 + ", actual new target: 0x%" PRIx32 + ", base: 0x%" PRIx64 + ", addend: %zu", + __func__, + address, + GetRelocationString((PeArmRelocationType) info.nativeType), + is_conditional_branch ? "conditional " : "", + target, curTarget, newTarget.word, + (uint32_t) ((uint32_t) address + newTarget.word), + info.base, info.addend + ); +#endif /* DEBUG_COFF */ + break; + } + case PE_IMAGE_REL_ARM_MOV32: + { + #pragma pack(push,1) + union _mov + { + uint32_t word; + struct { + // MOVW + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // cond 0 0 1 1 0 0 0 0 imm4 Rd imm12 + // MOVT + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // cond 0 0 1 1 0 1 0 0 imm4 Rd imm12 + uint32_t imm12:12; + uint32_t rd:4; + uint32_t imm4:4; + uint32_t bits_20_21:2; + uint32_t is_movt_flag:1; + uint32_t bit_23:1; + uint32_t bits_24_27:4; + uint32_t cond:4; + + }; + }; + struct _target + { + uint16_t imm12:12; + uint16_t imm4:4; + }; + #pragma pack(pop) + + _mov* mov = (_mov*)dest32; + int32_t newTarget = (target + (info.implicitAddend ? (mov->imm4 << 12 | mov->imm12) : info.addend)); + _target *targetHiLo = (_target*)&newTarget; + + mov->imm12 = targetHiLo[mov->is_movt_flag].imm12; + mov->imm4 = targetHiLo[mov->is_movt_flag].imm4; + break; + } + case PE_IMAGE_REL_ARM_PAIR: + // TODO + break; + case PE_IMAGE_REL_ARM_SECTION: + // dest16[0] = info.sectionIndex + 1; + break; + case PE_IMAGE_REL_ARM_SECREL: + { + // auto sections = view->GetSectionsAt(info.target); + // if (sections.size() > 0) + // { + // dest32[0] = info.target - sections[0]->GetStart(); + // } + break; + } + case PE_IMAGE_REL_ARM_ADDR32: + case PE_IMAGE_REL_ARM_ADDR32NB: + default: + return RelocationHandler::ApplyRelocation(view, arch, reloc, dest, len); + } + return true; + } + + virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override + { + (void)view; + (void)arch; + set<uint64_t> relocTypes; + for (auto& reloc: result) + { +#ifdef DEBUG_COFF + DEBUG_COFF("COFF %s relocation %s at 0x%" PRIx64, __func__, GetRelocationString((PeArmRelocationType)reloc.nativeType), reloc.address); +#endif + switch (reloc.nativeType) + { + case PE_IMAGE_REL_ARM_BRANCH24: + case PE_IMAGE_REL_ARM_BRANCH11: + case PE_IMAGE_REL_ARM_BLX24: + case PE_IMAGE_REL_ARM_BLX11: + reloc.pcRelative = true; + reloc.baseRelative = false; + reloc.size = 4; + reloc.addend = -8; + break; + case PE_IMAGE_REL_THUMB_BRANCH20: + case PE_IMAGE_REL_THUMB_BRANCH24: + case PE_IMAGE_REL_THUMB_BLX23: + reloc.pcRelative = true; + reloc.baseRelative = false; + reloc.size = 4; + reloc.addend = -4; + break; + case PE_IMAGE_REL_THUMB_MOV32: + case PE_IMAGE_REL_ARM_MOV32: + reloc.pcRelative = false; + reloc.baseRelative = true; + reloc.size = 4; + break; + case PE_IMAGE_REL_ARM_ABSOLUTE: + reloc.type = IgnoredRelocation; + break; + case PE_IMAGE_REL_ARM_ADDR32: + reloc.pcRelative = false; + reloc.baseRelative = true; + reloc.size = 4; + reloc.addend = 0; + break; + case PE_IMAGE_REL_ARM_ADDR32NB: // TODO: CHECK NB case + reloc.pcRelative = false; + reloc.baseRelative = false; + reloc.size = 4; + reloc.addend = 0; + break; + case PE_IMAGE_REL_ARM_REL32: + reloc.pcRelative = true; + reloc.baseRelative = false; + reloc.size = 4; + reloc.addend = -4; + break; + case PE_IMAGE_REL_ARM_SECTION: + // The 16-bit section index of the section that contains the target. This is used to support debugging information. + // TODO: is the section index 0-based or 1-based? + reloc.baseRelative = false; + reloc.size = 2; + reloc.addend = 0; + break; + case PE_IMAGE_REL_ARM_SECREL: + // The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage. reloc.baseRelative = false; + reloc.baseRelative = false; + reloc.size = 4; + reloc.addend = 0; + break; + default: + reloc.type = UnhandledRelocation; + relocTypes.insert(reloc.nativeType); + break; + } + } + for (auto& reloc : relocTypes) + LogWarn("Unsupported COFF relocation %s", GetRelocationString((PeArmRelocationType)reloc)); + return true; + } +}; + + +static void RegisterArmArchitecture(const char* armName, const char* thumbName, BNEndianness endian) +{ + ArmCommonArchitecture* armv7 = new Armv7Architecture(armName, endian); + ArmCommonArchitecture* thumb2 = InitThumb2Architecture(thumbName, endian); + armv7->SetArmAndThumbArchitectures(armv7, thumb2); + thumb2->SetArmAndThumbArchitectures(armv7, thumb2); + + Architecture::Register(armv7); + Architecture::Register(thumb2); + + // Register calling convention + Ref<CallingConvention> conv; + conv = new ArmCallingConvention(armv7); + armv7->RegisterCallingConvention(conv); + armv7->SetDefaultCallingConvention(conv); + armv7->SetCdeclCallingConvention(conv); + armv7->SetFastcallCallingConvention(conv); + armv7->SetStdcallCallingConvention(conv); + + conv = new LinuxArmv7SystemCallConvention(armv7); + armv7->RegisterCallingConvention(conv); + + conv = new ArmCallingConvention(thumb2); + thumb2->RegisterCallingConvention(conv); + thumb2->SetDefaultCallingConvention(conv); + thumb2->SetCdeclCallingConvention(conv); + thumb2->SetFastcallCallingConvention(conv); + thumb2->SetStdcallCallingConvention(conv); + + conv = new LinuxArmv7SystemCallConvention(thumb2); + thumb2->RegisterCallingConvention(conv); + + thumb2->RegisterFunctionRecognizer(new Thumb2ImportedFunctionRecognizer()); + + // Register the architectures with the binary format parsers so that they know when to use + // these architectures for disassembling an executable file + BinaryViewType::RegisterArchitecture("Mach-O", 0xc, endian, armv7); + BinaryViewType::RegisterArchitecture("ELF", 0x28, endian, armv7); + BinaryViewType::RegisterArchitecture("COFF", 0x1c0, endian, armv7); // ARM + BinaryViewType::RegisterArchitecture("COFF", 0x1c2, endian, thumb2); // THUMB + BinaryViewType::RegisterArchitecture("COFF", 0x1c4, endian, thumb2); // ARMNT (ARM Thumb-2) + BinaryViewType::RegisterArchitecture("PE", 0x1c0, endian, armv7); // ARM + BinaryViewType::RegisterArchitecture("PE", 0x1c2, endian, armv7); // THUMB + BinaryViewType::RegisterArchitecture("PE", 0x1c4, endian, armv7); // ARMv7 + + armv7->RegisterRelocationHandler("ELF", new ArmElfRelocationHandler()); + armv7->RegisterRelocationHandler("Mach-O", new ArmMachORelocationHandler()); + armv7->RegisterRelocationHandler("PE", new ArmPERelocationHandler()); + armv7->RegisterRelocationHandler("COFF", new ArmCOFFRelocationHandler()); + + thumb2->RegisterRelocationHandler("ELF", new ArmElfRelocationHandler()); + thumb2->RegisterRelocationHandler("Mach-O", new ArmMachORelocationHandler()); + thumb2->RegisterRelocationHandler("COFF", new ArmCOFFRelocationHandler()); + + armv7->GetStandalonePlatform()->AddRelatedPlatform(thumb2, thumb2->GetStandalonePlatform()); + thumb2->GetStandalonePlatform()->AddRelatedPlatform(armv7, armv7->GetStandalonePlatform()); +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifndef DEMO_VERSION + BINARYNINJAPLUGIN void CorePluginDependencies() + { + AddOptionalPluginDependency("view_elf"); + AddOptionalPluginDependency("view_macho"); + AddOptionalPluginDependency("view_pe"); + } +#endif + +#ifdef DEMO_VERSION + bool ARMv7PluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + RegisterArmArchitecture("armv7", "thumb2", LittleEndian); + RegisterArmArchitecture("armv7eb", "thumb2eb", BigEndian); + return true; + } +} |
