diff --git a/arch/msp430/CMakeLists.txt b/arch/msp430/CMakeLists.txt new file mode 100644 index 0000000000..034ed1e214 --- /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 0000000000..6f1b4c377a --- /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 0000000000..ce849cb03a --- /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 0000000000..ab392a3865 --- /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, +} + +impl Msp430 { + pub fn new(handle: CoreArchitecture, custom_handle: CustomArchitectureHandle) -> Self { + Msp430 { + handle, + custom_handle, + } + } +} + +impl Architecture for Msp430 { + type Handle = CustomArchitectureHandle; + type RegisterStackInfo = UnusedRegisterStackInfo; + type RegisterStack = UnusedRegisterStack; + 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 { + 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)> { + 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, + ) -> 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, + ) -> Vec { + 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, + ) -> Option> { + None + } + + fn registers_all(&self) -> Vec { + 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 { + 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 { + Vec::new() + } + + fn registers_system(&self) -> Vec { + Vec::new() + } + + fn flags(&self) -> Vec { + vec![Flag::C, Flag::Z, Flag::N, Flag::V] + } + + fn flag_write_types(&self) -> Vec { + vec![ + FlagWrite::All, + FlagWrite::Nz, + FlagWrite::Nvz, + FlagWrite::Cnz, + ] + } + + fn flag_classes(&self) -> Vec { + Vec::new() + } + + fn flag_groups(&self) -> Vec { + Vec::new() + } + + fn stack_pointer_reg(&self) -> Option { + Some(Register::Sp) + } + + fn link_reg(&self) -> Option { + None + } + + fn register_from_id(&self, id: u32) -> Option { + match id.try_into() { + Ok(register) => Some(register), + Err(_) => None, + } + } + + fn flag_from_id(&self, id: u32) -> Option { + match id.try_into() { + Ok(flag) => Some(flag), + Err(_) => { + error!("invalid flag id {}", id); + None + } + } + } + + fn flag_write_from_id(&self, id: u32) -> Option { + 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 { + None + } + + fn flag_group_from_id(&self, _: u32) -> Option { + None + } + + fn handle(&self) -> Self::Handle { + self.custom_handle + } +} + +impl AsRef for Msp430 { + fn as_ref(&self) -> &CoreArchitecture { + &self.handle + } +} + +fn generate_tokens(inst: &Instruction, addr: u64) -> Vec { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 0000000000..115fe866ff --- /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 { + match self { + Self::C => "c".into(), + Self::Z => "z".into(), + Self::N => "n".into(), + Self::V => "v".into(), + } + } + + fn role(&self, _class: Option) -> 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 for Flag { + type Error = (); + fn try_from(flag: u32) -> Result { + 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 { + 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 { + unimplemented!() + } + + fn id(&self) -> u32 { + unimplemented!() + } + + fn flags_required(&self) -> Vec { + unimplemented!() + } + + fn flag_conditions(&self) -> HashMap { + 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 { + match self { + Self::All => "*".into(), + Self::Nz => "nz".into(), + Self::Nvz => "nvz".into(), + Self::Cnz => "cnz".into(), + } + } + + fn class(&self) -> Option { + None + } + + fn id(&self) -> u32 { + match self { + Self::All => 1, + Self::Nz => 2, + Self::Nvz => 3, + Self::Cnz => 4, + } + } + + fn flags_written(&self) -> Vec { + 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 for FlagWrite { + type Error = (); + + fn try_from(value: u32) -> Result { + 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 0000000000..ffa89badff --- /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 0000000000..85d204e1a8 --- /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) { + 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, +) -> binaryninja::llil::Expression< + 'a, + Msp430, + Mutable, + NonSSA, + 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 0000000000..20a5dff867 --- /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 for Register { + type Error = (); + + fn try_from(id: u32) -> Result { + // 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 { + None + } + + fn size(&self) -> usize { + 2 + } + + fn offset(&self) -> usize { + 0 + } + + fn implicit_extend(&self) -> ImplicitRegisterExtend { + ImplicitRegisterExtend::NoExtend + } +} + +impl From for binaryninja::llil::Register { + fn from(register: Register) -> Self { + binaryninja::llil::Register::ArchReg(register) + } +}