From e624026143bc5c360d83d6d6d346919ec780adfd Mon Sep 17 00:00:00 2001 From: stuxnot <41650734+stuxnot@users.noreply.github.com> Date: Mon, 6 Nov 2023 23:51:39 +0100 Subject: [PATCH] Improve README (#26) * Fix cli tool help message * Use automatic integer base detection in cli tool * Improves README * Fix ObjectWriterWrapper documentation * Adds LICENSE file * clang-format * Rename nstone to nyxstone --------- Co-authored-by: Philipp Koppe --- .github/workflows/cpp.yml | 2 +- .github/workflows/python.yml | 2 +- .github/workflows/rust.yml | 3 - CMakeLists.txt | 12 +- LICENSE | 21 +++ README.md | 227 ++++++++++++++++++++++++++++----- bindings/python/README.md | 9 ++ bindings/python/pynyxstone.cpp | 4 +- bindings/rust/README.md | 4 - examples/nyxstone-cli.cpp | 19 ++- images/demo.svg | 1 + include/nyxstone.h | 2 + src/ObjectWriterWrapper.h | 11 +- src/nyxstone.cpp | 8 +- 14 files changed, 266 insertions(+), 59 deletions(-) create mode 100644 LICENSE create mode 100644 images/demo.svg diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml index 4970e19..25ac3a8 100644 --- a/.github/workflows/cpp.yml +++ b/.github/workflows/cpp.yml @@ -37,4 +37,4 @@ jobs: - name: Sample run: ./build/sample - name: cli - run: ./build/nstone -A "mov rax, rbx" + run: ./build/nyxstone -A "mov rax, rbx" diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 82855e3..3621c36 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -32,7 +32,7 @@ jobs: - name: Packages run: sudo apt-get install llvm-15 llvm-15-dev zlib1g-dev libzstd-dev - name: Build - run: make nyxstone && pip install . + run: pip install . working-directory: ${{ env.working-dir }} - name: Test run: python tests/test.py diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ab8c508..44fcb13 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,9 +30,6 @@ jobs: - uses: actions/checkout@v3 - name: Packages run: sudo apt-get install llvm-15 llvm-15-dev zlib1g-dev libzstd-dev - - name: Prepare - run: make nyxstone - working-directory: ${{ env.working-dir }} - name: Code quality run: cargo fmt --all -- --check && cargo clippy -- -D warnings working-directory: ${{ env.working-dir }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 80071d6..8d7d62c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,14 +4,14 @@ project(nyxstone VERSION 0.1.0) find_package(LLVM 15 CONFIG PATHS $ENV{NYXSTONE_LLVM_PREFIX} NO_DEFAULT_PATH) find_package(LLVM 15 CONFIG) -find_package(Boost 1.40 COMPONENTS program_options) -include_directories(${Boost_INCLUDE_DIR}) - include_directories(${LLVM_INCLUDE_DIRS} include/ src/) add_definitions(${LLVM_DEFINITIONS}) llvm_map_components_to_libnames(llvm_libs core mc AllTargetsCodeGens AllTargetsAsmParsers AllTargetsDescs AllTargetsDisassemblers AllTargetsInfos AllTargetsMCAs) +find_package(Boost 1.40 COMPONENTS program_options) +include_directories(${Boost_INCLUDE_DIR}) + file(GLOB srcs "src/*.hpp" "src/*.cpp" @@ -22,8 +22,10 @@ file(GLOB srcs add_library(nyxstone ${srcs}) -add_executable(nstone examples/nyxstone-cli.cpp) +add_executable(nyxstone-bin examples/nyxstone-cli.cpp) +set_target_properties(nyxstone-bin PROPERTIES OUTPUT_NAME nyxstone) + add_executable(sample examples/sample.cpp) -target_link_libraries(nstone nyxstone ${llvm_libs} ${Boost_LIBRARIES}) +target_link_libraries(nyxstone-bin nyxstone ${llvm_libs} ${Boost_LIBRARIES}) target_link_libraries(sample nyxstone ${llvm_libs}) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..40f3546 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 emproof GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index b4b7211..e5364fd 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,228 @@ # Nyxstone -The simple assemble/disassemble framework. Nyxstone was built as an internal software project for our powerful binary rewriter _NYX_. +[![Github Cpp CI Badge](https://github.com/emproof-com/nyxstone/actions/workflows/cpp.yml/badge.svg)](https://github.com/emproof-com/nyxstone/actions/workflows/cpp.yml) -With Nyxstone we do not ship our own LLVM patches, but wrap LLVM `MC` objects in custom types to assemble and disassemble for -different architectures. +Nyxstone is an assembly and disassembly library based on LLVM. It doesn’t require patches to the LLVM source tree and links against standard LLVM libraries available in most Linux distributions. Implemented in C++, Nyxstone also offers Rust and Python bindings. It supports all official LLVM architectures and allows configuration of architecture-specific target settings. -## Building +![Nyxstone Python binding demo](/images/demo.svg) -Building Nyxstone requires a modern compiler, we currently use `clang`. +## Index -Nyxstone uses the LLVM backend to assemble & disassemble instructions. Since the LLVM backend changes frequently with major releases, we currently only support LLVM major version 15. +1. [Core Features](#core-features) +2. [Using Nyxstone](#using-nyxstone) + 1. [Prerequisites](#prerequisites) + 2. [As a Rust library](#as-a-rust-library) + 3. [As a Python library](#as-a-python-library) + 4. [As a C++ library](#as-a-c-library) + 5. [C++ CLI Tool](#c-cli-tool) +3. [How it works](#how-it-works) +4. [License](#license) +5. [Contributing](#contributing) +6. [Contributers](#contributers) -When Nyxstone is being built, it first searches the directory supplied by the environment variable `$NYXSTONE_LLVM_PREFIX/bin` and afterwards the `$PATH` for `llvm-config`. When `llvm-config` is found, Nyxstone checks for both the major version and if LLVM can be statically linked. +## Core Features -There are multiple ways to install LLVM 15 if your package manager does not supply it: - - Use an external package manager, like `brew` - - Build llvm from source - - Ubuntu/Debian only: Use the install script from https://apt.llvm.org +- Assembles and Disassembles code for all architectures supported by LLVM 15, including x86 & x86_64, ARM & AArch64, MIPS, PowerPC, AVR, AMDGPU, NVPTX, RISC-V, and more. For a comprehensive list, refer to `clang -print-targets`. +- C++ library based on LLVM with Rust and Python bindings. +- Native platform support for Linux and macOS. +- Facilitates setting a custom start address and defining labels within assembly inputs, along with the ability to specify a label-to-address mapping through an additional argument. +- Assembles and disassembles to raw bytes or text respectively, or to detailed instruction objects that include additional information such as the instruction's address, raw bytes and its assembly representation. +- Provides an option to limit the number of instructions being disassembled from a given byte array. +- Supports the configuration of architecture-specific target features such as various Instruction Set Architecture (ISA) extensions or hardware features. For a comprehensive list of features for each architecture, refer to `llc -march=ARCH -mattr=help`. -Also make sure to install any libraries needed by your LLVM version for static linking. +> [!NOTE] +> Disclaimer: Nyxstone has been primarily developed and tested on x86_64, AArch64 and ARM architectures. We have a high degree of confidence in its ability to accurately generate assembly and identify errors for these platforms. For other architectures, Nyxstone's effectiveness is dependent on the reliability and performance of their respective LLVM backends. -You can build the nyxstone cli (nstone) example program and the small sample program in the `examples` folder using cmake. Use the following commands: +## Using Nyxstone + +This section provides instructions on how to get started with Nyxstone, covering the necessary prerequisites and step-by-step guidelines for using the library with C++, Rust, and Python, as well as how to utilize the C++ CLI tool. + +### Prerequisites + +Nyxstone requires clang and LLVM 15 static link libraries for building. It searches `llvm-config` in `$PATH` and `$NYXSTONE_LLVM_PREFIX/bin`. There are multiple ways to install LLVM 15. + +- On Debian and Ubuntu install the packages `llvm-15` and `llvm-15-dev`. ``` -$ mkdir build -$ cd build -$ cmake .. -$ make +$ apt install llvm-15 llvm-15-dev +$ export NYXSTONE_LLVM_PREFIX=/usr/lib/llvm-15/ ``` -## Using Nyxstone +- Using an external package manager like `brew`. +``` +$ brew install llvm@15 +$ export NYXSTONE_LLVM_PREFIX=/opt/brew/opt/llvm@15 +``` + +- Building LLVM from source. Consider specifying a custom installation directory with the cmake option `DCMAKE_INSTALL_PREFIX`. +``` +$ git clone https://github.com/llvm/llvm-project.git && cd llvm-project +$ git checkout origin/release/15.x +$ cmake -S llvm -B build -G Ninja -DCMAKE_INSTALL_PREFIX=~/lib/my-llvm-15 -DCMAKE_BUILD_TYPE=Debug +$ ninja -C build install +$ export NYXSTONE_LLVM_PREFIX=~/lib/my-llvm-15 +``` + +Also make sure to install any system dependent libraries needed by your LLVM version for static linking. They can be viewed with the command `$ llvm-config --system-libs`. The list might be empty. On Ubuntu/Debian you will need the packages `zlib1g-dev` and `zlibstd-dev`. + +### As a Rust library + +Add Nyxstone as a dependency in your `Cargo.toml`. + +```rust +use anyhow::Result; +use nyxstone::{LabelDefinition, NyxstoneBuilder}; + +fn main() -> Result<()> { + let nyxstone = NyxstoneBuilder::default().with_triple("x86_64").build()?; + + let bytes = nyxstone.assemble_to_bytes( + "mov rax, rbx; cmp rax, rdx; jne .label", + 0x1000, + &[LabelDefinition { name: ".label", address: 0x1200 }], + )?; + + println!("Bytes: {:x?}", bytes); + + Ok(()) +} +``` + +For more instructions regarding the Rust binding, refer to its [README](bindings/rust/README.md). + +### As a Python library + +Install Nyxstone with pip. On some distributions you may have to create a virtual environment. + +``` +$ pip install nyxstone +$ python -q +>>> from nyxstone import NyxstoneBuilder +>>> nyxstone = NyxstoneBuilder().with_triple("x86_64").build() +>>> nyxstone.assemble_to_bytes("jne .loop", 0x1100, {".loop": 0x1000}) +``` + +For more instructions regarding the Python binding, refer to its [README](bindings/python/README.md). + +### As a C++ library + +Link against Nyxstone and LLVM 15. This cmake example assumes Nyxstone in a subdirectory `nyxstone` in your project. + +```cmake +find_package(LLVM 15 CONFIG PATHS $ENV{NYXSTONE_LLVM_PREFIX} NO_DEFAULT_PATH) +find_package(LLVM 15 CONFIG) + +include_directories(${LLVM_INCLUDE_DIRS}) +add_definitions(${LLVM_DEFINITIONS}) +llvm_map_components_to_libnames(llvm_libs core mc AllTargetsCodeGens AllTargetsAsmParsers AllTargetsDescs AllTargetsDisassemblers AllTargetsInfos AllTargetsMCAs) -### CLI Tool +add_subdirectory(nyxstone EXCLUDE_FROM_ALL) # Add nyxstone cmake without executables +include_directories(nyxstone/include) # Nyxstone include directory -The Nyxstone cli tool allows for on-the-fly assembling and disassembling from your command line. +add_executable(my_executable main.cpp) + +target_link_libraries(my_executable nyxstone ${llvm_libs}) +``` + +C++ usage example. + +```c++ +#include +#include + +#include "nyxstone.h" + +int main(int, char**) { + // Create the nyxstone instance: + auto nyxstone {NyxstoneBuilder().with_triple("x86_64").build()}; + + // Assemble to bytes + std::vector bytes {}; + nyxstone->assemble_to_bytes(/*assembly=*/"mov rax, rbx", /*address=*/0x1000, /* labels= */ {}, bytes); + { + std::vector expected {0x48, 0x89, 0xd8}; + assert(bytes == expected); + } + + return 0; +} ``` -$ cd build && cmake .. && make -$ ./nstone --help + +For a comprehensive C++ example, refer to [example.cpp](example/sample.cpp). + +### C++ CLI Tool + +Nyxstone also comes with a handy CLI tool for quick assembly and disassembly tasks. Install boost with your distribution's package manager and build the tool with cmake. + +``` +$ apt install boost +$ mkdir build && cd build && cmake .. && make # run in nyxstone folder +``` + +Help message output. + +``` +$ ./nyxstone --help Allowed options: --help Show this message - --arch arg (=x86_64) LLVM architecture (triple), for example "x86_64", - "armv8m", "armv8meb", "thumbv8", "aarch64" + --arch arg (=x86_64) LLVM triple or architecture identifier of triple, + for example "x86_64", "x86_64-linux-gnu", "armv8", + "armv8eb", "thumbv8", "aarch64" --address arg (=0) Address Assembling: --labels arg Labels, for example "label0=0x10,label1=0x20" -A [ --assemble ] arg Assembly -Assembling: +Disassembling: -D [ --disassemble ] arg Byte code in hex, for example: "0203" + +``` + +Example usage. + +``` +$ ./nyxstone --arch "x86_64 " -A "mov rax, rbx" +Assembled: + 0x00000000: mov rax, rbx - [ 48 89 d8 ] + +$ ./nyxstone --arch "thumbv8" -D "13 37" +Disassembled: + 0x00000000: adds r7, #19 - [ 13 37 ] ``` -### In your project +## How it works + +Nyxstone leverages public C++ API functions from LLVM such as `Target::createMCAsmParser` and `Target::createMCDisassembler` to perform assembly and disassembly tasks. Nyxstone also extends two LLVM classes - `MCELFStreamer` and `MCObjectWriter` - to inject custom logic and extract additional information. Specifically, Nyxstone augments the assembly process with the following steps: +- `ELFStreamerWrapper::emitInstruction` + - Capture assembly representation and initial raw bytes of instructions if detailed instruction objects are requested by the user. +- `ObjectWriterWrapper::writeObject` + - Writes the final raw bytes of instructions, with relocation adjustments, to detailed instruction objects. + - Switches raw bytes output from complete ELF file to just the .text section. +- `ObjectWriterWrapper::validate_fixups` + - Conducts extra checks, such as verifying the range and alignment of relocations. +- `ObjectWriterWrapper::recordRelocation` + - Applies additional relocations. `MCObjectWriter` skips some relocations that are only applied during linking. Right now this is only relevant for the `fixup_aarch64_pcrel_adrp_imm21` in the Aarch64 `adrp` instruction. + +While extending LLVM classes introduces some drawbacks, like a strong dependency on a specific LLVM version, we believe this approach is still an improvement over alternatives that require hard to maintain patches in the LLVM source tree. We are committed to further remove complexity from the project and welcome suggestions for improvement. Looking ahead, we may eliminate the need to extend LLVM classes by leveraging the existing LLVM infrastructure in a smarter way or incorporating additional logic in a post-processing step. + +## Roadmap + +- [ ] Native Windows platform support +- [ ] Check thread safety +- [ ] Add support for more LLVM versions (auto select depending on found LLVM library version) +- [ ] Add dynamic linking support, e. g., Arch Linux has LLVM libraries without static linking support +- [ ] Explore option to make LLVM apply all relocations (including adrp) by configuring `MCObjectWriter` differently or using a different writer +- [ ] Explore option to generate detailed instructions objects by disassembling the raw bytes output of the assembly process instead of relying on the extension of LLVM classes +- [ ] Explore option to implement extra range and alignment of relocations in a post-processing step instead of relying on the extension of LLVM classes + +## License + +Nyxstone is available under the [MIT license](LICENSE). + +## Contributing -Currently, Nyxstone is only in use as a rust library via the rust bindings. If you want to use Nyxstone in your project, see the Makefile for how Nyxstone is build and how to link it into your projects. +We welcome contributions from the community! If you encounter any issues with Nyxstone, please feel free to open a GitHub issue. If you're interested in contributing directly to the project, you can start by addressing an existing issue and submitting a pull request. -## Documentation +## Contributers -The C++ API documentation can be generated by running `doxygen` in the top level directory. +Philipp Koppe, Rachid Mzannar, Darius Hartlief @ [emproof.com](https://www.emproof.com) diff --git a/bindings/python/README.md b/bindings/python/README.md index 3faf00a..8d867d3 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -10,6 +10,15 @@ You need to have LLVM 15 (with static library support) installed to build the ny pip install . ``` +If you are using Arch Linux, you will need to use a virtual environment to install nyxstone: + +``` +mkdir env +python -mvenv env/ +source env/bin/activate[.fish|.zsh] +pip install . +``` + ## Example After you have installed nyxstone, import the `Nyxstone`, `NyxstoneBuilder`, and `Instruction` classes from nyxstone. diff --git a/bindings/python/pynyxstone.cpp b/bindings/python/pynyxstone.cpp index 7dce1c3..04a9cf7 100644 --- a/bindings/python/pynyxstone.cpp +++ b/bindings/python/pynyxstone.cpp @@ -69,9 +69,7 @@ PYBIND11_MODULE(nyxstone, m) .def_readwrite("bytes", &Nyxstone::Instruction::bytes) .def_readwrite("assembly", &Nyxstone::Instruction::assembly) .def("__eq__", - [](const Nyxstone::Instruction& self, const Nyxstone::Instruction& other) { - return self.address == other.address && self.assembly == other.assembly && self.bytes == other.bytes; - }) + [](const Nyxstone::Instruction& self, const Nyxstone::Instruction& other) { return self == other; }) .def("__repr__", [](const Nyxstone::Instruction& i) { std::stringstream out; out << " Result<()> { The nyxstone-rs bindings are generated via the `cxx` crate. Since nyxstone is specifically a c++ library, we currently do not plan to support C bindings via bindgen. -## Future improvements - -The binding class `NyxstoneFFI` currently copies a lot of data between the rust and c++ code. It would be more efficient to not copy data which is only read inside the c++ code. - ## Acknowledgements The build script of the rust bindings borrow heavily from the [llvm-sys](https://gitlab.com/taricorp/llvm-sys.rs) build script. diff --git a/examples/nyxstone-cli.cpp b/examples/nyxstone-cli.cpp index 301b54f..2a2226d 100644 --- a/examples/nyxstone-cli.cpp +++ b/examples/nyxstone-cli.cpp @@ -28,8 +28,8 @@ int main(int argc, char** argv) desc.add_options() ("help", "Show this message") ("arch", po::value()->default_value("x86_64"), - R"(LLVM architecture (triple), for example "x86_64", "armv8m", "armv8meb", "thumbv8", "aarch64")") - ("address", po::value()->default_value(0u), "Address") + R"(LLVM triple or architecture identifier of triple, for example "x86_64", "x86_64-linux-gnu", "armv8", "armv8eb", "thumbv8", "aarch64")") + ("address", po::value()->default_value("0"), "Address") ; po::options_description desc_asm("Assembling"); @@ -38,7 +38,7 @@ int main(int argc, char** argv) ("assemble,A", po::value(), "Assembly") ; - po::options_description desc_disasm("Assembling"); + po::options_description desc_disasm("Disassembling"); desc_disasm.add_options() ("disassemble,D", po::value(), "Byte code in hex, for example: \"0203\"") ; @@ -94,6 +94,15 @@ int main(int argc, char** argv) labels = maybe_labels.value(); } + uint64_t address = 0; + auto addr = varmap["address"].as(); + try { + address = std::stoul(addr, nullptr, 0); + } catch (const std::exception&) { + std::cerr << "Could not parse address\n"; + return 1; + } + std::unique_ptr nyxstone { nullptr }; try { nyxstone = std::move(NyxstoneBuilder().with_triple(std::move(arch)).build()); @@ -102,8 +111,6 @@ int main(int argc, char** argv) return 1; } - auto address = varmap["address"].as(); - if (has_assemble) { std::vector instrs; const auto& assembly = varmap["assemble"].as(); @@ -187,7 +194,7 @@ std::optional> parse_labels(std::string l uint64_t value = 0; try { - value = std::stoul(val, nullptr, 16); + value = std::stoul(val, nullptr, 0); } catch (const std::exception& e) { std::cerr << "Could not parse label value: `" << val << "`\n"; return {}; diff --git a/images/demo.svg b/images/demo.svg new file mode 100644 index 0000000..13ad1bc --- /dev/null +++ b/images/demo.svg @@ -0,0 +1 @@ +nyxstone-demo:~$nyxstone-demo:~$pnyxstone-demo:~$pipnyxstone-demo:~$pipinstallnyxstone-demo:~$pipinstallnyxstonenyxstone-demo:~$pipinstallnyxstone-qnyxstone-demo:~$pythonnyxstone-demo:~$python-q>>>>>>from>>>fromnyxstone>>>fromnyxstoneimport>>>fromnyxstoneimportNyxstoneBuilder>>>n>>>ny>>>nyx>>>nyxs>>>nyxst>>>nyxsto>>>nyxston>>>nyxstone>>>nyxstone=>>>nyxstone=NyxstoneBuilder().with_triple("x86_64").build()>>>nyxstone.assemble_to_bytes("jne>>>nyxstone.assemble_to_bytes("jne.loop",>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":0x1000})[15,133,250,254,255,255]nyxstone-demo:~$pinyxstone-demo:~$pipinyxstone-demo:~$pipinnyxstone-demo:~$pipinsnyxstone-demo:~$pipinstnyxstone-demo:~$pipinstanyxstone-demo:~$pipinstalnyxstone-demo:~$pipinstallnnyxstone-demo:~$pipinstallnynyxstone-demo:~$pipinstallnyxnyxstone-demo:~$pipinstallnyxsnyxstone-demo:~$pipinstallnyxstnyxstone-demo:~$pipinstallnyxstonyxstone-demo:~$pipinstallnyxstonnyxstone-demo:~$pipinstallnyxstone-nyxstone-demo:~$pynyxstone-demo:~$pytnyxstone-demo:~$pythnyxstone-demo:~$pythonyxstone-demo:~$python->>>f>>>fr>>>fro>>>fromn>>>fromny>>>fromnyx>>>fromnyxs>>>fromnyxst>>>fromnyxsto>>>fromnyxston>>>fromnyxstonei>>>fromnyxstoneim>>>fromnyxstoneimp>>>fromnyxstoneimpo>>>fromnyxstoneimpor>>>fromnyxstoneimportN>>>fromnyxstoneimportNy>>>fromnyxstoneimportNyx>>>fromnyxstoneimportNyxs>>>fromnyxstoneimportNyxst>>>fromnyxstoneimportNyxsto>>>fromnyxstoneimportNyxston>>>fromnyxstoneimportNyxstone>>>fromnyxstoneimportNyxstoneB>>>fromnyxstoneimportNyxstoneBu>>>fromnyxstoneimportNyxstoneBui>>>fromnyxstoneimportNyxstoneBuil>>>fromnyxstoneimportNyxstoneBuild>>>fromnyxstoneimportNyxstoneBuilde>>>nyxstone=N>>>nyxstone=Ny>>>nyxstone=Nyx>>>nyxstone=Nyxs>>>nyxstone=Nyxst>>>nyxstone=Nyxsto>>>nyxstone=Nyxston>>>nyxstone=Nyxstone>>>nyxstone=NyxstoneB>>>nyxstone=NyxstoneBu>>>nyxstone=NyxstoneBui>>>nyxstone=NyxstoneBuil>>>nyxstone=NyxstoneBuild>>>nyxstone=NyxstoneBuilde>>>nyxstone=NyxstoneBuilder>>>nyxstone=NyxstoneBuilder(>>>nyxstone=NyxstoneBuilder()>>>nyxstone=NyxstoneBuilder().>>>nyxstone=NyxstoneBuilder().w>>>nyxstone=NyxstoneBuilder().wi>>>nyxstone=NyxstoneBuilder().wit>>>nyxstone=NyxstoneBuilder().with>>>nyxstone=NyxstoneBuilder().with_>>>nyxstone=NyxstoneBuilder().with_t>>>nyxstone=NyxstoneBuilder().with_tr>>>nyxstone=NyxstoneBuilder().with_tri>>>nyxstone=NyxstoneBuilder().with_trip>>>nyxstone=NyxstoneBuilder().with_tripl>>>nyxstone=NyxstoneBuilder().with_triple>>>nyxstone=NyxstoneBuilder().with_triple(>>>nyxstone=NyxstoneBuilder().with_triple(">>>nyxstone=NyxstoneBuilder().with_triple("x>>>nyxstone=NyxstoneBuilder().with_triple("x8>>>nyxstone=NyxstoneBuilder().with_triple("x86>>>nyxstone=NyxstoneBuilder().with_triple("x86_>>>nyxstone=NyxstoneBuilder().with_triple("x86_6>>>nyxstone=NyxstoneBuilder().with_triple("x86_64>>>nyxstone=NyxstoneBuilder().with_triple("x86_64">>>nyxstone=NyxstoneBuilder().with_triple("x86_64")>>>nyxstone=NyxstoneBuilder().with_triple("x86_64").>>>nyxstone=NyxstoneBuilder().with_triple("x86_64").b>>>nyxstone=NyxstoneBuilder().with_triple("x86_64").bu>>>nyxstone=NyxstoneBuilder().with_triple("x86_64").bui>>>nyxstone=NyxstoneBuilder().with_triple("x86_64").buil>>>nyxstone=NyxstoneBuilder().with_triple("x86_64").build>>>nyxstone=NyxstoneBuilder().with_triple("x86_64").build(>>>nyxstone.>>>nyxstone.a>>>nyxstone.as>>>nyxstone.ass>>>nyxstone.asse>>>nyxstone.assem>>>nyxstone.assemb>>>nyxstone.assembl>>>nyxstone.assemble>>>nyxstone.assemble_>>>nyxstone.assemble_t>>>nyxstone.assemble_to>>>nyxstone.assemble_to_>>>nyxstone.assemble_to_b>>>nyxstone.assemble_to_by>>>nyxstone.assemble_to_byt>>>nyxstone.assemble_to_byte>>>nyxstone.assemble_to_bytes>>>nyxstone.assemble_to_bytes(>>>nyxstone.assemble_to_bytes(">>>nyxstone.assemble_to_bytes("j>>>nyxstone.assemble_to_bytes("jn>>>nyxstone.assemble_to_bytes("jne.>>>nyxstone.assemble_to_bytes("jne.l>>>nyxstone.assemble_to_bytes("jne.lo>>>nyxstone.assemble_to_bytes("jne.loo>>>nyxstone.assemble_to_bytes("jne.loop>>>nyxstone.assemble_to_bytes("jne.loop">>>nyxstone.assemble_to_bytes("jne.loop",0>>>nyxstone.assemble_to_bytes("jne.loop",0x>>>nyxstone.assemble_to_bytes("jne.loop",0x1>>>nyxstone.assemble_to_bytes("jne.loop",0x11>>>nyxstone.assemble_to_bytes("jne.loop",0x110>>>nyxstone.assemble_to_bytes("jne.loop",0x1100>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{">>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".l>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".lo>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loo>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop">>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":0>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":0x>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":0x1>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":0x10>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":0x100>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":0x1000>>>nyxstone.assemble_to_bytes("jne.loop",0x1100,{".loop":0x1000} \ No newline at end of file diff --git a/include/nyxstone.h b/include/nyxstone.h index baeb774..1d409dd 100644 --- a/include/nyxstone.h +++ b/include/nyxstone.h @@ -75,6 +75,8 @@ class Nyxstone { std::string assembly; /// The byte code of the instruction std::vector bytes {}; + + bool operator==(const Instruction& other) const; }; /// @brief Nyxstone constructor called by NyxstoneBuilder::build. diff --git a/src/ObjectWriterWrapper.h b/src/ObjectWriterWrapper.h index 104eade..aafd59a 100644 --- a/src/ObjectWriterWrapper.h +++ b/src/ObjectWriterWrapper.h @@ -21,13 +21,14 @@ #pragma GCC diagnostic pop namespace nyxstone { -/// This class derives from LLVM's MCObjectWriter, which enables us to include -/// user-define symbols/labels into the internal address resolution process -/// (via 'executePostLayoutBinding()'). -/// -/// This class also enables us to limit the final output byte stream to the +/// This class enables us to limit the final output byte stream to the /// relevant bytes (as opposed to the whole ELF object file) and grab final /// instruction bytes (after relaxation and fixups) (via 'writeObject()'). +/// +/// This class is also used to insert custom relocations and fixup validations. +/// These are necessary when a relocation is normally performed at link time or when +/// LLVM does not verify a fixup according to the specification, leading to wrong +/// output for specific instruction/label combinations. class ObjectWriterWrapper : public llvm::MCObjectWriter { // Wrapped MCObjectWriter, f. i., ELFSingleObjectWriter std::unique_ptr inner_object_writer; diff --git a/src/nyxstone.cpp b/src/nyxstone.cpp index e18ecb6..ce84d7c 100644 --- a/src/nyxstone.cpp +++ b/src/nyxstone.cpp @@ -461,8 +461,12 @@ void Nyxstone::disassemble_impl(const std::vector& bytes, uint64_t addr } } -/// Detects all ARM Thumb architectures. LLVM doesn't seem to have a short way -/// to check this. +bool Nyxstone::Instruction::operator==(const Instruction& other) const +{ + return address == other.address && assembly == other.assembly && bytes == other.bytes; +} + +/// Detects all ARM Thumb architectures. LLVM doesn't seem to have a short way to check this. bool is_ArmT16_or_ArmT32(const llvm::Triple& triple) { return (triple.getSubArch() == Triple::SubArchType::ARMSubArch_v6m