summaryrefslogtreecommitdiff
path: root/arch/mips
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
parent0609276712622908254065546102381466033141 (diff)
Move architecture modules into the API repo
Diffstat (limited to 'arch/mips')
-rw-r--r--arch/mips/.gitignore8
-rw-r--r--arch/mips/CMakeLists.txt48
-rw-r--r--arch/mips/LICENSE13
-rw-r--r--arch/mips/README.md19
-rw-r--r--arch/mips/arch_mips.cpp2166
-rw-r--r--arch/mips/il.cpp1251
-rw-r--r--arch/mips/il.h28
-rw-r--r--arch/mips/mips/mips.c2049
-rw-r--r--arch/mips/mips/mips.h881
-rw-r--r--arch/mips/mips/test.c80
-rw-r--r--arch/mips/mips/unittest/mips-test.txt344
-rw-r--r--arch/mips/mips/unittest/unitTest.py370
12 files changed, 7257 insertions, 0 deletions
diff --git a/arch/mips/.gitignore b/arch/mips/.gitignore
new file mode 100644
index 00000000..c79e74ab
--- /dev/null
+++ b/arch/mips/.gitignore
@@ -0,0 +1,8 @@
+CMakeFiles
+CMakeCache.txt
+cmake_install.cmake
+Makefile
+api
+build
+libarch_*
+arch_*.dll
diff --git a/arch/mips/CMakeLists.txt b/arch/mips/CMakeLists.txt
new file mode 100644
index 00000000..b85248c8
--- /dev/null
+++ b/arch/mips/CMakeLists.txt
@@ -0,0 +1,48 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(arch_mips)
+
+if(NOT BN_INTERNAL_BUILD)
+ add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api)
+endif()
+
+file(GLOB SOURCES
+ *.cpp
+ *.h
+ mips/*.c
+ mips/*.h)
+
+if(DEMO)
+ add_library(arch_mips STATIC ${SOURCES})
+else()
+ add_library(arch_mips SHARED ${SOURCES})
+endif()
+
+target_include_directories(arch_mips
+ PRIVATE ${PROJECT_SOURCE_DIR}
+ PRIVATE ${PROJECT_SOURCE_DIR}/mips)
+
+if(WIN32)
+ target_link_directories(arch_mips
+ PRIVATE ${BN_INSTALL_DIR})
+ target_link_libraries(arch_mips binaryninjaapi binaryninjacore)
+else()
+ target_link_libraries(arch_mips binaryninjaapi)
+endif()
+
+set_target_properties(arch_mips PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(arch_mips)
+ set_target_properties(arch_mips PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+endif()
diff --git a/arch/mips/LICENSE b/arch/mips/LICENSE
new file mode 100644
index 00000000..af67d90c
--- /dev/null
+++ b/arch/mips/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2020-2024 Vector 35 Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/arch/mips/README.md b/arch/mips/README.md
new file mode 100644
index 00000000..d1400a6a
--- /dev/null
+++ b/arch/mips/README.md
@@ -0,0 +1,19 @@
+# arch-mips
+
+This is the MIPS architecture plugin that ships with Binary Ninja.
+
+## Building
+
+Building the architecture plugin requires `cmake` 3.9 or above. You will also need the [Binary Ninja API source](https://github.com/Vector35/binaryninja-api).
+
+Run `cmake`. This can be done either from a separate build directory or from the source directory. Once that is complete, run `make` in the build directory to compile the plugin.
+
+The plugin can be found in the root of the build directory as `libarch_mips.so`, `libarch_mips.dylib` or `arch_mips.dll` depending on your platform.
+
+To install the plugin, first launch Binary Ninja and uncheck the "MIPS architecture plugin" option in the "Core Plugins" section. This will cause Binary Ninja to stop loading the bundled plugin so that its replacement can be loaded. Once this is complete, you can copy the plugin into the user plugins directory (you can locate this by using the "Open Plugin Folder" option in the Binary Ninja UI).
+
+**Do not replace the architecture plugin in the Binary Ninja install directory. This will be overwritten every time there is a Binary Ninja update. Use the above process to ensure that updates do not automatically uninstall your custom build.**
+
+## Pull Requests
+
+Please follow whatever formatting conventions are present in the file you edit. Pay attention to curly brackets, spacing, tabs vs. spaces, etc.
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;
+ }
+}
diff --git a/arch/mips/il.cpp b/arch/mips/il.cpp
new file mode 100644
index 00000000..76a62274
--- /dev/null
+++ b/arch/mips/il.cpp
@@ -0,0 +1,1251 @@
+#include "il.h"
+
+using namespace BinaryNinja;
+using namespace mips;
+
+#define INVALID_EXPRID ((uint32_t)-1)
+
+typedef enum {
+ ZeroExtend,
+ SignExtend,
+} ExtendType;
+
+static ExprId SetRegisterOrNop(LowLevelILFunction& il,
+ size_t size,
+ size_t registerSize,
+ uint32_t reg,
+ ExprId expr,
+ ExtendType extend = SignExtend)
+{
+ if (reg == REG_ZERO)
+ return il.Nop();
+ else
+ {
+ if (size < registerSize)
+ {
+ switch (extend)
+ {
+ case ZeroExtend:
+ expr = il.ZeroExtend(registerSize, expr);
+ break;
+ case SignExtend:
+ expr = il.SignExtend(registerSize, expr);
+ break;
+ }
+ }
+ return il.SetRegister(registerSize, reg, expr);
+ }
+}
+
+
+static void ConditionExecute(LowLevelILFunction& il, ExprId cond, ExprId trueCase, ExprId falseCase=INVALID_EXPRID)
+{
+ LowLevelILLabel trueCode, falseCode, done;
+
+ if (falseCase == INVALID_EXPRID)
+ il.AddInstruction(il.If(cond, trueCode, done));
+ else
+ il.AddInstruction(il.If(cond, trueCode, falseCode));
+
+ il.MarkLabel(trueCode);
+ il.AddInstruction(trueCase);
+ il.AddInstruction(il.Goto(done));
+
+ if (falseCase != INVALID_EXPRID)
+ {
+ il.MarkLabel(falseCode);
+ il.AddInstruction(falseCase);
+ il.AddInstruction(il.Goto(done));
+ }
+ il.MarkLabel(done);
+ return;
+}
+
+
+static size_t GetILOperandMemoryAddress(LowLevelILFunction& il, InstructionOperand& operand, size_t addrSize)
+{
+ size_t offset = 0;
+ if (operand.reg == REG_ZERO)
+ return il.ConstPointer(addrSize, operand.immediate);
+
+ if (operand.operandClass == MEM_IMM)
+ {
+ if (operand.immediate >= 0x80000000)
+ offset = il.Sub(addrSize,
+ il.Register(addrSize, operand.reg),
+ il.Const(addrSize, -(int32_t)operand.immediate));
+ else
+ offset = il.Add(addrSize,
+ il.Register(addrSize, operand.reg),
+ il.Const(addrSize, operand.immediate));
+ }
+ else if (operand.operandClass == MEM_REG)
+ {
+ if (operand.immediate >= 0x80000000)
+ offset = il.Sub(addrSize,
+ il.Register(addrSize, operand.reg),
+ il.Register(addrSize, -(int32_t)operand.immediate));
+ else
+ offset = il.Add(addrSize,
+ il.Register(addrSize, operand.reg),
+ il.Register(addrSize, operand.immediate));
+ }
+ return offset;
+}
+
+
+static size_t ReadILOperand(LowLevelILFunction& il,
+ const Instruction& instr,
+ size_t i,
+ size_t registerSize,
+ size_t opSize = SIZE_MAX,
+ bool isAddress = false)
+{
+ if (opSize == SIZE_MAX) {
+ opSize = registerSize;
+ }
+ InstructionOperand operand = instr.operands[i - 1];
+ switch (operand.operandClass)
+ {
+ case NONE:
+ return il.Undefined();
+ case IMM:
+ if (isAddress)
+ return il.Operand(i - 1, il.ConstPointer(registerSize, operand.immediate));
+ return il.Operand(i - 1, il.Const(opSize, operand.immediate));
+ case MEM_REG:
+ case MEM_IMM:
+ return il.Operand(i - 1, il.Load(opSize, GetILOperandMemoryAddress(il, operand, registerSize)));
+ default:
+ if (operand.reg == REG_ZERO)
+ return il.Operand(i - 1, il.Const(opSize, 0));
+ return il.Operand(i - 1, il.Register(opSize, operand.reg));
+ }
+}
+
+
+static size_t WriteILOperand(LowLevelILFunction& il, Instruction& instr, size_t i, size_t addrSize, size_t value)
+{
+ InstructionOperand& operand = instr.operands[i - 1];
+ switch (operand.operandClass)
+ {
+ case NONE:
+ case IMM:
+ return il.Undefined();
+ case MEM_IMM:
+ case MEM_REG:
+ return il.Operand(i - 1, il.Store(addrSize, GetILOperandMemoryAddress(il, operand, addrSize), value));
+ default:
+ return il.Operand(i - 1, SetRegisterOrNop(il, addrSize, addrSize, operand.reg, value));
+ }
+}
+
+
+static size_t DirectJump(Architecture* arch, LowLevelILFunction& il, uint64_t target, size_t addrSize)
+{
+ BNLowLevelILLabel* label = il.GetLabelForAddress(arch, target);
+ if (label)
+ return il.Goto(*label);
+ else
+ return il.Jump(il.ConstPointer(addrSize, target));
+}
+
+
+static void ConditionalJump(Architecture* arch, LowLevelILFunction& il, size_t cond, size_t addrSize, uint64_t t, uint64_t f)
+{
+ BNLowLevelILLabel* trueLabel = il.GetLabelForAddress(arch, t);
+ BNLowLevelILLabel* falseLabel = il.GetLabelForAddress(arch, f);
+
+ if (trueLabel && falseLabel)
+ {
+ il.AddInstruction(il.If(cond, *trueLabel, *falseLabel));
+ return;
+ }
+
+ LowLevelILLabel trueCode, falseCode;
+
+ if (trueLabel)
+ {
+ il.AddInstruction(il.If(cond, *trueLabel, falseCode));
+ il.MarkLabel(falseCode);
+ il.AddInstruction(il.Jump(il.ConstPointer(addrSize, f)));
+ return;
+ }
+
+ if (falseLabel)
+ {
+ il.AddInstruction(il.If(cond, trueCode, *falseLabel));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.Jump(il.ConstPointer(addrSize, t)));
+ return;
+ }
+
+ il.AddInstruction(il.If(cond, trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.Jump(il.ConstPointer(addrSize, t)));
+ il.MarkLabel(falseCode);
+ il.AddInstruction(il.Jump(il.ConstPointer(addrSize, f)));
+}
+
+ExprId GetConditionForInstruction(LowLevelILFunction& il, Instruction& instr, size_t registerSize)
+{
+ switch (instr.operation)
+ {
+ case MIPS_BEQ:
+ case MIPS_BEQL:
+ return il.CompareEqual(registerSize, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize));
+ case MIPS_BNE:
+ case MIPS_BNEL:
+ return il.CompareNotEqual(registerSize, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize));
+ case MIPS_BEQZ:
+ return il.CompareEqual(registerSize, ReadILOperand(il, instr, 1, registerSize), il.Const(registerSize, 0));
+ case MIPS_BGEZ:
+ case MIPS_BGEZL:
+ case MIPS_BGEZAL:
+ return il.CompareSignedGreaterEqual(registerSize, ReadILOperand(il, instr, 1, registerSize), il.Const(registerSize, 0));
+ case MIPS_BGTZ:
+ case MIPS_BGTZL:
+ return il.CompareSignedGreaterThan(registerSize, ReadILOperand(il, instr, 1, registerSize), il.Const(registerSize, 0));
+ case MIPS_BLEZ:
+ case MIPS_BLEZL:
+ return il.CompareSignedLessEqual(registerSize, ReadILOperand(il, instr, 1, registerSize), il.Const(registerSize, 0));
+ case MIPS_BLTZ:
+ case MIPS_BLTZL:
+ case MIPS_BLTZAL:
+ return il.CompareSignedLessThan(registerSize, ReadILOperand(il, instr, 1, registerSize), il.Const(registerSize, 0));
+ case MIPS_BC1F:
+ case MIPS_BC1FL:
+ if (instr.operands[0].operandClass == FLAG)
+ return il.Not(0, il.Flag(instr.operands[0].reg));
+ return il.Not(0, il.Flag(FPCCREG_FCC0));
+ case MIPS_BC1T:
+ case MIPS_BC1TL:
+ if (instr.operands[0].operandClass == FLAG)
+ return il.Flag(instr.operands[0].reg);
+ return il.Flag(FPCCREG_FCC0);
+ default:
+ LogError("Missing conditional: %d", instr.operation);
+ return il.Unimplemented();
+ }
+}
+
+// Get the IL Register for a given cop0 register/selector pair.
+// Returns REG_ZERO for unsupported/unimplemented values.
+static Reg GetCop0Register(uint32_t reg, uint64_t sel)
+{
+ switch (reg) {
+ case 0:
+ switch (sel) {
+ case 0: return REG_INDEX;
+ case 1: return REG_MVP_CONTROL;
+ case 2: return REG_MVP_CONF0;
+ case 3: return REG_MVP_CONF1;
+ }
+ break;
+ case 1:
+ switch (sel) {
+ case 0: return REG_RANDOM;
+ case 1: return REG_VPE_CONTROL;
+ case 2: return REG_VPE_CONF0;
+ case 3: return REG_VPE_CONF1;
+ case 4: return REG_YQ_MASK;
+ case 5: return REG_VPE_SCHEDULE;
+ case 6: return REG_VPE_SCHE_FBACK;
+ case 7: return REG_VPE_OPT;
+ }
+ break;
+ case 2:
+ switch (sel) {
+ case 0: return REG_ENTRY_LO0;
+ case 1: return REG_TC_STATUS;
+ case 2: return REG_TC_BIND;
+ case 3: return REG_TC_RESTART;
+ case 4: return REG_TC_HALT;
+ case 5: return REG_TC_CONTEXT;
+ case 6: return REG_TC_SCHEDULE;
+ case 7: return REG_TC_SCHE_FBACK;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0: return REG_ENTRY_LO1;
+ }
+ break;
+ case 4:
+ switch (sel) {
+ case 0: return REG_CONTEXT;
+ case 1: return REG_CONTEXT_CONFIG;
+ }
+ break;
+ case 5:
+ switch (sel) {
+ case 0: return REG_PAGE_MASK;
+ case 1: return REG_PAGE_GRAIN;
+ }
+ break;
+ case 6:
+ switch (sel) {
+ case 0: return REG_WIRED;
+ case 1: return REG_SRS_CONF0;
+ case 2: return REG_SRS_CONF1;
+ case 3: return REG_SRS_CONF2;
+ case 4: return REG_SRS_CONF3;
+ case 5: return REG_SRS_CONF4;
+ }
+ break;
+ case 7:
+ switch (sel) {
+ case 0: return REG_HWR_ENA;
+ }
+ break;
+ case 8:
+ switch (sel) {
+ case 0: return REG_BAD_VADDR;
+ }
+ break;
+ case 9:
+ switch (sel) {
+ case 0: return REG_COUNT;
+ }
+ break;
+ case 10:
+ switch (sel) {
+ case 0: return REG_ENTRY_HI;
+ }
+ break;
+ case 11:
+ switch (sel) {
+ case 0: return REG_COMPARE;
+ }
+ break;
+ case 12:
+ switch (sel) {
+ case 0: return REG_STATUS;
+ case 1: return REG_INT_CTL;
+ case 2: return REG_SRS_CTL;
+ case 3: return REG_SRS_MAP;
+ }
+ break;
+ case 13:
+ switch (sel) {
+ case 0: return REG_CAUSE;
+ }
+ break;
+ case 14:
+ switch (sel) {
+ case 0: return REG_EPC;
+ }
+ break;
+ case 15:
+ switch (sel) {
+ case 0: return REG_PR_ID;
+ case 1: return REG_EBASE;
+ }
+ break;
+ case 16:
+ switch (sel) {
+ case 0: return REG_CONFIG;
+ case 1: return REG_CONFIG1;
+ case 2: return REG_CONFIG2;
+ case 3: return REG_CONFIG3;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0: return REG_LLADDR;
+ }
+ break;
+ case 20:
+ switch (sel) {
+ case 0: return REG_XCONTEXT;
+ }
+ break;
+ case 23:
+ switch (sel) {
+ case 0: return REG_DEBUG;
+ case 1: return REG_TRACE_CONTROL;
+ case 2: return REG_TRACE_CONTROL2;
+ case 3: return REG_USER_TRACE_DATA;
+ case 4: return REG_TRACE_BPC;
+ }
+ break;
+ case 24:
+ switch (sel) {
+ case 0: return REG_DEPC;
+ }
+ break;
+ case 26:
+ switch (sel) {
+ case 0: return REG_ERR_CTL;
+ }
+ break;
+ case 27:
+ switch (sel) {
+ case 0: return REG_CACHE_ERR0;
+ case 1: return REG_CACHE_ERR1;
+ case 2: return REG_CACHE_ERR2;
+ case 3: return REG_CACHE_ERR3;
+ }
+ break;
+ case 30:
+ switch (sel) {
+ case 0: return REG_ERROR_EPC;
+ }
+ break;
+ case 31:
+ switch (sel) {
+ case 0: return REG_DESAVE;
+ }
+ break;
+ }
+ return REG_ZERO;
+}
+
+static ExprId MoveFromCoprocessor(unsigned cop, LowLevelILFunction& il, size_t loadSize, uint32_t outReg, uint32_t reg, uint64_t sel)
+{
+ if (cop == 0) {
+ Reg copReg = GetCop0Register(reg, sel);
+ switch (copReg) {
+ case REG_ZERO: /* Unimplemented coprocessor register */
+ break;
+ default:
+ return il.Intrinsic(
+ {RegisterOrFlag::Register(outReg)},
+ loadSize == 4 ? MIPS_INTRIN_MFC0 : MIPS_INTRIN_DMFC0,
+ {il.Register(loadSize, copReg)});
+ }
+ }
+
+ return il.Intrinsic(
+ {RegisterOrFlag::Register(outReg)},
+ loadSize == 4 ? MIPS_INTRIN_MFC_UNIMPLEMENTED : MIPS_INTRIN_DMFC_UNIMPLEMENTED,
+ {il.Const(4, cop), il.Const(4, reg), il.Const(4, sel)});
+}
+
+static ExprId MoveToCoprocessor(unsigned cop, LowLevelILFunction& il, size_t storeSize, uint32_t reg, uint64_t sel, ExprId srcExpr)
+{
+ if (cop == 0) {
+ Reg copReg = GetCop0Register(reg, sel);
+ switch (copReg) {
+ case REG_ZERO: /* Unimplemented coprocessor register */
+ break;
+ default:
+ return il.Intrinsic(
+ {},
+ storeSize == 4 ? MIPS_INTRIN_MTC0 : MIPS_INTRIN_DMTC0,
+ {il.Register(storeSize, copReg), srcExpr});
+ }
+ }
+
+ return il.Intrinsic(
+ {},
+ storeSize == 4 ? MIPS_INTRIN_MTC_UNIMPLEMENTED : MIPS_INTRIN_DMTC_UNIMPLEMENTED,
+ {il.Const(4, cop), il.Const(4, reg), il.Const(4, sel), srcExpr});
+}
+
+bool GetLowLevelILForInstruction(Architecture* arch, uint64_t addr, LowLevelILFunction& il, Instruction& instr, size_t addrSize)
+{
+ LowLevelILLabel trueLabel, falseLabel, doneLabel, dirFlagSet, dirFlagClear, dirFlagDone;
+ InstructionOperand& op1 = instr.operands[0];
+ InstructionOperand& op2 = instr.operands[1];
+ InstructionOperand& op3 = instr.operands[2];
+ InstructionOperand& op4 = instr.operands[3];
+ LowLevelILLabel trueCode, falseCode, again;
+ size_t registerSize = addrSize;
+ switch (instr.operation)
+ {
+ case MIPS_ADD:
+ case MIPS_ADDU:
+ case MIPS_ADDI:
+ case MIPS_ADDIU:
+ if (op2.reg == REG_ZERO)
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, ReadILOperand(il, instr, 3, registerSize, 4)));
+ else
+ il.AddInstruction(
+ SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Add(4,
+ ReadILOperand(il, instr, 2, registerSize, 4),
+ ReadILOperand(il, instr, 3, registerSize, 4))));
+ break;
+ case MIPS_DADD:
+ case MIPS_DADDU:
+ case MIPS_DADDI:
+ case MIPS_DADDIU:
+ if (op2.reg == REG_ZERO)
+ il.AddInstruction(SetRegisterOrNop(il, 8, registerSize, op1.reg, ReadILOperand(il, instr, 3, registerSize)));
+ else
+ il.AddInstruction(
+ SetRegisterOrNop(il, 8, registerSize, op1.reg,
+ il.Add(8,
+ ReadILOperand(il, instr, 2, registerSize),
+ ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_SUB:
+ case MIPS_SUBU:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Sub(4,
+ ReadILOperand(il, instr, 2, registerSize, 4),
+ ReadILOperand(il, instr, 3, registerSize, 4))));
+ break;
+ case MIPS_AND:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg,
+ il.And(4,
+ ReadILOperand(il, instr, 2, registerSize),
+ ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_ANDI:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg,
+ il.And(4,
+ ReadILOperand(il, instr, 2, registerSize),
+ il.Operand(1, il.Const(4, 0x0000ffff & op3.immediate)))));
+ break;
+ case MIPS_DIV:
+ il.AddInstruction(il.SetRegister(4, REG_LO,
+ il.DivSigned(4,
+ ReadILOperand(il, instr, 1, registerSize, 4),
+ ReadILOperand(il, instr, 2, registerSize, 4))));
+ il.AddInstruction(il.SetRegister(4, REG_HI,
+ il.ModSigned(4,
+ ReadILOperand(il, instr, 1, registerSize, 4),
+ ReadILOperand(il, instr, 2, registerSize, 4))));
+ break;
+ case MIPS_DIVU:
+ il.AddInstruction(il.SetRegister(4, REG_LO,
+ il.DivUnsigned(4,
+ ReadILOperand(il, instr, 1, registerSize, 4),
+ ReadILOperand(il, instr, 2, registerSize, 4))));
+ il.AddInstruction(il.SetRegister(4, REG_HI,
+ il.ModUnsigned(4,
+ ReadILOperand(il, instr, 1, registerSize, 4),
+ ReadILOperand(il, instr, 2, registerSize, 4))));
+ break;
+ case MIPS_MUL:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Mult(4,
+ ReadILOperand(il, instr, 2, registerSize, 4),
+ ReadILOperand(il, instr, 3, registerSize, 4))));
+ break;
+ case MIPS_XOR:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Xor(4,
+ ReadILOperand(il, instr, 2, registerSize),
+ ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_XORI:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Xor(4,
+ ReadILOperand(il, instr, 2, registerSize, 4),
+ il.Operand(1,il.Const(4, 0x0000ffff & op3.immediate)))));
+ break;
+ case MIPS_B:
+ case MIPS_J:
+ il.AddInstruction(DirectJump(arch, il, op1.immediate, addrSize));
+ break;
+ case MIPS_JAL:
+ case MIPS_BAL:
+ if (op1.immediate == (addr + 8)) // Get PC construct
+ il.AddInstruction(il.SetRegister(addrSize, REG_RA, il.ConstPointer(addrSize ,addr + 8)));
+ else
+ il.AddInstruction(il.Call(il.ConstPointer(4, op1.immediate)));
+ break;
+
+ case MIPS_BEQ:
+ case MIPS_BNE:
+ case MIPS_BEQL: //Branch likely
+ case MIPS_BNEL:
+ ConditionalJump(arch, il, GetConditionForInstruction(il, instr, registerSize), addrSize, op3.immediate, addr + 8);
+ return false;
+
+ case MIPS_BEQZ:
+ case MIPS_BGEZ:
+ case MIPS_BGTZ:
+ case MIPS_BLEZ:
+ case MIPS_BLTZ:
+ case MIPS_BGEZL: //Branch likely
+ case MIPS_BGTZL:
+ case MIPS_BLEZL:
+ case MIPS_BLTZL:
+ ConditionalJump(arch, il, GetConditionForInstruction(il, instr, registerSize), addrSize, op2.immediate, addr + 8);
+ return false;
+
+ case MIPS_BC1F:
+ case MIPS_BC1FL:
+ if (op1.operandClass == FLAG)
+ ConditionalJump(arch, il, il.Not(0, il.Flag(op1.reg)), addrSize, op2.immediate, addr + 8);
+ else
+ ConditionalJump(arch, il, il.Not(0, il.Flag(FPCCREG_FCC0)), addrSize, op1.immediate, addr + 8);
+ return false;
+
+ case MIPS_BC1T:
+ case MIPS_BC1TL:
+ if (op1.operandClass == FLAG)
+ ConditionalJump(arch, il, il.Flag(op1.reg), addrSize, op2.immediate, addr + 8);
+ else
+ ConditionalJump(arch, il, il.Flag(FPCCREG_FCC0), addrSize, op1.immediate, addr + 8);
+ return false;
+
+ case MIPS_BGEZAL:
+ case MIPS_BLTZAL:
+ il.AddInstruction(il.If(GetConditionForInstruction(il, instr, registerSize), trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.Call(ReadILOperand(il, instr, 2, registerSize)));
+ il.MarkLabel(falseCode);
+ break;
+
+ case MIPS_BREAK:
+ il.AddInstruction(il.Breakpoint());
+ break;
+ case MIPS_CLO:
+ //count leading ones
+ //algorithm is as follows
+ //
+ //tmp0 = 0;
+ //again:
+ //if (((op2 << tmp) & 0x80000000) != 0)
+ //{
+ // tmp0 += 1;
+ // goto again;
+ //}
+ //
+ il.AddInstruction(il.SetRegister(1, LLIL_TEMP(0), il.Const(4,0)));
+ il.MarkLabel(again);
+ il.AddInstruction(il.If(il.CompareNotEqual(4,
+ il.And(4, il.ShiftLeft(4, ReadILOperand(il, instr, 2, registerSize, 4), il.Register(1, LLIL_TEMP(0))), il.Const(4, 0x80000000)),
+ il.Const(4,0)), trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.SetRegister(1, LLIL_TEMP(0), il.Add(1, il.Const(1,1), il.Register(1, LLIL_TEMP(0)))));
+ il.AddInstruction(il.Goto(again));
+ il.MarkLabel(falseCode);
+ break;
+ case MIPS_CLZ:
+ //count leading ones
+ //algorithm is as follows
+ //
+ //tmp0 = 0;
+ //again:
+ //if (((op2 << tmp) & 0x80000000) != 0)
+ //{
+ // tmp0 += 1;
+ // goto again;
+ //}
+ //
+ il.AddInstruction(il.SetRegister(1, LLIL_TEMP(0), il.Const(4,0)));
+ il.MarkLabel(again);
+ il.AddInstruction(il.If(il.CompareEqual(4,
+ il.And(4, il.ShiftLeft(4, ReadILOperand(il, instr, 2, registerSize, 4), il.Register(1, LLIL_TEMP(0))), il.Const(4, 0x80000000)),
+ il.Const(4,0)), trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.SetRegister(1, LLIL_TEMP(0), il.Add(1, il.Const(1,1), il.Register(1, LLIL_TEMP(0)))));
+ il.AddInstruction(il.Goto(again));
+ il.MarkLabel(falseCode);
+ break;
+ case MIPS_JALR:
+ case MIPS_JALR_HB:
+ {
+ uint32_t operand = 1;
+ if (instr.operands[1].operandClass != NONE)
+ {
+ operand = 2;
+ }
+ il.AddInstruction(il.Call(ReadILOperand(il, instr, operand, registerSize, addrSize, true)));
+ }
+ break;
+ case MIPS_JR:
+ case MIPS_JR_HB:
+ if (op1.reg == REG_RA)
+ il.AddInstruction(il.Return(ReadILOperand(il, instr, 1, registerSize)));
+ else
+ il.AddInstruction(il.Jump(ReadILOperand(il, instr, 1, registerSize)));
+ return false;
+ case MIPS_ERET:
+ il.AddInstruction(il.Return(il.Register(registerSize, REG_ERROR_EPC)));
+ break;
+ case MIPS_LBUX:
+ case MIPS_LBU:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, il.ZeroExtend(registerSize, ReadILOperand(il, instr, 2, registerSize, 1))));
+ break;
+ case MIPS_LB:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, il.SignExtend(registerSize, ReadILOperand(il, instr, 2, registerSize, 1))));
+ break;
+ case MIPS_MFHI:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, il.Register(registerSize, REG_HI)));
+ break;
+ case MIPS_MFLO:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, il.Register(registerSize, REG_LO)));
+ break;
+ case MIPS_MTHI:
+ il.AddInstruction(il.SetRegister(registerSize, REG_HI, ReadILOperand(il, instr, 1, registerSize)));
+ break;
+ case MIPS_MTLO:
+ il.AddInstruction(il.SetRegister(registerSize, REG_LO, ReadILOperand(il, instr, 1, registerSize)));
+ break;
+ case MIPS_DMFC0:
+ il.AddInstruction(MoveFromCoprocessor(0, il, 8, op1.reg, op2.immediate, op3.immediate));
+ break;
+ case MIPS_MFC0:
+ il.AddInstruction(MoveFromCoprocessor(0, il, 4, op1.reg, op2.immediate, op3.immediate));
+ break;
+ case MIPS_MFC1:
+ il.AddInstruction(MoveFromCoprocessor(1, il, 4, op1.reg, op2.immediate, op3.immediate));
+ break;
+ case MIPS_MFC2:
+ il.AddInstruction(MoveFromCoprocessor(2, il, 4, op1.reg, op2.immediate, op3.immediate));
+ break;
+ case MIPS_DMTC0:
+ il.AddInstruction(MoveToCoprocessor(0, il, 8, op2.immediate, op3.immediate, ReadILOperand(il, instr, 1, registerSize)));
+ break;
+ case MIPS_MTC0:
+ il.AddInstruction(MoveToCoprocessor(0, il, 4, op2.immediate, op3.immediate, ReadILOperand(il, instr, 1, registerSize)));
+ break;
+ case MIPS_MTC1:
+ il.AddInstruction(MoveToCoprocessor(1, il, 4, op2.immediate, op3.immediate, ReadILOperand(il, instr, 1, registerSize)));
+ break;
+ case MIPS_MTC2:
+ il.AddInstruction(MoveToCoprocessor(2, il, 4, op2.immediate, op3.immediate, ReadILOperand(il, instr, 1, registerSize)));
+ break;
+ case MIPS_MOVE:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, ReadILOperand(il, instr, 2, registerSize)));
+ break;
+ case MIPS_MOVN:
+ il.AddInstruction(il.If(il.CompareNotEqual(registerSize, ReadILOperand(il, instr, 3, registerSize), il.Const(registerSize, 0)), trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, ReadILOperand(il, instr, 2, registerSize)));
+ il.MarkLabel(falseCode);
+ break;
+ case MIPS_MOVZ:
+ il.AddInstruction(il.If(il.CompareEqual(registerSize, ReadILOperand(il, instr, 3, registerSize), il.Const(registerSize, 0)), trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, ReadILOperand(il, instr, 2, registerSize)));
+ il.MarkLabel(falseCode);
+ break;
+ case MIPS_MSUB:
+ //(HI,LO) = (HI,LO) - (GPR[rs] x GPR[rt])
+ //
+ //tmp0 = REG_HI << 32 | REG_LO
+ //(HI,LO) = tmp0 - (op1 * op2)
+ il.AddInstruction(il.SetRegister(8, LLIL_TEMP(0),
+ il.Or(8, il.ShiftLeft(8, il.Register(4, REG_HI), il.Const(1, 32)), il.ZeroExtend(8, il.Register(4, REG_LO)))));
+ il.AddInstruction(il.SetRegisterSplit(4, REG_HI, REG_LO,
+ il.Sub(8, il.Register(8, LLIL_TEMP(0)),
+ il.MultDoublePrecSigned(4, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize)))));
+ break;
+ case MIPS_MSUBU:
+ //(HI,LO) = (HI,LO) - (GPR[rs] x GPR[rt])
+ //
+ //tmp0 = REG_HI << 32 | REG_LO
+ //(HI,LO) = tmp0 - (op1 * op2)
+ il.AddInstruction(il.SetRegister(8, LLIL_TEMP(0),
+ il.Or(8, il.ShiftLeft(8, il.Register(4, REG_HI), il.Const(1, 32)), il.ZeroExtend(8, il.Register(4, REG_LO)))));
+ il.AddInstruction(il.SetRegisterSplit(4, REG_HI, REG_LO,
+ il.Sub(8, il.Register(8, LLIL_TEMP(0)),
+ il.MultDoublePrecUnsigned(8, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize)))));
+ break;
+ case MIPS_MULT:
+ il.AddInstruction(il.SetRegisterSplit(4, REG_HI, REG_LO, il.MultDoublePrecSigned(8, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize))));
+ break;
+ case MIPS_MULTU:
+ il.AddInstruction(il.SetRegisterSplit(4, REG_HI, REG_LO, il.MultDoublePrecUnsigned(8, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize))));
+ break;
+ case MIPS_NEG:
+ case MIPS_NEGU:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Neg(4, ReadILOperand(il, instr, 2, registerSize))));
+ break;
+ case MIPS_NOT:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Not(4, ReadILOperand(il, instr, 2, registerSize))));
+ break;
+ case MIPS_NOR:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Not(4, il.Or(4, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize)))));
+ break;
+ case MIPS_OR:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Or(4, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_ORI:
+ if (op2.reg == REG_ZERO)
+ il.AddInstruction(il.SetRegister(4, op1.reg, il.Operand(1, il.Const(4, 0x0000ffff & op3.immediate))));
+ else
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Or(4,
+ ReadILOperand(il, instr, 2, registerSize),
+ il.Operand(1, il.Const(4, 0x0000ffff & op3.immediate)))));
+ break;
+ case MIPS_RDHWR:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.Unimplemented()));
+ break;
+ case MIPS_SW:
+ il.AddInstruction(il.Store(4, GetILOperandMemoryAddress(il, op2, addrSize), ReadILOperand(il, instr, 1, registerSize, 4)));
+ break;
+ case MIPS_SD:
+ il.AddInstruction(il.Store(8, GetILOperandMemoryAddress(il, op2, addrSize), ReadILOperand(il, instr, 1, registerSize)));
+ break;
+ case MIPS_SWC1:
+ il.AddInstruction(MoveFromCoprocessor(1, il, 4, LLIL_TEMP(0), op1.immediate, 0));
+ il.AddInstruction(WriteILOperand(il, instr, 1, addrSize, il.Register(4, LLIL_TEMP(0))));
+ break;
+ case MIPS_SWC2:
+ il.AddInstruction(MoveFromCoprocessor(2, il, 4, LLIL_TEMP(0), op1.immediate, 0));
+ il.AddInstruction(WriteILOperand(il, instr, 1, addrSize, il.Register(4, LLIL_TEMP(0))));
+ break;
+ case MIPS_SWC3:
+ il.AddInstruction(MoveFromCoprocessor(3, il, 4, LLIL_TEMP(0), op1.immediate, 0));
+ il.AddInstruction(WriteILOperand(il, instr, 1, addrSize, il.Register(4, LLIL_TEMP(0))));
+ break;
+ case MIPS_SWL:
+ il.AddInstruction(il.Store(2,
+ GetILOperandMemoryAddress(il, op2, addrSize),
+ il.LogicalShiftRight(4, ReadILOperand(il, instr, 1, registerSize, 4), il.Const(1, 16))));
+ break;
+ case MIPS_SWR:
+ il.AddInstruction(il.Store(2,
+ il.Sub(4, GetILOperandMemoryAddress(il, op2, addrSize), il.Const(4, 1)),
+ il.And(4, ReadILOperand(il, instr, 1, registerSize, 4), il.Const(4, 0xffff))));
+ break;
+ case MIPS_SYSCALL:
+ il.AddInstruction(il.SystemCall());
+ break;
+ case MIPS_EXT:
+ //op1 = op4.imm bits in op2.reg at bit offset op3.imm
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg,
+ il.And(registerSize,
+ il.Const(registerSize, (1<<op4.immediate)-1),
+ il.ShiftLeft(registerSize, ReadILOperand(il, instr, 2, registerSize),
+ il.Const(1, op3.immediate)))));
+ break;
+ case MIPS_INS:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg,
+ il.Or(registerSize,
+ il.And(registerSize,
+ il.Const(registerSize, ((1<<op4.immediate)-1)<<op3.immediate),
+ ReadILOperand(il, instr, 1, registerSize)),
+ il.And(registerSize,
+ il.Const(registerSize, (1<<op4.immediate)-1),
+ ReadILOperand(il, instr, 2, registerSize)))));
+ break;
+ case MIPS_LUI:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.Const(4, op2.immediate << 16)));
+ break;
+ case MIPS_LI:
+ case MIPS_LW:
+ case MIPS_LWX:
+ case MIPS_LL: // TODO: Atomic access primitives
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, ReadILOperand(il, instr, 2, registerSize, 4)));
+ break;
+ case MIPS_LD:
+ il.AddInstruction(SetRegisterOrNop(il, 8, registerSize, op1.reg, ReadILOperand(il, instr, 2, registerSize)));
+ break;
+ case MIPS_SRA:
+ case MIPS_SRAV:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.ArithShiftRight(4, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_SLT:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, il.BoolToInt(registerSize,
+ il.CompareSignedLessThan(registerSize, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize)))));
+ break;
+ case MIPS_SLTI:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, il.BoolToInt(registerSize,
+ il.CompareSignedLessThan(registerSize, ReadILOperand(il, instr, 2, registerSize), il.Const(registerSize, op3.immediate)))));
+ break;
+ case MIPS_SLTIU:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, il.BoolToInt(registerSize,
+ il.CompareUnsignedLessThan(registerSize, ReadILOperand(il, instr, 2, registerSize), il.Const(registerSize, op3.immediate)))));
+ break;
+ case MIPS_SLTU:
+ il.AddInstruction(SetRegisterOrNop(il, registerSize, registerSize, op1.reg, il.BoolToInt(registerSize,
+ il.CompareUnsignedLessThan(registerSize, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize)))));
+ break;
+ case MIPS_SLL:
+ case MIPS_SLLV:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.ShiftLeft(4, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_DSLL:
+ case MIPS_DSLL32:
+ if (registerSize != 8) {
+ il.AddInstruction(il.Unimplemented());
+ break;
+ }
+ il.AddInstruction(SetRegisterOrNop(il, 8, registerSize, op1.reg, il.ShiftLeft(8, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_DSRL:
+ case MIPS_DSRL32:
+ if (registerSize != 8) {
+ il.AddInstruction(il.Unimplemented());
+ break;
+ }
+ il.AddInstruction(SetRegisterOrNop(il, 8, registerSize, op1.reg, il.LogicalShiftRight(8, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_SB:
+ il.AddInstruction(il.Store(1, GetILOperandMemoryAddress(il, op2, addrSize), il.LowPart(1, ReadILOperand(il, instr, 1, registerSize))));
+ break;
+ case MIPS_TRAP:
+ il.AddInstruction(il.Trap(0));
+ break;
+ case MIPS_TEQI:
+ case MIPS_TEQ:
+ ConditionExecute(il, il.CompareEqual(4, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize)),il.Trap(0));
+ break;
+ case MIPS_TNE:
+ case MIPS_TNEI:
+ ConditionExecute(il, il.CompareNotEqual(4, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize)),il.Trap(0));
+ break;
+ case MIPS_TGE:
+ case MIPS_TGEI:
+ ConditionExecute(il, il.CompareSignedGreaterEqual(4, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize)),il.Trap(0));
+ break;
+ case MIPS_TGEIU:
+ case MIPS_TGEU:
+ ConditionExecute(il, il.CompareUnsignedGreaterEqual(4, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize)),il.Trap(0));
+ break;
+ case MIPS_TLT:
+ case MIPS_TLTI:
+ ConditionExecute(il, il.CompareSignedLessThan(4, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize)),il.Trap(0));
+ break;
+ case MIPS_TLTIU:
+ case MIPS_TLTU:
+ ConditionExecute(il, il.CompareUnsignedLessThan(4, ReadILOperand(il, instr, 1, registerSize), ReadILOperand(il, instr, 2, registerSize)),il.Trap(0));
+ break;
+ case MIPS_LH:
+ case MIPS_LHX:
+ case MIPS_LHI:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.SignExtend(4, ReadILOperand(il, instr, 2, registerSize, 2))));
+ break;
+ case MIPS_LHU:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.ZeroExtend(4, ReadILOperand(il, instr, 2, registerSize, 2))));
+ break;
+ case MIPS_LWR:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.And(4,
+ il.Const(4, 0xffff0000),
+ il.Register(4, op1.reg))));
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Or(4,
+ ReadILOperand(il, instr, 2, registerSize, 2),
+ il.Register(4, op1.reg))));
+ break;
+ case MIPS_LWL:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.And(4,
+ il.Const(4, 0xffff),
+ il.Register(4, op1.reg))));
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg,
+ il.Or(4,
+ il.ShiftLeft(4,
+ ReadILOperand(il, instr, 2, registerSize, 2),
+ il.Const(1, 16)),
+ il.Register(4, op1.reg))));
+ break;
+ case MIPS_MADD:
+ il.AddInstruction(il.SetRegister(8, LLIL_TEMP(0),
+ il.MultDoublePrecSigned(8,
+ ReadILOperand(il, instr, 1, registerSize, 4),
+ ReadILOperand(il, instr, 2, registerSize, 4))));
+ il.AddInstruction(il.SetRegister(4, REG_LO,
+ il.Add(4,
+ il.Register(4, REG_LO),
+ il.LowPart(4, il.Register(8, LLIL_TEMP(0))))));
+ il.AddInstruction(il.SetRegister(4, REG_HI,
+ il.Add(4,
+ il.Register(4, REG_HI),
+ il.LogicalShiftRight(4,
+ il.Register(8, LLIL_TEMP(0)),
+ il.Const(1, 16)))));
+ break;
+ case MIPS_MADDU:
+ il.AddInstruction(il.SetRegister(8, LLIL_TEMP(0),
+ il.MultDoublePrecUnsigned(8,
+ ReadILOperand(il, instr, 1, registerSize, 4),
+ ReadILOperand(il, instr, 2, registerSize, 4))));
+ il.AddInstruction(il.SetRegister(4, REG_LO,
+ il.Add(4,
+ il.Register(4, REG_LO),
+ il.LowPart(4, il.Register(8, LLIL_TEMP(0))))));
+ il.AddInstruction(il.SetRegister(4, REG_HI,
+ il.Add(4,
+ il.Register(4, REG_HI),
+ il.LogicalShiftRight(4,
+ il.Register(8, LLIL_TEMP(0)),
+ il.Const(1, 16)))));
+ break;
+ case MIPS_ROTR:
+ case MIPS_ROTRV:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.RotateRight(4, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_SC:
+ il.AddInstruction(il.UnimplementedMemoryRef(4, ReadILOperand(il, instr, 2, registerSize)));
+ break;
+ case MIPS_SDBBP:
+ il.AddInstruction(il.Unimplemented());
+ break;
+ case MIPS_SEB:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.SignExtend(4, il.LowPart(1, ReadILOperand(il, instr, 2, registerSize)))));
+ break;
+ case MIPS_SEH:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.SignExtend(4, il.LowPart(2, ReadILOperand(il, instr, 2, registerSize)))));
+ break;
+ case MIPS_SH:
+ il.AddInstruction(il.Store(2, GetILOperandMemoryAddress(il, op2, addrSize), il.LowPart(2, ReadILOperand(il, instr, 1, registerSize))));
+ break;
+ case MIPS_SRL:
+ case MIPS_SRLV:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.LogicalShiftRight(4, ReadILOperand(il, instr, 2, registerSize), ReadILOperand(il, instr, 3, registerSize))));
+ break;
+ case MIPS_SSNOP:
+ case MIPS_NOP:
+ il.AddInstruction(il.Nop());
+ break;
+ case MIPS_WSBH:
+ il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(op1.reg)}, MIPS_INTRIN_WSBH, {ReadILOperand(il, instr, 2, registerSize)}));
+ break;
+ case MIPS_BGEZALL:
+ case MIPS_BLTZALL:
+ break;
+ case MIPS_ADD_S:
+ il.AddInstruction(il.SetRegister(4, op1.reg, il.FloatAdd(4, il.Register(4, op2.reg), il.Register(4, op3.reg))));
+ break;
+ case MIPS_ADD_D:
+ il.AddInstruction(il.SetRegisterSplit(4, op1.reg | 1, op1.reg & (~1),
+ il.FloatAdd(8, il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)),
+ il.RegisterSplit(4, op3.reg + 1, op3.reg))));
+ break;
+ case MIPS_SUB_S:
+ il.AddInstruction(il.SetRegister(4, op1.reg, il.FloatSub(4, il.Register(4, op2.reg), il.Register(4, op3.reg))));
+ break;
+ case MIPS_SUB_D:
+ il.AddInstruction(il.SetRegisterSplit(4, op1.reg | 1, op1.reg & (~1),
+ il.FloatSub(8, il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)),
+ il.RegisterSplit(4, op3.reg + 1, op3.reg))));
+ break;
+ case MIPS_MUL_S:
+ il.AddInstruction(il.SetRegister(4, op1.reg, il.FloatMult(4, il.Register(4, op2.reg), il.Register(4, op3.reg))));
+ break;
+ case MIPS_MUL_D:
+ il.AddInstruction(il.SetRegisterSplit(4, op1.reg | 1, op1.reg & (~1),
+ il.FloatMult(8, il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)),
+ il.RegisterSplit(4, op3.reg + 1, op3.reg))));
+ break;
+ case MIPS_DIV_S:
+ il.AddInstruction(il.SetRegister(4, op1.reg, il.FloatDiv(4, il.Register(4, op2.reg), il.Register(4, op3.reg))));
+ break;
+ case MIPS_DIV_D:
+ il.AddInstruction(il.SetRegisterSplit(4, op1.reg | 1, op1.reg & (~1),
+ il.FloatDiv(8, il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)),
+ il.RegisterSplit(4, op3.reg + 1, op3.reg))));
+ break;
+ case MIPS_SQRT_S:
+ il.AddInstruction(il.SetRegister(4, op1.reg, il.FloatSqrt(4, il.Register(4, op2.reg))));
+ break;
+ case MIPS_SQRT_D:
+ il.AddInstruction(il.SetRegisterSplit(4, op1.reg | 1, op1.reg & (~1),
+ il.FloatSqrt(8, il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)))));
+ break;
+ case MIPS_CVT_S_W:
+ il.AddInstruction(il.SetRegister(4, op1.reg, il.IntToFloat(4, ReadILOperand(il, instr, 2, registerSize))));
+ break;
+ case MIPS_CVT_D_W:
+ il.AddInstruction(il.SetRegisterSplit(4, op1.reg | 1, op1.reg & (~1),
+ il.IntToFloat(8, ReadILOperand(il, instr, 2, registerSize))));
+ break;
+ case MIPS_CVT_W_S:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.FloatToInt(4, il.Register(4, op2.reg))));
+ break;
+ case MIPS_CVT_W_D:
+ il.AddInstruction(SetRegisterOrNop(il, 4, registerSize, op1.reg, il.FloatToInt(4,
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)))));
+ break;
+ case MIPS_CVT_D_S:
+ il.AddInstruction(il.SetRegisterSplit(4, op1.reg | 1, op1.reg & (~1),
+ il.FloatConvert(8, il.Register(4, op2.reg))));
+ break;
+ case MIPS_CVT_S_D:
+ il.AddInstruction(il.SetRegister(4, op1.reg, il.FloatConvert(4,
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)))));
+ break;
+ case MIPS_C_EQ_S:
+ if (op1.operandClass == FLAG)
+ {
+ il.AddInstruction(il.SetFlag(op1.reg, il.FloatCompareEqual(4,
+ il.Register(4, op2.reg), il.Register(4, op3.reg))));
+ }
+ else
+ {
+ il.AddInstruction(il.SetFlag(FPCCREG_FCC0, il.FloatCompareEqual(4,
+ il.Register(4, op1.reg), il.Register(4, op2.reg))));
+ }
+ break;
+ case MIPS_C_EQ_D:
+ if (op1.operandClass == FLAG)
+ {
+ il.AddInstruction(il.SetFlag(op1.reg, il.FloatCompareEqual(8,
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)),
+ il.RegisterSplit(4, op3.reg | 1, op3.reg & (~1)))));
+ }
+ else
+ {
+ il.AddInstruction(il.SetFlag(FPCCREG_FCC0, il.FloatCompareEqual(8,
+ il.RegisterSplit(4, op1.reg | 1, op1.reg & (~1)),
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)))));
+ }
+ break;
+ case MIPS_C_LE_S:
+ if (op1.operandClass == FLAG)
+ {
+ il.AddInstruction(il.SetFlag(op1.reg, il.FloatCompareLessEqual(4,
+ il.Register(4, op2.reg), il.Register(4, op3.reg))));
+ }
+ else
+ {
+ il.AddInstruction(il.SetFlag(FPCCREG_FCC0, il.FloatCompareLessEqual(4,
+ il.Register(4, op1.reg), il.Register(4, op2.reg))));
+ }
+ break;
+ case MIPS_C_LE_D:
+ if (op1.operandClass == FLAG)
+ {
+ il.AddInstruction(il.SetFlag(op1.reg, il.FloatCompareLessEqual(8,
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)),
+ il.RegisterSplit(4, op3.reg | 1, op3.reg & (~1)))));
+ }
+ else
+ {
+ il.AddInstruction(il.SetFlag(FPCCREG_FCC0, il.FloatCompareLessEqual(8,
+ il.RegisterSplit(4, op1.reg | 1, op1.reg & (~1)),
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)))));
+ }
+ break;
+ case MIPS_C_LT_S:
+ if (op1.operandClass == FLAG)
+ {
+ il.AddInstruction(il.SetFlag(op1.reg, il.FloatCompareLessThan(4,
+ il.Register(4, op2.reg), il.Register(4, op3.reg))));
+ }
+ else
+ {
+ il.AddInstruction(il.SetFlag(FPCCREG_FCC0, il.FloatCompareLessThan(4,
+ il.Register(4, op1.reg), il.Register(4, op2.reg))));
+ }
+ break;
+ case MIPS_C_LT_D:
+ if (op1.operandClass == FLAG)
+ {
+ il.AddInstruction(il.SetFlag(op1.reg, il.FloatCompareLessThan(8,
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)),
+ il.RegisterSplit(4, op3.reg | 1, op3.reg & (~1)))));
+ }
+ else
+ {
+ il.AddInstruction(il.SetFlag(FPCCREG_FCC0, il.FloatCompareLessThan(8,
+ il.RegisterSplit(4, op1.reg | 1, op1.reg & (~1)),
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)))));
+ }
+ break;
+ case MIPS_C_UN_S:
+ if (op1.operandClass == FLAG)
+ {
+ il.AddInstruction(il.SetFlag(op1.reg, il.FloatCompareUnordered(4,
+ il.Register(4, op2.reg), il.Register(4, op3.reg))));
+ }
+ else
+ {
+ il.AddInstruction(il.SetFlag(FPCCREG_FCC0, il.FloatCompareUnordered(4,
+ il.Register(4, op1.reg), il.Register(4, op2.reg))));
+ }
+ break;
+ case MIPS_C_UN_D:
+ if (op1.operandClass == FLAG)
+ {
+ il.AddInstruction(il.SetFlag(op1.reg, il.FloatCompareUnordered(8,
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)),
+ il.RegisterSplit(4, op3.reg | 1, op3.reg & (~1)))));
+ }
+ else
+ {
+ il.AddInstruction(il.SetFlag(FPCCREG_FCC0, il.FloatCompareUnordered(8,
+ il.RegisterSplit(4, op1.reg | 1, op1.reg & (~1)),
+ il.RegisterSplit(4, op2.reg | 1, op2.reg & (~1)))));
+ }
+ break;
+ case MIPS_ADDR:
+ case MIPS_DSLLV:
+ case MIPS_DSRA32:
+ case MIPS_DSRA:
+ case MIPS_DSRAV:
+ case MIPS_DSLV:
+ case MIPS_DSUB:
+ case MIPS_DSUBU:
+ case MIPS_LDL:
+ case MIPS_LDR:
+ case MIPS_LDXC1:
+ case MIPS_LLD:
+ case MIPS_LLO:
+ case MIPS_LUXC1:
+ case MIPS_LWC1:
+ case MIPS_LWC2:
+ case MIPS_LWC3:
+ case MIPS_LWU:
+ case MIPS_LWXC1:
+ case MIPS_MFHC1:
+ case MIPS_MFHC2:
+ case MIPS_MOVT:
+ case MIPS_MULR:
+ case MIPS_SCD:
+ case MIPS_SDC1:
+ case MIPS_SDC2:
+ case MIPS_SDC3:
+ case MIPS_SDL:
+ case MIPS_SDR:
+ case MIPS_SDXC1:
+ case MIPS_LDC1:
+ case MIPS_LDC2:
+ case MIPS_LDC3:
+
+ //unimplemented system functions
+ case MIPS_BC1ANY2:
+ case MIPS_BC1ANY4:
+ case MIPS_BSHFL:
+ case MIPS_C2:
+ case MIPS_CFC1:
+ case MIPS_CFC2:
+ case MIPS_COP0:
+ case MIPS_COP1:
+ case MIPS_COP1X:
+ case MIPS_COP2:
+ case MIPS_COP3:
+ case MIPS_CTC1:
+ case MIPS_CTC2:
+ case MIPS_DERET:
+ case MIPS_DI:
+ case MIPS_DMULT:
+ case MIPS_DMULTU:
+ case MIPS_DRET:
+ case MIPS_EHB:
+ case MIPS_EI:
+ case MIPS_JALX: //Special instruction for switching to MIPS32/microMIPS32/MIPS16e
+ case MIPS_MTHC1:
+ case MIPS_MTHC2:
+ case MIPS_PAUSE:
+ case MIPS_PREF:
+ case MIPS_PREFX:
+ case MIPS_SYNC:
+ case MIPS_SYNCI:
+ case MIPS_TLBP:
+ case MIPS_TLBR:
+ case MIPS_TLBWI:
+ case MIPS_TLBWR:
+ case MIPS_WAIT:
+ case MIPS_WRPGPR:
+ case MIPS_RDPGPR:
+ case MIPS_RECIP1:
+ case MIPS_RECIP2:
+ case MIPS_RECIP:
+ case MIPS_SUXC1:
+ case MIPS_SWXC1:
+ il.AddInstruction(il.Unimplemented());
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ break;
+ }
+ return true;
+}
diff --git a/arch/mips/il.h b/arch/mips/il.h
new file mode 100644
index 00000000..5c03f82b
--- /dev/null
+++ b/arch/mips/il.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+#include "lowlevelilinstruction.h"
+#include "mips.h"
+
+enum MipsIntrinsic : 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,
+ MIPS_INTRIN_INVALID=0xFFFFFFFF,
+};
+
+bool GetLowLevelILForInstruction(
+ BinaryNinja::Architecture* arch,
+ uint64_t addr,
+ BinaryNinja::LowLevelILFunction& il,
+ mips::Instruction& instr,
+ size_t addrSize);
+
+BinaryNinja::ExprId GetConditionForInstruction(BinaryNinja::LowLevelILFunction& il, mips::Instruction& instr, size_t registerSize);
diff --git a/arch/mips/mips/mips.c b/arch/mips/mips/mips.c
new file mode 100644
index 00000000..42bb0311
--- /dev/null
+++ b/arch/mips/mips/mips.c
@@ -0,0 +1,2049 @@
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdlib.h>
+#include <stdio.h>
+#include "mips.h"
+
+#ifdef __cplusplus
+using namespace mips;
+#define restrict __restrict
+#endif
+
+#define REG_ reg
+#define FLAG_ reg
+#define FPREG_ reg
+#define FPCCREG_ reg
+#define CPREG_ reg
+#define IMM_ immediate
+#define LABEL_ immediate
+
+#define VAR(a) a ## _
+#define INS_1(A,a)\
+ do { \
+ instruction->operands[0].operandClass = A;\
+ instruction->operands[0].VAR(A) = a;\
+ } while (0);
+
+#define INS_2(A,a,B,b)\
+ do { \
+ instruction->operands[0].operandClass = A;\
+ instruction->operands[0].VAR(A) = a;\
+ instruction->operands[1].operandClass = B;\
+ instruction->operands[1].VAR(B) = b;\
+ } while (0);
+
+#define INS_3(A,a,B,b,C,c)\
+ do { \
+ instruction->operands[0].operandClass = A;\
+ instruction->operands[0].VAR(A) = a;\
+ instruction->operands[1].operandClass = B;\
+ instruction->operands[1].VAR(B) = b;\
+ instruction->operands[2].operandClass = C;\
+ instruction->operands[2].VAR(C) = c;\
+ } while (0);
+
+#define INS_4(A,a,B,b,C,c,D,d)\
+ do { \
+ instruction->operands[0].operandClass = A;\
+ instruction->operands[0].VAR(A) = a;\
+ instruction->operands[1].operandClass = B;\
+ instruction->operands[1].VAR(B) = b;\
+ instruction->operands[2].operandClass = C;\
+ instruction->operands[2].VAR(C) = c;\
+ instruction->operands[3].operandClass = D;\
+ instruction->operands[3].VAR(D) = d;\
+ } while (0);
+
+
+//Fields are: [Version][opcode_high][opcode_low]
+static Operation mips_base_table[6][8][8] = {
+ { //MIPS version 1
+ {MIPS_INVALID, MIPS_INVALID, MIPS_J, MIPS_JAL, MIPS_BEQ, MIPS_BNE, MIPS_BLEZ, MIPS_BGTZ},
+ {MIPS_ADDI, MIPS_ADDIU, MIPS_SLTI, MIPS_SLTIU, MIPS_ANDI, MIPS_ORI, MIPS_XORI, MIPS_LUI},
+ {MIPS_COP0, MIPS_COP1, MIPS_COP2, MIPS_COP3},
+ {MIPS_LLO, MIPS_LHI, MIPS_TRAP},
+ {MIPS_LB, MIPS_LH, MIPS_LWL, MIPS_LW, MIPS_LBU, MIPS_LHU, MIPS_LWR},
+ {MIPS_SB, MIPS_SH, MIPS_SWL, MIPS_SW, MIPS_INVALID, MIPS_INVALID, MIPS_SWR},
+ {MIPS_INVALID, MIPS_LWC1, MIPS_LWC2, MIPS_LWC3},
+ {MIPS_INVALID, MIPS_SWC1, MIPS_SWC2, MIPS_SWC3}
+ },{ //MIPS version 2
+ {MIPS_INVALID, MIPS_INVALID, MIPS_J, MIPS_JAL, MIPS_BEQ, MIPS_BNE, MIPS_BLEZ, MIPS_BGTZ},
+ {MIPS_ADDI, MIPS_ADDIU, MIPS_SLTI, MIPS_SLTIU, MIPS_ANDI, MIPS_ORI, MIPS_XORI, MIPS_LUI},
+ {MIPS_COP0, MIPS_COP1, MIPS_COP2, MIPS_COP3, MIPS_BEQL, MIPS_BNEL, MIPS_BLEZL, MIPS_BGTZL},
+ {MIPS_INVALID},
+ {MIPS_LB, MIPS_LH, MIPS_LWL, MIPS_LW, MIPS_LBU, MIPS_LHU, MIPS_LWR},
+ {MIPS_SB, MIPS_SH, MIPS_SWL, MIPS_SW, MIPS_INVALID, MIPS_INVALID, MIPS_SWR},
+ {MIPS_LL, MIPS_LWC1, MIPS_LWC2, MIPS_LWC3, MIPS_INVALID, MIPS_LDC1, MIPS_LDC2, MIPS_LDC3},
+ {MIPS_SC, MIPS_SWC1, MIPS_SWC2, MIPS_SWC3, MIPS_INVALID, MIPS_SDC1, MIPS_SDC2, MIPS_SDC3}
+ },{ //MIPS version 3
+ {MIPS_INVALID, MIPS_INVALID, MIPS_J, MIPS_JAL, MIPS_BEQ, MIPS_BNE, MIPS_BLEZ, MIPS_BGTZ},
+ {MIPS_ADDI, MIPS_ADDIU, MIPS_SLTI, MIPS_SLTIU, MIPS_ANDI, MIPS_ORI, MIPS_XORI, MIPS_LUI},
+ {MIPS_COP0, MIPS_COP1, MIPS_COP2, MIPS_INVALID, MIPS_BEQL, MIPS_BNEL, MIPS_BLEZL, MIPS_BGTZL},
+ {MIPS_DADDI, MIPS_DADDIU, MIPS_LDL, MIPS_LDR},
+ {MIPS_LB, MIPS_LH, MIPS_LWL, MIPS_LW, MIPS_LBU, MIPS_LHU, MIPS_LWR, MIPS_LWU},
+ {MIPS_SB, MIPS_SH, MIPS_SWL, MIPS_SW, MIPS_SDL, MIPS_SDR, MIPS_SWR},
+ {MIPS_LL, MIPS_LWC1, MIPS_LWC2, MIPS_INVALID, MIPS_LLD, MIPS_LDC1, MIPS_LDC2, MIPS_LD},
+ {MIPS_SC, MIPS_SWC1, MIPS_SWC2, MIPS_INVALID, MIPS_SCD, MIPS_SDC1, MIPS_SDC2, MIPS_SD}
+ },{ //MIPS version 4
+ {MIPS_INVALID, MIPS_INVALID, MIPS_J, MIPS_JAL, MIPS_BEQ, MIPS_BNE, MIPS_BLEZ, MIPS_BGTZ},
+ {MIPS_ADDI, MIPS_ADDIU, MIPS_SLTI, MIPS_SLTIU, MIPS_ANDI, MIPS_ORI, MIPS_XORI, MIPS_LUI},
+ {MIPS_COP0, MIPS_COP1, MIPS_COP2, MIPS_COP1X, MIPS_BEQL, MIPS_BNEL, MIPS_BLEZL, MIPS_BGTZL},
+ {MIPS_DADDI, MIPS_DADDIU, MIPS_LDL, MIPS_LDR},
+ {MIPS_LB, MIPS_LH, MIPS_LWL, MIPS_LW, MIPS_LBU, MIPS_LHU, MIPS_LWR, MIPS_LWU},
+ {MIPS_SB, MIPS_SH, MIPS_SWL, MIPS_SW, MIPS_SDL, MIPS_SDR, MIPS_SWR},
+ {MIPS_INVALID, MIPS_LWC1, MIPS_LWC2, MIPS_PREF, MIPS_LLD, MIPS_LDC1, MIPS_LDC2, MIPS_LD},
+ {MIPS_INVALID, MIPS_SWC1, MIPS_SWC2, MIPS_INVALID, MIPS_SCD, MIPS_SDC1, MIPS_SDC2, MIPS_SD}
+ },{ //MIPS version 5 (MIPS32)
+ {MIPS_INVALID, MIPS_INVALID, MIPS_J, MIPS_JAL, MIPS_BEQ, MIPS_BNE, MIPS_BLEZ, MIPS_BGTZ},
+ {MIPS_ADDI, MIPS_ADDIU, MIPS_SLTI, MIPS_SLTIU, MIPS_ANDI, MIPS_ORI, MIPS_XORI, MIPS_LUI},
+ {MIPS_COP0, MIPS_COP1, MIPS_COP2, MIPS_COP1X, MIPS_BEQL, MIPS_BNEL, MIPS_BLEZL, MIPS_BGTZL},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_JALX, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_LB, MIPS_LH, MIPS_LWL, MIPS_LW, MIPS_LBU, MIPS_LHU, MIPS_LWR, MIPS_LWU},
+ {MIPS_SB, MIPS_SH, MIPS_SWL, MIPS_SW, MIPS_SDL, MIPS_SDR, MIPS_SWR, MIPS_CACHE},
+ {MIPS_LL, MIPS_LWC1, MIPS_LWC2, MIPS_PREF, MIPS_LLD, MIPS_LDC1, MIPS_LDC2, MIPS_LD},
+ {MIPS_SC, MIPS_SWC1, MIPS_SWC2, MIPS_INVALID, MIPS_SCD, MIPS_SDC1, MIPS_SDC2, MIPS_SD}
+ },{ //MIPS version 6 (MIPS64)
+ {MIPS_INVALID, MIPS_INVALID, MIPS_J, MIPS_JAL, MIPS_BEQ, MIPS_BNE, MIPS_BLEZ, MIPS_BGTZ},
+ {MIPS_ADDI, MIPS_ADDIU, MIPS_SLTI, MIPS_SLTIU, MIPS_ANDI, MIPS_ORI, MIPS_XORI, MIPS_LUI},
+ {MIPS_COP0, MIPS_COP1, MIPS_COP2, MIPS_COP1X, MIPS_BEQL, MIPS_BNEL, MIPS_BLEZL, MIPS_BGTZL},
+ {MIPS_DADDI, MIPS_DADDIU, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_JALX, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_LB, MIPS_LH, MIPS_LWL, MIPS_LW, MIPS_LBU, MIPS_LHU, MIPS_LWR, MIPS_LWU},
+ {MIPS_SB, MIPS_SH, MIPS_SWL, MIPS_SW, MIPS_SDL, MIPS_SDR, MIPS_SWR, MIPS_CACHE},
+ {MIPS_LL, MIPS_LWC1, MIPS_LWC2, MIPS_PREF, MIPS_LLD, MIPS_LDC1, MIPS_LDC2, MIPS_LD},
+ {MIPS_SC, MIPS_SWC1, MIPS_SWC2, MIPS_INVALID, MIPS_SCD, MIPS_SDC1, MIPS_SDC2, MIPS_SD}
+ }
+};
+
+//Fields are: [Version][function_high][function_low]
+static Operation mips_special_table[6][8][8] = {
+ { //MIPS version 1
+ {MIPS_SLL, MIPS_INVALID, MIPS_SRL, MIPS_SRA, MIPS_SLLV, MIPS_INVALID, MIPS_SRLV, MIPS_SRAV},
+ {MIPS_JR, MIPS_JALR, MIPS_INVALID, MIPS_INVALID, MIPS_SYSCALL, MIPS_BREAK},
+ {MIPS_MFHI, MIPS_MTHI, MIPS_MFLO, MIPS_MTLO},
+ {MIPS_MULT, MIPS_MULTU, MIPS_DIV, MIPS_DIVU},
+ {MIPS_ADD, MIPS_ADDU, MIPS_SUB, MIPS_SUBU, MIPS_AND, MIPS_OR, MIPS_XOR, MIPS_NOR},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_SLT, MIPS_SLTU}
+ },{ //MIPS version 2
+ {MIPS_SLL, MIPS_INVALID, MIPS_SRL, MIPS_SRA, MIPS_SLLV, MIPS_INVALID, MIPS_SRLV, MIPS_SRAV},
+ {MIPS_JR, MIPS_JALR, MIPS_INVALID, MIPS_INVALID, MIPS_SYSCALL, MIPS_BREAK, MIPS_INVALID, MIPS_SYNC},
+ {MIPS_MFHI, MIPS_MTHI, MIPS_MFLO, MIPS_MTLO},
+ {MIPS_MULT, MIPS_MULTU, MIPS_DIV, MIPS_DIVU},
+ {MIPS_ADD, MIPS_ADDU, MIPS_SUB, MIPS_SUBU, MIPS_AND, MIPS_OR, MIPS_XOR, MIPS_NOR},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_SLT, MIPS_SLTU},
+ {MIPS_TGE, MIPS_TGEU, MIPS_TLT, MIPS_TLTU, MIPS_TEQ, MIPS_INVALID, MIPS_TNE}
+ },{ //MIPS version 3
+ {MIPS_SLL, MIPS_INVALID, MIPS_SRL, MIPS_SRA, MIPS_SLLV, MIPS_INVALID, MIPS_SRLV, MIPS_SRAV},
+ {MIPS_JR, MIPS_JALR, MIPS_INVALID, MIPS_INVALID, MIPS_SYSCALL, MIPS_BREAK, MIPS_INVALID, MIPS_SYNC},
+ {MIPS_MFHI, MIPS_MTHI, MIPS_MFLO, MIPS_MTLO, MIPS_DSLLV, MIPS_INVALID, MIPS_DSLV, MIPS_DSRAV},
+ {MIPS_MULT, MIPS_MULTU, MIPS_DIV, MIPS_DIVU, MIPS_DMULT, MIPS_DMULTU, MIPS_DDIV, MIPS_DDIVU},
+ {MIPS_ADD, MIPS_ADDU, MIPS_SUB, MIPS_SUBU, MIPS_AND, MIPS_OR, MIPS_XOR, MIPS_NOR},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_SLT, MIPS_SLTU, MIPS_DADD, MIPS_DADDU, MIPS_DSUB, MIPS_DSUBU},
+ {MIPS_TGE, MIPS_TGEU, MIPS_TLT, MIPS_TLTU, MIPS_TEQ, MIPS_INVALID, MIPS_TNE},
+ {MIPS_DSLL, MIPS_INVALID, MIPS_DSRL, MIPS_DSRA, MIPS_DSLL32, MIPS_INVALID, MIPS_DSRL32, MIPS_DSRA32}
+ },{ //MIPS version 4
+ {MIPS_SLL, MIPS_MOVCI, MIPS_SRL, MIPS_SRA, MIPS_SLLV, MIPS_INVALID, MIPS_SRLV, MIPS_SRAV},
+ {MIPS_JR, MIPS_JALR, MIPS_MOVZ, MIPS_MOVN, MIPS_SYSCALL, MIPS_BREAK, MIPS_INVALID, MIPS_SYNC},
+ {MIPS_MFHI, MIPS_MTHI, MIPS_MFLO, MIPS_MTLO, MIPS_DSLLV, MIPS_INVALID, MIPS_DSLV, MIPS_DSRAV},
+ {MIPS_MULT, MIPS_MULTU, MIPS_DIV, MIPS_DIVU, MIPS_DMULT, MIPS_DMULTU, MIPS_DDIV, MIPS_DDIVU},
+ {MIPS_ADD, MIPS_ADDU, MIPS_SUB, MIPS_SUBU, MIPS_AND, MIPS_OR, MIPS_XOR, MIPS_NOR},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_SLT, MIPS_SLTU, MIPS_DADD, MIPS_DADDU, MIPS_DSUB, MIPS_DSUBU},
+ {MIPS_TGE, MIPS_TGEU, MIPS_TLT, MIPS_TLTU, MIPS_TEQ, MIPS_INVALID, MIPS_TNE},
+ {MIPS_DSLL, MIPS_INVALID, MIPS_DSRL, MIPS_DSRA, MIPS_DSLL32, MIPS_INVALID, MIPS_DSRL32, MIPS_DSRA32}
+ },{ //MIPS version 5
+ {MIPS_SLL, MIPS_MOVCI, MIPS_SRL, MIPS_SRA, MIPS_SLLV, MIPS_INVALID, MIPS_SRLV, MIPS_SRAV},
+ {MIPS_JR, MIPS_JALR, MIPS_MOVZ, MIPS_MOVN, MIPS_SYSCALL, MIPS_BREAK, MIPS_INVALID, MIPS_SYNC},
+ {MIPS_MFHI, MIPS_MTHI, MIPS_MFLO, MIPS_MTLO},
+ {MIPS_MULT, MIPS_MULTU, MIPS_DIV, MIPS_DIVU},
+ {MIPS_ADD, MIPS_ADDU, MIPS_SUB, MIPS_SUBU, MIPS_AND, MIPS_OR, MIPS_XOR, MIPS_NOR},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_SLT, MIPS_SLTU, MIPS_DADD, MIPS_DADDU, MIPS_DSUB, MIPS_DSUBU},
+ {MIPS_TGE, MIPS_TGEU, MIPS_TLT, MIPS_TLTU, MIPS_TEQ, MIPS_INVALID, MIPS_TNE},
+ {MIPS_DSLL, MIPS_INVALID, MIPS_DSRL, MIPS_DSRA, MIPS_DSLL32, MIPS_INVALID, MIPS_DSRL32, MIPS_DSRA32}
+ },{ //MIPS version 6
+ {MIPS_SLL, MIPS_MOVCI, MIPS_SRL, MIPS_SRA, MIPS_SLLV, MIPS_INVALID, MIPS_SRLV, MIPS_SRAV},
+ {MIPS_JR, MIPS_JALR, MIPS_MOVZ, MIPS_MOVN, MIPS_SYSCALL, MIPS_BREAK, MIPS_INVALID, MIPS_SYNC},
+ {MIPS_MFHI, MIPS_MTHI, MIPS_MFLO, MIPS_MTLO},
+ {MIPS_MULT, MIPS_MULTU, MIPS_DIV, MIPS_DIVU},
+ {MIPS_ADD, MIPS_ADDU, MIPS_SUB, MIPS_SUBU, MIPS_AND, MIPS_OR, MIPS_XOR, MIPS_NOR},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_SLT, MIPS_SLTU, MIPS_DADD, MIPS_DADDU, MIPS_DSUB, MIPS_DSUBU},
+ {MIPS_TGE, MIPS_TGEU, MIPS_TLT, MIPS_TLTU, MIPS_TEQ, MIPS_INVALID, MIPS_TNE},
+ {MIPS_DSLL, MIPS_INVALID, MIPS_DSRL, MIPS_DSRA, MIPS_DSLL32, MIPS_INVALID, MIPS_DSRL32, MIPS_DSRA32}
+ }
+};
+
+static Operation mips_regimm_table[6][4][8] = {
+ { //MIPS version 1
+ {MIPS_BLTZ, MIPS_BGEZ},
+ {MIPS_INVALID},
+ {MIPS_BLTZAL, MIPS_BGEZAL},
+ },{ //MIPS version 2
+ {MIPS_BLTZ, MIPS_BGEZ, MIPS_BLTZL, MIPS_BGEZL},
+ {MIPS_TGEI, MIPS_TGEIU, MIPS_TLTI, MIPS_TLTIU, MIPS_TEQI, MIPS_INVALID, MIPS_TNEI},
+ {MIPS_BLTZAL, MIPS_BGEZAL, MIPS_BLTZALL, MIPS_BGEZALL},
+ },{ //MIPS version 3
+ {MIPS_BLTZ, MIPS_BGEZ, MIPS_BLTZL, MIPS_BGEZL},
+ {MIPS_TGEI, MIPS_TGEIU, MIPS_TLTI, MIPS_TLTIU, MIPS_TEQI, MIPS_INVALID, MIPS_TNEI},
+ {MIPS_BLTZAL, MIPS_BGEZAL, MIPS_BLTZALL, MIPS_BGEZALL},
+ },{ //MIPS version 4
+ {MIPS_BLTZ, MIPS_BGEZ, MIPS_BLTZL, MIPS_BGEZL},
+ {MIPS_TGEI, MIPS_TGEIU, MIPS_TLTI, MIPS_TLTIU, MIPS_TEQI, MIPS_INVALID, MIPS_TNEI},
+ {MIPS_BLTZAL, MIPS_BGEZAL, MIPS_BLTZALL, MIPS_BGEZALL},
+ },{ //MIPS version 5
+ {MIPS_BLTZ, MIPS_BGEZ, MIPS_BLTZL, MIPS_BGEZL},
+ {MIPS_TGEI, MIPS_TGEIU, MIPS_TLTI, MIPS_TLTIU, MIPS_TEQI, MIPS_INVALID, MIPS_TNEI},
+ {MIPS_BLTZAL, MIPS_BGEZAL, MIPS_BLTZALL, MIPS_BGEZALL},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_SYNCI}
+ },{ //MIPS version 6
+ {MIPS_BLTZ, MIPS_BGEZ, MIPS_BLTZL, MIPS_BGEZL},
+ {MIPS_TGEI, MIPS_TGEIU, MIPS_TLTI, MIPS_TLTIU, MIPS_TEQI, MIPS_INVALID, MIPS_TNEI},
+ {MIPS_BLTZAL, MIPS_BGEZAL, MIPS_BLTZALL, MIPS_BGEZALL},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_SYNCI}
+ }
+};
+
+static Operation mips_special2_table[8][8] = {
+ {MIPS_MADD, MIPS_MADDU, MIPS_MUL, MIPS_INVALID, MIPS_MSUB, MIPS_MSUBU},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_CLZ, MIPS_CLO, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_SDBBP}
+};
+
+static Operation mips_special3_table[8][8] = {
+ {MIPS_EXT, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INS, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_LX, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_BSHFL, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_RDHWR, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+};
+
+static Operation mips_v5_cop1_S_table[8][8] = {
+ {MIPS_ADD_S, MIPS_SUB_S, MIPS_MUL_S, MIPS_DIV_S, MIPS_SQRT_S, MIPS_ABS_S, MIPS_MOV_S, MIPS_NEG_S},
+ {MIPS_ROUND_L_S, MIPS_TRUNC_L_S, MIPS_CEIL_L_S, MIPS_FLOOR_L_S, MIPS_ROUND_W_S, MIPS_TRUNC_W_S, MIPS_CEIL_W_S, MIPS_FLOOR_W_S},
+ {MIPS_SEL_S, MIPS_MOVCF, MIPS_MOVZ_S, MIPS_MOVN_S, MIPS_INVALID, MIPS_RECIP_S, MIPS_RSQRT_S, MIPS_INVALID},
+ {MIPS_MADDF_S, MIPS_MSUBF_S, MIPS_RINT_S, MIPS_CLASS_S, MIPS_RECIP2, MIPS_RECIP1, MIPS_RSQRT1, MIPS_RSQRT2},
+ {MIPS_INVALID, MIPS_CVT_D_S, MIPS_INVALID, MIPS_INVALID, MIPS_CVT_W_S, MIPS_CVT_L_S, MIPS_CVT_PS_S, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_C_F_S, MIPS_C_UN_S, MIPS_C_EQ_S, MIPS_C_UEQ_S, MIPS_C_OLT_S, MIPS_C_ULT_S, MIPS_C_OLE_S, MIPS_C_ULE_S},
+ {MIPS_C_SF_S, MIPS_C_NGLE_S, MIPS_C_SEQ_S, MIPS_C_NGL_S, MIPS_C_LT_S, MIPS_C_NGE_S, MIPS_C_LE_S, MIPS_C_NGT_S}
+};
+static Operation mips_v5_cop1_D_table[8][8] = {
+ {MIPS_ADD_D, MIPS_SUB_D, MIPS_MUL_D, MIPS_DIV_D, MIPS_SQRT_D, MIPS_ABS_D, MIPS_MOV_D, MIPS_NEG_D},
+ {MIPS_ROUND_L_D, MIPS_TRUNC_L_D, MIPS_CEIL_L_D, MIPS_FLOOR_L_D, MIPS_ROUND_W_D, MIPS_TRUNC_W_D, MIPS_CEIL_W_D, MIPS_FLOOR_W_D},
+ {MIPS_SEL_D, MIPS_MOVCF, MIPS_MOVZ_D, MIPS_MOVN_D, MIPS_INVALID, MIPS_RECIP_S, MIPS_RSQRT_S, MIPS_INVALID},
+ {MIPS_MADDF_D, MIPS_MSUBF_D, MIPS_RINT_D, MIPS_CLASS_D, MIPS_RECIP2, MIPS_RECIP1, MIPS_RSQRT1, MIPS_RSQRT2},
+ {MIPS_CVT_S_D, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_CVT_W_D, MIPS_CVT_L_D, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_C_F_D, MIPS_C_UN_D, MIPS_C_EQ_D, MIPS_C_UEQ_D, MIPS_C_OLT_D, MIPS_C_ULT_D, MIPS_C_OLE_D, MIPS_C_ULE_D},
+ {MIPS_C_SF_D, MIPS_C_NGLE_D, MIPS_C_SEQ_D, MIPS_C_NGL_D, MIPS_C_LT_D, MIPS_C_NGE_D, MIPS_C_LE_D, MIPS_C_NGT_D}
+};
+static Operation mips_v5_cop1_LW_table[8][8] = {
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_CVT_S_W, MIPS_CVT_D_W, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_CVT_PS_PW, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID}
+};
+
+static Operation mips_v5_cop1_PS_table[8][8] = {
+ {MIPS_ADD_PS, MIPS_SUB_PS, MIPS_MUL_PS, MIPS_DIV_PS, MIPS_SQRT_PS, MIPS_ABS_PS, MIPS_MOV_PS, MIPS_NEG_PS},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_MOVCF, MIPS_MOVZ_PS, MIPS_MOVN_PS, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_ADDR, MIPS_INVALID, MIPS_MULR, MIPS_INVALID, MIPS_RECIP2, MIPS_RECIP1, MIPS_RSQRT1, MIPS_RSQRT2},
+ {MIPS_CVT_S_PU, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_CVT_PW_PS, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_CVT_S_PL, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_PLL_PS, MIPS_PLU_PS, MIPS_PUL_PS, MIPS_PUU_PS},
+ {MIPS_C_F_PS, MIPS_C_UN_PS, MIPS_C_EQ_PS, MIPS_C_UEQ_PS, MIPS_C_OLT_PS, MIPS_C_ULT_PS, MIPS_C_OLE_PS, MIPS_C_ULE_PS},
+ {MIPS_C_SF_PS, MIPS_C_NGLE_PS, MIPS_C_SEQ_PS, MIPS_C_NGL_PS, MIPS_C_LT_PS, MIPS_C_NGE_PS, MIPS_C_LE_PS, MIPS_C_NGT_PS}
+};
+
+static Operation mips_v5_cop1x_table[8][8] = {
+ {MIPS_LWXC1, MIPS_LDXC1, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_LUXC1, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_SWXC1, MIPS_SDXC1, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_SUXC1, MIPS_INVALID, MIPS_PREFX},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID},
+ {MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_ALNV_PS, MIPS_INVALID},
+ {MIPS_MADD_S, MIPS_MADD_D, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_MADD_PS, MIPS_INVALID},
+ {MIPS_MSUB_S, MIPS_MSUB_D, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_MSUB_PS, MIPS_INVALID},
+ {MIPS_NMADD_S, MIPS_NMADD_D, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_NMADD_PS, MIPS_INVALID},
+ {MIPS_NMSUB_S, MIPS_NMSUB_D, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_INVALID, MIPS_NMSUB_PS, MIPS_INVALID},
+};
+
+
+static const char* const OperationStrings[] = {
+ "INVALID",
+ "abs.d",
+ "abs.ps",
+ "abs.s",
+ "add.d",
+ "add.ps",
+ "add.s",
+ "add",
+ "addi",
+ "addiu",
+ "addr",
+ "addu",
+ "align",
+ "alnv.ps",
+ "and",
+ "andi",
+ "b",
+ "bal",
+ "bc1any2",
+ "bc1any4",
+ "bc1eqz",
+ "bc1f",
+ "bc1fl",
+ "bc1nez",
+ "bc1t",
+ "bc1tl",
+ "bc2eqz",
+ "bc2f",
+ "bc2fl",
+ "bc2nez",
+ "bc2t",
+ "bc2tl",
+ "bcnez",
+ "beq",
+ "beql",
+ "beqz",
+ "bgez",
+ "bgezal",
+ "bgezall",
+ "bgezl",
+ "bgtz",
+ "bgtzl",
+ "bitswap",
+ "blez",
+ "blezl",
+ "bltz",
+ "bltzal",
+ "bltzall",
+ "bltzl",
+ "bne",
+ "bnel",
+ "bnz.b",
+ "bnz.d",
+ "bnz.h",
+ "bnz.w",
+ "break",
+ "bshfl",
+ "bz.b",
+ "bz.d",
+ "bz.h",
+ "bz.w",
+ "c.df.d",
+ "c.eq.d",
+ "c.eq.ps",
+ "c.eq.s",
+ "c.eq",
+ "c.f.d",
+ "c.f.ps",
+ "c.f.s",
+ "c.f",
+ "c.le.d",
+ "c.le.ps",
+ "c.le.s",
+ "c.le",
+ "c.lt.d",
+ "c.lt.ps",
+ "c.lt.s",
+ "c.lt",
+ "c.nge.d",
+ "c.nge.ps",
+ "c.nge.s",
+ "c.nge",
+ "c.ngl.d",
+ "c.ngl.ps",
+ "c.ngl.s",
+ "c.ngl",
+ "c.ngle.d",
+ "c.ngle.ps",
+ "c.ngle.s",
+ "c.ngle",
+ "c.ngt.d",
+ "c.ngt.ps",
+ "c.ngt.s",
+ "c.ngt",
+ "c.ole.d",
+ "c.ole.ps",
+ "c.ole.s",
+ "c.ole",
+ "c.olt.d",
+ "c.olt.ps",
+ "c.olt.s",
+ "c.olt",
+ "c.seq.d",
+ "c.seq.ps",
+ "c.seq.s",
+ "c.seq",
+ "c.sf.d",
+ "c.sf.ps",
+ "c.sf.s",
+ "c.sf",
+ "c.ueq.d",
+ "c.ueq.ps",
+ "c.ueq.s",
+ "c.ueq",
+ "c.ule.d",
+ "c.ule.ps",
+ "c.ule.s",
+ "c.ule",
+ "c.ult.d",
+ "c.ult.ps",
+ "c.ult.s",
+ "c.ult",
+ "c.un.d",
+ "c.un.ps",
+ "c.un.s",
+ "c.un",
+ "c1",
+ "c2",
+ "cache",
+ "ceil.l.d",
+ "ceil.l.s",
+ "ceil.l",
+ "ceil.w.d",
+ "ceil.w.s",
+ "ceil.w",
+ "cfc0",
+ "cfc1",
+ "cfc2",
+ "class.d",
+ "class.s",
+ "clo",
+ "clz",
+ "cop0",
+ "cop1",
+ "cop1x",
+ "cop2",
+ "cop3",
+ "ctc0",
+ "ctc1",
+ "ctc2",
+ "cvt.d.s",
+ "cvt.d.w",
+ "cvt.l.d",
+ "cvt.l.s",
+ "cvt.l",
+ "cvt.ps.pw",
+ "cvt.ps.s",
+ "cvt.ps",
+ "cvt.pw.ps",
+ "cvt.s.d",
+ "cvt.s.l",
+ "cvt.s.pl",
+ "cvt.s.pu",
+ "cvt.s.w",
+ "cvt.w.d",
+ "cvt.w.s",
+ "cvt.w",
+ "dadd",
+ "daddi",
+ "daddiu",
+ "daddu",
+ "ddiv",
+ "ddivu",
+ "deret",
+ "di",
+ "div.d",
+ "div.ps",
+ "div.s",
+ "div",
+ "divu",
+ "dmfc0",
+ "dmfc1",
+ "dmfc2",
+ "dmult",
+ "dmultu",
+ "dmtc0",
+ "dmtc1",
+ "dmtc2",
+ "dret",
+ "dsll",
+ "dsll32",
+ "dsllv",
+ "dslv",
+ "dsra",
+ "dsra32",
+ "dsrav",
+ "dsrl",
+ "dsrl32",
+ "dsub",
+ "dsubu",
+ "ehb",
+ "ei",
+ "eret",
+ "ext",
+ "floor.l.d",
+ "floor.l.s",
+ "floor.l",
+ "floor.w.d",
+ "floor.w.s",
+ "floor.w",
+ "ins",
+ "j",
+ "jal",
+ "jalr.hb",
+ "jalr",
+ "jalx",
+ "jr.hb",
+ "jr",
+ "lb",
+ "lbu",
+ "lbux",
+ "ld",
+ "ldc1",
+ "ldc2",
+ "ldc3",
+ "ldl",
+ "ldr",
+ "ldxc1",
+ "lh",
+ "lhi",
+ "lhu",
+ "lhx",
+ "li",
+ "ll",
+ "lld",
+ "llo",
+ "lui",
+ "luxc1",
+ "lw",
+ "lwc1",
+ "lwc2",
+ "lwc3",
+ "lwl",
+ "lwr",
+ "lwu",
+ "lwx",
+ "lwxc1",
+ "lx",
+ "madd.d",
+ "madd.ps",
+ "madd.s",
+ "madd",
+ "maddf.d",
+ "maddf.s",
+ "maddu",
+ "mfc0",
+ "mfc1",
+ "mfc2",
+ "mfhc1",
+ "mfhc2",
+ "mfhi",
+ "mflo",
+ "mov.d",
+ "mov.ps",
+ "mov.s",
+ "movcf",
+ "movci",
+ "move",
+ "movf.d",
+ "movf.ps",
+ "movf.s",
+ "movf",
+ "movn.d",
+ "movn.ps",
+ "movn.s",
+ "movn",
+ "movt.d",
+ "movt.ps",
+ "movt.s",
+ "movt",
+ "movz.d",
+ "movz.ps",
+ "movz.s",
+ "movz",
+ "msub.d",
+ "msub.ps",
+ "msub.s",
+ "msub",
+ "msubf.d",
+ "msubf.s",
+ "msubu",
+ "mtc0",
+ "mtc1",
+ "mtc2",
+ "mthc1",
+ "mthc2",
+ "mthi",
+ "mtlo",
+ "mul.d",
+ "mul.ps",
+ "mul.s",
+ "mul",
+ "mulr",
+ "mult",
+ "multu",
+ "neg.d",
+ "neg.ps",
+ "neg.s",
+ "neg",
+ "negu",
+ "nmadd.d",
+ "nmadd.ps",
+ "nmadd.s",
+ "nmsub.d",
+ "nmsub.ps",
+ "nmsub.s",
+ "nop",
+ "nor",
+ "not",
+ "or",
+ "ori",
+ "pause",
+ "pll.ps",
+ "plu.ps",
+ "pref",
+ "prefx",
+ "pul.ps",
+ "puu.ps",
+ "rdhwr",
+ "rdpgpr",
+ "recip.d",
+ "recip.s",
+ "recip",
+ "recip1",
+ "recip2",
+ "rint.d",
+ "rint.s",
+ "rotr",
+ "rotrv",
+ "round.l.d",
+ "round.l.s",
+ "round.l",
+ "round.w.d",
+ "round.w.s",
+ "round.w",
+ "rsqrt.d",
+ "rsqrt.s",
+ "rsqrt",
+ "rsqrt1",
+ "rsqrt2",
+ "sb",
+ "sc",
+ "scd",
+ "sd",
+ "sdbbp",
+ "sdc1",
+ "sdc2",
+ "sdc3",
+ "sdl",
+ "sdr",
+ "sdxc1",
+ "seb",
+ "seh",
+ "sel.d",
+ "sel.s",
+ "sh",
+ "sll",
+ "sllv",
+ "slt",
+ "slti",
+ "sltiu",
+ "sltu",
+ "sqrt.d",
+ "sqrt.ps",
+ "sqrt.s",
+ "sra",
+ "srav",
+ "srl",
+ "srlv",
+ "ssnop",
+ "sub.d",
+ "sub.ps",
+ "sub.s",
+ "sub",
+ "subu",
+ "suxc1",
+ "sw",
+ "swc1",
+ "swc2",
+ "swc3",
+ "swl",
+ "swr",
+ "swxc1",
+ "sync",
+ "synci",
+ "syscall",
+ "teq",
+ "teqi",
+ "tge",
+ "tgei",
+ "tgeiu",
+ "tgeu",
+ "tlbp",
+ "tlbr",
+ "tlbwi",
+ "tlbwr",
+ "tlt",
+ "tlti",
+ "tltiu",
+ "tltu",
+ "tne",
+ "tnei",
+ "trap",
+ "trunc.l.d",
+ "trunc.l.s",
+ "trunc.l",
+ "trunc.w.d",
+ "trunc.w.s",
+ "trunc.w",
+ "wait",
+ "wrpgpr",
+ "wsbh",
+ "xor",
+ "xori",
+};
+
+static const char * const RegisterStrings[] = {
+ "$zero", // Hardware constant 0
+ "$at", // Reserved for assembler
+ "$v0", // Return values
+ "$v1",
+ "$a0", // Arguments
+ "$a1",
+ "$a2",
+ "$a3",
+ "$t0", // Temporaries
+ "$t1",
+ "$t2",
+ "$t3",
+ "$t4",
+ "$t5",
+ "$t6",
+ "$t7",
+ "$s0", // Saved values
+ "$s1",
+ "$s2",
+ "$s3",
+ "$s4",
+ "$s5",
+ "$s6",
+ "$s7",
+ "$t8", // Cont. Saved values
+ "$t9",
+ "$k0", // Reserved for OS
+ "$k1",
+ "$gp", // Global pointer
+ "$sp", // Stack Pointer
+ "$fp", // Frame Pointer
+ "$ra", // Return Adress
+ "$0",
+ "$1",
+ "$2",
+ "$3",
+ "$4",
+ "$5",
+ "$6",
+ "$7",
+ "$8",
+ "$9",
+ "$10",
+ "$11",
+ "$12",
+ "$13",
+ "$14",
+ "$15",
+ "$16",
+ "$17",
+ "$18",
+ "$19",
+ "$20",
+ "$21",
+ "$22",
+ "$23",
+ "$24",
+ "$25",
+ "$26",
+ "$27",
+ "$28",
+ "$29",
+ "$30",
+ "$31",
+ "$f0",
+ "$f1",
+ "$f2",
+ "$f3",
+ "$f4",
+ "$f5",
+ "$f6",
+ "$f7",
+ "$f8",
+ "$f9",
+ "$f10",
+ "$f11",
+ "$f12",
+ "$f13",
+ "$f14",
+ "$f15",
+ "$f16",
+ "$f17",
+ "$f18",
+ "$f19",
+ "$f20",
+ "$f21",
+ "$f22",
+ "$f23",
+ "$f24",
+ "$f25",
+ "$f26",
+ "$f27",
+ "$f28",
+ "$f29",
+ "$f30",
+ "$f31",
+ "$lo",
+ "$hi",
+ "cop0_Index",
+ "cop0_MVPControl",
+ "cop0_MVPConf0",
+ "cop0_MVPConf1",
+ "cop0_Random",
+ "cop0_VPEControl",
+ "cop0_VPEConf0",
+ "cop0_VPEConf1",
+ "cop0_YQMask",
+ "cop0_VPESchedule",
+ "cop0_VPEScheFBack",
+ "cop0_VPEOpt",
+ "cop0_EntryLo0",
+ "cop0_TCStatus",
+ "cop0_TCBind",
+ "cop0_TCRestart",
+ "cop0_TCHalt",
+ "cop0_TCContext",
+ "cop0_TCSchedule",
+ "cop0_TCScheFBack",
+ "cop0_EntryLo1",
+ "cop0_Context",
+ "cop0_ContextConfig",
+ "cop0_PageMask",
+ "cop0_PageGrain",
+ "cop0_Wired",
+ "cop0_SRSConf0",
+ "cop0_SRSConf1",
+ "cop0_SRSConf2",
+ "cop0_SRSConf3",
+ "cop0_SRSConf4",
+ "cop0_HWREna",
+ "cop0_BadVAddr",
+ "cop0_Count",
+ "cop0_EntryHi",
+ "cop0_Compare",
+ "cop0_Status",
+ "cop0_IntCtl",
+ "cop0_SRSCtl",
+ "cop0_SRSMap",
+ "cop0_Cause",
+ "cop0_EPC",
+ "cop0_PrId",
+ "cop0_EBase",
+ "cop0_Config",
+ "cop0_Config1",
+ "cop0_Config2",
+ "cop0_Config3",
+ "cop0_LLAddr",
+ "cop0_WatchLo",
+ "cop0_WatchHi",
+ "cop0_XContext",
+ "cop0_Debug",
+ "cop0_TraceControl",
+ "cop0_TraceControl2",
+ "cop0_UserTraceData",
+ "cop0_TraceBPC",
+ "cop0_DEPC",
+ "cop0_PerfCnt",
+ "cop0_ErrCtl",
+ "cop0_CacheErr0",
+ "cop0_CacheErr1",
+ "cop0_CacheErr2",
+ "cop0_CacheErr3",
+ "cop0_TagLo",
+ "cop0_DataLo",
+ "cop0_TagHi",
+ "cop0_DataHi",
+ "cop0_ErrorEPC",
+ "cop0_DESAVE",
+};
+
+static const char * const FlagStrings[] = {
+ "$fcc0",
+ "$fcc1",
+ "$fcc2",
+ "$fcc3",
+ "$fcc4",
+ "$fcc5",
+ "$fcc6",
+ "$fcc7"
+};
+
+static const char * const HintStrings[] = {
+ "load", // 0
+ "store", // 1
+ "l1_lru_hint", // 2
+ "3", // 3
+ "load_streamed", // 4
+ "store_streamed", // 5
+ "load_retained", // 6
+ "store_retained", // 7
+ "l2_operation_8",
+ "l2_operation_9",
+ "l2_operation_10",
+ "l2_operation_11",
+ "l2_operation_12",
+ "l2_operation_13",
+ "l2_operation_14",
+ "l2_operation_15", //15
+ "l3_operation_16", //16
+ "l3_operation_17",
+ "l3_operation_18",
+ "l3_operation_19",
+ "l3_operation_20",
+ "l3_operation_21",
+ "l3_operation_22",
+ "l3_operation_23", //23
+ "24", //24
+ "nudge", //25
+ "26", //26
+ "27",
+ "28",
+ "29", //29
+ "PrepareForStore", //30
+ "31" //31
+};
+
+const char* get_operation(Operation operation)
+{
+ if (operation > MIPS_INVALID && operation < MIPS_OPERATION_END)
+ return OperationStrings[operation];
+
+ return NULL;
+}
+
+const char* get_register(Reg reg)
+{
+ if (reg >= 0 && reg < END_REG)
+ return RegisterStrings[reg];
+ return NULL;
+}
+
+const char* get_flag(enum Flag flag)
+{
+ if (flag >= 0 && flag < END_FLAG)
+ return FlagStrings[flag];
+ return NULL;
+}
+
+const char* get_hint(Hint hint)
+{
+ if (hint >= 0 && hint < HINT_END)
+ return HintStrings[hint];
+ return NULL;
+}
+
+uint32_t bswap32(uint32_t x)
+{
+ return ((x << 24) & 0xff000000 ) |
+ ((x << 8) & 0x00ff0000 ) |
+ ((x >> 8) & 0x0000ff00 ) |
+ ((x >> 24) & 0x000000ff );
+}
+
+uint32_t mips_decompose_instruction(
+ combined ins,
+ Instruction* restrict instruction,
+ uint32_t version,
+ uint64_t address)
+{
+ uint64_t registerMask;
+
+ if (version >= MIPS_VERSION_END)
+ return 1;
+ if (version == MIPS_64) {
+ registerMask = 0xFFFFFFFFFFFFFFFFULL;
+ } else {
+ registerMask = 0xFFFFFFFFULL;
+ }
+ if (ins.value == 0)
+ {
+ instruction->operation = MIPS_NOP;
+ return 0;
+ }
+ //Do initial stage 1 decoding
+ switch(ins.value >> 26)
+ {
+ case 0:
+ instruction->operation = mips_special_table[version-1][ins.decode.func_hi][ins.decode.func_lo];
+ break;
+ case 1:
+ instruction->operation = mips_regimm_table[version-1][ins.decode.rt_hi][ins.decode.rt_lo];
+ break;
+ case 0x1c:
+ if (version == MIPS_32)
+ instruction->operation = mips_special2_table[ins.decode.func_hi][ins.decode.func_lo];
+ break;
+ case 0x1f:
+ if (version == MIPS_32)
+ instruction->operation = mips_special3_table[ins.decode.func_hi][ins.decode.func_lo];
+ break;
+ default:
+ instruction->operation = mips_base_table[version-1][ins.decode.op_hi][ins.decode.op_lo];
+ }
+
+ //Now deal with aliases and stage 2 decoding
+ if (version == MIPS_32 || version == MIPS_64)
+ {
+ switch (instruction->operation)
+ {
+ case MIPS_LX:
+ //MIPSDSP extension
+ switch (ins.r.sa)
+ {
+ case 0x00: instruction->operation = MIPS_LWX; break;
+ case 0x04: instruction->operation = MIPS_LHX; break;
+ case 0x06: instruction->operation = MIPS_LBUX; break;
+ default:
+ return 1;
+ }
+ break;
+
+ case MIPS_BSHFL:
+ //Version 5 only but no need for a check
+ switch (ins.r.sa)
+ {
+ case 0x00: instruction->operation = MIPS_BITSWAP; break;
+ case 0x02: instruction->operation = MIPS_WSBH; break;
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b: instruction->operation = MIPS_ALIGN; break;
+ case 0x10: instruction->operation = MIPS_SEB; break;
+ case 0x18: instruction->operation = MIPS_SEH; break;
+ default:
+ return 1;
+ }
+ break;
+ case MIPS_SRL:
+ if (ins.bits.bit21 == 1)
+ instruction->operation = MIPS_ROTR;
+ break;
+ case MIPS_SRLV:
+ if (ins.bits.bit6 == 1)
+ instruction->operation = MIPS_ROTRV;
+ break;
+ case MIPS_COP0:
+ switch (ins.r.rs)
+ {
+ case 0:
+ if (((ins.value >> 3) & 0xff) != 0)
+ return 1;
+ instruction->operation = MIPS_MFC0;
+ break;
+ case 1:
+ if (((ins.value >> 3) & 0xff) != 0)
+ return 1;
+ instruction->operation = MIPS_DMFC0;
+ break;
+ case 2: instruction->operation = MIPS_CFC0; break;
+ case 4: instruction->operation = MIPS_MTC0; break;
+ case 5:
+ if (((ins.value >> 3) & 0xff) != 0)
+ return 1;
+ instruction->operation = MIPS_DMTC0;
+ break;
+ case 6: instruction->operation = MIPS_CTC0; break;
+ case 10: instruction->operation = MIPS_RDPGPR; break;
+ case 11:
+ if (ins.bits.bit5 == 1)
+ instruction->operation = MIPS_EI;
+ else
+ instruction->operation = MIPS_DI;
+ break;
+ case 14: instruction->operation = MIPS_WRPGPR; break;
+ }
+ if (ins.r.rs > 15)
+ {
+ switch (ins.r.function)
+ {
+ case 1: instruction->operation = MIPS_TLBR; break;
+ case 2: instruction->operation = MIPS_TLBWI; break;
+ case 6: instruction->operation = MIPS_TLBWR; break;
+ case 8: instruction->operation = MIPS_TLBP; break;
+ case 24: instruction->operation = MIPS_ERET; break;
+ case 31: instruction->operation = MIPS_DERET; break;
+ case 32: instruction->operation = MIPS_WAIT; break;
+ }
+ }
+ break;
+ case MIPS_COP1:
+ switch (ins.r.rs)
+ {
+ case 0: instruction->operation = MIPS_MFC1; break;
+ case 1:
+ if ((ins.value & 0x7ff) != 0)
+ return 1;
+ instruction->operation = MIPS_DMFC1;
+ break;
+ case 2: instruction->operation = MIPS_CFC1; break;
+ case 3: instruction->operation = MIPS_MFHC1; break;
+ case 4: instruction->operation = MIPS_MTC1; break;
+ case 5:
+ if ((ins.value & 0x7ff) != 0)
+ return 1;
+ instruction->operation = MIPS_DMTC1;
+ break;
+ case 6: instruction->operation = MIPS_CTC1; break;
+ case 7: instruction->operation = MIPS_MTHC1; break;
+ case 8:
+ switch (ins.r.rt & 3)
+ {
+ case 0: instruction->operation = MIPS_BC1F; break;
+ case 1: instruction->operation = MIPS_BC1T; break;
+ case 2: instruction->operation = MIPS_BC1FL; break;
+ case 3: instruction->operation = MIPS_BC1TL; break;
+ }
+ break;
+ case 9:
+ instruction->operation = MIPS_BC1ANY2;
+ if (ins.r.rs == 9)
+ instruction->operation = MIPS_BC1EQZ;
+ else if (ins.r.rs == 13)
+ instruction->operation = MIPS_BC1NEZ;
+ break;
+ case 10: instruction->operation = MIPS_BC1ANY4; break;
+ case 16: //S
+ instruction->operation = mips_v5_cop1_S_table[ins.decode.func_hi][ins.decode.func_lo];
+ if (instruction->operation == MIPS_MOVCF)
+ {
+ if (ins.bits.bit16 == 1)
+ instruction->operation = MIPS_MOVT_S;
+ else
+ instruction->operation = MIPS_MOVF_S;
+ }
+ break;
+ case 17: //D
+ instruction->operation = mips_v5_cop1_D_table[ins.decode.func_hi][ins.decode.func_lo];
+ if (instruction->operation == MIPS_MOVCF)
+ {
+ if (ins.bits.bit16 == 1)
+ instruction->operation = MIPS_MOVT_D;
+ else
+ instruction->operation = MIPS_MOVF_D;
+ }
+ break;
+ case 20: //W
+ case 21: //L
+ instruction->operation = mips_v5_cop1_LW_table[ins.decode.func_hi][ins.decode.func_lo];
+ break;
+ case 22: //PS
+ instruction->operation = mips_v5_cop1_PS_table[ins.decode.func_hi][ins.decode.func_lo];
+ if (instruction->operation == MIPS_MOVCF)
+ {
+ if (ins.bits.bit16 == 1)
+ instruction->operation = MIPS_MOVT_PS;
+ else
+ instruction->operation = MIPS_MOVF_PS;
+ }
+ break;
+ /* Not yet supported
+ * case 24:
+ {
+ Operation operation[8] = {MIPS_BZ_B, MIPS_BZ_H, MIPS_BZ_W, MIPS_BZ_D, MIPS_BNZ_B, MIPS_BNZ_H, MIPS_BNZ_W, MIPS_BNZ_D};
+ instruction->operation = operation[ins.decode.func_lo];
+ }
+ break;
+ */
+ }
+ break;
+ case MIPS_COP2:
+ {
+ if (ins.r.rs < 8)
+ {
+ static const Operation opmap[8] =
+ {
+ MIPS_MFC2, // 00000
+ MIPS_DMFC2, // 00001
+ MIPS_CFC2, // 00010
+ MIPS_MFHC2, // 00011
+ MIPS_MTC2, // 00100
+ MIPS_DMTC2, // 00101
+ MIPS_CTC2, // 00110
+ MIPS_MTHC2 // 00111
+ };
+ instruction->operation = opmap[ins.r.rs];
+ }
+ else if (ins.r.rs == 8)
+ {
+ static const Operation opmap[4] =
+ {
+ MIPS_BC2F, // 01000:00
+ MIPS_BC2FL, // 01000:10
+ MIPS_BC2T, // 01000:01
+ MIPS_BC2TL // 01000:11
+ };
+ instruction->operation = opmap[ins.r.rt & 3];
+ }
+ else if (ins.r.rs < 16)
+ {
+ static const Operation opmap[8] =
+ {
+ MIPS_INVALID, // 01000
+ MIPS_BC2EQZ, // 01001
+ MIPS_LWC2, // 01010
+ MIPS_SWC2, // 01011
+ MIPS_INVALID, // 01100
+ MIPS_BC2NEZ, // 01101
+ MIPS_LDC2, // 01110
+ MIPS_SDC2 // 01111
+ };
+ instruction->operation = opmap[ins.r.rs & 7];
+ }
+ else
+ {
+ instruction->operation = MIPS_COP2;
+ }
+ break;
+ }
+
+ case MIPS_COP1X:
+ instruction->operation = mips_v5_cop1x_table[ins.decode.func_hi][ins.decode.func_lo];
+ break;
+ case MIPS_SLL:
+ if (ins.r.rs == 0 && ins.r.rd == 0 && ins.r.rt == 0)
+ {
+ if (ins.r.sa == 3)
+ instruction->operation = MIPS_EHB;
+ else if (ins.r.sa == 1)
+ instruction->operation = MIPS_SSNOP;
+ else if (ins.r.sa == 5)
+ instruction->operation = MIPS_PAUSE;
+ else if (ins.r.sa == 0)
+ instruction->operation = MIPS_NOP;
+ }
+ break;
+ case MIPS_JR:
+ {
+ uint32_t hint = ins.r.sa & 15;
+ if (ins.r.rt == 0 && ins.r.rd == 0 && ins.r.sa >= 16 &&
+ (hint == LOAD || hint == STORE || hint == LOAD_STREAMED ||
+ hint == STORE_STREAMED || hint == LOAD_RETAINED || hint == STORE_RETAINED))
+ {
+ instruction->operation = MIPS_JR_HB;
+ }
+ }
+ break;
+ case MIPS_JALR:
+ {
+ if (ins.r.rt != 0)
+ return 1;
+ if (ins.r.sa >= 16)
+ instruction->operation = MIPS_JALR_HB;
+ }
+ break;
+ case MIPS_MOVCI:
+ if (ins.bits.bit16 == 1)
+ instruction->operation = MIPS_MOVT;
+ else
+ instruction->operation = MIPS_MOVF;
+ break;
+ case MIPS_NOR:
+ if (ins.r.rt == 0)
+ instruction->operation = MIPS_NOT;
+ break;
+ case MIPS_SUB:
+ if (ins.r.rs == 0)
+ instruction->operation = MIPS_NEG;
+ if (ins.r.rd == 0)
+ instruction->operation = MIPS_NOP;
+ break;
+ case MIPS_SUBU:
+ if (ins.r.rs == 0)
+ instruction->operation = MIPS_NEGU;
+ break;
+ case MIPS_OR:
+ if (((ins.r.rd == ins.r.rt) || (ins.r.rd == ins.r.rs)) &&
+ ((ins.r.rt == ins.r.rs) || (ins.r.rt == 0) || (ins.r.rs == 0)))
+ {
+ instruction->operation = MIPS_NOP;
+ break;
+ }
+ FALL_THROUGH
+ case MIPS_ADDU:
+ if (ins.r.rt == 0)
+ instruction->operation = MIPS_MOVE;
+ if (ins.r.rd == 0)
+ instruction->operation = MIPS_NOP;
+ break;
+ case MIPS_BEQ:
+ if (ins.r.rt == 0)
+ {
+ if (ins.r.rs == 0)
+ instruction->operation = MIPS_B;
+ else
+ instruction->operation = MIPS_BEQZ;
+ }
+ break;
+ case MIPS_BGEZAL:
+ if (ins.r.rs == 0)
+ instruction->operation = MIPS_BAL;
+ default:
+ break;
+ }
+ }
+
+ //Now that we have the proper instructions aliased figure out what our operands are
+ switch(instruction->operation)
+ {
+ //Zero operand instructions
+ case MIPS_DRET:
+ case MIPS_ERET:
+ case MIPS_WAIT:
+ case MIPS_SSNOP:
+ case MIPS_NOP:
+ case MIPS_PAUSE:
+ case MIPS_EHB:
+ break;
+ case MIPS_BREAK:
+ case MIPS_SYSCALL:
+ if (ins.s.code != 0)
+ INS_1(IMM, ins.s.code);
+ break;
+ case MIPS_TLBWR:
+ case MIPS_TLBWI:
+ case MIPS_TLBR:
+ case MIPS_TLBP:
+ if (((ins.value >> 6) & 0x7ff) != 0 || ins.bits.bit25 != 1)
+ return 1;
+ break;
+ case MIPS_SYNC:
+ if (ins.r.rd + ins.r.rs + ins.r.rt != 0)
+ return 1;
+ if (ins.r.sa != 0)
+ INS_1(IMM, ins.r.sa);
+ break;
+ //1 operand instructions
+ case MIPS_COP2:
+ INS_1(IMM, (ins.value & 0x1ffffff))
+ break;
+ case MIPS_SYNCI:
+ instruction->operands[0].operandClass = MEM_IMM;
+ instruction->operands[0].reg = ins.i.rs;
+ instruction->operands[0].immediate = ins.i.immediate;
+ break;
+ case MIPS_SDBBP:
+ INS_1(IMM, ((ins.value >> 6) & 0xfffff))
+ break;
+ case MIPS_JALX:
+ INS_1(LABEL, (ins.j.immediate<<2));
+ break;
+ case MIPS_DI:
+ case MIPS_EI:
+ if (ins.r.rt != 0)
+ INS_1(REG, ins.r.rt)
+ break;
+ case MIPS_MFHI:
+ case MIPS_MFLO:
+ INS_1(REG, ins.r.rd)
+ if (ins.r.sa + ins.r.rt + ins.r.rs != 0)
+ return 1;
+ break;
+ case MIPS_J:
+ case MIPS_JAL:
+ INS_1(LABEL, (address & 0xf0000000) + (((uint32_t)ins.j.immediate)<<2))
+ break;
+ case MIPS_JR:
+ INS_1(REG, ins.r.rs)
+ if (ins.r.rt + ins.r.rd + ins.r.sa != 0)
+ return 1;
+ break;
+ case MIPS_JR_HB:
+ INS_1(REG, ins.r.rs)
+ break;
+ case MIPS_MTHI:
+ case MIPS_MTLO:
+ INS_1(REG,ins.r.rs)
+ if (ins.r.rd + ins.r.rt + ins.r.sa != 0)
+ return 1;
+ break;
+ case MIPS_BAL:
+ case MIPS_B:
+ INS_1(LABEL, (4 + address + (ins.i.immediate<<2)) & registerMask);
+ break;
+ //2 operand instructions
+ case MIPS_JALR_HB:
+ if (ins.r.rd == 31)
+ {
+ INS_1(REG, ins.r.rs)
+ }
+ else
+ {
+ INS_2(REG, ins.r.rd, REG, ins.r.rs)
+ }
+ if (ins.r.rt != 0)
+ return 1;
+ break;
+
+ case MIPS_BC1F:
+ case MIPS_BC1FL:
+ case MIPS_BC1T:
+ case MIPS_BC1TL:
+ if (((ins.value >> 18) & 7) == 0)
+ {
+ INS_1(LABEL, (4 + address + (ins.i.immediate<<2)) & registerMask);
+ }
+ else
+ {
+ INS_2(FLAG, (FPCCREG_FCC0 + ((ins.value >> 18) & 7)), LABEL, (4 + address + (ins.i.immediate<<2)) & registerMask);
+ }
+ break;
+ case MIPS_CLO:
+ case MIPS_CLZ:
+ case MIPS_NOT:
+ case MIPS_MOVE:
+ INS_2(REG, ins.r.rd, REG, ins.r.rs)
+ break;
+ case MIPS_RDHWR:
+ INS_2(REG, ins.r.rt, REG, (CPREG_0 + ins.r.rd));
+ break;
+ case MIPS_TRUNC_W_S:
+ case MIPS_TRUNC_W_D:
+ case MIPS_TRUNC_L_S:
+ case MIPS_TRUNC_L_D:
+ case MIPS_SQRT_S:
+ case MIPS_SQRT_D:
+ case MIPS_RSQRT_S:
+ case MIPS_RSQRT_D:
+ case MIPS_ROUND_W_S:
+ case MIPS_ROUND_W_D:
+ case MIPS_ROUND_L_S:
+ case MIPS_ROUND_L_D:
+ case MIPS_RECIP_S:
+ case MIPS_RECIP_D:
+ INS_2(REG, ins.f.fd + FPREG_F0, REG, ins.f.fs + FPREG_F0);
+ if (ins.f.ft != 0)
+ return 1;
+ break;
+ case MIPS_BC1EQZ:
+ case MIPS_BC1NEZ:
+ INS_2(REG, ins.f.ft + FPREG_F0, IMM, (address + 4 + (ins.i.immediate << 2)) & registerMask);
+ break;
+ case MIPS_BC2EQZ:
+ case MIPS_BC2NEZ:
+ INS_2(REG, (CPREG_0 + ins.r.rt), IMM, (address + 4 + (ins.i.immediate << 2)) & registerMask);
+ break;
+ case MIPS_ABS_S:
+ case MIPS_ABS_D:
+ case MIPS_ABS_PS:
+ case MIPS_CEIL_L_S:
+ case MIPS_CEIL_L_D:
+ case MIPS_CEIL_W_S:
+ case MIPS_CEIL_W_D:
+ case MIPS_CVT_D_S:
+ case MIPS_CVT_D_W:
+ case MIPS_CVT_L_S:
+ case MIPS_CVT_L_D:
+ case MIPS_CVT_S_D:
+ case MIPS_CVT_S_W:
+ case MIPS_CVT_S_L:
+ case MIPS_CVT_S_PL:
+ case MIPS_CVT_S_PU:
+ case MIPS_CVT_W_S:
+ case MIPS_CVT_W_D:
+ case MIPS_FLOOR_L_S:
+ case MIPS_FLOOR_L_D:
+ case MIPS_FLOOR_W_S:
+ case MIPS_FLOOR_W_D:
+ case MIPS_NEG_S:
+ case MIPS_NEG_D:
+ case MIPS_NEG_PS:
+ INS_2(REG, ins.f.fd + FPREG_F0, REG, ins.f.fs + FPREG_F0)
+ break;
+ case MIPS_MOV_S:
+ case MIPS_MOV_D:
+ case MIPS_MOV_PS:
+ INS_2(REG, ins.f.fd + FPREG_F0, REG, ins.f.fs + FPREG_F0)
+ if (ins.f.ft != 0)
+ return 1;
+ break;
+ case MIPS_BGEZ:
+ case MIPS_BGEZAL:
+ case MIPS_BGEZALL:
+ case MIPS_BGEZL:
+ case MIPS_BLTZAL:
+ case MIPS_BLTZALL:
+ case MIPS_BLTZL:
+ case MIPS_BEQZ:
+ INS_2(REG, ins.i.rs, LABEL, (4 + address + (ins.i.immediate<<2)) & registerMask)
+ break;
+ case MIPS_BGTZ:
+ case MIPS_BGTZL:
+ case MIPS_BLEZ:
+ case MIPS_BLEZL:
+ case MIPS_BLTZ:
+ INS_2(REG, ins.i.rs, LABEL, (4 + address + (ins.i.immediate<<2)) & registerMask)
+ if (ins.i.rt != 0)
+ return 1;
+ break;
+ case MIPS_TGEU:
+ case MIPS_TLT:
+ case MIPS_TLTU:
+ case MIPS_TNE:
+ case MIPS_TGE:
+ case MIPS_TEQ:
+ if (ins.t.code != 0)
+ {
+ INS_3(REG, ins.t.rs, REG, ins.t.rt, IMM, ins.t.code);
+ }
+ else
+ {
+ INS_2(REG, ins.t.rs, REG, ins.t.rt);
+ }
+ break;
+ case MIPS_DDIV:
+ case MIPS_DDIVU:
+ case MIPS_DIV:
+ case MIPS_DIVU:
+ case MIPS_DMULT:
+ case MIPS_DMULTU:
+ case MIPS_MULT:
+ case MIPS_MULTU:
+ case MIPS_MADD:
+ case MIPS_MADDU:
+ case MIPS_MSUB:
+ case MIPS_MSUBU:
+ if (ins.r.rd != 0 || ins.r.sa != 0)
+ return 1;
+ INS_2(REG, ins.r.rs, REG, ins.r.rt)
+ break;
+ case MIPS_LUI:
+ if (ins.i.rs != 0)
+ return 1;
+ // Unsigned immediate value
+ INS_2(REG, ins.i.rt, IMM, (ins.i.immediate & 0xffff))
+ break;
+ case MIPS_TEQI:
+ case MIPS_TGEI:
+ case MIPS_TGEIU:
+ case MIPS_TLTI:
+ case MIPS_TLTIU:
+ case MIPS_TNEI:
+ INS_2(REG, ins.i.rs, IMM, ins.i.immediate)
+ break;
+ case MIPS_WSBH:
+ case MIPS_WRPGPR:
+ case MIPS_SEB:
+ case MIPS_SEH:
+ case MIPS_RDPGPR:
+ case MIPS_NEG:
+ case MIPS_NEGU:
+ case MIPS_BITSWAP:
+ INS_2(REG, ins.r.rd, REG, ins.r.rt)
+ break;
+ case MIPS_CFC0:
+ case MIPS_CTC0:
+ case MIPS_CFC1:
+ case MIPS_CTC1:
+ INS_2(REG, ins.r.rt, REG, ins.f.fs + CPREG_0)
+ break;
+ case MIPS_DMFC1:
+ case MIPS_MFC1:
+ case MIPS_MFHC1:
+ case MIPS_DMTC1:
+ case MIPS_MTC1:
+ case MIPS_MTHC1:
+ INS_2(REG, ins.r.rt, REG, ins.f.fs + FPREG_F0)
+ if (ins.r.function + ins.r.sa != 0)
+ return 1;
+ break;
+ case MIPS_DMFC2:
+ case MIPS_MFC2:
+ case MIPS_DMTC2:
+ case MIPS_MTC2:
+ case MIPS_CFC2:
+ case MIPS_MFHC2:
+ case MIPS_CTC2:
+ case MIPS_MTHC2:
+ INS_2(REG, ins.i.rt, IMM, ins.i.immediate);
+ break;
+ case MIPS_JALR:
+ if (ins.r.rd == 31)
+ {
+ INS_1(REG, ins.r.rs);
+ }
+ else
+ {
+ INS_2(REG, ins.r.rd, REG, ins.r.rs);
+ }
+ break;
+ case MIPS_C_F_S:
+ case MIPS_C_SF_S:
+ case MIPS_C_UN_S:
+ case MIPS_C_EQ_S:
+ case MIPS_C_UEQ_S:
+ case MIPS_C_OLT_S:
+ case MIPS_C_ULT_S:
+ case MIPS_C_OLE_S:
+ case MIPS_C_ULE_S:
+ case MIPS_C_F_D:
+ case MIPS_C_SF_D:
+ case MIPS_C_UN_D:
+ case MIPS_C_EQ_D:
+ case MIPS_C_UEQ_D:
+ case MIPS_C_OLT_D:
+ case MIPS_C_ULT_D:
+ case MIPS_C_OLE_D:
+ case MIPS_C_ULE_D:
+ case MIPS_C_F_PS:
+ case MIPS_C_SF_PS:
+ case MIPS_C_UN_PS:
+ case MIPS_C_EQ_PS:
+ case MIPS_C_UEQ_PS:
+ case MIPS_C_OLT_PS:
+ case MIPS_C_ULT_PS:
+ case MIPS_C_OLE_PS:
+ case MIPS_C_ULE_PS:
+ case MIPS_C_NGLE_S:
+ case MIPS_C_SEQ_S:
+ case MIPS_C_NGL_S:
+ case MIPS_C_LT_S:
+ case MIPS_C_NGE_S:
+ case MIPS_C_LE_S:
+ case MIPS_C_NGT_S:
+ case MIPS_C_NGLE_D:
+ case MIPS_C_SEQ_D:
+ case MIPS_C_NGL_D:
+ case MIPS_C_LT_D:
+ case MIPS_C_NGE_D:
+ case MIPS_C_LE_D:
+ case MIPS_C_NGT_D:
+ case MIPS_C_NGLE_PS:
+ case MIPS_C_SEQ_PS:
+ case MIPS_C_NGL_PS:
+ case MIPS_C_LT_PS:
+ case MIPS_C_NGE_PS:
+ case MIPS_C_LE_PS:
+ case MIPS_C_NGT_PS:
+ {
+ uint32_t cc = (ins.value >> 8) & 7;
+ if (cc == 0)
+ {
+ INS_2(REG, ins.f.fs + FPREG_F0, REG, ins.f.ft + FPREG_F0)
+ }
+ else
+ {
+ INS_3(FLAG, cc + FPCCREG_FCC0, REG, ins.f.fs + FPREG_F0, REG, ins.f.ft + FPREG_F0)
+ }
+ }
+ break;
+ case MIPS_CLASS_D:
+ case MIPS_CLASS_S:
+ if (ins.f.ft == 0)
+ return 1;
+ INS_2(REG, ins.f.fd + FPREG_F0, REG, ins.f.fs + FPREG_F0)
+ break;
+
+ case MIPS_LBUX:
+ case MIPS_LHX:
+ case MIPS_LWX:
+ // MIPSDSP extensions
+ instruction->operands[0].operandClass = REG;
+ instruction->operands[1].operandClass = MEM_REG;
+ instruction->operands[0].reg = ins.r.rd;
+ instruction->operands[1].reg = ins.r.rs;
+ instruction->operands[1].immediate = ins.r.rt;
+ break;
+
+ case MIPS_LB:
+ case MIPS_LBU:
+ case MIPS_LD:
+ case MIPS_LDXC1:
+ case MIPS_LDL:
+ case MIPS_LDR:
+ case MIPS_LH:
+ case MIPS_LHU:
+ case MIPS_LL:
+ case MIPS_LLD:
+ case MIPS_LW:
+ case MIPS_LWL:
+ case MIPS_LWR:
+ case MIPS_LWU:
+ case MIPS_SB:
+ case MIPS_SC:
+ case MIPS_SCD:
+ case MIPS_SD:
+ case MIPS_SDL:
+ case MIPS_SDR:
+ case MIPS_SH:
+ case MIPS_SW:
+ case MIPS_SWL:
+ case MIPS_SWR:
+ instruction->operands[0].operandClass = REG;
+ instruction->operands[1].operandClass = MEM_IMM;
+ instruction->operands[0].reg = ins.i.rt;
+ instruction->operands[1].reg = ins.i.rs;
+ instruction->operands[1].immediate = ins.i.immediate;
+ break;
+ case MIPS_PREF:
+ case MIPS_PREFX:
+ case MIPS_CACHE:
+ instruction->operands[0].operandClass = HINT;
+ instruction->operands[1].operandClass = MEM_IMM;
+ instruction->operands[0].immediate = ins.i.rt;
+ instruction->operands[1].reg = ins.i.rs;
+ instruction->operands[1].immediate = ins.i.immediate;
+ break;
+ case MIPS_SUXC1:
+ case MIPS_SWXC1:
+ case MIPS_SDXC1:
+ instruction->operands[0].operandClass = REG;
+ instruction->operands[1].operandClass = MEM_REG;
+ instruction->operands[0].reg = ins.f.fs + FPREG_F0;
+ instruction->operands[1].immediate = ins.f.ft;
+ instruction->operands[1].reg = ins.f.fr;
+ break;
+ case MIPS_LUXC1:
+ case MIPS_LWXC1:
+ if (ins.f.ft != 0)
+ return 1;
+ instruction->operands[0].operandClass = REG;
+ instruction->operands[1].operandClass = MEM_REG;
+ instruction->operands[0].reg = ins.f.fd + FPREG_F0;
+ instruction->operands[1].immediate = ins.f.ft;
+ instruction->operands[1].reg = ins.f.fr;
+ break;
+ case MIPS_SWC1:
+ case MIPS_SWC2:
+ case MIPS_SWC3:
+ case MIPS_LDC1:
+ case MIPS_LDC2:
+ case MIPS_LDC3:
+ case MIPS_SDC1:
+ case MIPS_SDC2:
+ case MIPS_SDC3:
+ case MIPS_LWC1:
+ case MIPS_LWC2:
+ case MIPS_LWC3:
+ instruction->operands[0].operandClass = IMM;
+ instruction->operands[1].operandClass = MEM_IMM;
+ instruction->operands[0].reg = ins.i.rt;
+ instruction->operands[1].reg = ins.i.rs;
+ instruction->operands[1].immediate = ins.i.immediate;
+ break;
+ //3 operand instructions
+ case MIPS_DIV_S:
+ case MIPS_DIV_D:
+ case MIPS_MUL_S:
+ case MIPS_MUL_D:
+ case MIPS_MUL_PS:
+ case MIPS_SUB_S:
+ case MIPS_SUB_D:
+ case MIPS_SUB_PS:
+ case MIPS_PUU_PS:
+ case MIPS_PUL_PS:
+ INS_3(REG, ins.f.fd + FPREG_F0, REG, ins.f.fs + FPREG_F0, REG, ins.f.ft + FPREG_F0);
+ break;
+ case MIPS_ADD_S:
+ case MIPS_ADD_D:
+ case MIPS_ADD_PS:
+ case MIPS_CVT_PS_S:
+ case MIPS_PLU_PS:
+ case MIPS_PLL_PS:
+ case MIPS_SEL_D:
+ case MIPS_SEL_S:
+ case MIPS_MADDF_D:
+ case MIPS_MADDF_S:
+ case MIPS_MSUBF_D:
+ case MIPS_MSUBF_S:
+ INS_3(REG, ins.f.fd + FPREG_F0, REG, ins.f.fs + FPREG_F0, REG, ins.f.ft + FPREG_F0)
+ break;
+ case MIPS_MOVF:
+ case MIPS_MOVT:
+ INS_3(REG, ins.r.rd, REG, ins.r.rs, FLAG, (ins.r.rt>>2) + FPCCREG_FCC0)
+ if (ins.r.sa != 0 || ins.bits.bit17 != 0)
+ return 1;
+ break;
+ case MIPS_MOVF_S:
+ case MIPS_MOVF_D:
+ case MIPS_MOVF_PS:
+ case MIPS_MOVT_S:
+ case MIPS_MOVT_D:
+ case MIPS_MOVT_PS:
+ INS_3(REG, ins.f.fd + FPREG_F0, REG, ins.f.fs + FPREG_F0, FLAG, (ins.r.rt>>2) + FPCCREG_FCC0)
+ if (ins.bits.bit17 != 0)
+ return 1;
+ break;
+ case MIPS_MOVN_S:
+ case MIPS_MOVN_D:
+ case MIPS_MOVN_PS:
+ case MIPS_MOVZ_S:
+ case MIPS_MOVZ_D:
+ case MIPS_MOVZ_PS:
+ INS_3(REG, ins.f.fd + FPREG_F0, REG, ins.f.fs + FPREG_F0, REG, ins.r.rt)
+ break;
+ case MIPS_ADD:
+ case MIPS_ADDU:
+ case MIPS_AND:
+ case MIPS_DADD:
+ case MIPS_DADDU:
+ case MIPS_DSUB:
+ case MIPS_DSUBU:
+ case MIPS_MOVN:
+ case MIPS_MOVZ:
+ case MIPS_NOR:
+ case MIPS_OR:
+ case MIPS_SLT:
+ case MIPS_SLTU:
+ case MIPS_SUB:
+ case MIPS_SUBU:
+ case MIPS_XOR:
+ case MIPS_MUL:
+ INS_3(REG, ins.r.rd, REG, ins.r.rs, REG, ins.r.rt)
+ if (ins.r.sa != 0)
+ return 1;
+ break;
+ case MIPS_ADDI:
+ case MIPS_ADDIU:
+ case MIPS_DADDI:
+ case MIPS_DADDIU:
+ case MIPS_SLTI:
+ case MIPS_SLTIU:
+ INS_3(REG, ins.i.rt, REG, ins.i.rs, IMM, ins.i.immediate)
+ break;
+ case MIPS_ANDI:
+ case MIPS_ORI:
+ case MIPS_XORI:
+ INS_3(REG, ins.i.rt, REG, ins.i.rs, IMM, (ins.i.immediate & 0xffff))
+ break;
+ case MIPS_BEQ:
+ case MIPS_BEQL:
+ case MIPS_BNE:
+ case MIPS_BNEL:
+ INS_3(REG, ins.i.rs, REG, ins.i.rt, LABEL, (4 + address + (ins.i.immediate<<2)) & registerMask)
+ break;
+ case MIPS_ROTR:
+ INS_3(REG, ins.r.rd, REG, ins.r.rt, IMM, ins.r.sa)
+ if (ins.r.rs != 1)
+ return 1;
+ break;
+ case MIPS_DSLL:
+ case MIPS_DSRA:
+ case MIPS_DSRL:
+ case MIPS_SLL:
+ case MIPS_SRL:
+ case MIPS_SRA:
+ INS_3(REG, ins.r.rd, REG, ins.r.rt, IMM, ins.r.sa)
+ if (ins.r.rs != 0)
+ return 1;
+ break;
+ case MIPS_DSLL32:
+ case MIPS_DSRA32:
+ case MIPS_DSRL32:
+ INS_3(REG, ins.r.rd, REG, ins.r.rt, IMM, ins.r.sa+32)
+ if (ins.r.rs != 0)
+ return 1;
+ break;
+ case MIPS_ROTRV:
+ INS_3(REG, ins.r.rd, REG, ins.r.rt, REG, ins.r.rs)
+ if (ins.r.sa != 1)
+ return 1;
+ break;
+ case MIPS_SRLV:
+ case MIPS_DSLLV:
+ case MIPS_DSRAV:
+ case MIPS_DSLV:
+ case MIPS_SLLV:
+ case MIPS_SRAV:
+ INS_3(REG, ins.r.rd, REG, ins.r.rt, REG, ins.r.rs)
+ if (ins.r.sa != 0)
+ return 1;
+ break;
+ case MIPS_DMFC0:
+ case MIPS_DMTC0:
+ case MIPS_MFC0:
+ case MIPS_MTC0:
+ INS_3(REG, ins.r.rt, IMM, ins.r.rd, IMM, (ins.r.function & 7))
+ break;
+ case MIPS_MADD_S:
+ case MIPS_MADD_D:
+ case MIPS_MADD_PS:
+ case MIPS_MSUB_S:
+ case MIPS_MSUB_D:
+ case MIPS_MSUB_PS:
+ case MIPS_NMADD_S:
+ case MIPS_NMADD_D:
+ case MIPS_NMADD_PS:
+ case MIPS_NMSUB_S:
+ case MIPS_NMSUB_D:
+ case MIPS_NMSUB_PS:
+ INS_4(REG, ins.f.fd + FPREG_F0,
+ REG, ins.f.fr + FPREG_F0,
+ REG, ins.f.fs + FPREG_F0,
+ REG, ins.f.ft + FPREG_F0);
+ break;
+ case MIPS_INS:
+ INS_4(REG, ins.r.rt, REG, ins.r.rs, IMM, ins.r.sa, IMM, ((int32_t)ins.r.rd + 1) - ins.r.sa);
+ break;
+ case MIPS_EXT:
+ INS_4(REG, ins.r.rt, REG, ins.r.rs, IMM, ins.r.sa, IMM, ins.r.rd + 1);
+ break;
+ case MIPS_ALIGN:
+ INS_4(REG, ins.r.rd, REG, ins.r.rs, REG, ins.r.rt, IMM, (ins.r.sa & 3));
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+uint32_t mips_disassemble(
+ Instruction* restrict instruction,
+ char* outBuffer,
+ uint32_t outBufferSize)
+{
+ char operands[MAX_OPERANDS][64] = {{0},{0},{0},{0}};
+ char* operandPtr = NULL;
+ for (uint32_t i = 0;
+ i < MAX_OPERANDS && instruction->operands[i].operandClass != NONE; i++)
+ {
+ operandPtr = operands[i];
+ if (i != 0)
+ {
+ *operandPtr++ = ',';
+ *operandPtr++ = ' ';
+ }
+ switch(instruction->operands[i].operandClass)
+ {
+ case REG:
+ if (instruction->operands[i].reg < END_REG)
+ strcpy(operandPtr, RegisterStrings[instruction->operands[i].reg]);
+ break;
+ case HINT:
+ if (instruction->operands[i].reg < HINT_END)
+ strcpy(operandPtr, HintStrings[instruction->operands[i].reg]);
+ break;
+ case IMM:
+ case LABEL:
+ if (instruction->operands[i].immediate >= 0x80000000)
+ snprintf(operandPtr, 64, "-%#x", -(int32_t)instruction->operands[i].immediate);
+ else
+ snprintf(operandPtr, 64, "%#llx", instruction->operands[i].immediate);
+ break;
+ case MEM_IMM:
+ if (instruction->operands[i].immediate != 0)
+ {
+ if (instruction->operands[i].immediate >= 0x80000000)
+ {
+ snprintf(operandPtr, 64, "-%#x(%s)",
+ -(int32_t)instruction->operands[i].immediate,
+ RegisterStrings[instruction->operands[i].reg]);
+ }
+ else
+ {
+ snprintf(operandPtr, 64, "%#llx(%s)",
+ instruction->operands[i].immediate,
+ RegisterStrings[instruction->operands[i].reg]);
+ }
+ }
+ else
+ {
+ snprintf(operandPtr, 64, "(%s)", RegisterStrings[instruction->operands[i].reg]);
+ }
+ break;
+ case MEM_REG:
+ snprintf(operandPtr, 64, "%s(%s)",
+ RegisterStrings[instruction->operands[i].immediate],
+ RegisterStrings[instruction->operands[i].reg]);
+ break;
+ }
+ }
+ if (instruction->operation != MIPS_INVALID && instruction->operation < MIPS_OPERATION_END)
+ {
+ snprintf(outBuffer, outBufferSize, "%s\t%s%s%s%s",
+ OperationStrings[instruction->operation],
+ operands[0],
+ operands[1],
+ operands[2],
+ operands[3]);
+ return 0;
+ }
+ return 1;
+}
+
+
+uint32_t mips_decompose(
+ const uint32_t* instructionValue,
+ size_t size,
+ Instruction* restrict instruction,
+ uint32_t version,
+ uint64_t address,
+ uint32_t endianBig,
+ uint32_t enablePseudoOps)
+{
+ combined ins;
+ if (instructionValue == NULL)
+ return 1;
+
+ if (endianBig == 1)
+ ins.value = bswap32(instructionValue[0]);
+ else
+ ins.value = instructionValue[0];
+
+ uint32_t result = mips_decompose_instruction(ins, instruction, version, address);
+ if (result != 0)
+ return result;
+ instruction->size = 4;
+ //look for peudoinstructions by disassembling the next instruction too
+ if (enablePseudoOps != 0 && size >= 8)
+ {
+ if (endianBig == 1)
+ ins.value = bswap32(instructionValue[1]);
+ else
+ ins.value = instructionValue[1];
+ Instruction instruction2;
+ if (instruction->operation == MIPS_LUI)
+ {
+ result = mips_decompose_instruction(ins, &instruction2, version, address+4);
+ if (result != 0)
+ {
+ return result;
+ }
+ if (instruction->operands[0].reg == instruction2.operands[0].reg &&
+ instruction->operands[0].reg == instruction2.operands[1].reg)
+ {
+ if (instruction2.operation == MIPS_ADDIU)
+ {
+ instruction->operation = MIPS_LI;
+ instruction->operands[1].immediate = (instruction->operands[1].immediate << 16) + instruction2.operands[2].immediate;
+ }
+ else if (instruction2.operation == MIPS_ORI)
+ {
+ instruction->operation = MIPS_LI;
+ instruction->operands[1].immediate = (instruction->operands[1].immediate << 16) | (instruction2.operands[2].immediate & 0xffff);
+ }
+ else if (instruction2.operation == MIPS_LW)
+ {
+ instruction->operation = MIPS_LW;
+ instruction->operands[1].operandClass = MEM_IMM;
+ instruction->operands[1].immediate = (instruction->operands[1].immediate << 16) + instruction2.operands[1].immediate;
+ }
+ else if (instruction2.operation == MIPS_SW)
+ {
+ instruction->operation = MIPS_SW;
+ instruction->operands[1].operandClass = MEM_IMM;
+ instruction->operands[1].immediate = (instruction->operands[1].immediate << 16) + instruction2.operands[1].immediate;
+ }
+ else
+ return 0;
+
+ instruction->size = 8;
+ }
+ }
+ }
+ return result;
+}
diff --git a/arch/mips/mips/mips.h b/arch/mips/mips/mips.h
new file mode 100644
index 00000000..14c5f2d8
--- /dev/null
+++ b/arch/mips/mips/mips.h
@@ -0,0 +1,881 @@
+#pragma once
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#if defined(_MSC_VER)
+ #undef REG_NONE
+ #define snprintf _snprintf
+ #define restrict __restrict
+ #define inline __inline
+#else
+ #include <stdlib.h>
+ #ifdef __cplusplus
+ #define restrict __restrict
+ #endif
+#endif
+
+#ifdef __clang__
+#define FALL_THROUGH
+#elif defined(__GNUC__) && __GNUC__ >= 7
+#define FALL_THROUGH __attribute__((fallthrough));
+#else
+#define FALL_THROUGH
+#endif
+
+#define MAX_OPERANDS 4
+
+#ifdef __cplusplus
+#define restrict __restrict
+
+namespace mips
+{
+#endif
+ enum Operation {
+ MIPS_INVALID,
+ MIPS_ABS_D,
+ MIPS_ABS_PS,
+ MIPS_ABS_S,
+ MIPS_ADD_D,
+ MIPS_ADD_PS,
+ MIPS_ADD_S,
+ MIPS_ADD,
+ MIPS_ADDI,
+ MIPS_ADDIU,
+ MIPS_ADDR,
+ MIPS_ADDU,
+ MIPS_ALIGN,
+ MIPS_ALNV_PS,
+ MIPS_AND,
+ MIPS_ANDI,
+ MIPS_B,
+ MIPS_BAL,
+ MIPS_BC1ANY2,
+ MIPS_BC1ANY4,
+ MIPS_BC1EQZ,
+ MIPS_BC1F,
+ MIPS_BC1FL,
+ MIPS_BC1NEZ,
+ MIPS_BC1T,
+ MIPS_BC1TL,
+ MIPS_BC2EQZ,
+ MIPS_BC2F,
+ MIPS_BC2FL,
+ MIPS_BC2NEZ,
+ MIPS_BC2T,
+ MIPS_BC2TL,
+ MIPS_BCNEZ,
+ MIPS_BEQ,
+ MIPS_BEQL,
+ MIPS_BEQZ,
+ MIPS_BGEZ,
+ MIPS_BGEZAL,
+ MIPS_BGEZALL,
+ MIPS_BGEZL,
+ MIPS_BGTZ,
+ MIPS_BGTZL,
+ MIPS_BITSWAP,
+ MIPS_BLEZ,
+ MIPS_BLEZL,
+ MIPS_BLTZ,
+ MIPS_BLTZAL,
+ MIPS_BLTZALL,
+ MIPS_BLTZL,
+ MIPS_BNE,
+ MIPS_BNEL,
+ MIPS_BNZ_B,
+ MIPS_BNZ_D,
+ MIPS_BNZ_H,
+ MIPS_BNZ_W,
+ MIPS_BREAK,
+ MIPS_BSHFL,
+ MIPS_BZ_B,
+ MIPS_BZ_D,
+ MIPS_BZ_H,
+ MIPS_BZ_W,
+ MIPS_C_DF_D,
+ MIPS_C_EQ_D,
+ MIPS_C_EQ_PS,
+ MIPS_C_EQ_S,
+ MIPS_C_EQ,
+ MIPS_C_F_D,
+ MIPS_C_F_PS,
+ MIPS_C_F_S,
+ MIPS_C_F,
+ MIPS_C_LE_D,
+ MIPS_C_LE_PS,
+ MIPS_C_LE_S,
+ MIPS_C_LE,
+ MIPS_C_LT_D,
+ MIPS_C_LT_PS,
+ MIPS_C_LT_S,
+ MIPS_C_LT,
+ MIPS_C_NGE_D,
+ MIPS_C_NGE_PS,
+ MIPS_C_NGE_S,
+ MIPS_C_NGE,
+ MIPS_C_NGL_D,
+ MIPS_C_NGL_PS,
+ MIPS_C_NGL_S,
+ MIPS_C_NGL,
+ MIPS_C_NGLE_D,
+ MIPS_C_NGLE_PS,
+ MIPS_C_NGLE_S,
+ MIPS_C_NGLE,
+ MIPS_C_NGT_D,
+ MIPS_C_NGT_PS,
+ MIPS_C_NGT_S,
+ MIPS_C_NGT,
+ MIPS_C_OLE_D,
+ MIPS_C_OLE_PS,
+ MIPS_C_OLE_S,
+ MIPS_C_OLE,
+ MIPS_C_OLT_D,
+ MIPS_C_OLT_PS,
+ MIPS_C_OLT_S,
+ MIPS_C_OLT,
+ MIPS_C_SEQ_D,
+ MIPS_C_SEQ_PS,
+ MIPS_C_SEQ_S,
+ MIPS_C_SEQ,
+ MIPS_C_SF_D,
+ MIPS_C_SF_PS,
+ MIPS_C_SF_S,
+ MIPS_C_SF,
+ MIPS_C_UEQ_D,
+ MIPS_C_UEQ_PS,
+ MIPS_C_UEQ_S,
+ MIPS_C_UEQ,
+ MIPS_C_ULE_D,
+ MIPS_C_ULE_PS,
+ MIPS_C_ULE_S,
+ MIPS_C_ULE,
+ MIPS_C_ULT_D,
+ MIPS_C_ULT_PS,
+ MIPS_C_ULT_S,
+ MIPS_C_ULT,
+ MIPS_C_UN_D,
+ MIPS_C_UN_PS,
+ MIPS_C_UN_S,
+ MIPS_C_UN,
+ MIPS_C1,
+ MIPS_C2,
+ MIPS_CACHE,
+ MIPS_CEIL_L_D,
+ MIPS_CEIL_L_S,
+ MIPS_CEIL_L,
+ MIPS_CEIL_W_D,
+ MIPS_CEIL_W_S,
+ MIPS_CEIL_W,
+ MIPS_CFC0,
+ MIPS_CFC1,
+ MIPS_CFC2,
+ MIPS_CLASS_D,
+ MIPS_CLASS_S,
+ MIPS_CLO,
+ MIPS_CLZ,
+ MIPS_COP0,
+ MIPS_COP1,
+ MIPS_COP1X,
+ MIPS_COP2,
+ MIPS_COP3,
+ MIPS_CTC0,
+ MIPS_CTC1,
+ MIPS_CTC2,
+ MIPS_CVT_D_S,
+ MIPS_CVT_D_W,
+ MIPS_CVT_L_D,
+ MIPS_CVT_L_S,
+ MIPS_CVT_L,
+ MIPS_CVT_PS_PW,
+ MIPS_CVT_PS_S,
+ MIPS_CVT_PS,
+ MIPS_CVT_PW_PS,
+ MIPS_CVT_S_D,
+ MIPS_CVT_S_L,
+ MIPS_CVT_S_PL,
+ MIPS_CVT_S_PU,
+ MIPS_CVT_S_W,
+ MIPS_CVT_W_D,
+ MIPS_CVT_W_S,
+ MIPS_CVT_W,
+ MIPS_DADD,
+ MIPS_DADDI,
+ MIPS_DADDIU,
+ MIPS_DADDU,
+ MIPS_DDIV,
+ MIPS_DDIVU,
+ MIPS_DERET,
+ MIPS_DI,
+ MIPS_DIV_D,
+ MIPS_DIV_PS,
+ MIPS_DIV_S,
+ MIPS_DIV,
+ MIPS_DIVU,
+ MIPS_DMFC0,
+ MIPS_DMFC1,
+ MIPS_DMFC2,
+ MIPS_DMULT,
+ MIPS_DMULTU,
+ MIPS_DMTC0,
+ MIPS_DMTC1,
+ MIPS_DMTC2,
+ MIPS_DRET,
+ MIPS_DSLL,
+ MIPS_DSLL32,
+ MIPS_DSLLV,
+ MIPS_DSLV,
+ MIPS_DSRA,
+ MIPS_DSRA32,
+ MIPS_DSRAV,
+ MIPS_DSRL,
+ MIPS_DSRL32,
+ MIPS_DSUB,
+ MIPS_DSUBU,
+ MIPS_EHB,
+ MIPS_EI,
+ MIPS_ERET,
+ MIPS_EXT,
+ MIPS_FLOOR_L_D,
+ MIPS_FLOOR_L_S,
+ MIPS_FLOOR_L,
+ MIPS_FLOOR_W_D,
+ MIPS_FLOOR_W_S,
+ MIPS_FLOOR_W,
+ MIPS_INS,
+ MIPS_J,
+ MIPS_JAL,
+ MIPS_JALR_HB,
+ MIPS_JALR,
+ MIPS_JALX,
+ MIPS_JR_HB,
+ MIPS_JR,
+ MIPS_LB,
+ MIPS_LBU,
+ MIPS_LBUX,
+ MIPS_LD,
+ MIPS_LDC1,
+ MIPS_LDC2,
+ MIPS_LDC3,
+ MIPS_LDL,
+ MIPS_LDR,
+ MIPS_LDXC1,
+ MIPS_LH,
+ MIPS_LHI,
+ MIPS_LHU,
+ MIPS_LHX,
+ MIPS_LI,
+ MIPS_LL,
+ MIPS_LLD,
+ MIPS_LLO,
+ MIPS_LUI,
+ MIPS_LUXC1,
+ MIPS_LW,
+ MIPS_LWC1,
+ MIPS_LWC2,
+ MIPS_LWC3,
+ MIPS_LWL,
+ MIPS_LWR,
+ MIPS_LWU,
+ MIPS_LWX,
+ MIPS_LWXC1,
+ MIPS_LX,
+ MIPS_MADD_D,
+ MIPS_MADD_PS,
+ MIPS_MADD_S,
+ MIPS_MADD,
+ MIPS_MADDF_D,
+ MIPS_MADDF_S,
+ MIPS_MADDU,
+ MIPS_MFC0,
+ MIPS_MFC1,
+ MIPS_MFC2,
+ MIPS_MFHC1,
+ MIPS_MFHC2,
+ MIPS_MFHI,
+ MIPS_MFLO,
+ MIPS_MOV_D,
+ MIPS_MOV_PS,
+ MIPS_MOV_S,
+ MIPS_MOVCF,
+ MIPS_MOVCI,
+ MIPS_MOVE,
+ MIPS_MOVF_D,
+ MIPS_MOVF_PS,
+ MIPS_MOVF_S,
+ MIPS_MOVF,
+ MIPS_MOVN_D,
+ MIPS_MOVN_PS,
+ MIPS_MOVN_S,
+ MIPS_MOVN,
+ MIPS_MOVT_D,
+ MIPS_MOVT_PS,
+ MIPS_MOVT_S,
+ MIPS_MOVT,
+ MIPS_MOVZ_D,
+ MIPS_MOVZ_PS,
+ MIPS_MOVZ_S,
+ MIPS_MOVZ,
+ MIPS_MSUB_D,
+ MIPS_MSUB_PS,
+ MIPS_MSUB_S,
+ MIPS_MSUB,
+ MIPS_MSUBF_D,
+ MIPS_MSUBF_S,
+ MIPS_MSUBU,
+ MIPS_MTC0,
+ MIPS_MTC1,
+ MIPS_MTC2,
+ MIPS_MTHC1,
+ MIPS_MTHC2,
+ MIPS_MTHI,
+ MIPS_MTLO,
+ MIPS_MUL_D,
+ MIPS_MUL_PS,
+ MIPS_MUL_S,
+ MIPS_MUL,
+ MIPS_MULR,
+ MIPS_MULT,
+ MIPS_MULTU,
+ MIPS_NEG_D,
+ MIPS_NEG_PS,
+ MIPS_NEG_S,
+ MIPS_NEG,
+ MIPS_NEGU,
+ MIPS_NMADD_D,
+ MIPS_NMADD_PS,
+ MIPS_NMADD_S,
+ MIPS_NMSUB_D,
+ MIPS_NMSUB_PS,
+ MIPS_NMSUB_S,
+ MIPS_NOP,
+ MIPS_NOR,
+ MIPS_NOT,
+ MIPS_OR,
+ MIPS_ORI,
+ MIPS_PAUSE,
+ MIPS_PLL_PS,
+ MIPS_PLU_PS,
+ MIPS_PREF,
+ MIPS_PREFX,
+ MIPS_PUL_PS,
+ MIPS_PUU_PS,
+ MIPS_RDHWR,
+ MIPS_RDPGPR,
+ MIPS_RECIP_D,
+ MIPS_RECIP_S,
+ MIPS_RECIP,
+ MIPS_RECIP1,
+ MIPS_RECIP2,
+ MIPS_RINT_D,
+ MIPS_RINT_S,
+ MIPS_ROTR,
+ MIPS_ROTRV,
+ MIPS_ROUND_L_D,
+ MIPS_ROUND_L_S,
+ MIPS_ROUND_L,
+ MIPS_ROUND_W_D,
+ MIPS_ROUND_W_S,
+ MIPS_ROUND_W,
+ MIPS_RSQRT_D,
+ MIPS_RSQRT_S,
+ MIPS_RSQRT,
+ MIPS_RSQRT1,
+ MIPS_RSQRT2,
+ MIPS_SB,
+ MIPS_SC,
+ MIPS_SCD,
+ MIPS_SD,
+ MIPS_SDBBP,
+ MIPS_SDC1,
+ MIPS_SDC2,
+ MIPS_SDC3,
+ MIPS_SDL,
+ MIPS_SDR,
+ MIPS_SDXC1,
+ MIPS_SEB,
+ MIPS_SEH,
+ MIPS_SEL_D,
+ MIPS_SEL_S,
+ MIPS_SH,
+ MIPS_SLL,
+ MIPS_SLLV,
+ MIPS_SLT,
+ MIPS_SLTI,
+ MIPS_SLTIU,
+ MIPS_SLTU,
+ MIPS_SQRT_D,
+ MIPS_SQRT_PS,
+ MIPS_SQRT_S,
+ MIPS_SRA,
+ MIPS_SRAV,
+ MIPS_SRL,
+ MIPS_SRLV,
+ MIPS_SSNOP,
+ MIPS_SUB_D,
+ MIPS_SUB_PS,
+ MIPS_SUB_S,
+ MIPS_SUB,
+ MIPS_SUBU,
+ MIPS_SUXC1,
+ MIPS_SW,
+ MIPS_SWC1,
+ MIPS_SWC2,
+ MIPS_SWC3,
+ MIPS_SWL,
+ MIPS_SWR,
+ MIPS_SWXC1,
+ MIPS_SYNC,
+ MIPS_SYNCI,
+ MIPS_SYSCALL,
+ MIPS_TEQ,
+ MIPS_TEQI,
+ MIPS_TGE,
+ MIPS_TGEI,
+ MIPS_TGEIU,
+ MIPS_TGEU,
+ MIPS_TLBP,
+ MIPS_TLBR,
+ MIPS_TLBWI,
+ MIPS_TLBWR,
+ MIPS_TLT,
+ MIPS_TLTI,
+ MIPS_TLTIU,
+ MIPS_TLTU,
+ MIPS_TNE,
+ MIPS_TNEI,
+ MIPS_TRAP,
+ MIPS_TRUNC_L_D,
+ MIPS_TRUNC_L_S,
+ MIPS_TRUNC_L,
+ MIPS_TRUNC_W_D,
+ MIPS_TRUNC_W_S,
+ MIPS_TRUNC_W,
+ MIPS_WAIT,
+ MIPS_WRPGPR,
+ MIPS_WSBH,
+ MIPS_XOR,
+ MIPS_XORI,
+ MIPS_OPERATION_END
+ };
+
+
+
+ enum Reg {
+ REG_ZERO, // Hardware constant 0
+ REG_AT, // Reserved for assembler
+ REG_V0, // Return values
+ REG_V1,
+ REG_A0, // Arguments
+ REG_A1,
+ REG_A2,
+ REG_A3,
+ REG_T0, // Temporaries, or extra arguments if N64
+ REG_A4 = REG_T0,
+ REG_T1,
+ REG_A5 = REG_T1,
+ REG_T2,
+ REG_A6 = REG_T2,
+ REG_T3,
+ REG_A7 = REG_T3,
+ REG_T4,
+ REG_T5,
+ REG_T6,
+ REG_T7,
+ REG_S0, // Saved values
+ REG_S1,
+ REG_S2,
+ REG_S3,
+ REG_S4,
+ REG_S5,
+ REG_S6,
+ REG_S7,
+ REG_T8, // Cont. Saved values
+ REG_T9,
+ REG_K0, // Reserved for OS
+ REG_K1,
+ REG_GP, // Global pointer
+ REG_SP, // Stack Pointer
+ REG_FP, // Frame Pointer
+ REG_RA, // Return Adress
+ 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,
+
+ // Standardized coprocessor 0 registers (as of MIPS64 document MD00091 Rev 2.50)
+ // Coprocessor 0 register 0
+ // Register // Selector
+ REG_INDEX, // 0
+ REG_MVP_CONTROL, // 1
+ REG_MVP_CONF0, // 2
+ REG_MVP_CONF1, // 3
+ // Coprocessor 0 register 1
+ REG_RANDOM, // 0
+ REG_VPE_CONTROL, // 1
+ REG_VPE_CONF0, // 2
+ REG_VPE_CONF1, // 3
+ REG_YQ_MASK, // 4
+ REG_VPE_SCHEDULE, // 5
+ REG_VPE_SCHE_FBACK, // 6
+ REG_VPE_OPT, // 7
+ // Coprocessor 0 register 2
+ REG_ENTRY_LO0, // 0
+ REG_TC_STATUS, // 1
+ REG_TC_BIND, // 2
+ REG_TC_RESTART, // 3
+ REG_TC_HALT, // 4
+ REG_TC_CONTEXT, // 5
+ REG_TC_SCHEDULE, // 6
+ REG_TC_SCHE_FBACK, // 7
+ // Coprocessor 0 register 3
+ REG_ENTRY_LO1, // 0
+ // Coprocessor 0 register 4
+ REG_CONTEXT, // 0
+ REG_CONTEXT_CONFIG, // 1
+ // Coprocessor 0 register 5
+ REG_PAGE_MASK, // 0
+ REG_PAGE_GRAIN, // 1
+ // Coprocessor 0 register 6
+ REG_WIRED, // 0
+ REG_SRS_CONF0, // 1
+ REG_SRS_CONF1, // 2
+ REG_SRS_CONF2, // 3
+ REG_SRS_CONF3, // 4
+ REG_SRS_CONF4, // 5
+ // Coprocessor 0 register 7
+ REG_HWR_ENA, // 0
+ // Coprocessor 0 register 8
+ REG_BAD_VADDR, // 0
+ // Coprocessor 0 register 9
+ REG_COUNT, // 0
+ // Coprocessor 0 register 10
+ REG_ENTRY_HI, // 0
+ // Coprocessor 0 register 11
+ REG_COMPARE, // 0
+ // Coprocessor 0 register 12
+ REG_STATUS, // 0
+ REG_INT_CTL, // 1
+ REG_SRS_CTL, // 2
+ REG_SRS_MAP, // 3
+ // Coprocessor 0 register 13
+ REG_CAUSE, // 0
+ // Coprocessor 0 register 14
+ REG_EPC, // 0
+ // Coprocessor 0 register 15
+ REG_PR_ID, // 0
+ REG_EBASE, // 1
+ // Coprocessor 0 register 16
+ REG_CONFIG, // 0
+ REG_CONFIG1, // 1
+ REG_CONFIG2, // 2
+ REG_CONFIG3, // 3
+ // Coprocessor 0 register 17
+ REG_LLADDR, // 0
+ // Coprocessor 0 register 18
+ REG_WATCH_LO, // 0-n
+ // Coprocessor 0 register 19
+ REG_WATCH_HI, // 0-n
+ // Coprocessor 0 register 20
+ REG_XCONTEXT, // 0
+ // Coprocessor 0 register 23
+ REG_DEBUG, // 0
+ REG_TRACE_CONTROL, // 1
+ REG_TRACE_CONTROL2, // 2
+ REG_USER_TRACE_DATA, // 3
+ REG_TRACE_BPC, // 4
+ // Coprocessor 0 register 24
+ REG_DEPC, // 0
+ // Coprocessor 0 register 25
+ REG_PERF_CNT, // 0-n
+ // Coprocessor 0 register 26
+ REG_ERR_CTL, // 0
+ // Coprocessor 0 register 27
+ REG_CACHE_ERR0, // 0
+ REG_CACHE_ERR1, // 1
+ REG_CACHE_ERR2, // 2
+ REG_CACHE_ERR3, // 3
+ // Coprocessor 0 register 28
+ REG_TAG_LO, // even selects
+ REG_DATA_LO, // odd selects
+ // Coprocessor 0 register 29
+ REG_TAG_HI, // even selects
+ REG_DATA_HI, // odd selects
+ // Coprocessor 0 register 30
+ REG_ERROR_EPC, // 0
+ // Coprocessor 0 register 31
+ REG_DESAVE, // 0
+
+ // Last valid register
+ END_REG
+ };
+
+ enum Flag {
+ FPCCREG_FCC0,
+ FPCCREG_FCC1,
+ FPCCREG_FCC2,
+ FPCCREG_FCC3,
+ FPCCREG_FCC4,
+ FPCCREG_FCC5,
+ FPCCREG_FCC6,
+ FPCCREG_FCC7,
+ END_FLAG
+ };
+
+ enum OperandClass {
+ NONE = 0,
+ REG,
+ FLAG,
+ IMM,
+ LABEL,
+ MEM_IMM,
+ MEM_REG,
+ HINT
+ };
+
+ enum Hint {
+ LOAD,
+ STORE,
+ UNDEFINED2,
+ UNDEFINED3,
+ LOAD_STREAMED,
+ STORE_STREAMED,
+ LOAD_RETAINED,
+ STORE_RETAINED,
+ HINT_END,
+ };
+
+ enum MipsVersion {
+ MIPS_1 = 1,
+ MIPS_2,
+ MIPS_3,
+ MIPS_4,
+ MIPS_32,
+ MIPS_64,
+ MIPS_VERSION_END
+ };
+
+#ifndef __cplusplus
+ typedef enum Operation Operation;
+ typedef enum Reg Reg;
+ typedef enum Hint Hint;
+ typedef enum MipsVersion MipsVersion;
+#endif
+
+ struct inst {
+ uint32_t func_lo:3;
+ uint32_t func_hi:3;
+ uint32_t group1:10;
+ uint32_t rt_lo:3;
+ uint32_t rt_hi:2;
+ uint32_t rs_lo:3;
+ uint32_t rs_hi:2;
+ uint32_t op_lo:3;
+ uint32_t op_hi:3;
+ };
+
+ struct itype {
+ int32_t immediate:16;
+ uint32_t rt:5;
+ uint32_t rs:5;
+ uint32_t opcode:6;
+ };
+ struct jtype {
+ uint32_t immediate:26;
+ uint32_t opcode:6;
+ };
+
+ struct rtype {
+ uint32_t function:6;
+ uint32_t sa:5;
+ uint32_t rd:5;
+ uint32_t rt:5;
+ uint32_t rs:5;
+ uint32_t opcode:6;
+ };
+
+ struct ftype {
+ uint32_t fmt:3;
+ uint32_t func:3;
+ uint32_t fd:5;
+ uint32_t fs:5;
+ uint32_t ft:5;
+ uint32_t fr:5;
+ uint32_t opcode:6;
+ };
+
+ struct ttype {
+ uint32_t function:6;
+ uint32_t code:10;
+ uint32_t rt:5;
+ uint32_t rs:5;
+ uint32_t opcode:6;
+ };
+
+ struct stype {
+ uint32_t function:6;
+ uint32_t code:20;
+ uint32_t opcode:6;
+ };
+
+ union combined {
+ struct inst decode;
+ struct itype i;
+ struct jtype j;
+ struct rtype r;
+ struct ftype f;
+ struct ttype t;
+ struct stype s;
+ uint32_t value;
+ struct {
+ uint32_t bit0:1;
+ uint32_t bit1:1;
+ uint32_t bit2:1;
+ uint32_t bit3:1;
+ uint32_t bit4:1;
+ uint32_t bit5:1;
+ uint32_t bit6:1;
+ uint32_t bit7:1;
+ uint32_t bit8:1;
+ uint32_t bit9:1;
+ uint32_t bit10:1;
+ uint32_t bit11:1;
+ uint32_t bit12:1;
+ uint32_t bit13:1;
+ uint32_t bit14:1;
+ uint32_t bit15:1;
+ uint32_t bit16:1;
+ uint32_t bit17:1;
+ uint32_t bit18:1;
+ uint32_t bit19:1;
+ uint32_t bit20:1;
+ uint32_t bit21:1;
+ uint32_t bit22:1;
+ uint32_t bit23:1;
+ uint32_t bit24:1;
+ uint32_t bit25:1;
+ uint32_t bit26:1;
+ uint32_t bit27:1;
+ uint32_t bit28:1;
+ uint32_t bit29:1;
+ uint32_t bit30:1;
+ uint32_t bit31:1;
+ } bits;
+ };
+
+ struct InstructionOperand {
+ uint32_t operandClass;
+ uint32_t reg;
+ uint64_t immediate;
+ };
+
+#ifndef __cplusplus
+ typedef struct InstructionOperand InstructionOperand;
+#endif
+
+ struct Instruction{
+ Operation operation;
+ InstructionOperand operands[MAX_OPERANDS];
+ uint32_t size;
+ };
+
+#ifndef __cplusplus
+ typedef struct Instruction Instruction;
+ typedef struct inst inst;
+ typedef struct itype itype;
+ typedef struct jtype jtype;
+ typedef struct rtype rtype;
+ typedef union combined combined;
+#endif
+
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+ //Given a uint32_t instructionValue decopose the instruction
+ //into its components -> instruction
+ uint32_t mips_decompose(
+ const uint32_t* instructionValue,
+ size_t maxSize,
+ Instruction* restrict instruction,
+ MipsVersion version,
+ uint64_t address,
+ uint32_t bigEndian,
+ uint32_t enablePseudoOps);
+
+ //Get a text representation of the decomposed instruction
+ //into outBuffer
+ uint32_t mips_disassemble(
+ Instruction* restrict instruction,
+ char* outBuffer,
+ uint32_t outBufferSize);
+
+ const char* get_operation(Operation operation);
+ const char* get_register(Reg reg);
+ const char* get_flag(enum Flag flag);
+ const char* get_hint(Hint hint);
+#ifdef __cplusplus
+ }
+}//end namespace
+#endif
diff --git a/arch/mips/mips/test.c b/arch/mips/mips/test.c
new file mode 100644
index 00000000..fbe8335a
--- /dev/null
+++ b/arch/mips/mips/test.c
@@ -0,0 +1,80 @@
+/* build me, debug me:
+gcc -g test.c mips.c -o test
+lldb ./test -- e28f007b
+b mips_decompose
+b mips_disassemble
+*/
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "mips.h"
+
+int disassemble(uint32_t insword, uint64_t address, enum MipsVersion version, char *result)
+{
+ int rc;
+ Instruction instr;
+ uint32_t bigendian = 0;
+
+ memset(&instr, 0, sizeof(instr));
+ rc = mips_decompose(&insword, 4, &instr, version, address, bigendian, 1);
+ if(rc) {
+ printf("ERROR: mips_decompose() returned %d\n", rc);
+ return rc;
+ }
+
+ result[0] = '\0';
+ rc = mips_disassemble(&instr, result, 4096);
+ if(rc) {
+ printf("ERROR: mips_disassemble() returned %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#define ASSERT(X) \
+ if(!(X)) { \
+ printf("failed assert() at %s:%d\n", __FILE__, __LINE__); \
+ exit(-1); \
+ }
+
+int main(int ac, char **av)
+{
+ char instxt[4096];
+
+ if(ac == 1) {
+ printf("usage:\n");
+ printf("\t%s [<address>] <instruction_word>\n", av[0]);
+ printf("\t%s <instruction_word>\n", av[0]);
+ printf("\t%s test\n", av[0]);
+ printf("examples:\n");
+ printf("\t%s 0 14E00003\n", av[0]);
+ printf("\t%s 00405A58 14E00003\n", av[0]);
+ printf("\t%s test\n", av[0]);
+ exit(-1);
+ }
+
+ if(ac == 2 && !strcmp(av[1], "test")) {
+ disassemble(0x14E00003, 0, MIPS_32, instxt);
+ ASSERT(!strcmp(instxt, "bne\t$a3, $zero, 0x10"));
+ disassemble(0x14E00003, 0x405a58, MIPS_32, instxt);
+ ASSERT(!strcmp(instxt, "bne\t$a3, $zero, 0x405a68"));
+ exit(0);
+ }
+
+ uint64_t address = 0;
+ uint32_t insword = 0;
+ if(ac == 2) {
+ address = 0;
+ insword = strtoul(av[1], NULL, 16);
+ }
+ else if(ac == 3) {
+ address = strtoul(av[1], NULL, 16);
+ insword = strtoul(av[2], NULL, 16);
+ }
+
+ if(0 == disassemble(insword, address, MIPS_32, instxt)) {
+ printf("%08llX: %08X %s\n", address, insword, instxt);
+ }
+}
diff --git a/arch/mips/mips/unittest/mips-test.txt b/arch/mips/mips/unittest/mips-test.txt
new file mode 100644
index 00000000..0291d245
--- /dev/null
+++ b/arch/mips/mips/unittest/mips-test.txt
@@ -0,0 +1,344 @@
+# CS_ARCH_MIPS, CS_MODE_MIPS32, None
+0x24,0x48,0xc7,0x00 = and $9, $6, $7
+0x67,0x45,0xc9,0x30 = andi $9, $6, 17767
+0x67,0x45,0xc9,0x30 = andi $9, $6, 17767
+0x67,0x45,0x29,0x31 = andi $9, $9, 17767
+0x21,0x30,0xe6,0x70 = clo $6, $7
+0x20,0x30,0xe6,0x70 = clz $6, $7
+0x84,0x61,0x33,0x7d = ins $19, $9, 6, 7
+0x27,0x48,0xc7,0x00 = nor $9, $6, $7
+0x25,0x18,0x65,0x00 = or $3, $3, $5
+0x67,0x45,0xa4,0x34 = ori $4, $5, 17767
+0x67,0x45,0xc9,0x34 = ori $9, $6, 17767
+0x80,0x00,0x6b,0x35 = ori $11, $11, 128
+0xc2,0x49,0x26,0x00 = rotr $9, $6, 7
+0x46,0x48,0xe6,0x00 = rotrv $9, $6, $7
+0xc0,0x21,0x03,0x00 = sll $4, $3, 7
+0x04,0x10,0xa3,0x00 = sllv $2, $3, $5
+0x2a,0x18,0x65,0x00 = slt $3, $3, $5
+0x67,0x00,0x63,0x28 = slti $3, $3, 103
+0x67,0x00,0x63,0x28 = slti $3, $3, 103
+0x67,0x00,0x63,0x2c = sltiu $3, $3, 103
+0x2b,0x18,0x65,0x00 = sltu $3, $3, $5
+0xc3,0x21,0x03,0x00 = sra $4, $3, 7
+0x07,0x10,0xa3,0x00 = srav $2, $3, $5
+0xc2,0x21,0x03,0x00 = srl $4, $3, 7
+0x06,0x10,0xa3,0x00 = srlv $2, $3, $5
+0x26,0x18,0x65,0x00 = xor $3, $3, $5
+0x67,0x45,0xc9,0x38 = xori $9, $6, 17767
+0x67,0x45,0xc9,0x38 = xori $9, $6, 17767
+0x0c,0x00,0x6b,0x39 = xori $11, $11, 12
+0xa0,0x30,0x07,0x7c = wsbh $6, $7
+0x27,0x38,0x00,0x01 = not $7, $8
+0x20,0x48,0xc7,0x00 = add $9, $6, $7
+0x67,0x45,0xc9,0x20 = addi $9, $6, 17767
+0x67,0xc5,0xc9,0x24 = addiu $9, $6, -15001
+0x67,0x45,0xc9,0x20 = addi $9, $6, 17767
+0x67,0x45,0x29,0x21 = addi $9, $9, 17767
+0x67,0xc5,0xc9,0x24 = addiu $9, $6, -15001
+0x28,0x00,0x6b,0x25 = addiu $11, $11, 40
+0x21,0x48,0xc7,0x00 = addu $9, $6, $7
+0x00,0x00,0xc7,0x70 = madd $6, $7
+0x01,0x00,0xc7,0x70 = maddu $6, $7
+0x04,0x00,0xc7,0x70 = msub $6, $7
+0x05,0x00,0xc7,0x70 = msubu $6, $7
+0x18,0x00,0x65,0x00 = mult $3, $5
+0x19,0x00,0x65,0x00 = multu $3, $5
+0x22,0x48,0xc7,0x00 = sub $9, $6, $7
+0xc8,0xff,0xbd,0x23 = addi $sp, $sp, -56
+0x23,0x20,0x65,0x00 = subu $4, $3, $5
+0xd8,0xff,0xbd,0x27 = addiu $sp, $sp, -40
+0x22,0x30,0x07,0x00 = neg $6, $7
+0x23,0x30,0x07,0x00 = negu $6, $7
+0x21,0x38,0x00,0x01 = move $7, $8
+# CS_ARCH_MIPS, CS_MODE_MIPS64+CS_MODE_BIG_ENDIAN, None
+#0x00,0x00,0x00,0x0d = break
+#0x00,0x07,0x00,0x0d = break 7, 0
+#0x00,0x07,0x01,0x4d = break 7, 5
+#0x00,0x00,0x00,0x0c = syscall
+#0x00,0x0d,0x15,0x0c = syscall 13396
+#0x42,0x00,0x00,0x18 = eret
+#0x42,0x00,0x00,0x1f = deret
+#0x41,0x60,0x60,0x00 = di
+#0x41,0x60,0x60,0x00 = di
+#0x41,0x6a,0x60,0x00 = di $10
+#0x41,0x60,0x60,0x20 = ei
+#0x41,0x60,0x60,0x20 = ei
+#0x41,0x6a,0x60,0x20 = ei $10
+#0x42,0x00,0x00,0x20 = wait
+#0x00,0x03,0x00,0x34 = teq $zero, $3
+#0x00,0x03,0x00,0x74 = teq $zero, $3, 1
+#0x04,0x6c,0x00,0x01 = teqi $3, 1
+#0x00,0x03,0x00,0x30 = tge $zero, $3
+#0x00,0x03,0x00,0xf0 = tge $zero, $3, 3
+#0x04,0x68,0x00,0x03 = tgei $3, 3
+#0x00,0x03,0x00,0x31 = tgeu $zero, $3
+#0x00,0x03,0x01,0xf1 = tgeu $zero, $3, 7
+#0x04,0x69,0x00,0x07 = tgeiu $3, 7
+#0x00,0x03,0x00,0x32 = tlt $zero, $3
+#0x00,0x03,0x07,0xf2 = tlt $zero, $3, 31
+#0x04,0x6a,0x00,0x1f = tlti $3, 31
+#0x00,0x03,0x00,0x33 = tltu $zero, $3
+#0x00,0x03,0x3f,0xf3 = tltu $zero, $3, 255
+#0x04,0x6b,0x00,0xff = tltiu $3, 255
+#0x00,0x03,0x00,0x36 = tne $zero, $3
+#0x00,0x03,0xff,0xf6 = tne $zero, $3, 1023
+#0x04,0x6e,0x03,0xff = tnei $3, 1023
+# CS_ARCH_MIPS, CS_MODE_MIPS32+CS_MODE_BIG_ENDIAN, None
+#0x00,0x00,0x00,0x0d = break
+#0x00,0x07,0x00,0x0d = break 7, 0
+#0x00,0x07,0x01,0x4d = break 7, 5
+#0x00,0x00,0x00,0x0c = syscall
+#0x00,0x0d,0x15,0x0c = syscall 13396
+#0x42,0x00,0x00,0x18 = eret
+#0x42,0x00,0x00,0x1f = deret
+#0x41,0x60,0x60,0x00 = di
+#0x41,0x60,0x60,0x00 = di
+#0x41,0x6a,0x60,0x00 = di $10
+#0x41,0x60,0x60,0x20 = ei
+#0x41,0x60,0x60,0x20 = ei
+#0x41,0x6a,0x60,0x20 = ei $10
+#0x42,0x00,0x00,0x20 = wait
+#0x00,0x03,0x00,0x34 = teq $zero, $3
+#0x00,0x03,0x00,0x74 = teq $zero, $3, 1
+#0x04,0x6c,0x00,0x01 = teqi $3, 1
+#0x00,0x03,0x00,0x30 = tge $zero, $3
+#0x00,0x03,0x00,0xf0 = tge $zero, $3, 3
+#0x04,0x68,0x00,0x03 = tgei $3, 3
+#0x00,0x03,0x00,0x31 = tgeu $zero, $3
+#0x00,0x03,0x01,0xf1 = tgeu $zero, $3, 7
+#0x04,0x69,0x00,0x07 = tgeiu $3, 7
+#0x00,0x03,0x00,0x32 = tlt $zero, $3
+#0x00,0x03,0x07,0xf2 = tlt $zero, $3, 31
+#0x04,0x6a,0x00,0x1f = tlti $3, 31
+#0x00,0x03,0x00,0x33 = tltu $zero, $3
+#0x00,0x03,0x3f,0xf3 = tltu $zero, $3, 255
+#0x04,0x6b,0x00,0xff = tltiu $3, 255
+#0x00,0x03,0x00,0x36 = tne $zero, $3
+#0x00,0x03,0xff,0xf6 = tne $zero, $3, 1023
+#0x04,0x6e,0x03,0xff = tnei $3, 1023
+# CS_ARCH_MIPS, CS_MODE_MIPS64+CS_MODE_BIG_ENDIAN, None
+#0x40,0xac,0x80,0x02 = dmtc0 $12, $16, 2
+#0x40,0xac,0x80,0x00 = dmtc0 $12, $16, 0
+#0x40,0x8c,0x80,0x02 = mtc0 $12, $16, 2
+#0x40,0x8c,0x80,0x00 = mtc0 $12, $16, 0
+#0x40,0x2c,0x80,0x02 = dmfc0 $12, $16, 2
+#0x40,0x2c,0x80,0x00 = dmfc0 $12, $16, 0
+#0x40,0x0c,0x80,0x02 = mfc0 $12, $16, 2
+#0x40,0x0c,0x80,0x00 = mfc0 $12, $16, 0
+#0x48,0xac,0x80,0x02 = dmtc2 $12, $16, 2
+#0x48,0xac,0x80,0x00 = dmtc2 $12, $16, 0
+#0x48,0x8c,0x80,0x02 = mtc2 $12, $16, 2
+#0x48,0x8c,0x80,0x00 = mtc2 $12, $16, 0
+#0x48,0x2c,0x80,0x02 = dmfc2 $12, $16, 2
+#0x48,0x2c,0x80,0x00 = dmfc2 $12, $16, 0
+#0x48,0x0c,0x80,0x02 = mfc2 $12, $16, 2
+#0x48,0x0c,0x80,0x00 = mfc2 $12, $16, 0
+# CS_ARCH_MIPS, CS_MODE_MIPS32+CS_MODE_BIG_ENDIAN, None
+#0x7e,0x32,0x83,0x11 = precrq.qb.ph $16, $17, $18
+#0x7e,0x53,0x8d,0x11 = precrq.ph.w $17, $18, $19
+#0x7e,0x74,0x95,0x51 = precrq_rs.ph.w $18, $19, $20
+#0x7e,0x95,0x9b,0xd1 = precrqu_s.qb.ph $19, $20, $21
+#0x7c,0x15,0xa3,0x12 = preceq.w.phl $20, $21
+#0x7c,0x16,0xab,0x52 = preceq.w.phr $21, $22
+#0x7c,0x17,0xb1,0x12 = precequ.ph.qbl $22, $23
+#0x7c,0x18,0xb9,0x52 = precequ.ph.qbr $23, $24
+#0x7c,0x19,0xc1,0x92 = precequ.ph.qbla $24, $25
+#0x7c,0x1a,0xc9,0xd2 = precequ.ph.qbra $25, $26
+#0x7c,0x1b,0xd7,0x12 = preceu.ph.qbl $26, $27
+#0x7c,0x1c,0xdf,0x52 = preceu.ph.qbr $27, $gp
+#0x7c,0x1d,0xe7,0x92 = preceu.ph.qbla $gp, $sp
+#0x7c,0x1e,0xef,0xd2 = preceu.ph.qbra $sp, $fp
+#0x7f,0x19,0xbb,0x51 = precr.qb.ph $23, $24, $25
+#0x7f,0x38,0x07,0x91 = precr_sra.ph.w $24, $25, 0
+#0x7f,0x38,0xff,0x91 = precr_sra.ph.w $24, $25, 31
+#0x7f,0x59,0x07,0xd1 = precr_sra_r.ph.w $25, $26, 0
+#0x7f,0x59,0xff,0xd1 = precr_sra_r.ph.w $25, $26, 31
+#0x7f,0x54,0x51,0x8a = lbux $10, $20($26)
+#0x7f,0x75,0x59,0x0a = lhx $11, $21($27)
+#0x7f,0x96,0x60,0x0a = lwx $12, $22($gp)
+#0x00,0x43,0x18,0x18 = mult $ac3, $2, $3
+#0x00,0x85,0x10,0x19 = multu $ac2, $4, $5
+#0x70,0xc7,0x08,0x00 = madd $ac1, $6, $7
+#0x71,0x09,0x00,0x01 = maddu $ac0, $8, $9
+#0x71,0x4b,0x18,0x04 = msub $ac3, $10, $11
+#0x71,0x8d,0x10,0x05 = msubu $ac2, $12, $13
+#0x00,0x20,0x70,0x10 = mfhi $14, $ac1
+#0x00,0x00,0x78,0x12 = mflo $15, $ac0
+#0x02,0x00,0x18,0x11 = mthi $16, $ac3
+#0x02,0x20,0x10,0x13 = mtlo $17, $ac2
+#0x00,0x43,0x00,0x18 = mult $2, $3
+#0x00,0x85,0x00,0x19 = multu $4, $5
+#0x70,0xc7,0x00,0x00 = madd $6, $7
+#0x71,0x09,0x00,0x01 = maddu $8, $9
+#0x71,0x4b,0x00,0x04 = msub $10, $11
+#0x71,0x8d,0x00,0x05 = msubu $12, $13
+#0x00,0x00,0x70,0x10 = mfhi $14
+#0x00,0x00,0x78,0x12 = mflo $15
+#0x02,0x00,0x00,0x11 = mthi $16
+#0x02,0x20,0x00,0x13 = mtlo $17
+# CS_ARCH_MIPS, CS_MODE_MIPS32, None
+0x7b,0x00,0x05,0x34 = ori $5, $zero, 123
+0xd7,0xf6,0x06,0x24 = addiu $6, $zero, -2345
+0x01,0x00,0x07,0x3c = lui $7, 1
+0x02,0x00,0xe7,0x34 = ori $7, $7, 2
+0x14,0x00,0x04,0x24 = addiu $4, $zero, 20
+0x01,0x00,0x07,0x3c = lui $7, 1
+0x02,0x00,0xe7,0x34 = ori $7, $7, 2
+0x14,0x00,0xa4,0x24 = addiu $4, $5, 20
+0x01,0x00,0x07,0x3c = lui $7, 1
+0x02,0x00,0xe7,0x34 = ori $7, $7, 2
+0x21,0x38,0xe8,0x00 = addu $7, $7, $8
+0x21,0x50,0x44,0x01 = addu $10, $10, $4
+0x21,0x08,0x29,0x00 = addu $1, $1, $9
+0x0a,0x00,0x0a,0x3c = lui $10, 10
+0x21,0x50,0x44,0x01 = addu $10, $10, $4
+0x7b,0x00,0x4a,0x8d = lw $10, 123($10)
+0x02,0x00,0x01,0x3c = lui $1, 2
+0x21,0x08,0x29,0x00 = addu $1, $1, $9
+0x40,0xe2,0x2a,0xac = sw $10, 57920($1)
+# CS_ARCH_MIPS, CS_MODE_MIPS32, None
+0x05,0x73,0x20,0x46 = abs.d $f12, $f14
+0x85,0x39,0x00,0x46 = abs.s $f6, $f7
+0x00,0x62,0x2e,0x46 = add.d $f8, $f12, $f14
+0x40,0x32,0x07,0x46 = add.s $f9, $f6, $f7
+0x0f,0x73,0x20,0x46 = floor.w.d $f12, $f14
+0x8f,0x39,0x00,0x46 = floor.w.s $f6, $f7
+0x0e,0x73,0x20,0x46 = ceil.w.d $f12, $f14
+0x8e,0x39,0x00,0x46 = ceil.w.s $f6, $f7
+0x02,0x62,0x2e,0x46 = mul.d $f8, $f12, $f14
+0x42,0x32,0x07,0x46 = mul.s $f9, $f6, $f7
+0x07,0x73,0x20,0x46 = neg.d $f12, $f14
+0x87,0x39,0x00,0x46 = neg.s $f6, $f7
+0x0c,0x73,0x20,0x46 = round.w.d $f12, $f14
+0x8c,0x39,0x00,0x46 = round.w.s $f6, $f7
+0x04,0x73,0x20,0x46 = sqrt.d $f12, $f14
+0x84,0x39,0x00,0x46 = sqrt.s $f6, $f7
+0x01,0x62,0x2e,0x46 = sub.d $f8, $f12, $f14
+0x41,0x32,0x07,0x46 = sub.s $f9, $f6, $f7
+0x0d,0x73,0x20,0x46 = trunc.w.d $f12, $f14
+0x8d,0x39,0x00,0x46 = trunc.w.s $f6, $f7
+0x32,0x60,0x2e,0x46 = c.eq.d $f12, $f14
+0x32,0x30,0x07,0x46 = c.eq.s $f6, $f7
+0x30,0x60,0x2e,0x46 = c.f.d $f12, $f14
+0x30,0x30,0x07,0x46 = c.f.s $f6, $f7
+0x3e,0x60,0x2e,0x46 = c.le.d $f12, $f14
+0x3e,0x30,0x07,0x46 = c.le.s $f6, $f7
+0x3c,0x60,0x2e,0x46 = c.lt.d $f12, $f14
+0x3c,0x30,0x07,0x46 = c.lt.s $f6, $f7
+0x3d,0x60,0x2e,0x46 = c.nge.d $f12, $f14
+0x3d,0x30,0x07,0x46 = c.nge.s $f6, $f7
+0x3b,0x60,0x2e,0x46 = c.ngl.d $f12, $f14
+0x3b,0x30,0x07,0x46 = c.ngl.s $f6, $f7
+0x39,0x60,0x2e,0x46 = c.ngle.d $f12, $f14
+0x39,0x30,0x07,0x46 = c.ngle.s $f6, $f7
+0x3f,0x60,0x2e,0x46 = c.ngt.d $f12, $f14
+0x3f,0x30,0x07,0x46 = c.ngt.s $f6, $f7
+0x36,0x60,0x2e,0x46 = c.ole.d $f12, $f14
+0x36,0x30,0x07,0x46 = c.ole.s $f6, $f7
+0x34,0x60,0x2e,0x46 = c.olt.d $f12, $f14
+0x34,0x30,0x07,0x46 = c.olt.s $f6, $f7
+0x3a,0x60,0x2e,0x46 = c.seq.d $f12, $f14
+0x3a,0x30,0x07,0x46 = c.seq.s $f6, $f7
+0x38,0x60,0x2e,0x46 = c.sf.d $f12, $f14
+0x38,0x30,0x07,0x46 = c.sf.s $f6, $f7
+0x33,0x60,0x2e,0x46 = c.ueq.d $f12, $f14
+0x33,0xe0,0x12,0x46 = c.ueq.s $f28, $f18
+0x37,0x60,0x2e,0x46 = c.ule.d $f12, $f14
+0x37,0x30,0x07,0x46 = c.ule.s $f6, $f7
+0x35,0x60,0x2e,0x46 = c.ult.d $f12, $f14
+0x35,0x30,0x07,0x46 = c.ult.s $f6, $f7
+0x31,0x60,0x2e,0x46 = c.un.d $f12, $f14
+0x31,0x30,0x07,0x46 = c.un.s $f6, $f7
+0xa1,0x39,0x00,0x46 = cvt.d.s $f6, $f7
+0x21,0x73,0x80,0x46 = cvt.d.w $f12, $f14
+0x20,0x73,0x20,0x46 = cvt.s.d $f12, $f14
+0xa0,0x39,0x80,0x46 = cvt.s.w $f6, $f7
+0x24,0x73,0x20,0x46 = cvt.w.d $f12, $f14
+0xa4,0x39,0x00,0x46 = cvt.w.s $f6, $f7
+0x00,0x00,0x46,0x44 = cfc1 $6, $0
+0x00,0xf8,0xca,0x44 = ctc1 $10, $31
+0x00,0x38,0x06,0x44 = mfc1 $6, $f7
+0x10,0x28,0x00,0x00 = mfhi $5
+0x12,0x28,0x00,0x00 = mflo $5
+0x86,0x41,0x20,0x46 = mov.d $f6, $f8
+0x86,0x39,0x00,0x46 = mov.s $f6, $f7
+0x00,0x38,0x86,0x44 = mtc1 $6, $f7
+0x11,0x00,0xe0,0x00 = mthi $7
+0x13,0x00,0xe0,0x00 = mtlo $7
+0xc6,0x23,0xe9,0xe4 = swc1 $f9, 9158($7)
+0x00,0x38,0x06,0x40 = mfc0 $6, $7, 0
+0x00,0x40,0x89,0x40 = mtc0 $9, $8, 0
+0x00,0x38,0x05,0x48 = mfc2 $5, $7, 0
+0x00,0x20,0x89,0x48 = mtc2 $9, $4, 0
+0x02,0x38,0x06,0x40 = mfc0 $6, $7, 2
+0x03,0x40,0x89,0x40 = mtc0 $9, $8, 3
+0x04,0x38,0x05,0x48 = mfc2 $5, $7, 4
+0x05,0x20,0x89,0x48 = mtc2 $9, $4, 5
+0x01,0x10,0x20,0x00 = movf $2, $1, $fcc0
+0x01,0x10,0x21,0x00 = movt $2, $1, $fcc0
+0x01,0x20,0xb1,0x00 = movt $4, $5, $fcc4
+0x11,0x31,0x28,0x46 = movf.d $f4, $f6, $fcc2
+0x11,0x31,0x14,0x46 = movf.s $f4, $f6, $fcc5
+0x05,0x00,0xa6,0x4c = luxc1 $f0, $6($5)
+0x0d,0x20,0xb8,0x4c = suxc1 $f4, $24($5)
+0x00,0x05,0xcc,0x4d = lwxc1 $f20, $12($14)
+0x08,0xd0,0xd2,0x4e = swxc1 $f26, $18($22)
+0x00,0x20,0x71,0x44 = mfhc1 $17, $f4
+0x00,0x30,0xf1,0x44 = mthc1 $17, $f6
+0x10,0x00,0xa4,0xeb = swc2 $4, 16($sp)
+0x10,0x00,0xa4,0xfb = sdc2 $4, 16($sp)
+0x0c,0x00,0xeb,0xcb = lwc2 $11, 12($ra)
+0x0c,0x00,0xeb,0xdb = ldc2 $11, 12($ra)
+# CS_ARCH_MIPS, CS_MODE_MIPS32, None
+# CS_ARCH_MIPS, CS_MODE_MIPS32, None
+0x10,0x00,0xa4,0xa0 = sb $4, 16($5)
+0x10,0x00,0xa4,0xe0 = sc $4, 16($5)
+0x10,0x00,0xa4,0xa4 = sh $4, 16($5)
+0x10,0x00,0xa4,0xac = sw $4, 16($5)
+0x00,0x00,0xa7,0xac = sw $7, 0($5)
+0x10,0x00,0xa2,0xe4 = swc1 $f2, 16($5)
+0x10,0x00,0xa4,0xa8 = swl $4, 16($5)
+0x04,0x00,0xa4,0x80 = lb $4, 4($5)
+0x04,0x00,0xa4,0x8c = lw $4, 4($5)
+0x04,0x00,0xa4,0x90 = lbu $4, 4($5)
+0x04,0x00,0xa4,0x84 = lh $4, 4($5)
+0x04,0x00,0xa4,0x94 = lhu $4, 4($5)
+0x04,0x00,0xa4,0xc0 = ll $4, 4($5)
+0x04,0x00,0xa4,0x8c = lw $4, 4($5)
+0x00,0x00,0xe7,0x8c = lw $7, 0($7)
+0x10,0x00,0xa2,0x8f = lw $2, 16($sp)
+# CS_ARCH_MIPS, CS_MODE_MIPS32+CS_MODE_BIG_ENDIAN, None
+#0x24,0x00,0x00,0x00 = addiu $zero, $zero, 0
+#0x24,0x01,0x00,0x00 = addiu $at, $zero, 0
+#0x24,0x02,0x00,0x00 = addiu $v0, $zero, 0
+#0x24,0x03,0x00,0x00 = addiu $v1, $zero, 0
+#0x24,0x04,0x00,0x00 = addiu $a0, $zero, 0
+#0x24,0x05,0x00,0x00 = addiu $a1, $zero, 0
+#0x24,0x06,0x00,0x00 = addiu $a2, $zero, 0
+#0x24,0x07,0x00,0x00 = addiu $a3, $zero, 0
+#0x24,0x08,0x00,0x00 = addiu $t0, $zero, 0
+#0x24,0x09,0x00,0x00 = addiu $t1, $zero, 0
+#0x24,0x0a,0x00,0x00 = addiu $t2, $zero, 0
+#0x24,0x0b,0x00,0x00 = addiu $t3, $zero, 0
+#0x24,0x0c,0x00,0x00 = addiu $t4, $zero, 0
+#0x24,0x0d,0x00,0x00 = addiu $t5, $zero, 0
+#0x24,0x0e,0x00,0x00 = addiu $t6, $zero, 0
+#0x24,0x0f,0x00,0x00 = addiu $t7, $zero, 0
+#0x24,0x10,0x00,0x00 = addiu $s0, $zero, 0
+#0x24,0x11,0x00,0x00 = addiu $s1, $zero, 0
+#0x24,0x12,0x00,0x00 = addiu $s2, $zero, 0
+#0x24,0x13,0x00,0x00 = addiu $s3, $zero, 0
+#0x24,0x14,0x00,0x00 = addiu $s4, $zero, 0
+#0x24,0x15,0x00,0x00 = addiu $s5, $zero, 0
+#0x24,0x16,0x00,0x00 = addiu $s6, $zero, 0
+#0x24,0x17,0x00,0x00 = addiu $s7, $zero, 0
+#0x24,0x18,0x00,0x00 = addiu $t8, $zero, 0
+#0x24,0x19,0x00,0x00 = addiu $t9, $zero, 0
+#0x24,0x1a,0x00,0x00 = addiu $k0, $zero, 0
+#0x24,0x1b,0x00,0x00 = addiu $k1, $zero, 0
+#0x24,0x1c,0x00,0x00 = addiu $gp, $zero, 0
+#0x24,0x1d,0x00,0x00 = addiu $sp, $zero, 0
+#0x24,0x1e,0x00,0x00 = addiu $fp, $zero, 0
+#0x24,0x1f,0x00,0x00 = addiu $sp, $zero, 0
+# \ No newline at end of file
diff --git a/arch/mips/mips/unittest/unitTest.py b/arch/mips/mips/unittest/unitTest.py
new file mode 100644
index 00000000..bd67f310
--- /dev/null
+++ b/arch/mips/mips/unittest/unitTest.py
@@ -0,0 +1,370 @@
+import re
+import os, sys, subprocess
+import struct
+from ctypes import *
+from capstone import *
+import test
+import random
+
+disasmBuff = create_string_buffer(1024)
+instBuff = create_string_buffer(1024)
+binja = CDLL("../mips.so")
+md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32)
+
+unsupported = [
+ "bz.v",
+ "bz.b",
+ "bz.h",
+ "bz.w",
+ "bz.d",
+ "bnz.v",
+ "bnz.b",
+ "bnz.h",
+ "bnz.w",
+ "bnz.d",
+#DSP Instruction
+ "absq_s.ph",
+ "absq_s.qb",
+ "absq_s.w",
+ "addqph",
+ "addq_s.ph",
+ "addq_s.w",
+ "addqh.ph",
+ "addqh_r.ph",
+ "addqh.w",
+ "addqh_r.w",
+ "addsc",
+ "addu.ph",
+ "addu_s.ph",
+ "addu_s.qb",
+ "addu.qb",
+ "addwc",
+ "adduh.qb",
+ "adduh_r.qb",
+ "append",
+ "balign",
+ "bitrev",
+ "bposge32",
+ "bposge32c",
+ "cmp.eq.ph",
+ "cmp.lt.ph",
+ "cmp.le.ph",
+ "cmpgdu.eq.qb",
+ "cmpgdu.lt.qb",
+ "cmpgdu.le.qb",
+ "cmpgu.eq.qb",
+ "cmpgu.lt.qb",
+ "cmpgu.le.qb",
+ "cmpu.eq.qb",
+ "cmpu.lt.qb",
+ "cmpu.le.qb",
+ "dpa.w.ph",
+ "dpaq_s.w.ph",
+ "dpaq_sa.l.w",
+ "dpaqx_s.w.ph",
+ "dpaqx_sa.w.ph",
+ "dpau.h.qbl",
+ "dpau.h.qbr",
+ "dpaxw.ph",
+ "dps.w.ph",
+ "dpsq_s.w.ph",
+ "dpsq_salw",
+ "dpsqx_s.w.ph",
+ "dpsqx_saw.ph",
+ "dpsu.h.qbl",
+ "dpsu.h.qbr",
+ "dpsxw.ph",
+ "extp",
+ "extpdp",
+ "extpdpv",
+ "extpv",
+ "extrw",
+ "extr_rs.w",
+ "extr_s.h",
+ "extrv.w",
+ "extrv_rs.w",
+ "extrv_s.h",
+ "insv",
+ "lbux",
+ "lhx",
+ "lwx",
+ "madd",
+ "maddu",
+ "maq_s.w.phl",
+ "maq_sa.w,phl",
+ "maq_s.w.phr",
+ "maq_sa.w.phr",
+ "mfhi",
+ "mflo",
+ "modsub",
+ "msub",
+ "msubu",
+ "mthi",
+ "mthlip",
+ "mtlo",
+ "mul.ph",
+ "mul_s.ph",
+ "muleq_s.w.phl",
+ "muleq_s.w.phr",
+ "muleu_s.ph.qbl",
+ "muleu_s.ph.qbr",
+ "mulq_rs.ph",
+ "mulq_rsw",
+ "mulq_s.ph",
+ "mulq_sw",
+ "mulsaw.ph",
+ "mulsaq_s.w.ph",
+ "mult",
+ "multu",
+ "packrl.ph",
+ "pick.ph",
+ "pick.qb",
+ "preceq.w.phl",
+ "preceq.w.phr",
+ "precequ.ph.qbl",
+ "precequ.ph.qbla",
+ "precequ.ph.qbr",
+ "precequ.ph.qbra",
+ "preceu.ph.qbl",
+ "preceu.ph.qbla",
+ "preceu.ph.qbr",
+ "preceu.ph.qbra",
+ "precrqb.ph",
+ "precr_sra.ph.w",
+ "precr_sra_r.ph.w",
+ "precrq.ph.w",
+ "precrqqb.ph",
+ "precrqu_sqb.ph",
+ "precrq_rs.ph.w",
+ "prepend",
+ "radduw.qb",
+ "rddsp",
+ "repl.ph",
+ "repl.qb",
+ "replv.ph",
+ "replv.qb",
+ "shilo",
+ "shilov",
+ "shll.ph",
+ "shll_s.ph",
+ "shll.qb",
+ "shllv.ph",
+ "shllv_s.ph",
+ "shllv.qb ",
+ "shllv_s.w ",
+ "shll_s.w ",
+ "shra.qb ",
+ "shra_r.qb ",
+ "shra.ph ",
+ "shra_r.ph ",
+ "shrav.ph ",
+ "shrav_r.ph ",
+ "shrav.qb ",
+ "shrav_r.qb ",
+ "shrav_r.w ",
+ "shra_r.w",
+ "shrl.ph",
+ "shrl.qb",
+ "shrlv.ph",
+ "shrlv.qb",
+ "subq.ph",
+ "subq_s.ph",
+ "subq_s.w",
+ "subqh.ph",
+ "subqh_r.ph",
+ "subqh.w",
+ "subqh_rw",
+ "subu.ph",
+ "subu_s.ph",
+ "subu.qb",
+ "subu_s.qb",
+ "subuh.qb",
+ "subuh_r.qb",
+ "wrdsp" ]
+
+def disassemble_binja(instruction, baseAddress):
+ instruction = instruction[::-1]
+ for a in xrange(len(disasmBuff)):
+ disasmBuff[a] = '\0'
+ for a in xrange(len(instBuff)):
+ instBuff[a] = '\0'
+ for i,a in enumerate(instruction):
+ disasmBuff[i] = a
+ # uint32_t mips_decompose(
+ # uint32_t instructionValue,
+ # Instruction* restrict instruction,
+ # MipsVersion version,
+ # uint64_t address,
+ # uint32_t bigendian)
+ err = binja.mips_decompose(instruction, 4, instBuff, 5, baseAddress, 1)
+ if err != 0:
+ return "decomposer failed"
+
+ #uint32_t mips_disassemble(
+ # Instruction* restrict instruction,
+ # char* outBuffer,
+ # uint32_t outBufferSize);
+ if binja.mips_disassemble(instBuff, disasmBuff, 1024) == 0:
+ return disasmBuff.value
+
+ return "disassembly failed"
+
+def disassemble_capstone(instruction, baseAddress):
+ for a in md.disasm(instruction, baseAddress):
+ return a.mnemonic + "\t" + a.op_str
+
+def normalizeNumeric(numeric):
+ #if not (numeric.startswith("-") or numeric.startswith("0x")):
+ # return numeric
+
+ neg = False
+ if numeric.startswith("-"):
+ numeric = numeric[1:]
+ neg = True
+
+ try:
+ numeric = int(numeric, 16)
+ except:
+ return numeric
+
+ if neg:
+ return hex((-numeric + (1 << 16)) % (1 << 16))
+
+ return hex((numeric + (1 << 16)) % (1 << 16))
+
+def areEqual(binja, capstone):
+ capstone = capstone.strip()
+ if binja == capstone:
+ return True
+
+ belms = re.findall(r"[^ \]\[,\t\{\}\(\)]+", binja)
+ celms = re.findall(r"[^ \]\[,\t\{\}\(\)]+", capstone)
+ #print celms[0], unsupported[109]
+ if celms[0] in unsupported:
+ return True
+ #print "celms: ", celms
+ #print "belms: ", belms
+
+ if (belms[0] == "bne" and celms[0] == "bnez" and len(belms) > 3) or (belms[0] == "bnel" and celms[0] == "bnezl" and len(belms) > 3) or (belms[0] == "beql" and celms[0] == "beqzl" and len(belms) > 3):
+ del belms[2]
+ celms[0] = belms[0]
+
+ for i,a in enumerate(celms):
+ celms[i] = normalizeNumeric(a)
+ for i,a in enumerate(belms):
+ belms[i] = normalizeNumeric(a)
+
+ for a,b in zip(belms, celms):
+ if b != a:
+ #print "celms: ", celms
+ #print "belms: ", belms
+ return False
+ return True
+
+usage = "%s [-v] [-f <arm64File>] [-b] [-u <unitTestFile>] [<32-bitValue>]" % sys.argv[0]
+def main():
+ if len(sys.argv) < 2:
+ print usage
+ return
+
+ brute = False
+ instructions = []
+ verbose = False
+ if sys.argv[1] == "-v":
+ verbose = True
+ sys.argv = sys.argv[1:]
+ if sys.argv[1] == "-f":
+ if len(sys.argv) < 3:
+ print usage
+ return
+ tmp = open(sys.argv[2]).read()
+ if len(tmp) % 4 != 0:
+ print "File must be multiple of 4"
+ return
+ for a in xrange(0, len(tmp), 4):
+ instructions.append(tmp[a:a+4])
+ elif sys.argv[1] == "-t":
+ for a in test.tests:
+ instructions.extend(struct.pack("<L",a))
+ elif sys.argv[1] == "-u":
+ lines = open(sys.argv[2]).read().split("\n")
+ for line in lines:
+ if line.startswith("#") or len(line) == 0:
+ continue
+ hexvalues, disasm = line.split(" = ")
+ instructions.append((''.join(chr(a) for a in eval(hexvalues)), disasm))
+
+ elif sys.argv[1] == "-b":
+ brute = True
+ else:
+ try:
+ instructions.append((struct.pack("<L",int(sys.argv[1], 16)), ""))
+ except:
+ print "Failed to parse 32-bit hex value %s" % sys.argv[1]
+ return
+
+ errors = 0
+ success = 0
+ f = open('errors.bin', 'w')
+ if brute:
+ total = 1000000
+ random.seed(5)
+ for a in xrange(total):
+ instruction = struct.pack("<L", random.randint(0, 0xffffffff))
+ binja = disassemble_binja(instruction, 0x08040000)
+ capstone = disassemble_capstone(instruction, 0x08040000)
+ if binja == "decomposer failed":
+ continue
+ if (binja is not None and capstone is not None and not areEqual(binja, capstone)):
+ if "UNDEFINED" in binja or "failed" in binja:
+ if capstone is not None:
+ opcode = capstone.split('\t')[0]
+ print "ERROR: Oracle: %s '%s'\n You: %s '%s'" % (instruction.encode('hex'), capstone, instruction.encode('hex'), binja)
+ f.write(instruction)
+ errors += 1
+ else:
+ try:
+ print "ERROR: Oracle: %s '%s'\n You: %s '%s'" % (instruction.encode('hex'), capstone, instruction.encode('hex'), binja)
+ except:
+ print repr(capstone)
+ print repr(binja)
+ f.write(instruction)
+ errors += 1
+ else:
+ success += 1
+ print "errors: %d/%d success percentage %%%.2f" % (errors, total, (float(success)/float(total)) * 100.0)
+ sys.exit()
+ undefined = {}
+ for instruction, disasm in instructions:
+ binja = disassemble_binja(instruction, 0x08040000)
+ #capstone = disasm
+ capstone = disassemble_capstone(instruction, 0x08040000)
+ if verbose:
+ print "binja:", binja
+ print "capst:", capstone
+ if binja is not None and capstone is not None and not areEqual(binja, capstone):
+ if "UNDEFINED" in binja or "failed" in binja:
+ if capstone is not None:
+ opcode = capstone.split('\t')[0]
+ if opcode not in undefined.keys():
+ undefined[opcode] = 1
+ else:
+ undefined[opcode] += 1
+ else:
+ errors += 1
+ print "ERROR: Oracle: '%s'\nERROR: You: '%s'" % (capstone, binja)
+ f.write(instruction)
+ else:
+ success += 1
+ print "%d errors, %d successes, %d test cases success percentage %%%.2f" % (errors, success, len(instructions), (float(success)/float(len(instructions))) * 100.0)
+
+ print "%d undefined instructions" % len(undefined)
+ if verbose:
+ import operator
+ sorted_undefined = sorted(undefined.items(), key=operator.itemgetter(1))
+ for a,b in sorted_undefined:
+ print "%s\t%d" % (a, b)
+
+if __name__ == "__main__":
+ main()
+