From c1a7332233bc59b3a585e8958c768745830738fd Mon Sep 17 00:00:00 2001 From: Romain Thomas Date: Sat, 7 Dec 2024 18:27:27 +0100 Subject: [PATCH] Add support for AArch64 operands --- api/python/lief/assembly/aarch64/__init__.pyi | 10 ++ .../assembly/aarch64/operands/__init__.pyi | 48 ++++++ .../src/DyldSharedCache/pyDyldSharedCache.cpp | 2 +- api/python/src/asm/aarch64/CMakeLists.txt | 2 + api/python/src/asm/aarch64/init.cpp | 2 + .../src/asm/aarch64/operands/CMakeLists.txt | 6 + .../src/asm/aarch64/operands/pyImmediate.cpp | 26 ++++ .../src/asm/aarch64/operands/pyMemory.cpp | 101 +++++++++++++ .../src/asm/aarch64/operands/pyPCRelative.cpp | 28 ++++ .../src/asm/aarch64/operands/pyRegister.cpp | 66 +++++++++ api/python/src/asm/aarch64/pyInstruction.cpp | 13 ++ api/python/src/asm/aarch64/pyOperand.cpp | 39 +++++ api/rust/autocxx_ffi.rs | 30 ++++ api/rust/cargo/lief/src/assembly/aarch64.rs | 7 + .../lief/src/assembly/aarch64/instruction.rs | 17 +++ .../lief/src/assembly/aarch64/operands.rs | 139 ++++++++++++++++++ .../assembly/aarch64/operands/immediate.rs | 40 +++++ .../src/assembly/aarch64/operands/memory.rs | 124 ++++++++++++++++ .../assembly/aarch64/operands/pc_relative.rs | 33 +++++ .../src/assembly/aarch64/operands/register.rs | 58 ++++++++ api/rust/include/LIEF/rust/ASM.hpp | 3 + .../LIEF/rust/asm/aarch64/Instruction.hpp | 16 ++ .../include/LIEF/rust/asm/aarch64/Operand.hpp | 28 ++++ .../LIEF/rust/asm/aarch64/operands.hpp | 6 + .../rust/asm/aarch64/operands/Immediate.hpp | 34 +++++ .../LIEF/rust/asm/aarch64/operands/Memory.hpp | 61 ++++++++ .../rust/asm/aarch64/operands/PCRelative.hpp | 32 ++++ .../rust/asm/aarch64/operands/Register.hpp | 45 ++++++ doc/sphinx/conf.py | 5 + .../disassembler/cpp/arch/aarch64.rst | 26 ++++ .../disassembler/python/arch/aarch64.rst | 46 ++++++ .../extended/disassembler/python/arch/x86.rst | 1 - include/LIEF/asm/aarch64.hpp | 1 + include/LIEF/asm/aarch64/Instruction.hpp | 6 + include/LIEF/asm/aarch64/Operand.hpp | 125 ++++++++++++++++ include/LIEF/asm/aarch64/operands.hpp | 22 +++ .../LIEF/asm/aarch64/operands/Immediate.hpp | 49 ++++++ include/LIEF/asm/aarch64/operands/Memory.hpp | 103 +++++++++++++ .../LIEF/asm/aarch64/operands/PCRelative.hpp | 47 ++++++ .../LIEF/asm/aarch64/operands/Register.hpp | 67 +++++++++ src/asm/asm.cpp | 109 ++++++++++++++ tests/assembly/test_arm64.py | 4 + 42 files changed, 1625 insertions(+), 2 deletions(-) create mode 100644 api/python/lief/assembly/aarch64/operands/__init__.pyi create mode 100644 api/python/src/asm/aarch64/operands/CMakeLists.txt create mode 100644 api/python/src/asm/aarch64/operands/pyImmediate.cpp create mode 100644 api/python/src/asm/aarch64/operands/pyMemory.cpp create mode 100644 api/python/src/asm/aarch64/operands/pyPCRelative.cpp create mode 100644 api/python/src/asm/aarch64/operands/pyRegister.cpp create mode 100644 api/python/src/asm/aarch64/pyOperand.cpp create mode 100644 api/rust/cargo/lief/src/assembly/aarch64/operands.rs create mode 100644 api/rust/cargo/lief/src/assembly/aarch64/operands/immediate.rs create mode 100644 api/rust/cargo/lief/src/assembly/aarch64/operands/memory.rs create mode 100644 api/rust/cargo/lief/src/assembly/aarch64/operands/pc_relative.rs create mode 100644 api/rust/cargo/lief/src/assembly/aarch64/operands/register.rs create mode 100644 api/rust/include/LIEF/rust/asm/aarch64/Operand.hpp create mode 100644 api/rust/include/LIEF/rust/asm/aarch64/operands.hpp create mode 100644 api/rust/include/LIEF/rust/asm/aarch64/operands/Immediate.hpp create mode 100644 api/rust/include/LIEF/rust/asm/aarch64/operands/Memory.hpp create mode 100644 api/rust/include/LIEF/rust/asm/aarch64/operands/PCRelative.hpp create mode 100644 api/rust/include/LIEF/rust/asm/aarch64/operands/Register.hpp create mode 100644 include/LIEF/asm/aarch64/Operand.hpp create mode 100644 include/LIEF/asm/aarch64/operands.hpp create mode 100644 include/LIEF/asm/aarch64/operands/Immediate.hpp create mode 100644 include/LIEF/asm/aarch64/operands/Memory.hpp create mode 100644 include/LIEF/asm/aarch64/operands/PCRelative.hpp create mode 100644 include/LIEF/asm/aarch64/operands/Register.hpp diff --git a/api/python/lief/assembly/aarch64/__init__.pyi b/api/python/lief/assembly/aarch64/__init__.pyi index cf58b13ac8..79e4029153 100644 --- a/api/python/lief/assembly/aarch64/__init__.pyi +++ b/api/python/lief/assembly/aarch64/__init__.pyi @@ -1,6 +1,7 @@ import enum from typing import Iterator, Optional, Union +from . import operands as operands import lief @@ -8,6 +9,9 @@ class Instruction(lief.assembly.Instruction): @property def opcode(self) -> OPCODE: ... + @property + def operands(self) -> Iterator[Optional[Operand]]: ... + class OPCODE(enum.Enum): PHI = 0 @@ -16355,6 +16359,12 @@ class OPCODE(enum.Enum): INSTRUCTION_LIST_END = 8172 +class Operand: + @property + def to_string(self) -> str: ... + + def __str__(self) -> str: ... + class REG(enum.Enum): NoRegister = 0 diff --git a/api/python/lief/assembly/aarch64/operands/__init__.pyi b/api/python/lief/assembly/aarch64/operands/__init__.pyi new file mode 100644 index 0000000000..8836884c3b --- /dev/null +++ b/api/python/lief/assembly/aarch64/operands/__init__.pyi @@ -0,0 +1,48 @@ +import enum +import lief.assembly.aarch64 +from typing import Iterator, Optional, Union + +import lief + + +class Immediate(lief.assembly.aarch64.Operand): + @property + def value(self) -> int: ... + +class Memory(lief.assembly.aarch64.Operand): + class SHIFT(enum.Enum): + UNKNOWN = 0 + + LSL = 1 + + UXTX = 2 + + UXTW = 3 + + SXTX = 4 + + SXTW = 5 + + class shift_info_t: + @property + def type(self) -> Memory.SHIFT: ... + + @property + def value(self) -> int: ... + + @property + def base(self) -> lief.assembly.aarch64.REG: ... + + @property + def offset(self) -> Optional[Union[lief.assembly.aarch64.REG, int]]: ... + + @property + def shift(self) -> Memory.shift_info_t: ... + +class PCRelative(lief.assembly.aarch64.Operand): + @property + def value(self) -> int: ... + +class Register(lief.assembly.aarch64.Operand): + @property + def value(self) -> Optional[Union[lief.assembly.aarch64.REG, lief.assembly.aarch64.SYSREG]]: ... diff --git a/api/python/src/DyldSharedCache/pyDyldSharedCache.cpp b/api/python/src/DyldSharedCache/pyDyldSharedCache.cpp index 0d0b3e52df..34ec2d0dfa 100644 --- a/api/python/src/DyldSharedCache/pyDyldSharedCache.cpp +++ b/api/python/src/DyldSharedCache/pyDyldSharedCache.cpp @@ -226,7 +226,7 @@ void create(nb::module_& m) { R"doc( Convert the given virtual address into an offset. - .. code-block:: warning + .. warning:: If the shared cache contains multiple subcaches, this function needs to be called on the targeted subcache. diff --git a/api/python/src/asm/aarch64/CMakeLists.txt b/api/python/src/asm/aarch64/CMakeLists.txt index 784b3b7759..c5e4e70f06 100644 --- a/api/python/src/asm/aarch64/CMakeLists.txt +++ b/api/python/src/asm/aarch64/CMakeLists.txt @@ -2,6 +2,8 @@ target_sources(pyLIEF PRIVATE init.cpp pyInstruction.cpp pyOpcode.cpp + pyOperand.cpp pyRegister.cpp ) +add_subdirectory(operands) diff --git a/api/python/src/asm/aarch64/init.cpp b/api/python/src/asm/aarch64/init.cpp index d6af4a1d9a..7662da10b8 100644 --- a/api/python/src/asm/aarch64/init.cpp +++ b/api/python/src/asm/aarch64/init.cpp @@ -4,6 +4,7 @@ enum class OPCODE; enum class REG; enum class SYSREG; class Instruction; +class Operand; } namespace LIEF::assembly::aarch64::py { @@ -14,5 +15,6 @@ void init(nb::module_& m) { create(mod); create(mod); create(mod); + create(mod); } } diff --git a/api/python/src/asm/aarch64/operands/CMakeLists.txt b/api/python/src/asm/aarch64/operands/CMakeLists.txt new file mode 100644 index 0000000000..d7bf96508c --- /dev/null +++ b/api/python/src/asm/aarch64/operands/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(pyLIEF PRIVATE + pyImmediate.cpp + pyRegister.cpp + pyMemory.cpp + pyPCRelative.cpp +) diff --git a/api/python/src/asm/aarch64/operands/pyImmediate.cpp b/api/python/src/asm/aarch64/operands/pyImmediate.cpp new file mode 100644 index 0000000000..d8a0a9f30c --- /dev/null +++ b/api/python/src/asm/aarch64/operands/pyImmediate.cpp @@ -0,0 +1,26 @@ +#include "asm/aarch64/init.hpp" +#include "LIEF/asm/aarch64/operands/Immediate.hpp" + +namespace LIEF::assembly::aarch64::py { +template<> +void create(nb::module_& m) { + nb::class_ obj(m, "Immediate", + R"doc( + This class represents an immediate operand (i.e. a constant) + For instance: + + .. code-block:: text + + mov x0, #8; + | + +---> Immediate(8) + )doc"_doc + ); + + obj + .def_prop_ro("value", &operands::Immediate::value, + R"doc(The constant value wrapped by this operand)doc"_doc + ) + ; +} +} diff --git a/api/python/src/asm/aarch64/operands/pyMemory.cpp b/api/python/src/asm/aarch64/operands/pyMemory.cpp new file mode 100644 index 0000000000..cfda203f5f --- /dev/null +++ b/api/python/src/asm/aarch64/operands/pyMemory.cpp @@ -0,0 +1,101 @@ +#include + +#include "asm/aarch64/init.hpp" +#include "LIEF/asm/aarch64/operands/Memory.hpp" + +namespace nanobind::detail { +template<> +struct type_caster { + NB_TYPE_CASTER(LIEF::assembly::aarch64::operands::Memory::offset_t, + const_name("Optional[Union[lief.assembly.aarch64.REG, int]]")); + + bool from_python(handle src, uint8_t, cleanup_list *) noexcept { + return false; + } + + static handle from_cpp(LIEF::assembly::aarch64::operands::Memory::offset_t val, + rv_policy, cleanup_list *) noexcept + { + using namespace LIEF::assembly::aarch64; + using namespace LIEF::assembly::aarch64::operands; + switch (val.type) { + case Memory::offset_t::TYPE::REG: + return make_caster::from_cpp(val.reg, rv_policy::copy, + /*cleanup_list*/nullptr); + + case Memory::offset_t::TYPE::DISP: + return make_caster::from_cpp(val.displacement, rv_policy::copy, + /*cleanup_list*/nullptr); + case Memory::offset_t::TYPE::NONE: + return nb::none(); + } + return nb::none(); + } +}; +} + + +namespace LIEF::assembly::aarch64::py { +template<> +void create(nb::module_& m) { + nb::class_ obj(m, "Memory", + R"doc( + This class represents a memory operand. + + .. code-block:: text + + ldr x0, [x1, x2, lsl #3] + | | | + +------------+ | +--------+ + | | | + v v v + Base Reg Offset Shift + )doc"_doc + ); + + nb::enum_(obj, "SHIFT") + .value("UNKNOWN", operands::Memory::SHIFT::UNKNOWN) + .value("LSL", operands::Memory::SHIFT::LSL) + .value("UXTX", operands::Memory::SHIFT::UXTX) + .value("UXTW", operands::Memory::SHIFT::UXTW) + .value("SXTX", operands::Memory::SHIFT::SXTX) + .value("SXTW", operands::Memory::SHIFT::SXTW) + ; + + nb::class_(obj, "shift_info_t", + R"doc(This structure holds shift info (type + value))doc"_doc + ) + .def_ro("type", &operands::Memory::shift_info_t::type) + .def_ro("value", &operands::Memory::shift_info_t::value) + ; + + obj + .def_prop_ro("base", &operands::Memory::base, + R"doc( + The base register. + + For ``str x3, [x8, #8]`` it would return ``x8``. + )doc"_doc + ) + .def_prop_ro("offset", &operands::Memory::offset, + R"doc( + The addressing offset. + + It can be either: + + - A register (e.g. ``ldr x0, [x1, x3]``) + - An offset (e.g. ``ldr x0, [x1, #8]``) + )doc"_doc + ) + .def_prop_ro("shift", &operands::Memory::shift, + R"doc( + Shift information. + + For instance, for ``ldr x1, [x2, x3, lsl #3]`` it would + return a :attr:`~.Memory.SHIFT.LSL` with a :attr:`~.Memory.shift_info_t.value` + set to ``3``. + )doc"_doc + ) + ; +} +} diff --git a/api/python/src/asm/aarch64/operands/pyPCRelative.cpp b/api/python/src/asm/aarch64/operands/pyPCRelative.cpp new file mode 100644 index 0000000000..4fbed51860 --- /dev/null +++ b/api/python/src/asm/aarch64/operands/pyPCRelative.cpp @@ -0,0 +1,28 @@ +#include "asm/aarch64/init.hpp" +#include "LIEF/asm/aarch64/operands/PCRelative.hpp" + +namespace LIEF::assembly::aarch64::py { +template<> +void create(nb::module_& m) { + nb::class_ obj(m, "PCRelative", + R"doc( + This class represents a PC-relative operand. + + .. code-block:: text + + ldr x0, #8 + | + v + PC Relative operand + )doc"_doc + ); + + obj + .def_prop_ro("value", &aarch64::operands::PCRelative::value, + R"doc( + The effective value that is relative to the current ``pc`` register + )doc"_doc + ) + ; +} +} diff --git a/api/python/src/asm/aarch64/operands/pyRegister.cpp b/api/python/src/asm/aarch64/operands/pyRegister.cpp new file mode 100644 index 0000000000..0f99d782d0 --- /dev/null +++ b/api/python/src/asm/aarch64/operands/pyRegister.cpp @@ -0,0 +1,66 @@ +#include + +#include "asm/aarch64/init.hpp" +#include "LIEF/asm/aarch64/operands/Register.hpp" + +namespace nanobind::detail { +template<> +struct type_caster { + NB_TYPE_CASTER(LIEF::assembly::aarch64::operands::Register::reg_t, + const_name("Optional[Union[lief.assembly.aarch64.REG, lief.assembly.aarch64.SYSREG]]")); + + bool from_python(handle src, uint8_t, cleanup_list *) noexcept { + return false; + } + + static handle from_cpp(LIEF::assembly::aarch64::operands::Register::reg_t val, + rv_policy, cleanup_list *) noexcept + { + using namespace LIEF::assembly::aarch64; + using namespace LIEF::assembly::aarch64::operands; + switch (val.type) { + case Register::reg_t::TYPE::REG: + return make_caster::from_cpp(val.reg, rv_policy::copy, + /*cleanup_list*/nullptr); + + case Register::reg_t::TYPE::SYSREG: + return make_caster::from_cpp(val.sysreg, rv_policy::copy, + /*cleanup_list*/nullptr); + case Register::reg_t::TYPE::NONE: + return nb::none(); + } + return nb::none(); + } +}; +} + +namespace LIEF::assembly::aarch64::py { +template<> +void create(nb::module_& m) { + nb::class_ obj(m, "Register", + R"doc( + This class represents a register operand. + + .. code-block:: text + + mrs x0, TPIDR_EL0 + | | + +------+ +-------+ + | | + v v + REG SYSREG + )doc"_doc + ); + + obj + .def_prop_ro("value", &operands::Register::value, + R"doc( + The effective register as either: a :class:`lief.assembly.aarch64.REG` or + a :class:`lief.assembly.aarch64.SYSREG`. + )doc"_doc + ) + ; + + +} +} diff --git a/api/python/src/asm/aarch64/pyInstruction.cpp b/api/python/src/asm/aarch64/pyInstruction.cpp index 6a9a835869..4f29ebe40a 100644 --- a/api/python/src/asm/aarch64/pyInstruction.cpp +++ b/api/python/src/asm/aarch64/pyInstruction.cpp @@ -1,7 +1,12 @@ #include "LIEF/asm/aarch64/Instruction.hpp" +#include "LIEF/asm/aarch64/Operand.hpp" + +#include #include "asm/aarch64/init.hpp" +#include + namespace LIEF::assembly::aarch64::py { template<> void create(nb::module_& m) { @@ -15,6 +20,14 @@ void create(nb::module_& m) { .def_prop_ro("opcode", &Instruction::opcode, R"doc(The instruction opcode as defined in LLVM)doc"_doc ) + .def_prop_ro("operands", [] (const aarch64::Instruction& self) { + auto ops = self.operands(); + return nb::make_iterator( + nb::type(), "operands_it", ops + ); + }, nb::keep_alive<0, 1>(), + R"doc(Iterator over the operands of the current instruction)doc"_doc + ) ; } } diff --git a/api/python/src/asm/aarch64/pyOperand.cpp b/api/python/src/asm/aarch64/pyOperand.cpp new file mode 100644 index 0000000000..edaa90fee9 --- /dev/null +++ b/api/python/src/asm/aarch64/pyOperand.cpp @@ -0,0 +1,39 @@ +#include +#include + +#include "LIEF/asm/aarch64/Operand.hpp" + +#include "asm/aarch64/init.hpp" + +#include + +#include "pyLIEF.hpp" + +namespace LIEF::assembly::aarch64::operands { +class Immediate; +class Register; +class Memory; +class PCRelative; +} + +namespace LIEF::assembly::aarch64::py { +template<> +void create(nb::module_& m) { + nb::class_ obj(m, "Operand", + R"doc(This class represents an operand for an AArch64 instruction)doc"_doc + ); + + obj + .def_prop_ro("to_string", &Operand::to_string, + R"doc(Pretty representation of the operand)doc"_doc + ) + LIEF_DEFAULT_STR(aarch64::Operand) + ; + + nb::module_ operands = m.def_submodule("operands"); + create(operands); + create(operands); + create(operands); + create(operands); +} +} diff --git a/api/rust/autocxx_ffi.rs b/api/rust/autocxx_ffi.rs index 08bdb6ada8..c0d2452b53 100644 --- a/api/rust/autocxx_ffi.rs +++ b/api/rust/autocxx_ffi.rs @@ -701,6 +701,36 @@ include_cpp! { /* AArch64 { */ generate!("asm_aarch64_Instruction") block_constructors!("asm_aarch64_Instruction") + + generate!("asm_aarch64_Instruction_it_operands") + block_constructors!("asm_aarch64_Instruction_it_operands") + + /* Operands { */ + generate!("asm_aarch64_Operand") + block_constructors!("asm_aarch64_Operand") + + generate!("asm_aarch64_operands_Register") + block_constructors!("asm_aarch64_operands_Register") + + generate_pod!("asm_aarch64_operands_Register_reg_t") + block_constructors!("asm_aarch64_operands_Register_reg_t") + + generate!("asm_aarch64_operands_Memory") + block_constructors!("asm_aarch64_operands_Memory") + + generate_pod!("asm_aarch64_operands_Memory_offset_t") + block_constructors!("asm_aarch64_operands_Memory_offset_t") + + generate_pod!("asm_aarch64_operands_Memory_shift_info_t") + block_constructors!("asm_aarch64_operands_Memory_shift_info_t") + + generate!("asm_aarch64_operands_Immediate") + block_constructors!("asm_aarch64_operands_Immediate") + + generate!("asm_aarch64_operands_PCRelative") + block_constructors!("asm_aarch64_operands_PCRelative") + /* } */ + /* } AArch64 */ /* X86 { */ diff --git a/api/rust/cargo/lief/src/assembly/aarch64.rs b/api/rust/cargo/lief/src/assembly/aarch64.rs index bad5057d70..2d068b8677 100644 --- a/api/rust/cargo/lief/src/assembly/aarch64.rs +++ b/api/rust/cargo/lief/src/assembly/aarch64.rs @@ -3,6 +3,7 @@ pub mod opcodes; pub mod instruction; pub mod registers; +pub mod operands; #[doc(inline)] pub use opcodes::Opcode; @@ -12,3 +13,9 @@ pub use registers::{Reg, SysReg}; #[doc(inline)] pub use instruction::Instruction; + +#[doc(inline)] +pub use operands::Operands; + +#[doc(inline)] +pub use operands::Operand; diff --git a/api/rust/cargo/lief/src/assembly/aarch64/instruction.rs b/api/rust/cargo/lief/src/assembly/aarch64/instruction.rs index 8305668e70..a1d606dfd2 100644 --- a/api/rust/cargo/lief/src/assembly/aarch64/instruction.rs +++ b/api/rust/cargo/lief/src/assembly/aarch64/instruction.rs @@ -4,6 +4,9 @@ use crate::common::FromFFI; use crate::assembly; use super::Opcode; +use crate::declare_fwd_iterator; +use crate::assembly::aarch64; + /// This structure represents an AArch64 instruction pub struct Instruction { ptr: cxx::UniquePtr, @@ -29,4 +32,18 @@ impl Instruction { pub fn opcode(&self) -> Opcode { Opcode::from(self.ptr.opcode()) } + + /// Return an iterator over the [`aarch64::Operands`] operands + pub fn operands(&self) -> Operands { + Operands::new(self.ptr.operands()) + } } + + +declare_fwd_iterator!( + Operands, + aarch64::Operands, + ffi::asm_Instruction, + ffi::asm_aarch64_Operand, + ffi::asm_aarch64_Instruction_it_operands +); diff --git a/api/rust/cargo/lief/src/assembly/aarch64/operands.rs b/api/rust/cargo/lief/src/assembly/aarch64/operands.rs new file mode 100644 index 0000000000..5f22ff0ae8 --- /dev/null +++ b/api/rust/cargo/lief/src/assembly/aarch64/operands.rs @@ -0,0 +1,139 @@ +use lief_ffi as ffi; + +use crate::common::FromFFI; + +pub mod immediate; +pub mod memory; +pub mod pc_relative; +pub mod register; + +#[doc(inline)] +pub use register::Register; + +#[doc(inline)] +pub use pc_relative::PCRelative; + +#[doc(inline)] +pub use immediate::Immediate; + +#[doc(inline)] +pub use memory::Memory; + +/// Trait shared by **all** [`Operands`] +pub trait Operand { + #[doc(hidden)] + fn as_generic(&self) -> &ffi::asm_aarch64_Operand; + + /// Pretty representation of the operand + fn to_string(&self) -> String { + self.as_generic().to_string().to_string() + } +} + +/// This enum represents the different kind of operands associated with [`super::Instruction`] +pub enum Operands { + /// A register operand (e.g. `X0`) + Reg(Register), + + /// A PC-relative operand + PCRelative(PCRelative), + + /// An immediate value + Imm(Immediate), + + /// A memory operand + Mem(Memory), + + /// Operand that is not correctly supported + Unknown(Unknown), +} + +impl FromFFI for Operands { + fn from_ffi(ptr: cxx::UniquePtr) -> Self { + unsafe { + let op_ref = ptr.as_ref().unwrap(); + if ffi::asm_aarch64_operands_Memory::classof(op_ref) { + let raw = { + type From = cxx::UniquePtr; + type To = cxx::UniquePtr; + std::mem::transmute::(ptr) + }; + return Operands::Mem(Memory::from_ffi(raw)); + } + else if ffi::asm_aarch64_operands_Register::classof(op_ref) { + let raw = { + type From = cxx::UniquePtr; + type To = cxx::UniquePtr; + std::mem::transmute::(ptr) + }; + return Operands::Reg(Register::from_ffi(raw)); + } + else if ffi::asm_aarch64_operands_Immediate::classof(op_ref) { + let raw = { + type From = cxx::UniquePtr; + type To = cxx::UniquePtr; + std::mem::transmute::(ptr) + }; + return Operands::Imm(Immediate::from_ffi(raw)); + } + else if ffi::asm_aarch64_operands_PCRelative::classof(op_ref) { + let raw = { + type From = cxx::UniquePtr; + type To = cxx::UniquePtr; + std::mem::transmute::(ptr) + }; + return Operands::PCRelative(PCRelative::from_ffi(raw)); + } + return Operands::Unknown(Unknown::from_ffi(ptr)); + } + } +} + +impl Operand for Operands { + #[doc(hidden)] + fn as_generic(&self) -> &ffi::asm_aarch64_Operand { + match &self { + Operands::Reg(op) => { + op.as_generic() + } + + Operands::Imm(op) => { + op.as_generic() + } + + Operands::Mem(op) => { + op.as_generic() + } + + Operands::PCRelative(op) => { + op.as_generic() + } + + Operands::Unknown(op) => { + op.as_generic() + } + } + } +} + +pub struct Unknown { + ptr: cxx::UniquePtr, +} + +impl FromFFI for Unknown { + fn from_ffi(ptr: cxx::UniquePtr) -> Self { + Self { + ptr, + } + } +} + +impl Operand for Unknown { + #[doc(hidden)] + fn as_generic(&self) -> &ffi::asm_aarch64_Operand { + self.ptr.as_ref().unwrap() + } +} + + + diff --git a/api/rust/cargo/lief/src/assembly/aarch64/operands/immediate.rs b/api/rust/cargo/lief/src/assembly/aarch64/operands/immediate.rs new file mode 100644 index 0000000000..869f2d872b --- /dev/null +++ b/api/rust/cargo/lief/src/assembly/aarch64/operands/immediate.rs @@ -0,0 +1,40 @@ +use lief_ffi as ffi; + +use crate::common::FromFFI; + +use super::Operand; + +/// This structure represents an immediate operand. +/// +/// For instance: +/// +/// ```text +/// mov x0, #8; +/// | +/// +---> Immediate(8) +/// ``` +pub struct Immediate { + ptr: cxx::UniquePtr, +} + +impl FromFFI for Immediate { + fn from_ffi(ptr: cxx::UniquePtr) -> Self { + Self { + ptr, + } + } +} + +impl Operand for Immediate { + #[doc(hidden)] + fn as_generic(&self) -> &ffi::asm_aarch64_Operand { + self.ptr.as_ref().unwrap().as_ref() + } +} + +impl Immediate { + /// The constant value wrapped by this operand + pub fn value(&self) -> i64 { + self.ptr.value() + } +} diff --git a/api/rust/cargo/lief/src/assembly/aarch64/operands/memory.rs b/api/rust/cargo/lief/src/assembly/aarch64/operands/memory.rs new file mode 100644 index 0000000000..fccce2b2fd --- /dev/null +++ b/api/rust/cargo/lief/src/assembly/aarch64/operands/memory.rs @@ -0,0 +1,124 @@ +use lief_ffi as ffi; + +use crate::{assembly::aarch64::registers::Reg, common::FromFFI}; + +use super::Operand; + +/// This structure represents a memory operand. +/// +/// ```text +/// ldr x0, [x1, x2, lsl #3] +/// | | | +/// +------------+ | +--------+ +/// | | | +/// v v v +/// Base Reg Offset Shift +/// +/// ``` +pub struct Memory { + ptr: cxx::UniquePtr, +} + +impl FromFFI for Memory { + fn from_ffi(ptr: cxx::UniquePtr) -> Self { + Self { + ptr, + } + } +} + +impl Operand for Memory { + #[doc(hidden)] + fn as_generic(&self) -> &ffi::asm_aarch64_Operand { + self.ptr.as_ref().unwrap().as_ref() + } +} + +/// Wraps a memory offset as an integer offset or as a register offset +pub enum Offset { + /// Register offset + Reg(Reg), + /// Integer offset + Displacement(i64), +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Shift { + Unknown, + Lsl, + Uxtx, + Uxtw, + Sxtx, + Sxtw, +} + +impl From for Shift { + fn from(value: i32) -> Self { + match value { + 0 => Shift::Unknown, + 1 => Shift::Lsl, + 2 => Shift::Uxtx, + 3 => Shift::Uxtw, + 4 => Shift::Sxtx, + 5 => Shift::Sxtw, + _ => Shift::Unknown, + } + } +} + +/// This structure holds shift info (type + value) +pub struct ShiftInfo { + pub shift_type: Shift, + pub value: i8, +} + +impl ShiftInfo { + pub fn new(shift: Shift, value: i8) -> Self { + Self { + shift_type: shift, + value + } + } +} + +impl Memory { + /// The base register. + /// + /// For `str x3, [x8, #8]` it would return `x8` + pub fn base(&self) -> Reg { + Reg::from(self.ptr.base()) + } + + /// The addressing offset. + /// + /// It can be either: + /// - A register (e.g. `ldr x0, [x1, x3]`) + /// - An offset (e.g. `ldr x0, [x1, #8]`) + pub fn offset(&self) -> Option { + let ffi_offset = self.ptr.offset(); + match ffi_offset.enum_type { + 1 => { + Some(Offset::Reg(Reg::from(ffi_offset.value))) + } + 2 => { + Some(Offset::Displacement(ffi_offset.value as i64)) + } + _ => { + None + } + } + } + + /// Shift information. + /// + /// For instance, for `ldr x1, [x2, x3, lsl #3]` it would + /// return a [`Shift::Lsl`] with a [`ShiftInfo::value`] set to `3` + pub fn shift(&self) -> Option { + let ffi_shift = self.ptr.shift(); + if ffi_shift.value == 0 { + return None; + } + Some(ShiftInfo::new(Shift::from(ffi_shift.enum_type), ffi_shift.value)) + } +} diff --git a/api/rust/cargo/lief/src/assembly/aarch64/operands/pc_relative.rs b/api/rust/cargo/lief/src/assembly/aarch64/operands/pc_relative.rs new file mode 100644 index 0000000000..e58534eaba --- /dev/null +++ b/api/rust/cargo/lief/src/assembly/aarch64/operands/pc_relative.rs @@ -0,0 +1,33 @@ +use lief_ffi as ffi; + +use crate::common::FromFFI; + +use super::Operand; + +/// This structure represents a PC-relative operand. +/// +pub struct PCRelative { + ptr: cxx::UniquePtr, +} + +impl FromFFI for PCRelative { + fn from_ffi(ptr: cxx::UniquePtr) -> Self { + Self { + ptr, + } + } +} + +impl Operand for PCRelative { + #[doc(hidden)] + fn as_generic(&self) -> &ffi::asm_aarch64_Operand { + self.ptr.as_ref().unwrap().as_ref() + } +} + +impl PCRelative { + /// The effective value that is relative to the current `rip/eip` register + pub fn value(&self) -> i64 { + self.ptr.value() + } +} diff --git a/api/rust/cargo/lief/src/assembly/aarch64/operands/register.rs b/api/rust/cargo/lief/src/assembly/aarch64/operands/register.rs new file mode 100644 index 0000000000..2d6872a5d9 --- /dev/null +++ b/api/rust/cargo/lief/src/assembly/aarch64/operands/register.rs @@ -0,0 +1,58 @@ +use lief_ffi as ffi; + +use crate::assembly::aarch64::registers::{Reg, SysReg}; +use crate::common::FromFFI; + +use super::Operand; + +/// This structure represents a register operand. +/// +/// ```text +/// mrs x0, TPIDR_EL0 +/// | | +/// +------+ +-------+ +/// | | +/// v v +/// REG SYSREG +/// ``` +pub struct Register { + ptr: cxx::UniquePtr, +} + +impl FromFFI for Register { + fn from_ffi(ptr: cxx::UniquePtr) -> Self { + Self { + ptr, + } + } +} + +impl Operand for Register { + #[doc(hidden)] + fn as_generic(&self) -> &ffi::asm_aarch64_Operand { + self.ptr.as_ref().unwrap().as_ref() + } +} + +pub enum Value { + Reg(Reg), + SysReg(SysReg), +} + +impl Register { + /// The effective register as either: a [`Reg`] or a [`SysReg`] + pub fn value(&self) -> Option { + let ffi_value = self.ptr.value(); + match ffi_value.enum_type { + 1 => { + Some(Value::SysReg(SysReg::from(ffi_value.reg))) + } + 2 => { + Some(Value::Reg(Reg::from(ffi_value.reg))) + } + _ => { + None + } + } + } +} diff --git a/api/rust/include/LIEF/rust/ASM.hpp b/api/rust/include/LIEF/rust/ASM.hpp index 5b0acfdb84..0899f96162 100644 --- a/api/rust/include/LIEF/rust/ASM.hpp +++ b/api/rust/include/LIEF/rust/ASM.hpp @@ -17,6 +17,9 @@ #include "LIEF/rust/asm/Engine.hpp" #include "LIEF/rust/asm/aarch64/Instruction.hpp" +#include "LIEF/rust/asm/aarch64/operands.hpp" +#include "LIEF/rust/asm/aarch64/operands/Register.hpp" + #include "LIEF/rust/asm/x86/Instruction.hpp" #include "LIEF/rust/asm/x86/operands.hpp" #include "LIEF/rust/asm/x86/operands/Register.hpp" diff --git a/api/rust/include/LIEF/rust/asm/aarch64/Instruction.hpp b/api/rust/include/LIEF/rust/asm/aarch64/Instruction.hpp index 2f0781d596..53fbc74b9e 100644 --- a/api/rust/include/LIEF/rust/asm/aarch64/Instruction.hpp +++ b/api/rust/include/LIEF/rust/asm/aarch64/Instruction.hpp @@ -16,15 +16,31 @@ #include #include "LIEF/rust/asm/Instruction.hpp" +#include "LIEF/rust/asm/aarch64/Operand.hpp" + #include "LIEF/rust/helpers.hpp" +#include "LIEF/rust/Iterator.hpp" class asm_aarch64_Instruction : public asm_Instruction { public: using lief_t = LIEF::assembly::aarch64::Instruction; + class it_operands : + public ForwardIterator + { + public: + it_operands(const asm_aarch64_Instruction::lief_t& src) + : ForwardIterator(src.operands()) { } + + auto next() { return ForwardIterator::next(); } + }; + uint64_t opcode() const { return to_int(impl().opcode()); } + auto operands() const { + return std::make_unique(impl()); + } static bool classof(const asm_Instruction& inst) { return lief_t::classof(&inst.get()); diff --git a/api/rust/include/LIEF/rust/asm/aarch64/Operand.hpp b/api/rust/include/LIEF/rust/asm/aarch64/Operand.hpp new file mode 100644 index 0000000000..7c43356573 --- /dev/null +++ b/api/rust/include/LIEF/rust/asm/aarch64/Operand.hpp @@ -0,0 +1,28 @@ +/* Copyright 2022 - 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "LIEF/asm/aarch64/Operand.hpp" + +#include "LIEF/rust/Mirror.hpp" + +class asm_aarch64_Operand : public Mirror { + public: + using lief_t = LIEF::assembly::aarch64::Operand; + using Mirror::Mirror; + + std::string to_string() const { + return get().to_string(); + } +}; diff --git a/api/rust/include/LIEF/rust/asm/aarch64/operands.hpp b/api/rust/include/LIEF/rust/asm/aarch64/operands.hpp new file mode 100644 index 0000000000..da013e6b1f --- /dev/null +++ b/api/rust/include/LIEF/rust/asm/aarch64/operands.hpp @@ -0,0 +1,6 @@ +#pragma once +#include "LIEF/rust/asm/aarch64/Operand.hpp" +#include "LIEF/rust/asm/aarch64/operands/Register.hpp" +#include "LIEF/rust/asm/aarch64/operands/Immediate.hpp" +#include "LIEF/rust/asm/aarch64/operands/PCRelative.hpp" +#include "LIEF/rust/asm/aarch64/operands/Memory.hpp" diff --git a/api/rust/include/LIEF/rust/asm/aarch64/operands/Immediate.hpp b/api/rust/include/LIEF/rust/asm/aarch64/operands/Immediate.hpp new file mode 100644 index 0000000000..f148afe286 --- /dev/null +++ b/api/rust/include/LIEF/rust/asm/aarch64/operands/Immediate.hpp @@ -0,0 +1,34 @@ +/* Copyright 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +#include "LIEF/rust/asm/aarch64/Operand.hpp" + +class asm_aarch64_operands_Immediate : public asm_aarch64_Operand { + public: + using lief_t = LIEF::assembly::aarch64::operands::Immediate; + + auto value() const { + return impl().value(); + } + + static bool classof(const asm_aarch64_Operand& inst) { + return lief_t::classof(&inst.get()); + } + + private: + const lief_t& impl() const { return as(this); } +}; diff --git a/api/rust/include/LIEF/rust/asm/aarch64/operands/Memory.hpp b/api/rust/include/LIEF/rust/asm/aarch64/operands/Memory.hpp new file mode 100644 index 0000000000..3b02d43a6b --- /dev/null +++ b/api/rust/include/LIEF/rust/asm/aarch64/operands/Memory.hpp @@ -0,0 +1,61 @@ +/* Copyright 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +#include "LIEF/rust/asm/aarch64/Operand.hpp" +#include "LIEF/rust/helpers.hpp" + +class asm_aarch64_operands_Memory_offset_t { + public: + uint64_t value; + uint32_t enum_type; +}; + +class asm_aarch64_operands_Memory_shift_info_t { + public: + int32_t enum_type; + int8_t value; +}; + +class asm_aarch64_operands_Memory : public asm_aarch64_Operand { + public: + using lief_t = LIEF::assembly::aarch64::operands::Memory; + + uint64_t base() const { return to_int(impl().base()); } + + asm_aarch64_operands_Memory_offset_t offset() const { + const lief_t::offset_t off = impl().offset(); + return { + /*.value = */(uint64_t)off.reg, + /*.type = */(uint32_t)to_int(off.type), + }; + } + + asm_aarch64_operands_Memory_shift_info_t shift() const { + const lief_t::shift_info_t info = impl().shift(); + return { + /*.type = */(int32_t)info.type, + /*.value = */info.value, + }; + } + + static bool classof(const asm_aarch64_Operand& inst) { + return lief_t::classof(&inst.get()); + } + + private: + const lief_t& impl() const { return as(this); } +}; diff --git a/api/rust/include/LIEF/rust/asm/aarch64/operands/PCRelative.hpp b/api/rust/include/LIEF/rust/asm/aarch64/operands/PCRelative.hpp new file mode 100644 index 0000000000..fd8366c39f --- /dev/null +++ b/api/rust/include/LIEF/rust/asm/aarch64/operands/PCRelative.hpp @@ -0,0 +1,32 @@ +/* Copyright 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +#include "LIEF/rust/asm/aarch64/Operand.hpp" + +class asm_aarch64_operands_PCRelative : public asm_aarch64_Operand { + public: + using lief_t = LIEF::assembly::aarch64::operands::PCRelative; + + auto value() const { return impl().value(); } + + static bool classof(const asm_aarch64_Operand& inst) { + return lief_t::classof(&inst.get()); + } + + private: + const lief_t& impl() const { return as(this); } +}; diff --git a/api/rust/include/LIEF/rust/asm/aarch64/operands/Register.hpp b/api/rust/include/LIEF/rust/asm/aarch64/operands/Register.hpp new file mode 100644 index 0000000000..d06d28b38c --- /dev/null +++ b/api/rust/include/LIEF/rust/asm/aarch64/operands/Register.hpp @@ -0,0 +1,45 @@ +/* Copyright 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +#include "LIEF/rust/asm/aarch64/Operand.hpp" +#include "LIEF/rust/helpers.hpp" + +class asm_aarch64_operands_Register_reg_t { + public: + uint64_t reg; + uint32_t enum_type; +}; + +class asm_aarch64_operands_Register : public asm_aarch64_Operand { + public: + using lief_t = LIEF::assembly::aarch64::operands::Register; + + asm_aarch64_operands_Register_reg_t value() const { + lief_t::reg_t info = impl().value(); + return { + /*.reg =*/(uint64_t)to_int(info.reg), + /*.enum_type =*/(uint32_t)to_int(info.type), + }; + } + + static bool classof(const asm_aarch64_Operand& inst) { + return lief_t::classof(&inst.get()); + } + + private: + const lief_t& impl() const { return as(this); } +}; diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py index ef098ca74f..56482d3e9b 100644 --- a/doc/sphinx/conf.py +++ b/doc/sphinx/conf.py @@ -35,6 +35,11 @@ 'llvm-pr': ("https://github.com/llvm/llvm-project/pull/%s", "llvm/llvm-project#%s"), } +# Can be used for debugging breathe +#breathe_debug_trace_directives = True +#breathe_debug_trace_doxygen_ids = True +#breathe_debug_trace_qualification = True + master_doc = 'index' project = 'LIEF' diff --git a/doc/sphinx/extended/disassembler/cpp/arch/aarch64.rst b/doc/sphinx/extended/disassembler/cpp/arch/aarch64.rst index 0342ae74c6..7e266713f9 100644 --- a/doc/sphinx/extended/disassembler/cpp/arch/aarch64.rst +++ b/doc/sphinx/extended/disassembler/cpp/arch/aarch64.rst @@ -10,3 +10,29 @@ Opcodes ******* See ``LIEF::assembly::aarch64::OPCODE`` in ``include/asm/aarch64/opcodes.hpp`` + + +Operands +******** + +.. doxygenclass:: LIEF::assembly::aarch64::Operand + +Immediate +~~~~~~~~~ + +.. doxygenclass:: LIEF::assembly::aarch64::operands::Immediate + +Register +~~~~~~~~ + +.. doxygenclass:: LIEF::assembly::aarch64::operands::Register + +Memory +~~~~~~ + +.. doxygenclass:: LIEF::assembly::aarch64::operands::Memory + +PCRelative +~~~~~~~~~~ + +.. doxygenclass:: LIEF::assembly::aarch64::operands::PCRelative diff --git a/doc/sphinx/extended/disassembler/python/arch/aarch64.rst b/doc/sphinx/extended/disassembler/python/arch/aarch64.rst index 248b39c00f..06834c6339 100644 --- a/doc/sphinx/extended/disassembler/python/arch/aarch64.rst +++ b/doc/sphinx/extended/disassembler/python/arch/aarch64.rst @@ -14,3 +14,49 @@ Opcodes ******* See: ``lief.assembly.aarch64.OPCODE`` + + +Operands +******** + +.. lief-inheritance:: lief._lief.assembly.aarch64.Operand + :top-classes: lief._lief.assembly.aarch64.Operand + :parts: 2 + +.. autoclass:: lief.assembly.aarch64.Operand + +Immediate +~~~~~~~~~ + +.. lief-inheritance:: lief._lief.assembly.aarch64.operands.Immediate + :top-classes: lief._lief.assembly.aarch64.Operand + :parts: 2 + +.. autoclass:: lief.assembly.aarch64.operands.Immediate + +Register +~~~~~~~~ + +.. lief-inheritance:: lief._lief.assembly.aarch64.operands.Register + :top-classes: lief._lief.assembly.aarch64.Operand + :parts: 2 + +.. autoclass:: lief.assembly.aarch64.operands.Register + +Memory +~~~~~~ + +.. lief-inheritance:: lief._lief.assembly.aarch64.operands.Memory + :top-classes: lief._lief.assembly.aarch64.Operand + :parts: 2 + +.. autoclass:: lief.assembly.aarch64.operands.Memory + +PCRelative +~~~~~~~~~~ + +.. lief-inheritance:: lief._lief.assembly.aarch64.operands.PCRelative + :top-classes: lief._lief.assembly.aarch64.Operand + :parts: 2 + +.. autoclass:: lief.assembly.aarch64.operands.PCRelative diff --git a/doc/sphinx/extended/disassembler/python/arch/x86.rst b/doc/sphinx/extended/disassembler/python/arch/x86.rst index adb68883d4..7b70ee7a6c 100644 --- a/doc/sphinx/extended/disassembler/python/arch/x86.rst +++ b/doc/sphinx/extended/disassembler/python/arch/x86.rst @@ -18,7 +18,6 @@ See: ``lief.assembly.x86.OPCODE`` Operands ******** - .. lief-inheritance:: lief._lief.assembly.x86.Operand :top-classes: lief._lief.assembly.x86.Operand :parts: 2 diff --git a/include/LIEF/asm/aarch64.hpp b/include/LIEF/asm/aarch64.hpp index e2e3d7a867..7272524946 100644 --- a/include/LIEF/asm/aarch64.hpp +++ b/include/LIEF/asm/aarch64.hpp @@ -18,4 +18,5 @@ #include #include #include +#include #endif diff --git a/include/LIEF/asm/aarch64/Instruction.hpp b/include/LIEF/asm/aarch64/Instruction.hpp index 85c188e6a5..140e593af8 100644 --- a/include/LIEF/asm/aarch64/Instruction.hpp +++ b/include/LIEF/asm/aarch64/Instruction.hpp @@ -18,6 +18,7 @@ #include "LIEF/asm/Instruction.hpp" #include "LIEF/asm/aarch64/opcodes.hpp" +#include "LIEF/asm/aarch64/Operand.hpp" namespace LIEF { namespace assembly { @@ -30,9 +31,14 @@ class LIEF_API Instruction : public assembly::Instruction { public: using assembly::Instruction::Instruction; + using operands_it = iterator_range; + /// The instruction opcode as defined in LLVM OPCODE opcode() const; + /// Iterator over the operands of the current instruction + operands_it operands() const; + /// True if `inst` is an **effective** instance of aarch64::Instruction static bool classof(const assembly::Instruction* inst); diff --git a/include/LIEF/asm/aarch64/Operand.hpp b/include/LIEF/asm/aarch64/Operand.hpp new file mode 100644 index 0000000000..db74b8b552 --- /dev/null +++ b/include/LIEF/asm/aarch64/Operand.hpp @@ -0,0 +1,125 @@ +/* Copyright 2022 - 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIEF_ASM_AARCH64_OPERAND_H +#define LIEF_ASM_AARCH64_OPERAND_H +#include "LIEF/visibility.h" +#include "LIEF/iterators.hpp" + +#include +#include +#include + +#include + +namespace LIEF { +namespace assembly { +namespace aarch64 { + +namespace details { +class Operand; +class OperandIt; +} + +/// This class represents an operand for an AArch64 instruction +class LIEF_API Operand { + public: + + /// **Forward** iterator that outputs aarch64 Operand as `std::unique_ptr` + class Iterator final : + public iterator_facade_base, + std::ptrdiff_t, Operand*, std::unique_ptr> + { + public: + using implementation = details::OperandIt; + + LIEF_API Iterator(); + + LIEF_API Iterator(std::unique_ptr impl); + LIEF_API Iterator(const Iterator&); + LIEF_API Iterator& operator=(const Iterator&); + + LIEF_API Iterator(Iterator&&) noexcept; + LIEF_API Iterator& operator=(Iterator&&) noexcept; + + LIEF_API ~Iterator(); + + LIEF_API Iterator& operator++(); + + friend LIEF_API bool operator==(const Iterator& LHS, const Iterator& RHS); + + friend bool operator!=(const Iterator& LHS, const Iterator& RHS) { + return !(LHS == RHS); + } + + LIEF_API std::unique_ptr operator*() const; + + private: + std::unique_ptr impl_; + }; + + /// Pretty representation of the operand + std::string to_string() const; + + /// This function can be used to **down cast** an Operand instance: + /// + /// ```cpp + /// std::unique_ptr op = ...; + /// if (const auto* imm = inst->as()) { + /// const int64_t value = imm->value(); + /// } + /// ``` + template + const T* as() const { + static_assert(std::is_base_of::value, + "Require Operand inheritance"); + if (T::classof(this)) { + return static_cast(this); + } + return nullptr; + } + + virtual ~Operand(); + + /// \private + static LIEF_LOCAL std::unique_ptr + create(std::unique_ptr impl); + + /// \private + LIEF_LOCAL const details::Operand& impl() const { + assert(impl_ != nullptr); + return *impl_; + } + + /// \private + LIEF_LOCAL details::Operand& impl() { + assert(impl_ != nullptr); + return *impl_; + } + + friend LIEF_API std::ostream& operator<<(std::ostream& os, const Operand& op) { + os << op.to_string(); + return os; + } + + protected: + LIEF_LOCAL Operand(std::unique_ptr impl); + std::unique_ptr impl_; +}; + +} +} +} + +#endif diff --git a/include/LIEF/asm/aarch64/operands.hpp b/include/LIEF/asm/aarch64/operands.hpp new file mode 100644 index 0000000000..154f405fa2 --- /dev/null +++ b/include/LIEF/asm/aarch64/operands.hpp @@ -0,0 +1,22 @@ +/* Copyright 2017 - 2024 R. Thomas + * Copyright 2017 - 2024 Quarkslab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIEF_ASM_AARCH64_OPERANDS_H +#define LIEF_ASM_AARCH64_OPERANDS_H +#include +#include +#include +#include +#endif diff --git a/include/LIEF/asm/aarch64/operands/Immediate.hpp b/include/LIEF/asm/aarch64/operands/Immediate.hpp new file mode 100644 index 0000000000..16ebdc729a --- /dev/null +++ b/include/LIEF/asm/aarch64/operands/Immediate.hpp @@ -0,0 +1,49 @@ +/* Copyright 2022 - 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIEF_ASM_AARCH64_OPERAND_IMM_H +#define LIEF_ASM_AARCH64_OPERAND_IMM_H +#include "LIEF/asm/aarch64/Operand.hpp" + +namespace LIEF { +namespace assembly { +namespace aarch64 { +/// Namespace that wraps the different aarch64 operands +namespace operands { + + +/// This class represents an immediate operand (i.e. a constant) +/// +/// For instance: +/// +/// ```text +/// mov x0, #8; +/// | +/// +---> Immediate(8) +/// ``` +class LIEF_API Immediate : public Operand { + public: + using Operand::Operand; + + /// The constant value wrapped by this operand + int64_t value() const; + + static bool classof(const Operand* op); + ~Immediate() override = default; +}; +} +} +} +} +#endif diff --git a/include/LIEF/asm/aarch64/operands/Memory.hpp b/include/LIEF/asm/aarch64/operands/Memory.hpp new file mode 100644 index 0000000000..78fd3d3f6e --- /dev/null +++ b/include/LIEF/asm/aarch64/operands/Memory.hpp @@ -0,0 +1,103 @@ +/* Copyright 2022 - 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIEF_ASM_AARCH64_OPERAND_MEMORY_H +#define LIEF_ASM_AARCH64_OPERAND_MEMORY_H +#include "LIEF/asm/aarch64/Operand.hpp" +#include "LIEF/asm/aarch64/registers.hpp" + +namespace LIEF { +namespace assembly { +namespace aarch64 { +namespace operands { + +/// This class represents a memory operand. +/// +/// ```text +/// ldr x0, [x1, x2, lsl #3] +/// | | | +/// +------------+ | +--------+ +/// | | | +/// v v v +/// Base Reg Offset Shift +/// +/// ``` +class LIEF_API Memory : public Operand { + public: + using Operand::Operand; + + enum class SHIFT : int32_t { + UNKNOWN = 0, + LSL, + UXTX, + UXTW, + SXTX, + SXTW, + }; + + /// This structure holds shift info (type + value) + struct shift_info_t { + SHIFT type = SHIFT::UNKNOWN; + int8_t value = -1; + }; + + /// Wraps a memory offset as an integer offset + /// or as a register offset + struct offset_t { + /// Enum type used to discriminate the anonymous union + enum class TYPE { + NONE = 0, + /// The *union* holds the REG attribute + REG, + /// The *union* holds the `displacement` attribute (`int64_t`) + DISP, + }; + + union { + /// Register offset + REG reg; + + /// Integer offset + int64_t displacement = 0; + }; + TYPE type = TYPE::NONE; + }; + + /// The base register. + /// + /// For `str x3, [x8, #8]` it would return `x8`. + REG base() const; + + /// The addressing offset. + /// + /// It can be either: + /// - A register (e.g. `ldr x0, [x1, x3]`) + /// - An offset (e.g. `ldr x0, [x1, #8]`) + offset_t offset() const; + + /// Shift information. + /// + /// For instance, for `ldr x1, [x2, x3, lsl #3]` it would + /// return a SHIFT::LSL with a shift_info_t::value set to `3`. + shift_info_t shift() const; + + static bool classof(const Operand* op); + + ~Memory() override = default; +}; +} +} +} +} +#endif diff --git a/include/LIEF/asm/aarch64/operands/PCRelative.hpp b/include/LIEF/asm/aarch64/operands/PCRelative.hpp new file mode 100644 index 0000000000..8ce5d50084 --- /dev/null +++ b/include/LIEF/asm/aarch64/operands/PCRelative.hpp @@ -0,0 +1,47 @@ +/* Copyright 2022 - 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIEF_ASM_AARCH64_OPERAND_PCREL_H +#define LIEF_ASM_AARCH64_OPERAND_PCREL_H +#include "LIEF/asm/aarch64/Operand.hpp" + +namespace LIEF { +namespace assembly { +namespace aarch64 { +namespace operands { + +/// This class represents a PC-relative operand. +/// +/// ```text +/// ldr x0, #8 +/// | +/// v +/// PC Relative operand +/// ``` +class LIEF_API PCRelative : public Operand { + public: + using Operand::Operand; + + /// The effective value that is relative to the current `pc` register + int64_t value() const; + + static bool classof(const Operand* op); + + ~PCRelative() override = default; +}; +} +} +} +} +#endif diff --git a/include/LIEF/asm/aarch64/operands/Register.hpp b/include/LIEF/asm/aarch64/operands/Register.hpp new file mode 100644 index 0000000000..8ead274a52 --- /dev/null +++ b/include/LIEF/asm/aarch64/operands/Register.hpp @@ -0,0 +1,67 @@ +/* Copyright 2022 - 2024 R. Thomas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIEF_ASM_AARCH64_OPERAND_REG_H +#define LIEF_ASM_AARCH64_OPERAND_REG_H + +#include "LIEF/asm/aarch64/Operand.hpp" +#include "LIEF/asm/aarch64/registers.hpp" + +namespace LIEF { +namespace assembly { +namespace aarch64 { +namespace operands { + +/// This class represents a register operand. +/// +/// ```text +/// mrs x0, TPIDR_EL0 +/// | | +/// +------+ +-------+ +/// | | +/// v v +/// REG SYSREG +/// ``` +class LIEF_API Register : public Operand { + public: + using Operand::Operand; + + struct reg_t { + /// Enum type used to discriminate the anonymous union + enum class TYPE { + NONE = 0, + /// The union holds a sysreg attribute + SYSREG, + /// The union holds the reg attribute + REG, + }; + + union { + REG reg = REG::NoRegister; + SYSREG sysreg; + }; + TYPE type = TYPE::NONE; + }; + + /// The effective register as either: a REG or a SYSREG + reg_t value() const; + + static bool classof(const Operand* op); + ~Register() override = default; +}; +} +} +} +} +#endif diff --git a/src/asm/asm.cpp b/src/asm/asm.cpp index 2d3bb476f8..dcb4a9350f 100644 --- a/src/asm/asm.cpp +++ b/src/asm/asm.cpp @@ -16,8 +16,14 @@ #include "LIEF/asm/Engine.hpp" #include "LIEF/asm/aarch64/Instruction.hpp" +#include "LIEF/asm/aarch64/Operand.hpp" #include "LIEF/asm/aarch64/registers.hpp" +#include "LIEF/asm/aarch64/operands/Immediate.hpp" +#include "LIEF/asm/aarch64/operands/Register.hpp" +#include "LIEF/asm/aarch64/operands/PCRelative.hpp" +#include "LIEF/asm/aarch64/operands/Memory.hpp" + #include "LIEF/asm/x86/Instruction.hpp" #include "LIEF/asm/x86/Operand.hpp" #include "LIEF/asm/x86/registers.hpp" @@ -96,6 +102,11 @@ class Operand {}; class OperandIt {}; } +namespace aarch64::details { +class Operand {}; +class OperandIt {}; +} + // ---------------------------------------------------------------------------- // asm/Instruction.hpp // ---------------------------------------------------------------------------- @@ -236,6 +247,10 @@ aarch64::OPCODE aarch64::Instruction::opcode() const { return aarch64::OPCODE::INSTRUCTION_LIST_END; } +aarch64::Instruction::operands_it aarch64::Instruction::operands() const { + return make_empty_iterator(); +} + bool aarch64::Instruction::classof(const assembly::Instruction*) { return false; } @@ -436,5 +451,99 @@ bool x86::operands::Immediate::classof(const Operand*) { return false; } int64_t x86::operands::Immediate::value() const { return 0; } + +// ---------------------------------------------------------------------------- +// asm/aarch64/Operand.hpp +// ---------------------------------------------------------------------------- +aarch64::Operand::Iterator::Iterator() : + impl_(nullptr) +{} + +aarch64::Operand::Iterator::Iterator(std::unique_ptr) : + impl_(nullptr) +{} + +aarch64::Operand::Iterator::Iterator(const Iterator&) : + impl_(nullptr) +{} + +aarch64::Operand::Iterator& aarch64::Operand::Iterator::operator=(const Iterator&) { + return *this; +} + +aarch64::Operand::Iterator::Iterator(Iterator&&) noexcept = default; +aarch64::Operand::Iterator& aarch64::Operand::Iterator::operator=(Iterator&&) noexcept = default; + +aarch64::Operand::Iterator& aarch64::Operand::Iterator::operator++() { + return *this; +} + +std::unique_ptr aarch64::Operand::Iterator::operator*() const { + return nullptr; +} + +bool aarch64::operator==(const aarch64::Operand::Iterator&, const aarch64::Operand::Iterator&) { + return true; +} + +aarch64::Operand::Iterator::~Iterator() = default; + +aarch64::Operand::~Operand() = default; +aarch64::Operand::Operand(std::unique_ptr/*impl*/) : + impl_(nullptr) +{} + +std::string aarch64::Operand::to_string() const { + return ""; +} + +std::unique_ptr aarch64::Operand::create(std::unique_ptr /*impl*/) { + return nullptr; +} + +// ---------------------------------------------------------------------------- +// asm/aarch64/Memory.hpp +// ---------------------------------------------------------------------------- +bool aarch64::operands::Memory::classof(const Operand*) { return false; } + +aarch64::REG aarch64::operands::Memory::base() const { + return REG::NoRegister; +} + +aarch64::operands::Memory::offset_t aarch64::operands::Memory::offset() const { + return {}; +} + +aarch64::operands::Memory::shift_info_t aarch64::operands::Memory::shift() const { + return {}; +} + +// ---------------------------------------------------------------------------- +// asm/aarch64/PCRelative.hpp +// ---------------------------------------------------------------------------- +bool aarch64::operands::PCRelative::classof(const Operand*) { return false; } + +int64_t aarch64::operands::PCRelative::value() const { + return 0; +} + +// ---------------------------------------------------------------------------- +// asm/aarch64/Register.hpp +// ---------------------------------------------------------------------------- +bool aarch64::operands::Register::classof(const Operand*) { return false; } + +aarch64::operands::Register::reg_t aarch64::operands::Register::value() const { + return {}; +} + +// ---------------------------------------------------------------------------- +// asm/aarch64/Immediate.hpp +// ---------------------------------------------------------------------------- +bool aarch64::operands::Immediate::classof(const Operand*) { return false; } + +int64_t aarch64::operands::Immediate::value() const { + return 0; +} + } } diff --git a/tests/assembly/test_arm64.py b/tests/assembly/test_arm64.py index f59616cd0c..122c846db2 100644 --- a/tests/assembly/test_arm64.py +++ b/tests/assembly/test_arm64.py @@ -58,3 +58,7 @@ def test_elf_arm64(): assert instructions[0].to_string() == "0x56c19b4: paciasp" assert isinstance(instructions[0], lief.assembly.aarch64.Instruction) assert instructions[0].opcode == lief.assembly.aarch64.OPCODE.PACIASP + +def test_arm64_operands(): + elf = lief.ELF.parse(get_sample("ELF/libmonochrome-arm64.so")) + instructions = list(elf.disassemble(elf.get_section((".text").virtual_address)))