summaryrefslogtreecommitdiff
path: root/arch/mips/arch_mips.cpp
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2024-03-05 19:50:13 -0500
committerRusty Wagner <rusty.wagner@gmail.com>2024-03-05 20:34:34 -0500
commite093c21ed880ac3eb72119be15093ee04f8ce299 (patch)
tree9f720ebdc0ae415734b1199ed341668c69710a94 /arch/mips/arch_mips.cpp
parent0609276712622908254065546102381466033141 (diff)
Move architecture modules into the API repo
Diffstat (limited to 'arch/mips/arch_mips.cpp')
-rw-r--r--arch/mips/arch_mips.cpp2166
1 files changed, 2166 insertions, 0 deletions
diff --git a/arch/mips/arch_mips.cpp b/arch/mips/arch_mips.cpp
new file mode 100644
index 00000000..0d01862d
--- /dev/null
+++ b/arch/mips/arch_mips.cpp
@@ -0,0 +1,2166 @@
+#define _CRT_SECURE_NO_WARNINGS
+#define NOMINMAX
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "binaryninjaapi.h"
+#include "lowlevelilinstruction.h"
+#include "mips.h"
+#include "il.h"
+
+using namespace BinaryNinja;
+using namespace mips;
+using namespace std;
+
+
+#if defined(_MSC_VER)
+#define snprintf _snprintf
+#endif
+
+uint32_t bswap32(uint32_t x)
+{
+ return ((x << 24) & 0xff000000 ) |
+ ((x << 8) & 0x00ff0000 ) |
+ ((x >> 8) & 0x0000ff00 ) |
+ ((x >> 24) & 0x000000ff );
+}
+
+uint64_t bswap64(uint64_t x)
+{
+ return ((x << 56) & 0xff00000000000000UL) |
+ ((x << 40) & 0x00ff000000000000UL) |
+ ((x << 24) & 0x0000ff0000000000UL) |
+ ((x << 8) & 0x000000ff00000000UL) |
+ ((x >> 8) & 0x00000000ff000000UL) |
+ ((x >> 24) & 0x0000000000ff0000UL) |
+ ((x >> 40) & 0x000000000000ff00UL) |
+ ((x >> 56) & 0x00000000000000ffUL);
+}
+
+enum ElfMipsRelocationType : uint32_t
+{
+ R_MIPS_NONE = 0,
+ R_MIPS_16 = 1,
+ R_MIPS_32 = 2,
+ R_MIPS_REL32 = 3,
+ R_MIPS_26 = 4,
+ R_MIPS_HI16 = 5,
+ R_MIPS_LO16 = 6,
+ R_MIPS_GPREL16 = 7,
+ R_MIPS_LITERAL = 8,
+ R_MIPS_GOT16 = 9,
+ R_MIPS_PC16 = 10,
+ R_MIPS_CALL16 = 11,
+ R_MIPS_GPREL32 = 12,
+ // The remaining relocs are defined on Irix, although they are not
+ // in the MIPS ELF ABI.
+ R_MIPS_UNUSED1 = 13,
+ R_MIPS_UNUSED2 = 14,
+ R_MIPS_UNUSED3 = 15,
+ R_MIPS_SHIFT5 = 16,
+ R_MIPS_SHIFT6 = 17,
+ R_MIPS_64 = 18,
+ R_MIPS_GOT_DISP = 19,
+ R_MIPS_GOT_PAGE = 20,
+ R_MIPS_GOT_OFST = 21,
+ //The following two relocation types are specified in the MIPS ABI
+ //conformance guide version 1.2 but not yet in the psABI.
+ R_MIPS_GOTHI16 = 22,
+ R_MIPS_GOTLO16 = 23,
+ R_MIPS_SUB = 24,
+ R_MIPS_INSERT_A = 25,
+ R_MIPS_INSERT_B = 26,
+ R_MIPS_DELETE = 27,
+ R_MIPS_HIGHER = 28,
+ R_MIPS_HIGHEST = 29,
+ // The following two relocation types are specified in the MIPS ABI
+ // conformance guide version 1.2 but not yet in the psABI.
+ R_MIPS_CALLHI16 = 30,
+ R_MIPS_CALLLO16 = 31,
+ R_MIPS_SCN_DISP = 32,
+ R_MIPS_REL16 = 33,
+ R_MIPS_ADD_IMMEDIATE = 34,
+ R_MIPS_PJUMP = 35,
+ R_MIPS_RELGOT = 36,
+ R_MIPS_JALR = 37,
+ R_MIPS_TLS_DTPMOD32 = 38, // Module number 32 bit
+ R_MIPS_TLS_DTPREL32 = 39, // Module-relative offset 32 bit
+ R_MIPS_TLS_DTPMOD64 = 40, // Module number 64 bit
+ R_MIPS_TLS_DTPREL64 = 41, // Module-relative offset 64 bit
+ R_MIPS_TLS_GD = 42, // 16 bit GOT offset for GD
+ R_MIPS_TLS_LDM = 43, // 16 bit GOT offset for LDM
+ R_MIPS_TLS_DTPREL_HI16 = 44, // Module-relative offset, high 16 bits
+ R_MIPS_TLS_DTPREL_LO16 = 45, // Module-relative offset, low 16 bits
+ R_MIPS_TLS_GOTTPREL = 46, // 16 bit GOT offset for IE
+ R_MIPS_TLS_TPREL32 = 47, // TP-relative offset, 32 bit
+ R_MIPS_TLS_TPREL64 = 48, // TP-relative offset, 64 bit
+ R_MIPS_TLS_TPREL_HI16 = 49, // TP-relative offset, high 16 bits
+ R_MIPS_TLS_TPREL_LO16 = 50, // TP-relative offset, low 16 bits
+ R_MIPS_GLOB_DAT = 51,
+
+ // This range is reserved for vendor specific relocations.
+ R_MIPS_LOVENDOR = 100,
+ R_MIPS64_COPY = 125,
+ R_MIPS_COPY = 126,
+ R_MIPS_JUMP_SLOT = 127,
+ R_MIPS_HIVENDOR = 127
+};
+
+
+static const char* GetRelocationString(ElfMipsRelocationType rel)
+{
+ static map<ElfMipsRelocationType, const char*> relocTable = {
+ { R_MIPS_NONE, "R_MIPS_NONE"},
+ { R_MIPS_16, "R_MIPS_16"},
+ { R_MIPS_32, "R_MIPS_32"},
+ { R_MIPS_REL32, "R_MIPS_REL32"},
+ { R_MIPS_26, "R_MIPS_26"},
+ { R_MIPS_HI16, "R_MIPS_HI16"},
+ { R_MIPS_LO16, "R_MIPS_LO16"},
+ { R_MIPS_GPREL16, "R_MIPS_GPREL16"},
+ { R_MIPS_LITERAL, "R_MIPS_LITERAL"},
+ { R_MIPS_GOT16, "R_MIPS_GOT16"},
+ { R_MIPS_PC16, "R_MIPS_PC16"},
+ { R_MIPS_CALL16, "R_MIPS_CALL16"},
+ { R_MIPS_GPREL32, "R_MIPS_GPREL32"},
+ { R_MIPS_UNUSED1, "R_MIPS_UNUSED1"},
+ { R_MIPS_UNUSED2, "R_MIPS_UNUSED2"},
+ { R_MIPS_UNUSED3, "R_MIPS_UNUSED3"},
+ { R_MIPS_SHIFT5, "R_MIPS_SHIFT5"},
+ { R_MIPS_SHIFT6, "R_MIPS_SHIFT6"},
+ { R_MIPS_64, "R_MIPS_64"},
+ { R_MIPS_GOT_DISP, "R_MIPS_GOT_DISP"},
+ { R_MIPS_GOT_PAGE, "R_MIPS_GOT_PAGE"},
+ { R_MIPS_GOT_OFST, "R_MIPS_GOT_OFST"},
+ { R_MIPS_GOTHI16, "R_MIPS_GOTHI16"},
+ { R_MIPS_GOTLO16, "R_MIPS_GOTLO16"},
+ { R_MIPS_SUB, "R_MIPS_SUB"},
+ { R_MIPS_INSERT_A, "R_MIPS_INSERT_A"},
+ { R_MIPS_INSERT_B, "R_MIPS_INSERT_B"},
+ { R_MIPS_DELETE, "R_MIPS_DELETE"},
+ { R_MIPS_HIGHER, "R_MIPS_HIGHER"},
+ { R_MIPS_HIGHEST, "R_MIPS_HIGHEST"},
+ { R_MIPS_CALLHI16, "R_MIPS_CALLHI16"},
+ { R_MIPS_CALLLO16, "R_MIPS_CALLLO16"},
+ { R_MIPS_SCN_DISP, "R_MIPS_SCN_DISP"},
+ { R_MIPS_REL16, "R_MIPS_REL16"},
+ { R_MIPS_ADD_IMMEDIATE, "R_MIPS_ADD_IMMEDIATE"},
+ { R_MIPS_PJUMP, "R_MIPS_PJUMP"},
+ { R_MIPS_RELGOT, "R_MIPS_RELGOT"},
+ { R_MIPS_JALR, "R_MIPS_JALR"},
+ { R_MIPS_TLS_DTPMOD32, "R_MIPS_TLS_DTPMOD32"},
+ { R_MIPS_TLS_DTPREL32, "R_MIPS_TLS_DTPREL32"},
+ { R_MIPS_TLS_DTPMOD64, "R_MIPS_TLS_DTPMOD64"},
+ { R_MIPS_TLS_DTPREL64, "R_MIPS_TLS_DTPREL64"},
+ { R_MIPS_TLS_GD, "R_MIPS_TLS_GD"},
+ { R_MIPS_TLS_LDM, "R_MIPS_TLS_LDM"},
+ { R_MIPS_TLS_DTPREL_HI16, "R_MIPS_TLS_DTPREL_HI16"},
+ { R_MIPS_TLS_DTPREL_LO16, "R_MIPS_TLS_DTPREL_LO16"},
+ { R_MIPS_TLS_GOTTPREL, "R_MIPS_TLS_GOTTPREL"},
+ { R_MIPS_TLS_TPREL32, "R_MIPS_TLS_TPREL32"},
+ { R_MIPS_TLS_TPREL64, "R_MIPS_TLS_TPREL64"},
+ { R_MIPS_TLS_TPREL_HI16, "R_MIPS_TLS_TPREL_HI16"},
+ { R_MIPS_TLS_TPREL_LO16, "R_MIPS_TLS_TPREL_LO16"},
+ { R_MIPS_GLOB_DAT, "R_MIPS_GLOB_DAT"},
+ { R_MIPS_LOVENDOR, "R_MIPS_LOVENDOR"},
+ { R_MIPS64_COPY, "R_MIPS64_COPY"},
+ { R_MIPS_COPY, "R_MIPS_COPY"},
+ { R_MIPS_JUMP_SLOT, "R_MIPS_JUMP_SLOT"},
+ { R_MIPS_HIVENDOR, "R_MIPS_HIVENDOR"}
+ };
+
+ if (relocTable.count(rel))
+ return relocTable.at(rel);
+ return "Unknown MIPS relocation";
+}
+
+class MipsArchitecture: public Architecture
+{
+protected:
+ size_t m_bits;
+ BNEndianness m_endian;
+ uint32_t m_enablePseudoOps;
+
+ virtual bool Disassemble(const uint8_t* data, uint64_t addr, size_t maxLen, Instruction& result)
+ {
+ memset(&result, 0, sizeof(result));
+ if (mips_decompose((uint32_t*)data, maxLen, &result, m_bits == 64 ? MIPS_64 : MIPS_32, addr, m_endian, m_enablePseudoOps) != 0)
+ return false;
+ return true;
+ }
+
+ virtual size_t GetAddressSize() const override
+ {
+ return m_bits / 8;
+ }
+
+ size_t InstructionHasBranchDelay(const Instruction& instr)
+ {
+ switch (instr.operation)
+ {
+ case MIPS_B:
+ case MIPS_BAL:
+ case MIPS_BEQ:
+ case MIPS_BEQL:
+ case MIPS_BEQZ:
+ case MIPS_BGEZ:
+ case MIPS_BGEZAL:
+ case MIPS_BGEZALL:
+ case MIPS_BGEZL:
+ case MIPS_BGTZ:
+ case MIPS_BGTZL:
+ case MIPS_BLEZ:
+ case MIPS_BLEZL:
+ case MIPS_BLTZ:
+ case MIPS_BLTZAL:
+ case MIPS_BLTZALL:
+ case MIPS_BLTZL:
+ case MIPS_BNE:
+ case MIPS_BNEL:
+ case MIPS_JR:
+ case MIPS_JR_HB:
+ case MIPS_J:
+ case MIPS_JAL:
+ case MIPS_JALR:
+ case MIPS_JALR_HB:
+ case MIPS_BC1F:
+ case MIPS_BC1FL:
+ case MIPS_BC1T:
+ case MIPS_BC1TL:
+ case MIPS_BC2FL:
+ case MIPS_BC2TL:
+ case MIPS_BC2F:
+ case MIPS_BC2T:
+ return 1;
+ default:
+ return 0;
+ }
+ }
+
+
+ bool InstructionIsUnalignedMemAccess(const Instruction& instr)
+ {
+ switch (instr.operation)
+ {
+ case MIPS_LWL:
+ case MIPS_LWR:
+ case MIPS_SWL:
+ case MIPS_SWR:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+
+ bool IsConditionalBranch(const Instruction& instr)
+ {
+ switch (instr.operation)
+ {
+ case MIPS_BEQZ:
+ case MIPS_BGEZ:
+ case MIPS_BGTZ:
+ case MIPS_BLEZ:
+ case MIPS_BLTZ:
+ case MIPS_BGEZL:
+ case MIPS_BGTZL:
+ case MIPS_BLEZL:
+ case MIPS_BLTZL:
+ case MIPS_BEQ:
+ case MIPS_BNE:
+ case MIPS_BEQL:
+ case MIPS_BNEL:
+ case MIPS_BC1F:
+ case MIPS_BC1FL:
+ case MIPS_BC1T:
+ case MIPS_BC1TL:
+ case MIPS_BC2FL:
+ case MIPS_BC2TL:
+ case MIPS_BC2F:
+ case MIPS_BC2T:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void SetInstructionInfoForInstruction(uint64_t addr, const Instruction& instr, InstructionInfo& result)
+ {
+ result.length = 4;
+
+ auto hasBranchDelay = InstructionHasBranchDelay(instr);
+
+ switch (instr.operation)
+ {
+ //case MIPS_JALX: //This case jumps to a different processor mode microMIPS32/MIPS32/MIPS16e
+ // break;
+ //Branch/jump and link immediate
+ case MIPS_BAL:
+ if (instr.operands[0].immediate != addr + 8)
+ result.AddBranch(CallDestination, instr.operands[0].immediate, nullptr, hasBranchDelay);
+ else
+ result.branchDelay = 1; // We have a "get pc" mnemonic; do nothing
+ break;
+
+ case MIPS_JAL:
+ result.AddBranch(CallDestination, instr.operands[0].immediate, nullptr, hasBranchDelay);
+ break;
+
+ //Jmp to register register value is unknown
+ case MIPS_JALR:
+ case MIPS_JALR_HB:
+ result.branchDelay = 1;
+ break;
+
+ case MIPS_BGEZAL:
+ case MIPS_BLTZAL:
+ case MIPS_BGEZALL:
+ case MIPS_BLTZALL:
+ result.AddBranch(CallDestination, instr.operands[1].immediate, nullptr, hasBranchDelay);
+ break;
+
+ //Unconditional branch and jump
+ case MIPS_B:
+ case MIPS_J:
+ result.AddBranch(UnconditionalBranch, instr.operands[0].immediate, nullptr, hasBranchDelay);
+ break;
+
+ //Conditional branch instructions
+ case MIPS_BEQZ:
+ case MIPS_BGEZ:
+ case MIPS_BGTZ:
+ case MIPS_BLEZ:
+ case MIPS_BLTZ:
+ case MIPS_BGEZL:
+ case MIPS_BGTZL:
+ case MIPS_BLEZL:
+ case MIPS_BLTZL:
+ result.AddBranch(TrueBranch, instr.operands[1].immediate, nullptr, hasBranchDelay);
+ //need to jump over the branch delay slot and current instruction
+ result.AddBranch(FalseBranch, addr + 8, nullptr, hasBranchDelay);
+ break;
+
+ case MIPS_BEQ:
+ case MIPS_BNE:
+ case MIPS_BEQL:
+ case MIPS_BNEL:
+ result.AddBranch(TrueBranch, instr.operands[2].immediate, nullptr, hasBranchDelay);
+ //need to jump over the branch delay slot and current instruction
+ result.AddBranch(FalseBranch, addr + 8, nullptr, hasBranchDelay);
+ break;
+
+ //Jmp reg isntructions, if they are jumping to the return address register then it is a function return
+ case MIPS_JR:
+ case MIPS_JR_HB:
+ if (instr.operands[0].reg == REG_RA)
+ result.AddBranch(FunctionReturn, 0, nullptr, hasBranchDelay);
+ else
+ result.AddBranch(UnresolvedBranch, 0, nullptr, hasBranchDelay);
+ break;
+ case MIPS_BC1F:
+ case MIPS_BC1FL:
+ case MIPS_BC1T:
+ case MIPS_BC1TL:
+ case MIPS_BC2FL:
+ case MIPS_BC2TL:
+ case MIPS_BC2F:
+ case MIPS_BC2T:
+ result.AddBranch(TrueBranch, instr.operands[0].immediate, nullptr, hasBranchDelay);
+ //need to jump over the branch delay slot and current instruction
+ result.AddBranch(FalseBranch, addr + 8, nullptr, hasBranchDelay);
+ break;
+
+ //Exception return instruction
+ case MIPS_ERET:
+ result.AddBranch(FunctionReturn, 0, nullptr, hasBranchDelay);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+public:
+ MipsArchitecture(const std::string& name, BNEndianness endian, size_t bits): Architecture(name), m_bits(bits), m_endian(endian)
+ {
+ Ref<Settings> settings = Settings::Instance();
+ m_enablePseudoOps = settings->Get<bool>("arch.mips.disassembly.pseudoOps") ? 1 : 0;
+ }
+
+ virtual BNEndianness GetEndianness() const override
+ {
+ return m_endian;
+ }
+
+ virtual size_t GetInstructionAlignment() const override
+ {
+ return 4;
+ }
+
+ virtual size_t GetMaxInstructionLength() const override
+ {
+ return 8; // To disassemble delay slots, allow two instructions
+ }
+
+ virtual size_t GetOpcodeDisplayLength() const override
+ {
+ return 4;
+ }
+
+ virtual bool CanAssemble() override
+ {
+ return true;
+ }
+
+ bool Assemble(const string& code, uint64_t addr, DataBuffer& result, string& errors) override
+ {
+ (void)addr;
+
+ int assembleResult;
+ char *instrBytes=NULL, *err=NULL;
+ int instrBytesLen=0, errLen=0;
+
+ BNLlvmServicesInit();
+
+ errors.clear();
+ const char* triple = "mips-pc-none-o32";
+ if (m_endian == LittleEndian)
+ triple = "mipsel-pc-none-o32";
+
+ assembleResult = BNLlvmServicesAssemble(code.c_str(), LLVM_SVCS_DIALECT_UNSPEC,
+ triple, 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;
+ }
+
+ bool InstructionIsBranchLikely(Instruction& instr)
+ {
+ switch (instr.operation)
+ {
+ case MIPS_BEQL:
+ case MIPS_BNEL:
+ case MIPS_BGTZL:
+ case MIPS_BGEZL:
+ case MIPS_BLTZL:
+ case MIPS_BLEZL:
+ case MIPS_BC1TL:
+ case MIPS_BC1FL:
+ case MIPS_BC2FL:
+ case MIPS_BC2TL:
+ case MIPS_BGEZALL:
+ case MIPS_BLTZALL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ virtual bool GetInstructionLowLevelIL(const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il) override
+ {
+ Instruction instr, secondInstr;
+ if (!Disassemble(data, addr, len, instr))
+ {
+ il.AddInstruction(il.Undefined());
+ return false;
+ }
+
+ if (InstructionHasBranchDelay(instr) == 1)
+ {
+ if (len < 8)
+ {
+ LogWarn("Can not lift instruction with delay slot @ 0x%08" PRIx64, addr);
+ return false;
+ }
+
+ if (!Disassemble(data + instr.size, addr + instr.size, len - instr.size, secondInstr))
+ {
+ il.AddInstruction(il.Undefined());
+ return false;
+ }
+
+ bool status = true;
+ bool isBranchLikely = InstructionIsBranchLikely(instr);
+ if (isBranchLikely)
+ {
+ InstructionInfo instrInfo;
+ LowLevelILLabel trueCode, falseCode;
+ SetInstructionInfoForInstruction(addr, instr, instrInfo);
+ il.AddInstruction(il.If(GetConditionForInstruction(il, instr, GetAddressSize()), trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.SetCurrentAddress(this, addr + instr.size);
+ GetLowLevelILForInstruction(this, addr + instr.size, il, secondInstr, GetAddressSize());
+ for (size_t i = 0; i < instrInfo.branchCount; i++)
+ {
+ if (instrInfo.branchType[i] == TrueBranch)
+ {
+ BNLowLevelILLabel* trueLabel = il.GetLabelForAddress(this, instrInfo.branchTarget[i]);
+ if (trueLabel)
+ il.AddInstruction(il.Goto(*trueLabel));
+ else
+ il.AddInstruction(il.Jump(il.ConstPointer(GetAddressSize(), instrInfo.branchTarget[i])));
+ break;
+ }
+ }
+ il.MarkLabel(falseCode);
+ }
+ else
+ {
+ size_t nop;
+
+ // ensure we have space to preserve one register in case the delay slot
+ // clobbers a value needed by the branch. this will be eliminated when
+ // normal LLIL is generated from Lifted IL if we don't need it
+ il.SetCurrentAddress(this, addr + instr.size);
+ nop = il.Nop();
+ il.AddInstruction(nop);
+
+ GetLowLevelILForInstruction(this, addr + instr.size, il, secondInstr, GetAddressSize());
+
+ LowLevelILInstruction delayed;
+ uint32_t clobbered = BN_INVALID_REGISTER;
+ size_t instrIdx = il.GetInstructionCount();
+ if (instrIdx != 0)
+ {
+ // FIXME: this assumes that the instruction in the delay slot
+ // only changed registers in the last IL instruction that it
+ // added -- strictly speaking we should be starting from the
+ // first instruction that could have been added and follow all
+ // paths to the end of that instruction.
+ delayed = il.GetInstruction(instrIdx - 1);
+ if ((delayed.operation == LLIL_SET_REG) && (delayed.address == (addr + instr.size)))
+ clobbered = delayed.GetDestRegister<LLIL_SET_REG>();
+ }
+
+ il.SetCurrentAddress(this, addr);
+
+ if ((instr.operation == MIPS_JR) && (instr.operands[0].reg == REG_T9) &&
+ (secondInstr.operation == MIPS_ADDIU) && (secondInstr.operands[0].reg == REG_SP) &&
+ (secondInstr.operands[1].reg == REG_SP) && (secondInstr.operands[2].immediate < 0x80000000))
+ {
+ il.AddInstruction(il.TailCall(il.Register(4, REG_T9)));
+ }
+ else
+ {
+ status = GetLowLevelILForInstruction(this, addr, il, instr, GetAddressSize());
+ }
+
+ if (clobbered != BN_INVALID_REGISTER)
+ {
+ // FIXME: this approach will break with any of the REG_SPLIT operations as well
+ // any use of partial registers -- this approach needs to be expanded substantially
+ // to be correct in the general case. also, it uses LLIL_TEMP(1) for the simple reason
+ // that the mips lifter only uses LLIL_TEMP(0) at the moment.
+ LowLevelILInstruction lifted = il.GetInstruction(instrIdx);
+ if ((lifted.operation == LLIL_IF || lifted.operation == LLIL_CALL) && (lifted.address == addr))
+ {
+ bool replace = false;
+
+ lifted.VisitExprs([&](const LowLevelILInstruction& expr) -> bool {
+ if (expr.operation == LLIL_REG && expr.GetSourceRegister<LLIL_REG>() == clobbered)
+ {
+ // Replace all reads from the clobbered register to a temp register
+ // that we're going to set (by replacing the earlier nop we added)
+ il.ReplaceExpr(expr.exprIndex, il.Register(expr.size, LLIL_TEMP(1)));
+ replace = true;
+ }
+ return true;
+ });
+
+ if (replace)
+ {
+ // Preserve the value of the clobbered register by replacing the LLIL_NOP
+ // instruction we added at the beginning with an assignment to the temp
+ // register we rewrote in the LLIL_IF condition expression
+ il.SetCurrentAddress(this, addr + instr.size);
+ il.ReplaceExpr(nop, il.SetRegister(delayed.size, LLIL_TEMP(1), il.Register(delayed.size, delayed.GetDestRegister<LLIL_SET_REG>())));
+ il.SetCurrentAddress(this, addr);
+ }
+ }
+ }
+ }
+
+ len = instr.size + secondInstr.size;
+ return status;
+ }
+ else if (InstructionIsUnalignedMemAccess(instr) && len >= 8
+ && Disassemble(data + 4, addr + 4, len - 4, secondInstr))
+ {
+ Instruction* left = nullptr;
+ Instruction* right = nullptr;
+ Instruction* base;
+ uint32_t addrToUse;
+ bool store = false;
+ bool proceed = true;
+
+ switch (instr.operation)
+ {
+ case MIPS_SWL:
+ store = true;
+ // fall through
+ case MIPS_LWL:
+ proceed = (secondInstr.operation == (store ? MIPS_SWR : MIPS_LWR));
+ left = &instr;
+ right = &secondInstr;
+ break;
+
+ case MIPS_SWR:
+ store = true;
+ // fall through
+ case MIPS_LWR:
+ proceed = (secondInstr.operation == (store ? MIPS_SWL : MIPS_LWL));
+ left = &secondInstr;
+ right = &instr;
+ break;
+
+ default:
+ proceed = false;
+ break;
+ }
+
+ proceed = proceed && (instr.operands[0].reg == secondInstr.operands[0].reg);
+
+ if (m_endian == BigEndian)
+ {
+ proceed = proceed && ((left->operands[1].immediate + 3) == right->operands[1].immediate);
+ addrToUse = (uint32_t)addr + ((&instr == left) ? 0 : 4);
+ base = left;
+ }
+ else
+ {
+ proceed = proceed && (left->operands[1].immediate == (right->operands[1].immediate + 3));
+ addrToUse = (uint32_t)addr + ((&instr == right) ? 0 : 4);
+ base = right;
+ }
+
+ if (proceed)
+ {
+ len = 8;
+ il.SetCurrentAddress(this, addrToUse);
+ base->operation = store ? MIPS_SW : MIPS_LW;
+ return GetLowLevelILForInstruction(this, addrToUse, il, *base, GetAddressSize());
+ }
+ }
+
+ len = instr.size;
+ return GetLowLevelILForInstruction(this, addr, il, instr, GetAddressSize());
+ }
+
+ 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;
+ }
+
+ virtual bool GetInstructionText(const uint8_t* data, uint64_t addr, size_t& len, vector<InstructionTextToken>& result) override
+ {
+ Instruction instr;
+ char operand[64];
+ char padding[9];
+ const char* reg = NULL;
+ if (!Disassemble(data, addr, len, instr))
+ return false;
+
+ len = instr.size;
+ memset(padding, 0x20, sizeof(padding));
+ const char* operation = get_operation(instr.operation);
+ 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);
+ for (size_t i = 0; i < MAX_OPERANDS; i++)
+ {
+ if (instr.operands[i].operandClass == NONE)
+ return true;
+
+ int32_t imm = instr.operands[i].immediate;
+ if (i != 0)
+ result.emplace_back(OperandSeparatorToken, ", ");
+
+ switch (instr.operands[i].operandClass)
+ {
+ case IMM:
+ if (imm < -9)
+ snprintf(operand, sizeof(operand), "-%#x", -imm);
+ else if (imm < 0)
+ snprintf(operand, sizeof(operand), "-%d", -imm);
+ else if (imm < 10)
+ snprintf(operand, sizeof(operand), "%d", imm);
+ else
+ snprintf(operand, sizeof(operand), "%#x", imm);
+
+ result.emplace_back(IntegerToken, operand, imm);
+ break;
+ case LABEL:
+ snprintf(operand, sizeof(operand), "%#x", imm);
+ result.emplace_back(PossibleAddressToken, operand, imm);
+ break;
+ case REG:
+ reg = get_register((Reg)instr.operands[i].reg);
+ if (reg == NULL)
+ {
+ return false;
+ }
+ result.emplace_back(RegisterToken, reg);
+ break;
+ case FLAG:
+ reg = get_flag((Flag)instr.operands[i].reg);
+ if (reg == NULL)
+ {
+ return false;
+ }
+ result.push_back(InstructionTextToken(RegisterToken, reg));
+ break;
+ case HINT:
+ reg = get_hint((Hint)instr.operands[i].reg);
+ if (reg == NULL)
+ {
+ return false;
+ }
+ result.emplace_back(RegisterToken, reg);
+ break;
+ case MEM_IMM:
+ result.emplace_back(BeginMemoryOperandToken, "");
+ if (imm != 0)
+ {
+ if (imm < -9)
+ snprintf(operand, sizeof(operand), "-%#x", -imm);
+ else if (imm < 0)
+ snprintf(operand, sizeof(operand), "-%d", -imm);
+ else if (imm < 10)
+ snprintf(operand, sizeof(operand), "%d", imm);
+ else
+ snprintf(operand, sizeof(operand), "%#x", imm);
+ result.emplace_back(IntegerToken, operand, imm);
+ }
+ if (instr.operands[i].reg == REG_ZERO)
+ break;
+ result.emplace_back(TextToken, "(");
+ reg = get_register((Reg)instr.operands[i].reg);
+ if (reg == NULL)
+ return false;
+ result.emplace_back(RegisterToken, reg);
+ result.emplace_back(TextToken, ")");
+ result.emplace_back(EndMemoryOperandToken, "");
+ break;
+ case MEM_REG:
+ result.emplace_back(BeginMemoryOperandToken, "");
+ reg = get_register((Reg)imm);
+ if (reg == NULL)
+ return false;
+ result.emplace_back(RegisterToken, reg);
+ result.emplace_back(TextToken, "(");
+
+ reg = get_register((Reg)instr.operands[i].reg);
+ if (reg == NULL)
+ return false;
+ result.emplace_back(RegisterToken, reg);
+ result.emplace_back(TextToken, ")");
+ result.emplace_back(EndMemoryOperandToken, "");
+ break;
+ default:
+ LogError("operandClass %x\n", instr.operands[i].operandClass);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual string GetIntrinsicName(uint32_t intrinsic) override
+ {
+ switch (intrinsic)
+ {
+ case MIPS_INTRIN_WSBH:
+ return "__wsbh";
+ case MIPS_INTRIN_MFC0:
+ return "moveFromCoprocessor";
+ case MIPS_INTRIN_MFC_UNIMPLEMENTED:
+ return "moveFromCoprocessorUnimplemented";
+ case MIPS_INTRIN_MTC0:
+ return "moveToCoprocessor";
+ case MIPS_INTRIN_MTC_UNIMPLEMENTED:
+ return "moveToCoprocessorUnimplemented";
+ case MIPS_INTRIN_DMFC0:
+ return "moveDwordFromCoprocessor";
+ case MIPS_INTRIN_DMFC_UNIMPLEMENTED:
+ return "moveDwordFromCoprocessorUnimplemented";
+ case MIPS_INTRIN_DMTC0:
+ return "moveDwordToCoprocessor";
+ case MIPS_INTRIN_DMTC_UNIMPLEMENTED:
+ return "moveDwordToCoprocessorUnimplemented";
+ default:
+ return "";
+ }
+ }
+
+ virtual vector<uint32_t> GetAllIntrinsics() override
+ {
+ return vector<uint32_t>{
+ MIPS_INTRIN_WSBH,
+ MIPS_INTRIN_MFC0,
+ MIPS_INTRIN_MFC_UNIMPLEMENTED,
+ MIPS_INTRIN_MTC0,
+ MIPS_INTRIN_MTC_UNIMPLEMENTED,
+ MIPS_INTRIN_DMFC0,
+ MIPS_INTRIN_DMFC_UNIMPLEMENTED,
+ MIPS_INTRIN_DMTC0,
+ MIPS_INTRIN_DMTC_UNIMPLEMENTED,
+ };
+ }
+
+ virtual vector<NameAndType> GetIntrinsicInputs(uint32_t intrinsic) override
+ {
+ switch (intrinsic)
+ {
+ case MIPS_INTRIN_WSBH:
+ return {NameAndType(Type::IntegerType(4, false))};
+ case MIPS_INTRIN_MFC0:
+ return {
+ NameAndType("register", Type::IntegerType(4, false)),
+ };
+ case MIPS_INTRIN_MFC_UNIMPLEMENTED:
+ return {
+ NameAndType("coprocessor", Type::IntegerType(4, false)),
+ NameAndType("register", Type::IntegerType(4, false)),
+ NameAndType("selector", Type::IntegerType(4, false)),
+ };
+ case MIPS_INTRIN_MTC0:
+ return {
+ NameAndType("register", Type::IntegerType(4, false)),
+ NameAndType("value", Type::IntegerType(4, false)),
+ };
+ case MIPS_INTRIN_MTC_UNIMPLEMENTED:
+ return {
+ NameAndType("coprocessor", Type::IntegerType(4, false)),
+ NameAndType("register", Type::IntegerType(4, false)),
+ NameAndType("selector", Type::IntegerType(4, false)),
+ NameAndType("value", Type::IntegerType(4, false)),
+ };
+ case MIPS_INTRIN_DMFC0:
+ return {
+ NameAndType("register", Type::IntegerType(8, false)),
+ };
+ case MIPS_INTRIN_DMFC_UNIMPLEMENTED:
+ return {
+ NameAndType("coprocessor", Type::IntegerType(4, false)),
+ NameAndType("register", Type::IntegerType(4, false)),
+ NameAndType("selector", Type::IntegerType(4, false)),
+ };
+ case MIPS_INTRIN_DMTC0:
+ return {
+ NameAndType("register", Type::IntegerType(8, false)),
+ NameAndType("value", Type::IntegerType(8, false)),
+ };
+ case MIPS_INTRIN_DMTC_UNIMPLEMENTED:
+ return {
+ NameAndType("coprocessor", Type::IntegerType(4, false)),
+ NameAndType("register", Type::IntegerType(4, false)),
+ NameAndType("selector", Type::IntegerType(4, false)),
+ NameAndType("value", Type::IntegerType(8, false)),
+ };
+ default:
+ return vector<NameAndType>();
+ }
+ }
+
+ virtual vector<Confidence<Ref<Type>>> GetIntrinsicOutputs(uint32_t intrinsic) override
+ {
+ switch (intrinsic)
+ {
+ case MIPS_INTRIN_WSBH:
+ return {Type::IntegerType(4, false)};
+ case MIPS_INTRIN_MFC0:
+ case MIPS_INTRIN_MFC_UNIMPLEMENTED:
+ return {Type::IntegerType(4, false)};
+ case MIPS_INTRIN_DMFC0:
+ case MIPS_INTRIN_DMFC_UNIMPLEMENTED:
+ return {Type::IntegerType(8, false)};
+ 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 IsConditionalBranch(instr);
+ }
+
+ 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 IsConditionalBranch(instr);
+ }
+
+ 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 IsConditionalBranch(instr);
+ }
+
+ 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 == MIPS_BAL) || (instr.operation == MIPS_JAL);
+ }
+
+ // 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 == MIPS_BAL) || (instr.operation == MIPS_JAL);
+ // }
+
+ virtual bool ConvertToNop(uint8_t* data, uint64_t, size_t len) override
+ {
+ uint32_t nop = 0;
+ 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;
+ Instruction instr;
+ if (!Disassemble(data, addr, len, instr))
+ return false;
+
+ uint32_t *value = (uint32_t*)data;
+ uint32_t instValue = *value;
+
+ if (GetEndianness() == LittleEndian)
+ instValue = bswap32(instValue);
+
+ instValue = (0x00000010 | (instValue & 0xffff0000));
+
+ if (GetEndianness() == LittleEndian)
+ instValue = bswap32(instValue);
+
+ *value = instValue;
+ return true;
+ }
+
+ virtual bool InvertBranch(uint8_t* data, uint64_t addr, size_t len) override
+ {
+ (void)addr;
+ (void)len;
+ uint32_t *value = (uint32_t*)data;
+
+ uint32_t instValue = *value;
+ if (GetEndianness() == LittleEndian)
+ instValue = bswap32(instValue);
+
+ uint32_t op = instValue >> 25;
+ switch (op)
+ {
+ case 1: //REGIMM
+ op = (instValue >> 16) & 0xf;
+ switch (op)
+ {
+ case 0: //BLTZ
+ case 1: //BGEZ
+ case 2: //BLTZL
+ case 3: //BGEZL
+ case 0x10: //BLTZAL
+ case 0x11: //BGEZAL
+ case 0x12: //BLTZALL
+ case 0x13: //BGEZALL
+ //Invert the bit
+ instValue ^= 0x00000100;
+ break;
+ default:
+ return false;
+ }
+ break;
+ case 4: //BEQ
+ case 5: //BNE
+ case 6: //BLEZ
+ case 7: //BGTZ
+ case 0x14: //BEQL
+ case 0x15: //BNEL
+ case 0x16: //BLEZL
+ case 0x17: //BGTZL
+ //Invert the bit
+ instValue ^= 0x00000004;
+ break;
+ default:
+ return false;
+ }
+ if (GetEndianness() == LittleEndian)
+ instValue = bswap32(instValue);
+
+ *value = instValue;
+ return true;
+ }
+ /*
+ 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 string GetRegisterName(uint32_t reg) override
+ {
+ const char* regsz = nullptr;
+ //Integers compared here are according to the list returned by GetAllRegisters
+ if (reg < END_REG)
+ regsz = get_register((Reg)reg);
+ if (regsz == nullptr)
+ return "";
+ return regsz;
+ }
+
+ virtual string GetFlagName(uint32_t reg) override
+ {
+ const char* regsz = nullptr;
+ if (reg < END_FLAG)
+ regsz = get_flag((Flag)reg);
+ if (regsz == nullptr)
+ return "";
+ return regsz;
+ }
+
+ virtual vector<uint32_t> GetFullWidthRegisters() override
+ {
+ return vector<uint32_t>{
+ REG_ZERO, REG_AT, REG_V0, REG_V1, REG_A0, REG_A1, REG_A2, REG_A3,
+ REG_T0, REG_T1, REG_T2, REG_T3, REG_T4, REG_T5, REG_T6, REG_T7,
+ REG_S0, REG_S1, REG_S2, REG_S3, REG_S4, REG_S5, REG_S6, REG_S7,
+ REG_T8, REG_T9, REG_K0, REG_K1, REG_GP, REG_SP, REG_FP, REG_RA,
+ CPREG_0, CPREG_1, CPREG_2, CPREG_3, CPREG_4, CPREG_5, CPREG_6, CPREG_7,
+ CPREG_8, CPREG_9, CPREG_10, CPREG_11, CPREG_12, CPREG_13, CPREG_14, CPREG_15,
+ CPREG_16, CPREG_17, CPREG_18, CPREG_19, CPREG_20, CPREG_21, CPREG_22, CPREG_23,
+ CPREG_24, CPREG_25, CPREG_26, CPREG_27, CPREG_28, CPREG_29, CPREG_30, CPREG_31,
+ FPREG_F0, FPREG_F1, FPREG_F2, FPREG_F3, FPREG_F4, FPREG_F5, FPREG_F6, FPREG_F7,
+ FPREG_F8, FPREG_F9, FPREG_F10, FPREG_F11, FPREG_F12, FPREG_F13, FPREG_F14, FPREG_F15,
+ FPREG_F16, FPREG_F17, FPREG_F18, FPREG_F19, FPREG_F20, FPREG_F21, FPREG_F22, FPREG_F23,
+ FPREG_F24, FPREG_F25, FPREG_F26, FPREG_F27, FPREG_F28, FPREG_F29, FPREG_F30, FPREG_F31,
+ FPCCREG_FCC0, FPCCREG_FCC1, FPCCREG_FCC2, FPCCREG_FCC3, FPCCREG_FCC4, FPCCREG_FCC5, FPCCREG_FCC6, FPCCREG_FCC7,
+ REG_LO, REG_HI,
+ // Coprocessor 0 register 0
+ REG_INDEX,
+ REG_MVP_CONTROL,
+ REG_MVP_CONF0,
+ REG_MVP_CONF1,
+ // Coprocessor 0 register 1
+ REG_RANDOM,
+ REG_VPE_CONTROL,
+ REG_VPE_CONF0,
+ REG_VPE_CONF1,
+ REG_YQ_MASK,
+ REG_VPE_SCHEDULE,
+ REG_VPE_SCHE_FBACK,
+ REG_VPE_OPT,
+ // Coprocessor 0 register 2
+ REG_ENTRY_LO0,
+ REG_TC_STATUS,
+ REG_TC_BIND,
+ REG_TC_RESTART,
+ REG_TC_HALT,
+ REG_TC_CONTEXT,
+ REG_TC_SCHEDULE,
+ REG_TC_SCHE_FBACK,
+ // Coprocessor 0 register 3
+ REG_ENTRY_LO1,
+ // Coprocessor 0 register 4
+ REG_CONTEXT,
+ REG_CONTEXT_CONFIG,
+ // Coprocessor 0 register 5
+ REG_PAGE_MASK,
+ REG_PAGE_GRAIN,
+ // Coprocessor 0 register 6
+ REG_WIRED,
+ REG_SRS_CONF0,
+ REG_SRS_CONF1,
+ REG_SRS_CONF2,
+ REG_SRS_CONF3,
+ REG_SRS_CONF4,
+ // Coprocessor 0 register 7
+ REG_HWR_ENA,
+ // Coprocessor 0 register 8
+ REG_BAD_VADDR,
+ // Coprocessor 0 register 9
+ REG_COUNT,
+ // Coprocessor 0 register 10
+ REG_ENTRY_HI,
+ // Coprocessor 0 register 11
+ REG_COMPARE,
+ // Coprocessor 0 register 12
+ REG_STATUS,
+ REG_INT_CTL,
+ REG_SRS_CTL,
+ REG_SRS_MAP,
+ // Coprocessor 0 register 13
+ REG_CAUSE,
+ // Coprocessor 0 register 14
+ REG_EPC,
+ // Coprocessor 0 register 15
+ REG_PR_ID,
+ REG_EBASE,
+ // Coprocessor 0 register 16
+ REG_CONFIG,
+ REG_CONFIG1,
+ REG_CONFIG2,
+ REG_CONFIG3,
+ // Coprocessor 0 register 17
+ REG_LLADDR,
+ // Coprocessor 0 register 18
+ REG_WATCH_LO,
+ // Coprocessor 0 register 19
+ REG_WATCH_HI,
+ // Coprocessor 0 register 20
+ REG_XCONTEXT,
+ // Coprocessor 0 register 23
+ REG_DEBUG,
+ REG_TRACE_CONTROL,
+ REG_TRACE_CONTROL2,
+ REG_USER_TRACE_DATA,
+ REG_TRACE_BPC,
+ // Coprocessor 0 register 24
+ REG_DEPC,
+ // Coprocessor 0 register 25
+ REG_PERF_CNT,
+ // Coprocessor 0 register 26
+ REG_ERR_CTL,
+ // Coprocessor 0 register 27
+ REG_CACHE_ERR0,
+ REG_CACHE_ERR1,
+ REG_CACHE_ERR2,
+ REG_CACHE_ERR3,
+ // Coprocessor 0 register 28
+ REG_TAG_LO,
+ REG_DATA_LO,
+ // Coprocessor 0 register 29
+ REG_TAG_HI,
+ REG_DATA_HI,
+ // Coprocessor 0 register 30
+ REG_ERROR_EPC,
+ // Coprocessor 0 register 31
+ REG_DESAVE,
+ };
+ }
+
+ virtual vector<uint32_t> GetAllRegisters() override
+ {
+ return vector<uint32_t>{
+ REG_ZERO, REG_AT, REG_V0, REG_V1, REG_A0, REG_A1, REG_A2, REG_A3,
+ REG_T0, REG_T1, REG_T2, REG_T3, REG_T4, REG_T5, REG_T6, REG_T7,
+ REG_S0, REG_S1, REG_S2, REG_S3, REG_S4, REG_S5, REG_S6, REG_S7,
+ REG_T8, REG_T9, REG_K0, REG_K1, REG_GP, REG_SP, REG_FP, REG_RA,
+ CPREG_0, CPREG_1, CPREG_2, CPREG_3, CPREG_4, CPREG_5, CPREG_6, CPREG_7,
+ CPREG_8, CPREG_9, CPREG_10, CPREG_11, CPREG_12, CPREG_13, CPREG_14, CPREG_15,
+ CPREG_16, CPREG_17, CPREG_18, CPREG_19, CPREG_20, CPREG_21, CPREG_22, CPREG_23,
+ CPREG_24, CPREG_25, CPREG_26, CPREG_27, CPREG_28, CPREG_29, CPREG_30, CPREG_31,
+ FPREG_F0, FPREG_F1, FPREG_F2, FPREG_F3, FPREG_F4, FPREG_F5, FPREG_F6, FPREG_F7,
+ FPREG_F8, FPREG_F9, FPREG_F10, FPREG_F11, FPREG_F12, FPREG_F13, FPREG_F14, FPREG_F15,
+ FPREG_F16, FPREG_F17, FPREG_F18, FPREG_F19, FPREG_F20, FPREG_F21, FPREG_F22, FPREG_F23,
+ FPREG_F24, FPREG_F25, FPREG_F26, FPREG_F27, FPREG_F28, FPREG_F29, FPREG_F30, FPREG_F31,
+ REG_LO, REG_HI,
+ // Coprocessor 0 register 0
+ REG_INDEX,
+ REG_MVP_CONTROL,
+ REG_MVP_CONF0,
+ REG_MVP_CONF1,
+ // Coprocessor 0 register 1
+ REG_RANDOM,
+ REG_VPE_CONTROL,
+ REG_VPE_CONF0,
+ REG_VPE_CONF1,
+ REG_YQ_MASK,
+ REG_VPE_SCHEDULE,
+ REG_VPE_SCHE_FBACK,
+ REG_VPE_OPT,
+ // Coprocessor 0 register 2
+ REG_ENTRY_LO0,
+ REG_TC_STATUS,
+ REG_TC_BIND,
+ REG_TC_RESTART,
+ REG_TC_HALT,
+ REG_TC_CONTEXT,
+ REG_TC_SCHEDULE,
+ REG_TC_SCHE_FBACK,
+ // Coprocessor 0 register 3
+ REG_ENTRY_LO1,
+ // Coprocessor 0 register 4
+ REG_CONTEXT,
+ REG_CONTEXT_CONFIG,
+ // Coprocessor 0 register 5
+ REG_PAGE_MASK,
+ REG_PAGE_GRAIN,
+ // Coprocessor 0 register 6
+ REG_WIRED,
+ REG_SRS_CONF0,
+ REG_SRS_CONF1,
+ REG_SRS_CONF2,
+ REG_SRS_CONF3,
+ REG_SRS_CONF4,
+ // Coprocessor 0 register 7
+ REG_HWR_ENA,
+ // Coprocessor 0 register 8
+ REG_BAD_VADDR,
+ // Coprocessor 0 register 9
+ REG_COUNT,
+ // Coprocessor 0 register 10
+ REG_ENTRY_HI,
+ // Coprocessor 0 register 11
+ REG_COMPARE,
+ // Coprocessor 0 register 12
+ REG_STATUS,
+ REG_INT_CTL,
+ REG_SRS_CTL,
+ REG_SRS_MAP,
+ // Coprocessor 0 register 13
+ REG_CAUSE,
+ // Coprocessor 0 register 14
+ REG_EPC,
+ // Coprocessor 0 register 15
+ REG_PR_ID,
+ REG_EBASE,
+ // Coprocessor 0 register 16
+ REG_CONFIG,
+ REG_CONFIG1,
+ REG_CONFIG2,
+ REG_CONFIG3,
+ // Coprocessor 0 register 17
+ REG_LLADDR,
+ // Coprocessor 0 register 18
+ REG_WATCH_LO,
+ // Coprocessor 0 register 19
+ REG_WATCH_HI,
+ // Coprocessor 0 register 20
+ REG_XCONTEXT,
+ // Coprocessor 0 register 23
+ REG_DEBUG,
+ REG_TRACE_CONTROL,
+ REG_TRACE_CONTROL2,
+ REG_USER_TRACE_DATA,
+ REG_TRACE_BPC,
+ // Coprocessor 0 register 24
+ REG_DEPC,
+ // Coprocessor 0 register 25
+ REG_PERF_CNT,
+ // Coprocessor 0 register 26
+ REG_ERR_CTL,
+ // Coprocessor 0 register 27
+ REG_CACHE_ERR0,
+ REG_CACHE_ERR1,
+ REG_CACHE_ERR2,
+ REG_CACHE_ERR3,
+ // Coprocessor 0 register 28
+ REG_TAG_LO,
+ REG_DATA_LO,
+ // Coprocessor 0 register 29
+ REG_TAG_HI,
+ REG_DATA_HI,
+ // Coprocessor 0 register 30
+ REG_ERROR_EPC,
+ // Coprocessor 0 register 31
+ REG_DESAVE,
+ };
+ }
+
+ virtual vector<uint32_t> GetAllFlags() override
+ {
+ return vector<uint32_t>{
+ FPCCREG_FCC0, FPCCREG_FCC1, FPCCREG_FCC2, FPCCREG_FCC3, FPCCREG_FCC4, FPCCREG_FCC5, FPCCREG_FCC6, FPCCREG_FCC7
+ };
+ }
+
+ virtual BNRegisterInfo GetRegisterInfo(uint32_t reg) override
+ {
+ BNRegisterInfo result = {reg, 0, m_bits / 8, NoExtend};
+ return result;
+ }
+
+ virtual uint32_t GetStackPointerRegister() override
+ {
+ return REG_SP;
+ }
+
+ virtual uint32_t GetLinkRegister() override
+ {
+ return REG_RA;
+ }
+
+ virtual vector<uint32_t> GetSystemRegisters() override
+ {
+ return vector< uint32_t> {
+ // Coprocessor 0 register 0
+ REG_INDEX,
+ REG_MVP_CONTROL,
+ REG_MVP_CONF0,
+ REG_MVP_CONF1,
+ // Coprocessor 0 register 1
+ REG_RANDOM,
+ REG_VPE_CONTROL,
+ REG_VPE_CONF0,
+ REG_VPE_CONF1,
+ REG_YQ_MASK,
+ REG_VPE_SCHEDULE,
+ REG_VPE_SCHE_FBACK,
+ REG_VPE_OPT,
+ // Coprocessor 0 register 2
+ REG_ENTRY_LO0,
+ REG_TC_STATUS,
+ REG_TC_BIND,
+ REG_TC_RESTART,
+ REG_TC_HALT,
+ REG_TC_CONTEXT,
+ REG_TC_SCHEDULE,
+ REG_TC_SCHE_FBACK,
+ // Coprocessor 0 register 3
+ REG_ENTRY_LO1,
+ // Coprocessor 0 register 4
+ REG_CONTEXT,
+ REG_CONTEXT_CONFIG,
+ // Coprocessor 0 register 5
+ REG_PAGE_MASK,
+ REG_PAGE_GRAIN,
+ // Coprocessor 0 register 6
+ REG_WIRED,
+ REG_SRS_CONF0,
+ REG_SRS_CONF1,
+ REG_SRS_CONF2,
+ REG_SRS_CONF3,
+ REG_SRS_CONF4,
+ // Coprocessor 0 register 7
+ REG_HWR_ENA,
+ // Coprocessor 0 register 8
+ REG_BAD_VADDR,
+ // Coprocessor 0 register 9
+ REG_COUNT,
+ // Coprocessor 0 register 10
+ REG_ENTRY_HI,
+ // Coprocessor 0 register 11
+ REG_COMPARE,
+ // Coprocessor 0 register 12
+ REG_STATUS,
+ REG_INT_CTL,
+ REG_SRS_CTL,
+ REG_SRS_MAP,
+ // Coprocessor 0 register 13
+ REG_CAUSE,
+ // Coprocessor 0 register 14
+ REG_EPC,
+ // Coprocessor 0 register 15
+ REG_PR_ID,
+ REG_EBASE,
+ // Coprocessor 0 register 16
+ REG_CONFIG,
+ REG_CONFIG1,
+ REG_CONFIG2,
+ REG_CONFIG3,
+ // Coprocessor 0 register 17
+ REG_LLADDR,
+ // Coprocessor 0 register 18
+ REG_WATCH_LO,
+ // Coprocessor 0 register 19
+ REG_WATCH_HI,
+ // Coprocessor 0 register 20
+ REG_XCONTEXT,
+ // Coprocessor 0 register 23
+ REG_DEBUG,
+ REG_TRACE_CONTROL,
+ REG_TRACE_CONTROL2,
+ REG_USER_TRACE_DATA,
+ REG_TRACE_BPC,
+ // Coprocessor 0 register 24
+ REG_DEPC,
+ // Coprocessor 0 register 25
+ REG_PERF_CNT,
+ // Coprocessor 0 register 26
+ REG_ERR_CTL,
+ // Coprocessor 0 register 27
+ REG_CACHE_ERR0,
+ REG_CACHE_ERR1,
+ REG_CACHE_ERR2,
+ REG_CACHE_ERR3,
+ // Coprocessor 0 register 28
+ REG_TAG_LO,
+ REG_DATA_LO,
+ // Coprocessor 0 register 29
+ REG_TAG_HI,
+ REG_DATA_HI,
+ // Coprocessor 0 register 30
+ REG_ERROR_EPC,
+ // Coprocessor 0 register 31
+ REG_DESAVE,
+ };
+ }
+
+};
+
+class MipsO32CallingConvention: public CallingConvention
+{
+public:
+ MipsO32CallingConvention(Architecture* arch): CallingConvention(arch, "o32")
+ {
+ }
+
+ virtual uint32_t GetIntegerReturnValueRegister() override
+ {
+ return REG_V0;
+ }
+
+ virtual uint32_t GetHighIntegerReturnValueRegister() override
+ {
+ return REG_V1;
+ }
+
+ virtual vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ return vector<uint32_t>{ REG_A0, REG_A1, REG_A2, REG_A3 };
+ }
+
+ virtual bool IsStackReservedForArgumentRegisters() override
+ {
+ return true;
+ }
+
+ virtual vector<uint32_t> GetCallerSavedRegisters() override
+ {
+ return vector<uint32_t> { REG_AT, REG_V0, REG_V1, REG_A0, REG_A1, REG_A2, REG_A3, REG_T0, REG_T1,
+ REG_T2, REG_T3, REG_T4, REG_T5, REG_T6, REG_T7, REG_T8, REG_T9 };
+ }
+
+ virtual vector<uint32_t> GetCalleeSavedRegisters() override
+ {
+ return vector<uint32_t> { REG_S0, REG_S1, REG_S2, REG_S3, REG_S4, REG_S5, REG_S6, REG_S7,
+ REG_GP, REG_FP };
+ }
+
+ virtual uint32_t GetGlobalPointerRegister() override
+ {
+ return REG_GP;
+ }
+
+ virtual vector<uint32_t> GetImplicitlyDefinedRegisters() override
+ {
+ return vector<uint32_t> { REG_T9 };
+ }
+
+ virtual RegisterValue GetIncomingRegisterValue(uint32_t reg, Function* func) override
+ {
+ RegisterValue result;
+ if (reg == REG_T9)
+ {
+ result.state = ConstantPointerValue;
+ result.value = func->GetStart();
+ }
+ return result;
+ }
+};
+
+class MipsN64CallingConvention: public CallingConvention
+{
+public:
+ MipsN64CallingConvention(Architecture* arch): CallingConvention(arch, "n64")
+ {
+ }
+
+ virtual uint32_t GetIntegerReturnValueRegister() override
+ {
+ return REG_V0;
+ }
+
+ virtual uint32_t GetHighIntegerReturnValueRegister() override
+ {
+ return REG_V1;
+ }
+
+ virtual vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ return vector<uint32_t>{
+ REG_A0, REG_A1, REG_A2, REG_A3,
+ REG_A4, REG_A5, REG_A6, REG_A7,
+ };
+ }
+
+ virtual bool IsStackReservedForArgumentRegisters() override
+ {
+ return false;
+ }
+
+ virtual vector<uint32_t> GetCallerSavedRegisters() override
+ {
+ return vector<uint32_t> { REG_AT, REG_V0, REG_V1, REG_A0, REG_A1, REG_A2, REG_A3, REG_A4, REG_A5,
+ REG_A6, REG_A7, REG_T4, REG_T5, REG_T6, REG_T7, REG_T8, REG_T9, REG_RA,
+ FPREG_F0, FPREG_F1, FPREG_F2, FPREG_F3, FPREG_F4, FPREG_F5, FPREG_F6, FPREG_F7, FPREG_F8,
+ FPREG_F9, FPREG_F10, FPREG_F11, FPREG_F12, FPREG_F13, FPREG_F14, FPREG_F15, FPREG_F16, FPREG_F17,
+ FPREG_F18, FPREG_F19, FPREG_F20, FPREG_F21, FPREG_F22, FPREG_F23, };
+ }
+
+ virtual vector<uint32_t> GetCalleeSavedRegisters() override
+ {
+ return vector<uint32_t> { REG_S0, REG_S1, REG_S2, REG_S3, REG_S4, REG_S5, REG_S6, REG_S7,
+ REG_GP, REG_FP, FPREG_F24, FPREG_F25, FPREG_F26, FPREG_F27, FPREG_F28, FPREG_F29, FPREG_F30, FPREG_F31 };
+ }
+
+ virtual uint32_t GetGlobalPointerRegister() override
+ {
+ return REG_GP;
+ }
+
+ virtual vector<uint32_t> GetImplicitlyDefinedRegisters() override
+ {
+ return vector<uint32_t> { REG_T9 };
+ }
+
+ virtual RegisterValue GetIncomingRegisterValue(uint32_t reg, Function* func) override
+ {
+ RegisterValue result;
+ if (reg == REG_T9)
+ {
+ result.state = ConstantPointerValue;
+ result.value = func->GetStart();
+ }
+ return result;
+ }
+};
+
+class MipsLinuxSyscallCallingConvention: public CallingConvention
+{
+public:
+ MipsLinuxSyscallCallingConvention(Architecture* arch): CallingConvention(arch, "linux-syscall")
+ {
+ }
+
+ virtual uint32_t GetIntegerReturnValueRegister() override
+ {
+ return REG_V0;
+ }
+
+ virtual uint32_t GetHighIntegerReturnValueRegister() override
+ {
+ return REG_V1;
+ }
+
+ virtual vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ return vector<uint32_t>{ REG_V0, REG_A0, REG_A1, REG_A2, REG_A3 };
+ }
+
+ virtual vector<uint32_t> GetCallerSavedRegisters() override
+ {
+ return vector<uint32_t> { REG_AT, REG_V0, REG_V1 };
+ }
+
+ virtual vector<uint32_t> GetCalleeSavedRegisters() override
+ {
+ return vector<uint32_t> { REG_S0, REG_S1, REG_S2, REG_S3, REG_S4, REG_S5, REG_S6, REG_S7,
+ REG_GP, REG_FP };
+ }
+
+ virtual bool IsEligibleForHeuristics() override
+ {
+ return false;
+ }
+};
+
+class MipsLinuxRtlResolveCallingConvention: public CallingConvention
+{
+public:
+ MipsLinuxRtlResolveCallingConvention(Architecture* arch): CallingConvention(arch, "linux-rtlresolve")
+ {
+ }
+
+ virtual vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ return vector<uint32_t>{
+ REG_T7, /* return address of caller of PLT stub */
+ REG_T8 /* symbol index */
+ };
+ }
+
+ virtual uint32_t GetIntegerReturnValueRegister() override
+ {
+ return REG_T0;
+ }
+
+ virtual bool IsEligibleForHeuristics() override
+ {
+ return false;
+ }
+};
+
+class MipsImportedFunctionRecognizer: public FunctionRecognizer
+{
+private:
+ bool RecognizeELFPLTEntries0(BinaryView* data, Function* func, LowLevelILFunction* il)
+ {
+ // Look for the following code pattern:
+ // $t7 = got_hi
+ // $t9 = [$t7 + got_lo].d
+ // $t8 = $t7 + got_lo
+ // OPTIONAL: $t7 = got_hi
+ // tailcall($t9)
+ if (il->GetInstructionCount() < 4)
+ return false;
+ if (il->GetInstructionCount() > 5)
+ return false;
+
+ LowLevelILInstruction lui = il->GetInstruction(0);
+ if (lui.operation != LLIL_SET_REG)
+ return false;
+ LowLevelILInstruction luiOperand = lui.GetSourceExpr<LLIL_SET_REG>();
+ if (!LowLevelILFunction::IsConstantType(luiOperand.operation))
+ return false;
+ if (luiOperand.size != func->GetArchitecture()->GetAddressSize())
+ return false;
+ uint64_t pltHi = luiOperand.GetConstant();
+ uint32_t pltReg = lui.GetDestRegister<LLIL_SET_REG>();
+
+ LowLevelILInstruction ld = il->GetInstruction(1);
+ if (ld.operation != LLIL_SET_REG)
+ return false;
+ uint32_t targetReg = ld.GetDestRegister<LLIL_SET_REG>();
+ LowLevelILInstruction ldOperand = ld.GetSourceExpr<LLIL_SET_REG>();
+ if (ldOperand.operation != LLIL_LOAD)
+ return false;
+ if (ldOperand.size != func->GetArchitecture()->GetAddressSize())
+ return false;
+ LowLevelILInstruction ldAddrOperand = ldOperand.GetSourceExpr<LLIL_LOAD>();
+ uint64_t entry = pltHi;
+ int64_t ldAddrRightOperandValue = 0;
+
+ if ((ldAddrOperand.operation == LLIL_ADD) || (ldAddrOperand.operation == LLIL_SUB))
+ {
+ LowLevelILInstruction ldAddrLeftOperand = ldAddrOperand.GetRawOperandAsExpr(0);
+ LowLevelILInstruction ldAddrRightOperand = ldAddrOperand.GetRawOperandAsExpr(1);
+ if (ldAddrLeftOperand.operation != LLIL_REG)
+ return false;
+ if (ldAddrLeftOperand.GetSourceRegister<LLIL_REG>() != pltReg)
+ return false;
+ if (!LowLevelILFunction::IsConstantType(ldAddrRightOperand.operation))
+ return false;
+ ldAddrRightOperandValue = ldAddrRightOperand.GetConstant();
+ if (ldAddrOperand.operation == LLIL_SUB)
+ ldAddrRightOperandValue = -ldAddrRightOperandValue;
+ entry = pltHi + ldAddrRightOperandValue;
+ }
+ else if (ldAddrOperand.operation != LLIL_REG) //If theres no constant
+ return false;
+
+ Ref<Symbol> sym = data->GetSymbolByAddress(entry);
+ if (!sym)
+ return false;
+ if (sym->GetType() != ImportAddressSymbol)
+ return false;
+
+ LowLevelILInstruction add = il->GetInstruction(2);
+ if (add.operation != LLIL_SET_REG)
+ return false;
+ LowLevelILInstruction addOperand = add.GetSourceExpr<LLIL_SET_REG>();
+
+ if (addOperand.operation == LLIL_ADD)
+ {
+ LowLevelILInstruction addLeftOperand = addOperand.GetLeftExpr<LLIL_ADD>();
+ LowLevelILInstruction addRightOperand = addOperand.GetRightExpr<LLIL_ADD>();
+ if (addLeftOperand.operation != LLIL_REG)
+ return false;
+ if (addLeftOperand.GetSourceRegister<LLIL_REG>() != pltReg)
+ return false;
+ if (!LowLevelILFunction::IsConstantType(addRightOperand.operation))
+ return false;
+ if (addRightOperand.GetConstant() != (ldAddrRightOperandValue & 0xffffffff))
+ return false;
+ }
+ else if ((addOperand.operation != LLIL_REG) || (addOperand.GetSourceRegister<LLIL_REG>() != pltReg)) //Simple assignment
+ return false;
+
+ LowLevelILInstruction jump = il->GetInstruction(3);
+ if (jump.operation == LLIL_SET_REG)
+ {
+ if (il->GetInstructionCount() != 5)
+ return false;
+ if (jump.GetDestRegister<LLIL_SET_REG>() != pltReg)
+ return false;
+ LowLevelILInstruction luiOperand = jump.GetSourceExpr<LLIL_SET_REG>();
+ if (!LowLevelILFunction::IsConstantType(luiOperand.operation))
+ return false;
+ if (luiOperand.size != func->GetArchitecture()->GetAddressSize())
+ return false;
+ if (((uint64_t) luiOperand.GetConstant()) != pltHi)
+ return false;
+ jump = il->GetInstruction(4);
+ }
+
+ if ((jump.operation != LLIL_JUMP) && (jump.operation != LLIL_TAILCALL))
+ return false;
+ LowLevelILInstruction jumpOperand = (jump.operation == LLIL_JUMP) ? jump.GetDestExpr<LLIL_JUMP>() : jump.GetDestExpr<LLIL_TAILCALL>();
+ if (jumpOperand.operation != LLIL_REG)
+ return false;
+ if (jumpOperand.GetSourceRegister<LLIL_REG>() != targetReg)
+ return false;
+
+ Ref<Symbol> funcSym = Symbol::ImportedFunctionFromImportAddressSymbol(sym, func->GetStart());
+ data->DefineAutoSymbol(funcSym);
+ func->ApplyImportedTypes(funcSym);
+ return true;
+ }
+
+
+ bool RecognizeELFPLTEntries1(BinaryView* data, Function* func, LowLevelILFunction* il)
+ {
+ LowLevelILInstruction tmp, left, right;
+
+ // Look for the following code pattern:
+ // 0: $t9 = [$gp - ????] // get to base of GOT
+ // 1: $t7 = $ra // transmit address so RTLD!service_stub() can return to caller
+ // 2: $t8 = ?? // transmit symbol index to RTLD!service_stub()
+ // 3: call($t9) // call RTLD!service_stub()
+ // 4: tailcall(??)
+ if (il->GetInstructionCount() != 5)
+ return false;
+
+ // test instruction0
+ tmp = il->GetInstruction(0); // $t9 = ...
+ if (tmp.operation != LLIL_SET_REG) return false;
+ tmp = tmp.GetSourceExpr<LLIL_SET_REG>(); // [$gp - ????]
+ if (tmp.operation != LLIL_LOAD) return false;
+ tmp = tmp.GetSourceExpr<LLIL_LOAD>(); // $gp - ????
+ if (tmp.operation != LLIL_SUB) return false;
+ auto value = il->GetExprValue(tmp); // accept if Binja has resolved to a value
+ //if (value.state != ConstantValue) return false;
+ //uint64_t got_base = value.value;
+
+ // test instruction1
+ tmp = il->GetInstruction(1); // $t7 = $ra
+ if (tmp.operation != LLIL_SET_REG) return false;
+ if (tmp.GetDestRegister<LLIL_SET_REG>() != REG_T7) return false; // $t7
+ tmp = tmp.GetSourceExpr<LLIL_SET_REG>(); // $ra
+ if (tmp.operation != LLIL_REG) return false;
+ if (tmp.GetSourceRegister<LLIL_REG>() != REG_RA) return false;
+
+ // test instruction2
+ tmp = il->GetInstruction(2); // $t8 = ????
+ if (tmp.operation != LLIL_SET_REG) return false;
+ tmp = tmp.GetSourceExpr<LLIL_SET_REG>(); // ????
+ value = il->GetExprValue(tmp); // accept if Binja has resolved to a value
+ if (value.state != ConstantValue) return false;
+
+ // test instruction3
+ tmp = il->GetInstruction(3); // call($t9)
+ if (tmp.operation != LLIL_CALL) return false;
+ tmp = tmp.GetDestExpr<LLIL_CALL>(); // ????
+ if (tmp.GetSourceRegister<LLIL_REG>() != REG_T9) return false;
+
+ // test instruction4
+ tmp = il->GetInstruction(4); // tailcall(??)
+ if (tmp.operation != LLIL_TAILCALL) return false;
+
+ // There should be three symbols:
+ // 1. ImportedFunctionSymbol has address of the PLT stub (where we are now)
+ // 2. ImportAddressSymbol has address of corresponding GOT entry
+ // 3. ExternalSymbol has address of corresponding address in .extern
+ //
+ // We need to locate #3, resolve its type, and apply it to #1
+ Ref<Symbol> pltSym = data->GetSymbolByAddress(func->GetStart());
+
+ if (pltSym)
+ {
+ for (auto& extSym : data->GetSymbolsByName(pltSym->GetRawName()))
+ {
+ if (extSym->GetType() == ExternalSymbol)
+ {
+ DataVariable var;
+ if (data->GetDataVariableAtAddress(extSym->GetAddress(), var))
+ {
+ func->ApplyImportedTypes(pltSym, var.type);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+public:
+ virtual bool RecognizeLowLevelIL(BinaryView* data, Function* func, LowLevelILFunction* il) override
+ {
+ if (RecognizeELFPLTEntries0(data, func, il))
+ return true;
+
+ if (RecognizeELFPLTEntries1(data, func, il))
+ return true;
+
+ return false;
+ }
+};
+
+class MipsElfRelocationHandler: public RelocationHandler
+{
+public:
+
+ bool GetGpAddr(Ref<BinaryView> view, int32_t& gpAddr)
+ {
+ auto sym = view->GetSymbolByRawName("_gp");
+ if (!sym)
+ sym = view->GetSymbolByRawName("__gnu_local_gp");
+ if (!sym)
+ return false;
+ gpAddr = (int32_t)sym->GetAddress();
+ return true;
+ }
+
+
+ virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override
+ {
+ if (len < 4)
+ return false;
+ // All ELF MIPS relocations are implicitAddend
+ auto info = reloc->GetInfo();
+ auto addr = reloc->GetAddress();
+ auto symbol = reloc->GetSymbol();
+ uint64_t target = reloc->GetTarget();
+
+ uint32_t* dest32 = (uint32_t*)dest;
+ uint64_t* dest64 = (uint64_t*)dest;
+ auto swap = [&arch](uint32_t x) { return (arch->GetEndianness() == LittleEndian)? x : bswap32(x); };
+ auto swap64 = [&arch](uint64_t x) { return (arch->GetEndianness() == LittleEndian)? x : bswap64(x); };
+ uint32_t inst = swap(dest32[0]);
+ uint64_t inst64 = swap64(dest64[0]);
+ switch (info.nativeType)
+ {
+ case R_MIPS_JUMP_SLOT:
+ case R_MIPS_COPY:
+ dest32[0] = swap((uint32_t)target);
+ break;
+ case R_MIPS64_COPY:
+ dest64[0] = swap64(target);
+ break;
+ case R_MIPS_32:
+ dest32[0] = swap((uint32_t)(inst + target));
+ break;
+ case R_MIPS_64:
+ dest64[0] = swap64(inst64 + target);
+ break;
+ case R_MIPS_HI16:
+ {
+ // Find the first _LO16 in the list of relocations
+ BNRelocationInfo* cur = info.next;
+ while (cur && (cur->nativeType != R_MIPS_LO16))
+ {
+ cur = cur->next;
+ }
+
+ if (cur)
+ {
+ uint32_t inst2 = *(uint32_t*)(cur->relocationDataCache);
+ Instruction instruction;
+ memset(&instruction, 0, sizeof(instruction));
+ if (mips_decompose(&inst2, sizeof(uint32_t), &instruction, MIPS_32, cur->address, arch->GetEndianness(), 1))
+ break;
+
+ int32_t immediate = swap(inst2) & 0xffff;
+ // ADDIU and LW has a signed immediate we have to subtract
+ if (instruction.operation == MIPS_ADDIU)
+ {
+ immediate = instruction.operands[2].immediate;
+ }
+ else if (instruction.operation == MIPS_LW)
+ {
+ immediate = instruction.operands[1].immediate;
+ }
+ uint32_t ahl = ((inst & 0xffff) << 16) + immediate;
+
+ // ((AHL + S) – (short)(AHL + S)) >> 16
+ dest32[0] = swap((uint32_t)(
+ (inst & ~0xffff) |
+ (((ahl + target) - (short)(ahl + target)) >> 16)
+ ));
+ }
+ else
+ {
+ LogError("No corresponding R_MIPS_LO16 relocation for R_MIPS_HI16 relocation");
+ }
+ break;
+ }
+ case R_MIPS_LO16:
+ {
+ uint32_t ahl = ((inst & 0xffff) + target) & 0xffff;
+ dest32[0] = swap((inst & ~0xffff) | (ahl & 0xffff));
+ break;
+ }
+ case R_MIPS_26:
+ {
+ // ((A << 2) | (P & 0xf0000000) + S) >> 2
+ uint32_t A = (inst & ~0xfc000000) << 2;
+ uint32_t P = (uint32_t)addr;
+ uint32_t S = (uint32_t)target;
+ uint32_t realTarget = (A | (P & 0xf0000000)) + S;
+ dest32[0] = swap(((realTarget >> 2) & ~0xfc000000) | (inst & 0xfc000000));
+ break;
+ }
+ case R_MIPS_GOT16:
+ case R_MIPS_CALL16:
+ {
+ int32_t gpAddr;
+ if (!GetGpAddr(view, gpAddr))
+ break;
+ int32_t vRel16 = (int32_t)(target - gpAddr);
+ dest32[0] = swap((inst & ~0xffff) | (vRel16 & 0xffff));
+ break;
+ }
+ case R_MIPS_REL32:
+ {
+ uint32_t originalValue = inst;
+ uint64_t displacement = target;
+ dest32[0] = swap((uint32_t)(originalValue + displacement));
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
+ {
+ (void)view; (void)arch;
+ for (size_t i = 0; i < result.size(); i++)
+ {
+ result[i].type = StandardRelocationType;
+ result[i].size = 4;
+ result[i].pcRelative = false;
+ result[i].dataRelocation = true;
+ switch (result[i].nativeType)
+ {
+ case R_MIPS_NONE:
+ case R_MIPS_JALR: // Note: optimization hint that can safely be ignored TODO: link-time mutable opcode bytes
+ result[i].type = IgnoredRelocation;
+ break;
+ case R_MIPS_COPY:
+ case R_MIPS64_COPY:
+ result[i].type = ELFCopyRelocationType;
+ break;
+ case R_MIPS_JUMP_SLOT:
+ result[i].type = ELFJumpSlotRelocationType;
+ break;
+ case R_MIPS_HI16:
+ {
+ result[i].dataRelocation = false;
+ result[i].pcRelative = false;
+ // MIPS_HI16 relocations can have multiple MIPS_LO16 relocations following them
+ for (size_t j = i + 1; j < result.size(); j++)
+ {
+ if (result[j].nativeType == R_MIPS_LO16 && result[j].symbolIndex == result[i].symbolIndex)
+ {
+ result[j].type = StandardRelocationType;
+ result[j].size = 4;
+ result[j].pcRelative = false;
+ result[j].dataRelocation = false;
+ result[i].next = new BNRelocationInfo(result[j]);
+ break;
+ }
+ }
+ break;
+ }
+ case R_MIPS_LO16:
+ result[i].pcRelative = false;
+ result[i].dataRelocation = false;
+ break;
+ case R_MIPS_26:
+ result[i].pcRelative = true;
+ result[i].dataRelocation = false;
+ break;
+ case R_MIPS_GOT16:
+ case R_MIPS_CALL16:
+ {
+ // Note: GP addr not avaiable pre-view-finalization, however symbol may exist
+ int32_t gpAddr;
+ if (!GetGpAddr(view, gpAddr))
+ {
+ result[i].type = UnhandledRelocation;
+ LogWarn("Unsupported relocation type: %s : Unable to locate _gp symbol.", GetRelocationString((ElfMipsRelocationType)result[i].nativeType));
+ }
+ break;
+ }
+ case R_MIPS_32:
+ case R_MIPS_64:
+ break;
+
+ case R_MIPS_REL32:
+ /* elfview delivers relocs on a symbol's GOT entry with symbolIndex populated */
+ if (result[i].symbolIndex)
+ {
+ /* UNSUPPORTED! need a binary with R_MIPS_REL32 on GOT entry to test */
+ while(0);
+ }
+ else
+ {
+ break;
+ }
+ default:
+ result[i].type = UnhandledRelocation;
+ LogWarn("Unsupported relocation type: %llu (%s) @0x%llX", result[i].nativeType,
+ GetRelocationString((ElfMipsRelocationType)result[i].nativeType), result[i].address);
+ }
+ }
+ return true;
+ }
+
+ 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;
+ auto info = relocation->GetInfo();
+ size_t result;
+
+ switch (info.nativeType)
+ {
+ case R_MIPS_HI16:
+ case R_MIPS_LO16:
+ case R_MIPS_CALL16:
+ case R_MIPS_GOT16:
+ result = BN_NOCOERCE_EXTERN_PTR;
+ break;
+ default:
+ result = BN_AUTOCOERCE_EXTERN_PTR;
+ break;
+ }
+
+ return result;
+ }
+};
+
+static void InitMipsSettings()
+{
+ Ref<Settings> settings = Settings::Instance();
+
+ settings->RegisterSetting("arch.mips.disassembly.pseudoOps",
+ R"({
+ "title" : "MIPS Disassembly Pseudo-Op",
+ "type" : "boolean",
+ "default" : true,
+ "description" : "Enable use of pseudo-op instructions in MIPS disassembly."
+ })");
+}
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+#ifndef DEMO_VERSION
+ BINARYNINJAPLUGIN void CorePluginDependencies()
+ {
+ AddOptionalPluginDependency("view_elf");
+ AddOptionalPluginDependency("view_pe");
+ }
+#endif
+
+ BINARYNINJAPLUGIN bool CorePluginInit()
+ {
+ InitMipsSettings();
+
+ Architecture* mipsel = new MipsArchitecture("mipsel32", LittleEndian, 32);
+ Architecture* mipseb = new MipsArchitecture("mips32", BigEndian, 32);
+ Architecture* mips64eb = new MipsArchitecture("mips64", BigEndian, 64);
+
+ Architecture::Register(mipsel);
+ Architecture::Register(mipseb);
+ Architecture::Register(mips64eb);
+
+ /* calling conventions */
+ MipsO32CallingConvention* o32LE = new MipsO32CallingConvention(mipsel);
+ MipsO32CallingConvention* o32BE = new MipsO32CallingConvention(mipseb);
+ MipsN64CallingConvention* n64BE = new MipsN64CallingConvention(mips64eb);
+
+ mipsel->RegisterCallingConvention(o32LE);
+ mipseb->RegisterCallingConvention(o32BE);
+ mipsel->SetDefaultCallingConvention(o32LE);
+ mipseb->SetDefaultCallingConvention(o32BE);
+ mips64eb->RegisterCallingConvention(n64BE);
+ mips64eb->SetDefaultCallingConvention(n64BE);
+
+ MipsLinuxSyscallCallingConvention* linuxSyscallLE = new MipsLinuxSyscallCallingConvention(mipsel);
+ MipsLinuxSyscallCallingConvention* linuxSyscallBE = new MipsLinuxSyscallCallingConvention(mipseb);
+ mipsel->RegisterCallingConvention(linuxSyscallLE);
+ mipseb->RegisterCallingConvention(linuxSyscallBE);
+
+ mipsel->RegisterCallingConvention(new MipsLinuxRtlResolveCallingConvention(mipsel));
+ mipseb->RegisterCallingConvention(new MipsLinuxRtlResolveCallingConvention(mipseb));
+ mips64eb->RegisterCallingConvention(new MipsLinuxRtlResolveCallingConvention(mips64eb));
+
+ /* function recognizers */
+ mipsel->RegisterFunctionRecognizer(new MipsImportedFunctionRecognizer());
+ mipseb->RegisterFunctionRecognizer(new MipsImportedFunctionRecognizer());
+
+ mipsel->RegisterRelocationHandler("ELF", new MipsElfRelocationHandler());
+ mipseb->RegisterRelocationHandler("ELF", new MipsElfRelocationHandler());
+ mips64eb->RegisterRelocationHandler("ELF", new MipsElfRelocationHandler());
+
+ // Register the architectures with the binary format parsers so that they know when to use
+ // these architectures for disassembling an executable file
+
+ /* since elfXX_hdr.e_machine == EM_MIPS (8) on both mips and mips64, we adopt the following
+ convention to disambiguate: shift in elf64_hdr.e_ident[EI_CLASS]: */
+ #define EM_MIPS (8)
+ #define EI_CLASS_32 (1)
+ #define EI_CLASS_64 (2)
+ #define ARCH_ID_MIPS32 ((EI_CLASS_32<<16)|EM_MIPS) /* 0x10008 */
+ #define ARCH_ID_MIPS64 ((EI_CLASS_64<<16)|EM_MIPS) /* 0x20008 */
+ BinaryViewType::RegisterArchitecture("ELF", ARCH_ID_MIPS64, BigEndian, mips64eb);
+ BinaryViewType::RegisterArchitecture("ELF", ARCH_ID_MIPS32, LittleEndian, mipsel);
+ BinaryViewType::RegisterArchitecture("ELF", ARCH_ID_MIPS32, BigEndian, mipseb);
+ BinaryViewType::RegisterArchitecture("PE", 0x166, LittleEndian, mipsel);
+ return true;
+ }
+}