summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Rozner <joe@deadbytes.net>2024-08-20 21:55:05 -0700
committerAlexander Taylor <alex@vector35.com>2024-08-22 19:35:30 -0400
commitf038c7c9a9646c2a126d693eab7d297cd25565e3 (patch)
tree51b1366dabb78fad2e2a7cc0518b3604620f66d8
parent2a9d07d7fefe5a6d7f7a482397060a96782142ac (diff)
Add msp430 architecture
-rw-r--r--arch/msp430/CMakeLists.txt114
-rw-r--r--arch/msp430/Cargo.lock393
-rw-r--r--arch/msp430/Cargo.toml13
-rw-r--r--arch/msp430/src/architecture.rs763
-rw-r--r--arch/msp430/src/flag.rs153
-rw-r--r--arch/msp430/src/lib.rs55
-rw-r--r--arch/msp430/src/lift.rs670
-rw-r--r--arch/msp430/src/register.rs130
8 files changed, 2291 insertions, 0 deletions
diff --git a/arch/msp430/CMakeLists.txt b/arch/msp430/CMakeLists.txt
new file mode 100644
index 00000000..034ed1e2
--- /dev/null
+++ b/arch/msp430/CMakeLists.txt
@@ -0,0 +1,114 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(arch_msp430)
+
+file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS
+ ../../binaryninjacore.h
+ ${PROJECT_SOURCE_DIR}/Cargo.toml
+ ${PROJECT_SOURCE_DIR}/src/*.rs
+ ${PROJECT_SOURCE_DIR}/disasm/src/*.rs)
+
+if(CMAKE_BUILD_TYPE MATCHES Debug)
+ set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug)
+ set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target)
+else()
+ set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/release)
+ set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --release)
+endif()
+
+if(FORCE_COLORED_OUTPUT)
+ set(CARGO_OPTS ${CARGO_OPTS} --color always)
+endif()
+
+set(CARGO_FEATURES "")
+set(OUTPUT_FILE_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}arch_msp430${CMAKE_SHARED_LIBRARY_SUFFIX})
+set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}arch_msp430.pdb)
+set(OUTPUT_FILE_PATH ${BN_CORE_PLUGIN_DIR}/${OUTPUT_FILE_NAME})
+set(OUTPUT_PDB_PATH ${BN_CORE_PLUGIN_DIR}/${OUTPUT_PDB_NAME})
+
+set(BINJA_LIB_DIR ${BN_INSTALL_BIN_DIR})
+
+add_custom_target(arch_msp430 ALL DEPENDS ${OUTPUT_FILE_PATH})
+add_dependencies(arch_msp430 binaryninjaapi)
+get_target_property(BN_API_SOURCE_DIR binaryninjaapi SOURCE_DIR)
+list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake")
+find_package(BinaryNinjaCore REQUIRED)
+
+set_property(TARGET arch_msp430 PROPERTY OUTPUT_FILE_PATH ${OUTPUT_FILE_PATH})
+
+# Add the whole api to the depends too
+file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
+ ${BN_API_SOURCE_DIR}/rust/src/*.rs
+ ${BN_API_SOURCE_DIR}/rust/binaryninjacore-sys/src/*.rs)
+
+find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
+set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_STABLE_VERSION} cargo)
+
+if(APPLE)
+ if(UNIVERSAL)
+ if(CMAKE_BUILD_TYPE MATCHES Debug)
+ set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE_NAME})
+ set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE_NAME})
+ else()
+ set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE_NAME})
+ set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE_NAME})
+ endif()
+
+ add_custom_command(
+ OUTPUT ${OUTPUT_FILE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
+ ${RUSTUP_COMMAND} clean --target=aarch64-apple-darwin ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
+ ${RUSTUP_COMMAND} clean --target=x86_64-apple-darwin ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
+ ${RUSTUP_COMMAND} build --target=aarch64-apple-darwin ${CARGO_OPTS} ${CARGO_FEATURES}
+ COMMAND ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
+ ${RUSTUP_COMMAND} build --target=x86_64-apple-darwin ${CARGO_OPTS} ${CARGO_FEATURES}
+ COMMAND lipo -create ${AARCH64_LIB_PATH} ${X86_64_LIB_PATH} -output ${OUTPUT_FILE_PATH}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
+ )
+ else()
+ if(CMAKE_BUILD_TYPE MATCHES Debug)
+ set(LIB_PATH ${PROJECT_BINARY_DIR}/target/debug/${OUTPUT_FILE_NAME})
+ else()
+ set(LIB_PATH ${PROJECT_BINARY_DIR}/target/release/${OUTPUT_FILE_NAME})
+ endif()
+
+ add_custom_command(
+ OUTPUT ${OUTPUT_FILE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
+ ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
+ ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIB_PATH} ${OUTPUT_FILE_PATH}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
+ )
+ endif()
+elseif(WIN32)
+ add_custom_command(
+ OUTPUT ${OUTPUT_FILE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
+ COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${OUTPUT_PDB_PATH}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
+ )
+else()
+ add_custom_command(
+ OUTPUT ${OUTPUT_FILE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
+ COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
+ COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
+ )
+endif()
diff --git a/arch/msp430/Cargo.lock b/arch/msp430/Cargo.lock
new file mode 100644
index 00000000..6f1b4c37
--- /dev/null
+++ b/arch/msp430/Cargo.lock
@@ -0,0 +1,393 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "arch_msp430"
+version = "0.1.0"
+dependencies = [
+ "binaryninja",
+ "log",
+ "msp430-asm",
+]
+
+[[package]]
+name = "binaryninja"
+version = "0.1.0"
+source = "git+https://github.com/Vector35/binaryninja-api.git?branch=dev#46b26cf9f4a8eee8e94f86532220e61120c2a54d"
+dependencies = [
+ "binaryninjacore-sys",
+ "lazy_static",
+ "libc",
+ "log",
+]
+
+[[package]]
+name = "binaryninjacore-sys"
+version = "0.1.0"
+source = "git+https://github.com/Vector35/binaryninja-api.git?branch=dev#46b26cf9f4a8eee8e94f86532220e61120c2a54d"
+dependencies = [
+ "bindgen",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.69.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "itertools",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn",
+ "which",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "home"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "libloading"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
+dependencies = [
+ "cfg-if",
+ "windows-targets",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "msp430-asm"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91c22f111c205b788cc10e2a58aed039d5dd5554d3a9236be3d413a912ff3cb"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "syn"
+version = "2.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/arch/msp430/Cargo.toml b/arch/msp430/Cargo.toml
new file mode 100644
index 00000000..ce849cb0
--- /dev/null
+++ b/arch/msp430/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "arch_msp430"
+version = "0.1.0"
+authors = ["jrozner"]
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev" }
+log = "0.4.14"
+msp430-asm = "^0.2"
diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs
new file mode 100644
index 00000000..ab392a38
--- /dev/null
+++ b/arch/msp430/src/architecture.rs
@@ -0,0 +1,763 @@
+use crate::flag::{Flag, FlagClass, FlagGroup, FlagWrite};
+use crate::lift::lift_instruction;
+use crate::register::Register;
+
+use binaryninja::{
+ architecture::{
+ Architecture, BranchInfo, CoreArchitecture, CustomArchitectureHandle, FlagCondition,
+ InstructionInfo, UnusedIntrinsic, UnusedRegisterStack, UnusedRegisterStackInfo,
+ },
+ disassembly::{InstructionTextToken, InstructionTextTokenContents},
+ llil::{LiftedExpr, Lifter},
+ Endianness,
+};
+
+use msp430_asm::{
+ emulate::Emulated, instruction::Instruction, jxx::Jxx, operand::Operand,
+ single_operand::SingleOperand, two_operand::TwoOperand,
+};
+
+use log::error;
+
+const MIN_MNEMONIC: usize = 9;
+
+pub struct Msp430 {
+ handle: CoreArchitecture,
+ custom_handle: CustomArchitectureHandle<Msp430>,
+}
+
+impl Msp430 {
+ pub fn new(handle: CoreArchitecture, custom_handle: CustomArchitectureHandle<Msp430>) -> Self {
+ Msp430 {
+ handle,
+ custom_handle,
+ }
+ }
+}
+
+impl Architecture for Msp430 {
+ type Handle = CustomArchitectureHandle<Self>;
+ type RegisterStackInfo = UnusedRegisterStackInfo<Self::Register>;
+ type RegisterStack = UnusedRegisterStack<Self::Register>;
+ type Register = Register;
+ type RegisterInfo = Register;
+ type Flag = Flag;
+ type FlagWrite = FlagWrite;
+ type FlagClass = FlagClass;
+ type FlagGroup = FlagGroup;
+ type Intrinsic = UnusedIntrinsic;
+
+ fn endianness(&self) -> Endianness {
+ Endianness::LittleEndian
+ }
+
+ fn address_size(&self) -> usize {
+ 2 // 16 bit
+ }
+
+ fn default_integer_size(&self) -> usize {
+ 2 // 16 bit integers
+ }
+
+ fn instruction_alignment(&self) -> usize {
+ 2
+ }
+
+ fn max_instr_len(&self) -> usize {
+ 6
+ }
+
+ fn opcode_display_len(&self) -> usize {
+ self.max_instr_len()
+ }
+
+ fn associated_arch_by_addr(&self, _addr: &mut u64) -> CoreArchitecture {
+ self.handle
+ }
+
+ fn instruction_info(&self, data: &[u8], addr: u64) -> Option<InstructionInfo> {
+ match msp430_asm::decode(data) {
+ Ok(inst) => {
+ let mut info = InstructionInfo::new(inst.size(), 0);
+
+ match inst {
+ Instruction::Jnz(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jz(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jlo(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jc(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jn(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jge(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jl(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jmp(inst) => {
+ info.add_branch(
+ BranchInfo::Unconditional(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ }
+ Instruction::Br(inst) => match inst.destination() {
+ Some(Operand::RegisterDirect(_)) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ Some(Operand::Indexed(_)) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ Some(Operand::Absolute(value)) => info.add_branch(
+ BranchInfo::Unconditional(*value as u64),
+ Some(self.handle),
+ ),
+ Some(Operand::Symbolic(offset)) => info.add_branch(
+ BranchInfo::Unconditional((addr as i64 + *offset as i64) as u64),
+ Some(self.handle),
+ ),
+ Some(Operand::Immediate(addr)) => info
+ .add_branch(BranchInfo::Unconditional(*addr as u64), Some(self.handle)),
+ Some(Operand::Constant(_)) => {
+ info.add_branch(BranchInfo::Unconditional(addr), Some(self.handle))
+ }
+ Some(Operand::RegisterIndirect(_))
+ | Some(Operand::RegisterIndirectAutoIncrement(_)) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ None => {}
+ },
+ Instruction::Call(inst) => match inst.source() {
+ Operand::RegisterDirect(_) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ Operand::Indexed(_) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ Operand::Absolute(value) => {
+ info.add_branch(BranchInfo::Call(*value as u64), Some(self.handle))
+ }
+ Operand::Symbolic(offset) => info.add_branch(
+ BranchInfo::Call((addr as i64 + *offset as i64) as u64),
+ Some(self.handle),
+ ),
+ Operand::Immediate(addr) => {
+ info.add_branch(BranchInfo::Call(*addr as u64), Some(self.handle))
+ }
+ Operand::Constant(_) => {
+ info.add_branch(BranchInfo::Call(addr), Some(self.handle))
+ }
+ Operand::RegisterIndirect(_)
+ | Operand::RegisterIndirectAutoIncrement(_) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ },
+ Instruction::Reti(_) => {
+ info.add_branch(BranchInfo::FunctionReturn, Some(self.handle));
+ }
+ Instruction::Ret(_) => {
+ info.add_branch(BranchInfo::FunctionReturn, Some(self.handle));
+ }
+ _ => {}
+ }
+
+ Some(info)
+ }
+ Err(_) => None,
+ }
+ }
+
+ fn instruction_text(
+ &self,
+ data: &[u8],
+ addr: u64,
+ ) -> Option<(usize, Vec<InstructionTextToken>)> {
+ match msp430_asm::decode(data) {
+ Ok(inst) => {
+ let tokens = generate_tokens(&inst, addr);
+ if tokens.is_empty() {
+ None
+ } else {
+ Some((inst.size(), tokens))
+ }
+ }
+ Err(_) => None,
+ }
+ }
+
+ fn instruction_llil(
+ &self,
+ data: &[u8],
+ addr: u64,
+ il: &mut Lifter<Self>,
+ ) -> Option<(usize, bool)> {
+ match msp430_asm::decode(data) {
+ Ok(inst) => {
+ lift_instruction(&inst, addr, il);
+ Some((inst.size(), true))
+ }
+ Err(_) => None,
+ }
+ }
+
+ fn flags_required_for_flag_condition(
+ &self,
+ condition: FlagCondition,
+ _class: Option<Self::FlagClass>,
+ ) -> Vec<Self::Flag> {
+ match condition {
+ FlagCondition::LLFC_UGE => vec![Flag::C],
+ FlagCondition::LLFC_ULT => vec![Flag::C],
+ FlagCondition::LLFC_SGE => vec![Flag::N, Flag::V],
+ FlagCondition::LLFC_SLT => vec![Flag::N, Flag::V],
+ FlagCondition::LLFC_E => vec![Flag::Z],
+ FlagCondition::LLFC_NE => vec![Flag::Z],
+ FlagCondition::LLFC_NEG => vec![Flag::N],
+ FlagCondition::LLFC_POS => vec![Flag::N],
+ _ => vec![],
+ }
+ }
+
+ fn flag_group_llil<'a>(
+ &self,
+ _group: Self::FlagGroup,
+ _il: &'a mut Lifter<Self>,
+ ) -> Option<LiftedExpr<'a, Self>> {
+ None
+ }
+
+ fn registers_all(&self) -> Vec<Self::Register> {
+ vec![
+ Register::Pc,
+ Register::Sp,
+ Register::Sr,
+ Register::Cg,
+ Register::R4,
+ Register::R5,
+ Register::R6,
+ Register::R7,
+ Register::R8,
+ Register::R9,
+ Register::R10,
+ Register::R11,
+ Register::R12,
+ Register::R13,
+ Register::R14,
+ Register::R15,
+ ]
+ }
+
+ fn registers_full_width(&self) -> Vec<Self::Register> {
+ vec![
+ Register::Pc,
+ Register::Sp,
+ Register::Sr,
+ Register::Cg,
+ Register::R4,
+ Register::R5,
+ Register::R6,
+ Register::R7,
+ Register::R8,
+ Register::R9,
+ Register::R10,
+ Register::R11,
+ Register::R12,
+ Register::R13,
+ Register::R14,
+ Register::R15,
+ ]
+ }
+
+ fn registers_global(&self) -> Vec<Self::Register> {
+ Vec::new()
+ }
+
+ fn registers_system(&self) -> Vec<Self::Register> {
+ Vec::new()
+ }
+
+ fn flags(&self) -> Vec<Self::Flag> {
+ vec![Flag::C, Flag::Z, Flag::N, Flag::V]
+ }
+
+ fn flag_write_types(&self) -> Vec<Self::FlagWrite> {
+ vec![
+ FlagWrite::All,
+ FlagWrite::Nz,
+ FlagWrite::Nvz,
+ FlagWrite::Cnz,
+ ]
+ }
+
+ fn flag_classes(&self) -> Vec<Self::FlagClass> {
+ Vec::new()
+ }
+
+ fn flag_groups(&self) -> Vec<Self::FlagGroup> {
+ Vec::new()
+ }
+
+ fn stack_pointer_reg(&self) -> Option<Self::Register> {
+ Some(Register::Sp)
+ }
+
+ fn link_reg(&self) -> Option<Self::Register> {
+ None
+ }
+
+ fn register_from_id(&self, id: u32) -> Option<Self::Register> {
+ match id.try_into() {
+ Ok(register) => Some(register),
+ Err(_) => None,
+ }
+ }
+
+ fn flag_from_id(&self, id: u32) -> Option<Self::Flag> {
+ match id.try_into() {
+ Ok(flag) => Some(flag),
+ Err(_) => {
+ error!("invalid flag id {}", id);
+ None
+ }
+ }
+ }
+
+ fn flag_write_from_id(&self, id: u32) -> Option<Self::FlagWrite> {
+ match id.try_into() {
+ Ok(flag_write) => Some(flag_write),
+ Err(_) => {
+ error!("invalid flag write id {}", id);
+ None
+ }
+ }
+ }
+
+ fn flag_class_from_id(&self, _: u32) -> Option<Self::FlagClass> {
+ None
+ }
+
+ fn flag_group_from_id(&self, _: u32) -> Option<Self::FlagGroup> {
+ None
+ }
+
+ fn handle(&self) -> Self::Handle {
+ self.custom_handle
+ }
+}
+
+impl AsRef<CoreArchitecture> for Msp430 {
+ fn as_ref(&self) -> &CoreArchitecture {
+ &self.handle
+ }
+}
+
+fn generate_tokens(inst: &Instruction, addr: u64) -> Vec<InstructionTextToken> {
+ match inst {
+ Instruction::Rrc(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Swpb(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Rra(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Sxt(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Push(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Call(inst) => generate_single_operand_tokens(inst, addr, true),
+ Instruction::Reti(_) => vec![InstructionTextToken::new(
+ "reti",
+ InstructionTextTokenContents::Instruction,
+ )],
+
+ // Jxx instructions
+ Instruction::Jnz(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jz(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jlo(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jc(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jn(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jge(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jl(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jmp(inst) => generate_jxx_tokens(inst, addr),
+
+ // two operand instructions
+ Instruction::Mov(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Add(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Addc(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Subc(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Sub(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Cmp(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Dadd(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Bit(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Bic(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Bis(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Xor(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::And(inst) => generate_two_operand_tokens(inst, addr),
+
+ // emulated
+ Instruction::Adc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Br(inst) => generate_emulated_tokens(inst, addr, true),
+ Instruction::Clr(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Clrc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Clrn(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Clrz(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Dadc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Dec(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Decd(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Dint(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Eint(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Inc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Incd(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Inv(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Nop(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Pop(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Ret(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Rla(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Rlc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Sbc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Setc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Setn(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Setz(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Tst(inst) => generate_emulated_tokens(inst, addr, false),
+ }
+}
+
+fn generate_single_operand_tokens(
+ inst: &impl SingleOperand,
+ addr: u64,
+ call: bool,
+) -> Vec<InstructionTextToken> {
+ let mut res = vec![InstructionTextToken::new(
+ inst.mnemonic(),
+ InstructionTextTokenContents::Instruction,
+ )];
+
+ if inst.mnemonic().len() < MIN_MNEMONIC {
+ let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len());
+ res.push(InstructionTextToken::new(
+ &padding,
+ InstructionTextTokenContents::Text,
+ ))
+ }
+
+ res.extend_from_slice(&generate_operand_tokens(inst.source(), addr, call));
+
+ res
+}
+
+fn generate_jxx_tokens(inst: &impl Jxx, addr: u64) -> Vec<InstructionTextToken> {
+ let fixed_addr = offset_to_absolute(addr, inst.offset());
+
+ let mut res = vec![InstructionTextToken::new(
+ inst.mnemonic(),
+ InstructionTextTokenContents::Instruction,
+ )];
+
+ if inst.mnemonic().len() < MIN_MNEMONIC {
+ let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len());
+ res.push(InstructionTextToken::new(
+ &padding,
+ InstructionTextTokenContents::Text,
+ ))
+ }
+
+ res.push(InstructionTextToken::new(
+ &format!("0x{fixed_addr:4x}"),
+ InstructionTextTokenContents::CodeRelativeAddress(fixed_addr),
+ ));
+
+ res
+}
+
+fn generate_two_operand_tokens(inst: &impl TwoOperand, addr: u64) -> Vec<InstructionTextToken> {
+ let mut res = vec![InstructionTextToken::new(
+ inst.mnemonic(),
+ InstructionTextTokenContents::Instruction,
+ )];
+
+ if inst.mnemonic().len() < MIN_MNEMONIC {
+ let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len());
+ res.push(InstructionTextToken::new(
+ &padding,
+ InstructionTextTokenContents::Text,
+ ))
+ }
+
+ res.extend_from_slice(&generate_operand_tokens(inst.source(), addr, false));
+ res.push(InstructionTextToken::new(
+ ", ",
+ InstructionTextTokenContents::OperandSeparator,
+ ));
+ res.extend_from_slice(&generate_operand_tokens(inst.destination(), addr, false));
+
+ res
+}
+
+fn generate_emulated_tokens(
+ inst: &impl Emulated,
+ addr: u64,
+ call: bool,
+) -> Vec<InstructionTextToken> {
+ let mut res = vec![InstructionTextToken::new(
+ inst.mnemonic(),
+ InstructionTextTokenContents::Instruction,
+ )];
+
+ if inst.mnemonic().len() < MIN_MNEMONIC {
+ let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len());
+ res.push(InstructionTextToken::new(
+ &padding,
+ InstructionTextTokenContents::Text,
+ ))
+ }
+
+ if inst.destination().is_some() {
+ res.extend_from_slice(&generate_operand_tokens(
+ &inst.destination().unwrap(),
+ addr,
+ call,
+ ))
+ }
+
+ res
+}
+
+fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec<InstructionTextToken> {
+ match source {
+ Operand::RegisterDirect(r) => match r {
+ 0 => vec![InstructionTextToken::new(
+ "pc",
+ InstructionTextTokenContents::Register,
+ )],
+ 1 => vec![InstructionTextToken::new(
+ "sp",
+ InstructionTextTokenContents::Register,
+ )],
+ 2 => vec![InstructionTextToken::new(
+ "sr",
+ InstructionTextTokenContents::Register,
+ )],
+ 3 => vec![InstructionTextToken::new(
+ "cg",
+ InstructionTextTokenContents::Register,
+ )],
+ _ => vec![InstructionTextToken::new(
+ &format!("r{r}"),
+ InstructionTextTokenContents::Register,
+ )],
+ },
+ Operand::Indexed((r, i)) => match r {
+ 0 => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new("pc", InstructionTextTokenContents::Register),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ 1 => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new("sp", InstructionTextTokenContents::Register),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ 2 => {
+ let num_text = if *i >= 0 {
+ &format!("{i:#x}")
+ } else {
+ &format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new("sr", InstructionTextTokenContents::Register),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ 3 => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new("cg", InstructionTextTokenContents::Register),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ _ => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new(
+ &format!("r{r}"),
+ InstructionTextTokenContents::Register,
+ ),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ },
+ Operand::RegisterIndirect(r) => {
+ let r_text = if *r == 1 {
+ "sp".into()
+ } else {
+ format!("r{r}")
+ };
+
+ vec![
+ InstructionTextToken::new("@", InstructionTextTokenContents::Text),
+ InstructionTextToken::new(&r_text, InstructionTextTokenContents::Register),
+ ]
+ }
+ Operand::RegisterIndirectAutoIncrement(r) => {
+ let r_text = if *r == 1 {
+ "sp".into()
+ } else {
+ format!("r{r}")
+ };
+
+ vec![
+ InstructionTextToken::new("@", InstructionTextTokenContents::Text),
+ InstructionTextToken::new(&r_text, InstructionTextTokenContents::Register),
+ InstructionTextToken::new("+", InstructionTextTokenContents::Text),
+ ]
+ }
+ Operand::Symbolic(i) => {
+ let val = (addr as i64 + *i as i64) as u64;
+ vec![InstructionTextToken::new(
+ &format!("{val:#x}"),
+ InstructionTextTokenContents::CodeRelativeAddress(val),
+ )]
+ }
+ Operand::Immediate(i) => {
+ if call {
+ vec![InstructionTextToken::new(
+ &format!("{i:#x}"),
+ InstructionTextTokenContents::CodeRelativeAddress(*i as u64),
+ )]
+ } else {
+ vec![InstructionTextToken::new(
+ &format!("{i:#x}"),
+ InstructionTextTokenContents::PossibleAddress(*i as u64),
+ )]
+ }
+ }
+ Operand::Absolute(a) => {
+ if call {
+ vec![InstructionTextToken::new(
+ &format!("{a:#x}"),
+ InstructionTextTokenContents::CodeRelativeAddress(*a as u64),
+ )]
+ } else {
+ vec![InstructionTextToken::new(
+ &format!("{a:#x}"),
+ InstructionTextTokenContents::PossibleAddress(*a as u64),
+ )]
+ }
+ }
+ Operand::Constant(i) => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+
+ vec![
+ InstructionTextToken::new("#", InstructionTextTokenContents::Text),
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ ]
+ }
+ }
+}
+
+pub(crate) fn offset_to_absolute(addr: u64, offset: i16) -> u64 {
+ // add + 2 to addr to get past the jxx instruction which is always 2 bytes
+ ((addr + 2) as i64 + ((offset * 2) as i64)) as u64
+}
diff --git a/arch/msp430/src/flag.rs b/arch/msp430/src/flag.rs
new file mode 100644
index 00000000..115fe866
--- /dev/null
+++ b/arch/msp430/src/flag.rs
@@ -0,0 +1,153 @@
+use binaryninja::architecture;
+use binaryninja::architecture::FlagRole;
+
+use std::borrow::Cow;
+use std::collections::HashMap;
+
+// NOTE: GIE, CPUOFF, OSCOFF, SG0, and SG1 not implemented as it's not clear how they would be used
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Flag {
+ C,
+ Z,
+ N,
+ V,
+}
+
+impl architecture::Flag for Flag {
+ type FlagClass = FlagClass;
+
+ fn name(&self) -> Cow<str> {
+ match self {
+ Self::C => "c".into(),
+ Self::Z => "z".into(),
+ Self::N => "n".into(),
+ Self::V => "v".into(),
+ }
+ }
+
+ fn role(&self, _class: Option<Self::FlagClass>) -> architecture::FlagRole {
+ match self {
+ Self::C => FlagRole::CarryFlagRole,
+ Self::Z => FlagRole::ZeroFlagRole,
+ Self::N => FlagRole::NegativeSignFlagRole,
+ Self::V => FlagRole::OverflowFlagRole,
+ }
+ }
+
+ fn id(&self) -> u32 {
+ match self {
+ Self::C => 0,
+ Self::Z => 1,
+ Self::N => 2,
+ Self::V => 8,
+ }
+ }
+}
+
+impl TryFrom<u32> for Flag {
+ type Error = ();
+ fn try_from(flag: u32) -> Result<Self, Self::Error> {
+ match flag {
+ 0 => Ok(Self::C),
+ 1 => Ok(Self::Z),
+ 2 => Ok(Self::N),
+ 8 => Ok(Self::V),
+ _ => Err(()),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct FlagClass {}
+
+impl architecture::FlagClass for FlagClass {
+ fn name(&self) -> Cow<str> {
+ unimplemented!()
+ }
+
+ fn id(&self) -> u32 {
+ unimplemented!()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum FlagGroup {}
+
+impl architecture::FlagGroup for FlagGroup {
+ type FlagType = Flag;
+ type FlagClass = FlagClass;
+
+ fn name(&self) -> Cow<str> {
+ unimplemented!()
+ }
+
+ fn id(&self) -> u32 {
+ unimplemented!()
+ }
+
+ fn flags_required(&self) -> Vec<Self::FlagType> {
+ unimplemented!()
+ }
+
+ fn flag_conditions(&self) -> HashMap<Self::FlagClass, architecture::FlagCondition> {
+ unimplemented!()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum FlagWrite {
+ All,
+ Nz,
+ Nvz,
+ Cnz,
+}
+
+impl architecture::FlagWrite for FlagWrite {
+ type FlagType = Flag;
+ type FlagClass = FlagClass;
+
+ fn name(&self) -> Cow<str> {
+ match self {
+ Self::All => "*".into(),
+ Self::Nz => "nz".into(),
+ Self::Nvz => "nvz".into(),
+ Self::Cnz => "cnz".into(),
+ }
+ }
+
+ fn class(&self) -> Option<Self::FlagClass> {
+ None
+ }
+
+ fn id(&self) -> u32 {
+ match self {
+ Self::All => 1,
+ Self::Nz => 2,
+ Self::Nvz => 3,
+ Self::Cnz => 4,
+ }
+ }
+
+ fn flags_written(&self) -> Vec<Self::FlagType> {
+ match self {
+ Self::All => vec![Flag::C, Flag::N, Flag::V, Flag::Z],
+ Self::Nz => vec![Flag::N, Flag::Z],
+ Self::Nvz => vec![Flag::N, Flag::V, Flag::Z],
+ Self::Cnz => vec![Flag::C, Flag::N, Flag::Z],
+ }
+ }
+}
+
+impl TryFrom<u32> for FlagWrite {
+ type Error = ();
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ match value {
+ 1 => Ok(Self::All),
+ 2 => Ok(Self::Nz),
+ 3 => Ok(Self::Nvz),
+ 4 => Ok(Self::Cnz),
+ _ => Err(()),
+ }
+ }
+}
diff --git a/arch/msp430/src/lib.rs b/arch/msp430/src/lib.rs
new file mode 100644
index 00000000..ffa89bad
--- /dev/null
+++ b/arch/msp430/src/lib.rs
@@ -0,0 +1,55 @@
+extern crate binaryninja;
+extern crate log;
+extern crate msp430_asm;
+
+use binaryninja::{
+ architecture::ArchitectureExt,
+ callingconvention,
+ custombinaryview::{BinaryViewType, BinaryViewTypeExt},
+ Endianness,
+};
+
+mod architecture;
+mod flag;
+mod lift;
+mod register;
+
+use architecture::Msp430;
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "C" fn CorePluginInit() -> bool {
+ binaryninja::logger::init(log::LevelFilter::Info).unwrap();
+ let arch = binaryninja::architecture::register_architecture(
+ "msp430",
+ |custom_handle, handle| Msp430::new(handle, custom_handle),
+ );
+
+ // we may need to introduce additional calling conventions here to
+ // support additional ABIs. MSPGCC's calling convention (what
+ // microcorruption seems to use) has some differences between the EABI
+ // calling convention though GCC has since added support for the EABI
+ // calling convention according to
+ // https://www.ti.com/lit/an/slaa664/slaa664.pdf?ts=1613210655081. MSPGCC
+ // appears to be a legacy calling convention while EABI is the newer
+ // standardized one that is compatible with TI's compiler
+ let default = callingconvention::ConventionBuilder::new(arch)
+ .is_eligible_for_heuristics(true)
+ .int_arg_registers(&["r15", "r14", "r13", "r12"])
+ .return_int_reg("r15")
+ .return_hi_int_reg("r14")
+ .register("default");
+ callingconvention::ConventionBuilder::new(arch)
+ .is_eligible_for_heuristics(true)
+ .return_int_reg("r15")
+ .return_hi_int_reg("r14")
+ .register("stack");
+
+ arch.set_default_calling_convention(&default);
+
+ if let Ok(bv) = BinaryViewType::by_name("ELF") {
+ bv.register_arch(105, Endianness::LittleEndian, arch);
+ }
+
+ true
+}
diff --git a/arch/msp430/src/lift.rs b/arch/msp430/src/lift.rs
new file mode 100644
index 00000000..85d204e1
--- /dev/null
+++ b/arch/msp430/src/lift.rs
@@ -0,0 +1,670 @@
+use crate::architecture::offset_to_absolute;
+use crate::flag::{Flag, FlagWrite};
+use crate::register::Register;
+use crate::Msp430;
+
+use binaryninja::{
+ architecture::FlagCondition,
+ llil::{Label, LiftedNonSSA, Lifter, Mutable, NonSSA},
+};
+
+use msp430_asm::emulate::Emulated;
+use msp430_asm::instruction::Instruction;
+use msp430_asm::jxx::Jxx;
+use msp430_asm::operand::{Operand, OperandWidth};
+use msp430_asm::single_operand::SingleOperand;
+use msp430_asm::two_operand::TwoOperand;
+
+use log::info;
+
+macro_rules! auto_increment {
+ ($src:expr, $il:ident) => {
+ if let Operand::RegisterIndirectAutoIncrement(r) = $src {
+ $il.set_reg(
+ 2,
+ Register::try_from(*r as u32).unwrap(),
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, 2),
+ ),
+ )
+ .append();
+ }
+ };
+}
+
+macro_rules! one_operand {
+ ($source:expr, $il:ident, $op:ident) => {
+ match $source {
+ Operand::RegisterDirect(r) => $il
+ .set_reg(2, Register::try_from(*r as u32).unwrap(), $op)
+ .append(),
+ Operand::Indexed((r, offset)) => $il
+ .store(
+ 2,
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, *offset as u64),
+ ),
+ $op,
+ )
+ .append(),
+ Operand::Symbolic(offset) => $il
+ .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op)
+ .append(),
+ Operand::Absolute(val) => $il.store(2, $il.const_ptr(*val as u64), $op).append(),
+ Operand::Immediate(_) => $op.append(),
+ Operand::RegisterIndirect(r) => $il
+ .store(2, $il.reg(2, Register::try_from(*r as u32).unwrap()), $op)
+ .append(),
+ Operand::RegisterIndirectAutoIncrement(r) => {
+ $il.store(2, $il.reg(2, Register::try_from(*r as u32).unwrap()), $op)
+ .append();
+ $il.set_reg(
+ 2,
+ Register::try_from(*r as u32).unwrap(),
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, 2),
+ ),
+ )
+ .append()
+ }
+ _ => {
+ unreachable!()
+ }
+ };
+ };
+}
+
+macro_rules! two_operand {
+ ($destination:expr, $il:ident, $op:ident) => {
+ match $destination {
+ Operand::RegisterDirect(r) => $il
+ .set_reg(2, Register::try_from(*r as u32).unwrap(), $op)
+ .append(),
+ Operand::Indexed((r, offset)) => $il
+ .store(
+ 2,
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, *offset as u64),
+ ),
+ $op,
+ )
+ .append(),
+ Operand::Symbolic(offset) => $il
+ .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op)
+ .append(),
+ Operand::Absolute(val) => $il.store(2, $il.const_ptr(*val as u64), $op).append(),
+ _ => {
+ unreachable!()
+ }
+ };
+ };
+}
+
+macro_rules! emulated {
+ ($inst:ident, $il:ident, $op:ident) => {
+ match $inst.destination() {
+ Some(Operand::RegisterDirect(r)) => $il
+ .set_reg(2, Register::try_from(*r as u32).unwrap(), $op)
+ .append(),
+ Some(Operand::Indexed((r, offset))) => $il
+ .store(
+ 2,
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, *offset as u64),
+ ),
+ $op,
+ )
+ .append(),
+ Some(Operand::Symbolic(offset)) => $il
+ .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op)
+ .append(),
+ Some(Operand::Absolute(val)) => $il.store(2, $il.const_ptr(*val as u64), $op).append(),
+ _ => {
+ unreachable!()
+ }
+ };
+ };
+}
+
+macro_rules! conditional_jump {
+ ($addr:ident, $inst:ident, $cond:ident, $il:ident) => {
+ let true_addr = offset_to_absolute($addr, $inst.offset());
+ let false_addr = $addr + $inst.size() as u64;
+ let mut new_true = Label::new();
+ let mut new_false = Label::new();
+
+ let true_label = $il.label_for_address(true_addr);
+ let false_label = $il.label_for_address(false_addr);
+
+ $il.if_expr(
+ $cond,
+ true_label.unwrap_or_else(|| &new_true),
+ false_label.unwrap_or_else(|| &new_false),
+ )
+ .append();
+
+ if true_label.is_none() {
+ $il.mark_label(&mut new_true);
+ }
+
+ $il.goto(true_label.unwrap_or_else(|| &new_true)).append();
+
+ if false_label.is_none() {
+ $il.mark_label(&mut new_false);
+ }
+ };
+}
+
+pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &Lifter<Msp430>) {
+ match inst {
+ Instruction::Rrc(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = il.const_int(size, 1);
+ let dest = lift_source_operand(inst.source(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.rrc(size, dest, src).with_flag_write(FlagWrite::All))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.rrc(size, dest, src).with_flag_write(FlagWrite::All)
+ }
+ };
+ one_operand!(inst.source(), il, op);
+ }
+ Instruction::Swpb(inst) => {
+ let src = lift_source_operand(inst.source(), 2, il);
+ let op = il.rol(2, src, il.const_int(2, 8));
+ one_operand!(inst.source(), il, op);
+ }
+ Instruction::Rra(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = il.const_int(size, 1);
+ let dest = lift_source_operand(inst.source(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.ror(size, dest, src).with_flag_write(FlagWrite::Cnz))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.ror(size, dest, src).with_flag_write(FlagWrite::Cnz)
+ }
+ };
+ one_operand!(inst.source(), il, op);
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ }
+ Instruction::Sxt(inst) => {
+ // source is always 1 byte and instruction is always 2 bytes for sxt because we're sign
+ // extending the low byte into the high and the result is always 2 bytes
+ let src = lift_source_operand(inst.source(), 1, il);
+ let op = il.sx(2, src).with_flag_write(FlagWrite::Nz);
+ one_operand!(inst.source(), il, op);
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ }
+ Instruction::Push(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = lift_source_operand(inst.source(), size, il);
+ il.push(2, src).append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Call(inst) => {
+ // TODO: verify the special autoincrement behavior Josh implemented?
+ let src = if let Operand::Immediate(src) = inst.source() {
+ il.const_ptr(*src as u64)
+ } else {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+
+ lift_source_operand(inst.source(), size, il)
+ };
+ il.call(src).append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Reti(_) => {
+ il.set_reg(2, Register::Sr, il.pop(2))
+ .with_flag_write(FlagWrite::All)
+ .append();
+ il.ret(il.pop(2)).append();
+ }
+
+ // Jxx instructions
+ Instruction::Jnz(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_NE);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jz(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_E);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jlo(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_ULT);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jc(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_UGE);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jn(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_NEG);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jge(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_SGE);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jl(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_SLT);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jmp(inst) => {
+ let fixed_addr = offset_to_absolute(addr, inst.offset());
+ let label = il.label_for_address(fixed_addr);
+ match label {
+ Some(label) => {
+ il.goto(label).append();
+ }
+ None => {
+ il.jump(il.const_ptr(fixed_addr)).append();
+ }
+ }
+ }
+
+ // two operand instructions
+ Instruction::Mov(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = match inst.operand_width() {
+ OperandWidth::Byte => il
+ .sx(2, lift_source_operand(inst.source(), size, il))
+ .build(),
+ OperandWidth::Word => lift_source_operand(inst.source(), size, il),
+ };
+ two_operand!(inst.destination(), il, src);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Add(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => {
+ il.sx(2, il.add(size, src, dest).with_flag_write(FlagWrite::All))
+ }
+ OperandWidth::Word => il.add(size, src, dest).with_flag_write(FlagWrite::All),
+ };
+ two_operand!(inst.destination(), il, op);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Addc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Subc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Sub(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => {
+ il.sx(2, il.sub(size, src, dest).with_flag_write(FlagWrite::All))
+ }
+ OperandWidth::Word => il.sub(size, src, dest).with_flag_write(FlagWrite::All),
+ };
+ two_operand!(inst.destination(), il, op);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Cmp(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ il.sub(size, dest, src)
+ .with_flag_write(FlagWrite::All)
+ .append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Dadd(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Bit(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ il.and(size, src, dest)
+ .with_flag_write(FlagWrite::Nz)
+ .append();
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Bic(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => il.sx(2, il.and(size, il.not(size, src), dest)),
+ OperandWidth::Word => il.and(size, il.not(size, src), dest),
+ };
+ two_operand!(inst.destination(), il, op);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Bis(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => il.sx(2, il.or(size, src, dest)),
+ OperandWidth::Word => il.or(size, src, dest),
+ };
+ two_operand!(inst.destination(), il, op);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Xor(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => {
+ il.sx(2, il.xor(size, src, dest).with_flag_write(FlagWrite::Nvz))
+ }
+ OperandWidth::Word => il.xor(size, src, dest).with_flag_write(FlagWrite::Nvz),
+ };
+ two_operand!(inst.destination(), il, op);
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::And(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => {
+ il.sx(2, il.and(size, src, dest).with_flag_write(FlagWrite::Nz))
+ }
+ OperandWidth::Word => il.and(size, src, dest).with_flag_write(FlagWrite::Nz),
+ };
+ two_operand!(inst.destination(), il, op);
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ auto_increment!(inst.source(), il);
+ }
+
+ // emulated
+ Instruction::Adc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Br(inst) => {
+ let dest = if let Some(Operand::Immediate(dest)) = inst.destination() {
+ if let Some(label) = il.label_for_address(*dest as u64) {
+ il.goto(label).append();
+ return;
+ } else {
+ il.const_ptr(*dest as u64)
+ }
+ } else {
+ lift_source_operand(&inst.destination().unwrap(), 2, il)
+ };
+
+ il.jump(dest).append();
+ }
+ Instruction::Clr(inst) => {
+ let op = il.const_int(2, 0);
+ emulated!(inst, il, op);
+ }
+ Instruction::Clrc(_) => {
+ // TODO: should we lift clearing the C bit in the SR register as well?
+ il.set_flag(Flag::C, il.const_int(0, 0)).append();
+ }
+ Instruction::Clrn(_) => {
+ // TODO: should we lift clearing the N bit in the SR register as well?
+ il.set_flag(Flag::N, il.const_int(0, 0)).append();
+ }
+ Instruction::Clrz(_) => {
+ // TODO: should we lift clearing the Z bit in the SR register as well?
+ il.set_flag(Flag::Z, il.const_int(0, 0)).append();
+ }
+ Instruction::Dadc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Dec(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => il.sx(
+ 2,
+ il.sub(size, dest, il.const_int(size, 1))
+ .with_flag_write(FlagWrite::All),
+ ),
+ Some(OperandWidth::Word) | None => il
+ .sub(size, dest, il.const_int(size, 1))
+ .with_flag_write(FlagWrite::All),
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Decd(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => il.sx(
+ 2,
+ il.sub(size, dest, il.const_int(size, 2))
+ .with_flag_write(FlagWrite::All),
+ ),
+ Some(OperandWidth::Word) | None => il
+ .sub(size, dest, il.const_int(size, 2))
+ .with_flag_write(FlagWrite::All),
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Dint(_) => {
+ // If GIE flag is ever exposed this should clear it
+ }
+ Instruction::Eint(_) => {
+ // If GIE flag is ever exposed this should set it
+ }
+ Instruction::Inc(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => il.sx(
+ 2,
+ il.add(size, dest, il.const_int(size, 1))
+ .with_flag_write(FlagWrite::All),
+ ),
+ Some(OperandWidth::Word) | None => il
+ .add(size, dest, il.const_int(size, 1))
+ .with_flag_write(FlagWrite::All),
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Incd(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => il.sx(
+ 2,
+ il.add(size, dest, il.const_int(size, 2))
+ .with_flag_write(FlagWrite::All),
+ ),
+ Some(OperandWidth::Word) | None => il
+ .add(size, dest, il.const_int(size, 2))
+ .with_flag_write(FlagWrite::All),
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Inv(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.not(size, dest).with_flag_write(FlagWrite::Nvz))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.not(size, dest).with_flag_write(FlagWrite::Nvz)
+ }
+ };
+ emulated!(inst, il, op);
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ }
+ Instruction::Nop(_) => {
+ il.nop().append();
+ }
+ Instruction::Pop(inst) => {
+ if let Some(Operand::RegisterDirect(r)) = inst.destination() {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ il.set_reg(size, Register::try_from(*r as u32).unwrap(), il.pop(2))
+ .append();
+ } else {
+ info!("pop: invalid destination operand");
+ }
+ }
+ Instruction::Ret(_) => {
+ il.ret(il.pop(2)).append();
+ }
+ Instruction::Rla(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = il.const_int(size, 1);
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.rol(size, dest, src).with_flag_write(FlagWrite::All))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.rol(size, dest, src).with_flag_write(FlagWrite::All)
+ }
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Rlc(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = il.const_int(size, 1);
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.rlc(size, dest, src).with_flag_write(FlagWrite::All))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.rlc(size, dest, src).with_flag_write(FlagWrite::All)
+ }
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Sbc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Setc(_) => {
+ // TODO: should we lift setting the C bit in the SR register as well?
+ il.set_flag(Flag::C, il.const_int(0, 1)).append();
+ }
+ Instruction::Setn(_) => {
+ // TODO: should we lift setting the N bit in the SR register as well?
+ il.set_flag(Flag::N, il.const_int(0, 1)).append();
+ }
+ Instruction::Setz(_) => {
+ // TODO: should we lift setting the Z bit in the SR register as well?
+ il.set_flag(Flag::Z, il.const_int(0, 1)).append();
+ }
+ Instruction::Tst(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ il.sub(size, dest, il.const_int(size, 0))
+ .with_flag_write(FlagWrite::Nz)
+ .append();
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ il.set_flag(Flag::C, il.const_int(0, 1)).append();
+ }
+ }
+}
+
+fn lift_source_operand<'a>(
+ operand: &Operand,
+ size: usize,
+ il: &'a Lifter<Msp430>,
+) -> binaryninja::llil::Expression<
+ 'a,
+ Msp430,
+ Mutable,
+ NonSSA<LiftedNonSSA>,
+ binaryninja::llil::ValueExpr,
+> {
+ match operand {
+ Operand::RegisterDirect(r) => il.reg(size, Register::try_from(*r as u32).unwrap()),
+ Operand::Indexed((r, offset)) => il
+ .load(
+ size,
+ il.add(
+ 2,
+ il.reg(2, Register::try_from(*r as u32).unwrap()),
+ il.const_int(2, *offset as u64),
+ ),
+ )
+ .build(),
+ // should we add offset to addr here rather than lifting to the register since we know where PC is?
+ Operand::Symbolic(offset) => il
+ .load(
+ size,
+ il.add(2, il.reg(2, Register::Pc), il.const_int(2, *offset as u64)),
+ )
+ .build(),
+ Operand::Absolute(addr) => il.load(size, il.const_ptr(*addr as u64)).build(),
+ // these are the same, we need to autoincrement in a separate il instruction
+ Operand::RegisterIndirect(r) | Operand::RegisterIndirectAutoIncrement(r) => il
+ .load(size, il.reg(2, Register::try_from(*r as u32).unwrap()))
+ .build(),
+ Operand::Immediate(val) => il.const_int(size, *val as u64),
+ Operand::Constant(val) => il.const_int(size, *val as u64),
+ }
+}
+
+fn width_to_size(width: &OperandWidth) -> usize {
+ match width {
+ OperandWidth::Byte => 1,
+ OperandWidth::Word => 2,
+ }
+}
diff --git a/arch/msp430/src/register.rs b/arch/msp430/src/register.rs
new file mode 100644
index 00000000..20a5dff8
--- /dev/null
+++ b/arch/msp430/src/register.rs
@@ -0,0 +1,130 @@
+use binaryninja::architecture;
+use binaryninja::architecture::ImplicitRegisterExtend;
+
+use std::borrow::Cow;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Register {
+ Pc,
+ Sp,
+ Sr,
+ Cg,
+ R4,
+ R5,
+ R6,
+ R7,
+ R8,
+ R9,
+ R10,
+ R11,
+ R12,
+ R13,
+ R14,
+ R15,
+}
+
+impl TryFrom<u32> for Register {
+ type Error = ();
+
+ fn try_from(id: u32) -> Result<Self, Self::Error> {
+ // TODO: we should return separate errors if the id is between 0x7fff_ffff and 0xffff_ffff
+ // vs outside of that range. Temporary registers have have the high bit set which we
+ // shouldn't get, unless there is a bug in core. An id that isn't within that range but we
+ // don't handle is a bug in the architecture.
+ match id {
+ 0 => Ok(Self::Pc),
+ 1 => Ok(Self::Sp),
+ 2 => Ok(Self::Sr),
+ 3 => Ok(Self::Cg),
+ 4 => Ok(Self::R4),
+ 5 => Ok(Self::R5),
+ 6 => Ok(Self::R6),
+ 7 => Ok(Self::R7),
+ 8 => Ok(Self::R8),
+ 9 => Ok(Self::R9),
+ 10 => Ok(Self::R10),
+ 11 => Ok(Self::R11),
+ 12 => Ok(Self::R12),
+ 13 => Ok(Self::R13),
+ 14 => Ok(Self::R14),
+ 15 => Ok(Self::R15),
+ _ => Err(()),
+ }
+ }
+}
+
+impl architecture::Register for Register {
+ type InfoType = Self;
+
+ fn name(&self) -> Cow<'_, str> {
+ match self {
+ Self::Pc => "pc".into(),
+ Self::Sp => "sp".into(),
+ Self::Sr => "sr".into(),
+ Self::Cg => "cg".into(),
+ Self::R4
+ | Self::R5
+ | Self::R6
+ | Self::R7
+ | Self::R8
+ | Self::R9
+ | Self::R10
+ | Self::R11
+ | Self::R12
+ | Self::R13
+ | Self::R14
+ | Self::R15 => format!("r{}", self.id()).into(),
+ }
+ }
+
+ fn info(&self) -> Self::InfoType {
+ *self
+ }
+
+ fn id(&self) -> u32 {
+ match self {
+ Self::Pc => 0,
+ Self::Sp => 1,
+ Self::Sr => 2,
+ Self::Cg => 3,
+ Self::R4 => 4,
+ Self::R5 => 5,
+ Self::R6 => 6,
+ Self::R7 => 7,
+ Self::R8 => 8,
+ Self::R9 => 9,
+ Self::R10 => 10,
+ Self::R11 => 11,
+ Self::R12 => 12,
+ Self::R13 => 13,
+ Self::R14 => 14,
+ Self::R15 => 15,
+ }
+ }
+}
+
+impl architecture::RegisterInfo for Register {
+ type RegType = Self;
+
+ fn parent(&self) -> Option<Self::RegType> {
+ None
+ }
+
+ fn size(&self) -> usize {
+ 2
+ }
+
+ fn offset(&self) -> usize {
+ 0
+ }
+
+ fn implicit_extend(&self) -> ImplicitRegisterExtend {
+ ImplicitRegisterExtend::NoExtend
+ }
+}
+
+impl From<Register> for binaryninja::llil::Register<Register> {
+ fn from(register: Register) -> Self {
+ binaryninja::llil::Register::ArchReg(register)
+ }
+}