From e41b68ac47b062c041611766d2c57ad7de665061 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Mon, 29 Dec 2025 13:37:31 +0100 Subject: [PATCH 01/20] runtime: should be renamed to `translator` or `dynasm` --- .DS_Store | Bin 0 -> 6148 bytes harm-runtime/src/labels.rs | 84 +++++++++++++++++++++++++++++++++++- harm-runtime/src/lib.rs | 1 + harm-runtime/src/runtime.rs | 82 +++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 .DS_Store create mode 100644 harm-runtime/src/runtime.rs diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..693900ac1a55f1952f09dd1eb7e71c7c1733a0c5 GIT binary patch literal 6148 zcmeHKy-EW?5S~qZb81%IEVa!_;zmWlcc6r*M4t2WC^z%C#CYB)d{aQDyLF265-dw+w@3zy{ z#uwIJD_M2s;uT@ELLeT{xJin*(}byLFt77VL%udF~Hvk7md-g7#q}|4s`ko04zYQ1>+(QfUZT) zVr&o=h%%u-6RPYLLz!^sUF+vrj18J_Qugwp?9R&GP?X*s>$}>WlxI+SVL%wjGLSdZ zBG><&$@l+kkj#VuVc<_OpbCD~uOKB`TgQ^)TI-_iqp`4GY;Y8UfiA`H, + labels: HashMap, + next_id: usize, +} + +impl LabelRegistry { + #[inline] + pub fn new() -> Self { + Self::default() + } + + #[inline] + pub fn forward_named_label(&mut self, name: &str) -> LabelId { + if let Some(id) = self.named_labels.get(name) { + *id + } else { + let id = self.next_label(); + self.named_labels.insert(name.to_string(), id); + self.labels.insert(id, LabelInfo::Forward); + id + } + } + + #[inline] + pub fn forward_label(&mut self) -> LabelId { + let id = self.next_label(); + self.labels.insert(id, LabelInfo::Forward); + id + } + + pub fn define_label(&mut self, label_id: LabelId, offset: Offset) { + if let Some(info) = self.labels.get_mut(&label_id) { + match info { + LabelInfo::Forward => { + *info = LabelInfo::Offset(offset); + } + LabelInfo::Offset(_) => { + todo!("Label {label_id:?} is already defined"); + } + } + } else { + panic!("Label {label_id:?} is not registered"); + } + } + + #[inline] + pub fn define_named_label(&mut self, name: &str, offset: Offset) -> LabelId { + if let Some(id) = self.named_labels.get(name).copied() { + self.labels.insert(id, LabelInfo::Offset(offset)); + id + } else { + let id = self.next_label(); + self.named_labels.insert(name.to_string(), id); + self.labels.insert(id, LabelInfo::Offset(offset)); + id + } + } + + pub fn name_label(&mut self, id: LabelId, name: &str) { + if self.labels.contains_key(&id) { + self.named_labels.insert(name.to_string(), id); + } else { + panic!("Label {id:?} is not registered"); + } + } + + #[inline] + pub fn label_info(&self, id: LabelId) -> Option<&LabelInfo> { + self.labels.get(&id) + } + + fn next_label(&mut self) -> LabelId { + let id = LabelId(self.next_id); + self.next_id += 1; + id + } +} diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs index cdcc60b..cf7cc34 100644 --- a/harm-runtime/src/lib.rs +++ b/harm-runtime/src/lib.rs @@ -4,3 +4,4 @@ */ pub mod labels; +pub mod runtime; diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs new file mode 100644 index 0000000..ddad018 --- /dev/null +++ b/harm-runtime/src/runtime.rs @@ -0,0 +1,82 @@ +/* Copyright (C) 2025 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use std::collections::HashMap; + +use crate::labels::LabelRegistry; +use harm::InstructionCode; +use harm::instructions::InstructionSeq; +use harm::reloc::{LabelId, Offset, Rel64}; + +// N.N. we keep here internal relocation type, and convert it to external on serialization. +#[derive(Default)] +pub struct Assembler { + label_manager: LabelRegistry, + memory: Vec, + relocations: HashMap, +} + +impl Assembler { + #[inline] + pub fn new() -> Self { + <_>::default() + } + + pub fn build(self) { + todo!() + } + + #[inline] + pub fn with_capacity(cap: usize) -> Self { + Self { + label_manager: LabelRegistry::new(), + memory: Vec::with_capacity(cap), + relocations: HashMap::new(), + } + } + + pub fn insert(&mut self, s: InstSeq) { + // TODO align by instruction alignment? + for (inst, rel) in s.encode() { + let pos = self.memory.len(); + if let Some(rel) = rel { + self.relocations.insert(pos, rel); + } + self.memory.push(inst); + } + } + + // TODO the label have to be aligned. Except for data labels?.. + // For an instruction, it is alwasy 4 bytes, but for data it can be different, from 1 to N bytes. + pub fn current_label(&mut self) -> LabelId { + let pos = self.memory.len(); + + // TODO can be fused + let label_id = self.label_manager.forward_label(); + self.label_manager.define_label(label_id, pos as Offset); + + label_id + } + + pub fn current_named_label(&mut self, name: &str) -> LabelId { + let id = self.new_forward_named_label(name); + self.assign_forward_label(id); + id + } + + pub fn new_forward_label(&mut self) -> LabelId { + self.label_manager.forward_label() + } + + pub fn new_forward_named_label(&mut self, name: &str) -> LabelId { + self.label_manager.forward_named_label(name) + } + + pub fn assign_forward_label(&mut self, label_id: LabelId) { + let pos = self.memory.len(); + + self.label_manager.define_label(label_id, pos as Offset); + } +} From 4dd3f4d5c7fb8f3653b2ef4499a178edf07286df Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sun, 18 Jan 2026 19:12:08 +0100 Subject: [PATCH 02/20] runtime: memory abstraction --- harm-runtime/Cargo.toml | 7 +- harm-runtime/src/lib.rs | 1 + harm-runtime/src/memory.rs | 72 ++++++++++ harm-runtime/src/memory/memmap2.rs | 220 +++++++++++++++++++++++++++++ harm-runtime/src/runtime.rs | 5 +- 5 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 harm-runtime/src/memory.rs create mode 100644 harm-runtime/src/memory/memmap2.rs diff --git a/harm-runtime/Cargo.toml b/harm-runtime/Cargo.toml index aa4a5a1..027e327 100644 --- a/harm-runtime/Cargo.toml +++ b/harm-runtime/Cargo.toml @@ -12,4 +12,9 @@ publish = false [dependencies] harm = { workspace = true } -memmap2 = "0.9.9" +memmap2 = { version = "0.9.9", optional = true } +thiserror = "2.0.18" + +[features] +default = ["memmap2"] +memmap2 = ["dep:memmap2"] diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs index cf7cc34..dfb11f2 100644 --- a/harm-runtime/src/lib.rs +++ b/harm-runtime/src/lib.rs @@ -4,4 +4,5 @@ */ pub mod labels; +pub mod memory; pub mod runtime; diff --git a/harm-runtime/src/memory.rs b/harm-runtime/src/memory.rs new file mode 100644 index 0000000..7c014b4 --- /dev/null +++ b/harm-runtime/src/memory.rs @@ -0,0 +1,72 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +#[cfg(feature = "memmap2")] +mod memmap2; + +#[cfg(feature = "memmap2")] +pub use self::memmap2::{Mmap2Buffer, Mmap2FixedMemory}; + +pub trait Memory { + type ExtendError; + type FixedMemoryError; + + /// Current writing position. + fn pos(&self) -> usize; + + /// If memory has fixed capacity, return it. + /// + /// A `Vec` is not considered a memory of fixed capacity because it can grow indefinitely. + fn capacity(&self) -> Option; + + /// Append data to the memory. Should fail when it reaches memory's capacity. + fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError>; + + /// Transform into fixed-location memory. + fn into_fixed_memory(self) -> Result; + + /// Align position. + fn align(&mut self, alignment: usize) -> Result<(), Self::ExtendError> { + let pos = self.pos(); + let remn = pos % alignment; + if remn != 0 { + self.try_extend(core::iter::repeat(0).take(alignment - remn))?; + } + Ok(()) + } +} + +/// Memory with fixed location that can be transformed to an executable one after relocations are applied. +pub trait FixedMemory: AsMut<[u8]> { + type ExecutableMemory; + type ExecutableMemoryError; + + fn into_executable_memory(self) -> Result; +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "memmap2")] + #[test] + fn test_align() { + use super::*; + + let mut data = Vec::::new(); + + Memory::align(&mut data, 8); + assert!(data.is_empty()); + + data.push(1); + Memory::align(&mut data, 8); + assert_eq!(data.len(), 8); + + Memory::align(&mut data, 8); + assert_eq!(data.len(), 8); + + data.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7]); + Memory::align(&mut data, 8); + assert_eq!(data.len(), 16); + } +} diff --git a/harm-runtime/src/memory/memmap2.rs b/harm-runtime/src/memory/memmap2.rs new file mode 100644 index 0000000..fde4709 --- /dev/null +++ b/harm-runtime/src/memory/memmap2.rs @@ -0,0 +1,220 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use std::convert::Infallible; + +use super::{FixedMemory, Memory}; + +#[derive(thiserror::Error, Debug)] +pub enum Map2BufferError { + #[error("buffer overflow: {0}")] + Overflow(usize), +} + +pub struct Mmap2Buffer { + pos: usize, + memory: memmap2::MmapMut, +} + +impl Mmap2Buffer { + #[inline] + pub fn new(mmap_mut: memmap2::MmapMut) -> Self { + Self { + pos: 0, + memory: mmap_mut, + } + } + + #[inline] + pub fn allocate(length: usize) -> std::io::Result { + let mmap_mut = memmap2::MmapMut::map_anon(length)?; + Ok(Self::new(mmap_mut)) + } +} + +impl Memory for Mmap2Buffer { + type ExtendError = Map2BufferError; + + type FixedMemoryError = Infallible; + + #[inline] + fn pos(&self) -> usize { + self.pos + } + + #[inline] + fn capacity(&self) -> Option { + Some(self.memory.len()) + } + + #[inline] + fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError> { + for byte in bytes { + if self.pos >= self.memory.len() { + return Err(Map2BufferError::Overflow(self.pos)); + } + + self.memory[self.pos] = byte; + self.pos += 1; + } + Ok(()) + } + + #[inline] + fn into_fixed_memory(self) -> Result { + Ok(Mmap2FixedMemory::new(self.memory)) + } +} + +pub struct Mmap2FixedMemory(memmap2::MmapMut); + +impl Mmap2FixedMemory { + #[inline] + pub fn new(mmap_mut: memmap2::MmapMut) -> Self { + Self(mmap_mut) + } + + #[inline] + pub fn allocate(length: usize) -> std::io::Result { + let mmap_mut = memmap2::MmapMut::map_anon(length)?; + Ok(Self(mmap_mut)) + } +} + +impl AsRef<[u8]> for Mmap2FixedMemory { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for Mmap2FixedMemory { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +impl FixedMemory for Mmap2FixedMemory { + // TODO a wrapper type? + type ExecutableMemory = memmap2::Mmap; + + type ExecutableMemoryError = std::io::Error; + + #[inline] + fn into_executable_memory(self) -> Result { + self.0.make_exec() + } +} + +impl Memory for Vec { + type ExtendError = Infallible; + type FixedMemoryError = std::io::Error; + + #[inline] + fn pos(&self) -> usize { + self.len() + } + + #[inline] + fn capacity(&self) -> Option { + None + } + + #[inline] + fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError> { + self.extend(bytes); + Ok(()) + } + + #[inline] + fn into_fixed_memory(self) -> Result { + let mut mem = Mmap2FixedMemory::allocate(self.len())?; + mem.as_mut().copy_from_slice(&self); + Ok(mem) + } +} + +impl Memory for &mut Vec { + type ExtendError = Infallible; + type FixedMemoryError = std::io::Error; + + #[inline] + fn pos(&self) -> usize { + self.len() + } + + #[inline] + fn capacity(&self) -> Option { + None + } + + #[inline] + fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError> { + self.extend(bytes); + Ok(()) + } + + #[inline] + fn into_fixed_memory(self) -> Result { + let mut mem = Mmap2FixedMemory::allocate(self.len())?; + // The memmap2 spec doesn't say that the length can be different... + mem.as_mut().copy_from_slice(self); + Ok(mem) + } +} + +#[cfg(test)] +mod tests { + use harm::instructions::InstructionSeq; + + use super::*; + + #[test] + #[cfg(target_arch = "aarch64")] + fn test_mmap_execute() { + let mut buf = Mmap2Buffer::allocate(4).expect("mmap failed, system problem"); + buf.try_extend(harm::instructions::control::ret().bytes()) + .unwrap(); + + let mem = buf.into_fixed_memory().unwrap(); + // Doing relocations... + + let exec = mem.into_executable_memory().unwrap(); + + unsafe { + let func: unsafe extern "C" fn() = std::mem::transmute(exec.as_ptr()); + func(); + } + } + + #[test] + fn test_try_extend_1023() { + let mut buf = Mmap2Buffer::allocate(1024).expect("mmap failed, system problem"); + buf.try_extend(vec![1; 1023].into_iter()).unwrap(); + } + + #[test] + fn test_try_extend_1024() { + let mut buf = Mmap2Buffer::allocate(1024).expect("mmap failed, system problem"); + buf.try_extend(vec![1; 1024].into_iter()).unwrap(); + } + + #[test] + fn test_try_extend_1025() { + let mut buf = Mmap2Buffer::allocate(1024).expect("mmap failed, system problem"); + assert!(buf.try_extend(vec![1; 1025].into_iter()).is_err()); + } + + #[test] + fn test_try_extend_pair() { + let mut buf = Mmap2Buffer::allocate(1024).expect("mmap failed, system problem"); + buf.try_extend(vec![1; 512].into_iter()).unwrap(); + buf.try_extend(vec![1; 512].into_iter()).unwrap(); + assert_eq!(buf.pos(), 1024); + + assert!(buf.try_extend(vec![1; 1].into_iter()).is_err()); + } +} diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index ddad018..825fc39 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -6,7 +6,6 @@ use std::collections::HashMap; use crate::labels::LabelRegistry; -use harm::InstructionCode; use harm::instructions::InstructionSeq; use harm::reloc::{LabelId, Offset, Rel64}; @@ -14,7 +13,7 @@ use harm::reloc::{LabelId, Offset, Rel64}; #[derive(Default)] pub struct Assembler { label_manager: LabelRegistry, - memory: Vec, + memory: Vec, relocations: HashMap, } @@ -41,10 +40,10 @@ impl Assembler { // TODO align by instruction alignment? for (inst, rel) in s.encode() { let pos = self.memory.len(); + self.memory.extend(inst.0); if let Some(rel) = rel { self.relocations.insert(pos, rel); } - self.memory.push(inst); } } From af94ee90e2f48b1c885466ae1731d1d8f581a076 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 18 Apr 2026 08:26:24 +0200 Subject: [PATCH 03/20] `ForeignMemory` --- harm-runtime/Cargo.toml | 3 +- harm-runtime/src/lib.rs | 4 + harm-runtime/src/memory.rs | 7 +- harm-runtime/src/memory/foreign_memory.rs | 91 +++++++++++++++++++++++ 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 harm-runtime/src/memory/foreign_memory.rs diff --git a/harm-runtime/Cargo.toml b/harm-runtime/Cargo.toml index 027e327..400172e 100644 --- a/harm-runtime/Cargo.toml +++ b/harm-runtime/Cargo.toml @@ -16,5 +16,6 @@ memmap2 = { version = "0.9.9", optional = true } thiserror = "2.0.18" [features] -default = ["memmap2"] +default = ["memmap2", "alloc"] memmap2 = ["dep:memmap2"] +alloc = [] diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs index dfb11f2..755c633 100644 --- a/harm-runtime/src/lib.rs +++ b/harm-runtime/src/lib.rs @@ -3,6 +3,10 @@ * This document is licensed under the BSD 3-clause license. */ + +#[cfg(feature = "alloc")] +extern crate alloc; + pub mod labels; pub mod memory; pub mod runtime; diff --git a/harm-runtime/src/memory.rs b/harm-runtime/src/memory.rs index 7c014b4..0264c99 100644 --- a/harm-runtime/src/memory.rs +++ b/harm-runtime/src/memory.rs @@ -9,7 +9,12 @@ mod memmap2; #[cfg(feature = "memmap2")] pub use self::memmap2::{Mmap2Buffer, Mmap2FixedMemory}; -pub trait Memory { +#[cfg(feature = "alloc")] +pub mod foreign_memory; +#[cfg(feature = "alloc")] +pub use self::foreign_memory::ForeignMemoryBuffer; + +pub trait Memory { type ExtendError; type FixedMemoryError; diff --git a/harm-runtime/src/memory/foreign_memory.rs b/harm-runtime/src/memory/foreign_memory.rs new file mode 100644 index 0000000..52028a3 --- /dev/null +++ b/harm-runtime/src/memory/foreign_memory.rs @@ -0,0 +1,91 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use harm::reloc::Addr; + +use super::Memory; + +/// Memory that is not intended to be executed immediately, but stored or transferred. +pub struct ForeignMemoryBuffer { + mem: alloc::vec::Vec, + base_addr: Addr, +} + +impl<'mem> ForeignMemoryBuffer { + pub fn new(base_addr: Addr) -> Self { + Self { + mem: Vec::new(), + base_addr, + } + } + + pub fn with_capacity(base_addr: Addr, capacity: usize) -> Self { + Self { + mem: Vec::with_capacity(capacity), + base_addr, + } + } + + pub fn base_addr(&self) -> Addr { + self.base_addr + } +} + +impl AsRef<[u8]> for ForeignMemoryBuffer { + fn as_ref(&self) -> &[u8] { + &self.mem + } +} + +impl Memory for ForeignMemoryBuffer { + type ExtendError = core::convert::Infallible; + type FixedMemoryError = core::convert::Infallible; + + fn pos(&self) -> usize { + self.mem.len() + } + + fn capacity(&self) -> Option { + None // unrestricted + } + + fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError> { + self.mem.extend(bytes); + Ok(()) + } + + fn into_fixed_memory(self) -> Result { + Ok(ForeignMemory { + mem: self.mem, + base_addr: self.base_addr, + }) + } +} + +pub struct ForeignMemory { + mem: alloc::vec::Vec, + base_addr: Addr, +} + +impl ForeignMemory { + pub fn base_addr(&self) -> Addr { + self.base_addr + } + pub fn into_inner(self) -> (Addr, alloc::vec::Vec) { + (self.base_addr, self.mem) + } +} + +impl AsRef<[u8]> for ForeignMemory { + fn as_ref(&self) -> &[u8] { + &self.mem + } +} + +impl AsMut<[u8]> for ForeignMemory { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.mem + } +} From 3f4807432358096410386e6e066a1327278d96ef Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sun, 1 Feb 2026 21:47:17 +0100 Subject: [PATCH 04/20] runtime: memory with labels and applied relocations --- harm-runtime/src/builder.rs | 89 +++++++++++++++++++++++ harm-runtime/src/labels.rs | 12 +-- harm-runtime/src/lib.rs | 3 +- harm-runtime/src/memory/foreign_memory.rs | 16 ++-- harm-runtime/src/runtime.rs | 6 +- harm/src/reloc.rs | 57 ++++++++++++--- harm/src/reloc/addr.rs | 30 ++++---- harm/src/reloc/control.rs | 18 ++--- harm/src/reloc/data.rs | 18 ++--- harm/src/reloc/movs.rs | 30 ++++---- 10 files changed, 204 insertions(+), 75 deletions(-) create mode 100644 harm-runtime/src/builder.rs diff --git a/harm-runtime/src/builder.rs b/harm-runtime/src/builder.rs new file mode 100644 index 0000000..49e0fdb --- /dev/null +++ b/harm-runtime/src/builder.rs @@ -0,0 +1,89 @@ +/* Copyright (C) 2026 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use std::collections::HashMap; + +use harm::reloc::{Rel64, Rel64Error}; + +#[derive(Debug, thiserror::Error)] +pub enum BuilderError { + #[error("Relocation offset out of range: {0}")] + BadRelocationOffset(usize), + #[error("Address overflow: base {0}, offset {1}")] + AddressOverflow(u64, usize), + #[error("Relocation error: {nested:?} at offset {offset}")] + Relocation { + nested: Rel64Error, + offset: usize, + }, +} + +/// Do static relocations: recalculate labels and applies relocations, producing memory ready for execution. +/// +/// Please note that real memory location may be different from base address: it allows to build at some buffer +/// and then move data to real position later. +pub struct Builder<'mem> { + mem: &'mem mut [u8], // real memory + base: u64, // virtual base, on ARM system usually matches with `mem` start +} + +impl<'mem> Builder<'mem> { + pub fn new(mem: &'mem mut [u8], base: u64) -> Self { + Self { mem, base } + } + + pub fn build( + self, + _labels: impl Iterator, + relocations: impl Iterator, + ) -> Result, BuilderError> { + // Recalculate labels. + // todo!(); + + // Apply relocations to the self.mem. + for (offset, rel) in relocations { + let label_addr = todo!(); // Get the address of the label for this relocation. + rel.apply(self.base, label_addr, self.mem, offset) + .map_err(|nested| BuilderError::Relocation { nested, offset })?; + } + + Ok(Default::default()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_good_offset() { + let mut mem = vec![0u8; 4]; + let builder = Builder::new(&mut mem, 0); + let relocations = [(0, Rel64::None)]; + let res = builder.build([].into_iter(), relocations.into_iter()); + + assert!(matches!(res, Ok(_))); + } + + #[test] + fn test_bad_offset() { + let mut mem = vec![0u8; 4]; + let builder = Builder::new(&mut mem, 0); + let relocations = [(1, Rel64::None)]; + let res = builder.build([].into_iter(), relocations.into_iter()); + + assert!(matches!(res, Err(BuilderError::BadRelocationOffset(_)))); + } + + #[test] + fn test_bad_offset_max() { + let mut mem = vec![0u8; 4]; + let builder = Builder::new(&mut mem, 0); + let relocations = [(usize::MAX, Rel64::None)]; + let res = builder.build([].into_iter(), relocations.into_iter()); + + assert!(matches!(res, Err(BuilderError::BadRelocationOffset(_)))); + } +} diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs index 4abe91d..a51320f 100644 --- a/harm-runtime/src/labels.rs +++ b/harm-runtime/src/labels.rs @@ -5,13 +5,13 @@ use std::collections::HashMap; -use harm::reloc::{LabelId, Offset}; +use harm::reloc::{LabelId, Offset64}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum LabelInfo { Forward, // TODO segment - Offset(Offset), + Offset(Offset64), } #[derive(Debug, Default)] @@ -46,7 +46,7 @@ impl LabelRegistry { id } - pub fn define_label(&mut self, label_id: LabelId, offset: Offset) { + pub fn define_label(&mut self, label_id: LabelId, offset: Offset64) { if let Some(info) = self.labels.get_mut(&label_id) { match info { LabelInfo::Forward => { @@ -57,12 +57,12 @@ impl LabelRegistry { } } } else { - panic!("Label {label_id:?} is not registered"); + todo!("Label {label_id:?} is not registered"); } } #[inline] - pub fn define_named_label(&mut self, name: &str, offset: Offset) -> LabelId { + pub fn define_named_label(&mut self, name: &str, offset: Offset64) -> LabelId { if let Some(id) = self.named_labels.get(name).copied() { self.labels.insert(id, LabelInfo::Offset(offset)); id @@ -78,7 +78,7 @@ impl LabelRegistry { if self.labels.contains_key(&id) { self.named_labels.insert(name.to_string(), id); } else { - panic!("Label {id:?} is not registered"); + todo!("Label {id:?} is not registered"); } } diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs index 755c633..2551769 100644 --- a/harm-runtime/src/lib.rs +++ b/harm-runtime/src/lib.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Ivan Boldyrev +/* Copyright (C) 2026 Ivan Boldyrev * * This document is licensed under the BSD 3-clause license. */ @@ -7,6 +7,7 @@ #[cfg(feature = "alloc")] extern crate alloc; +pub mod builder; pub mod labels; pub mod memory; pub mod runtime; diff --git a/harm-runtime/src/memory/foreign_memory.rs b/harm-runtime/src/memory/foreign_memory.rs index 52028a3..80b5cdf 100644 --- a/harm-runtime/src/memory/foreign_memory.rs +++ b/harm-runtime/src/memory/foreign_memory.rs @@ -3,32 +3,32 @@ * This document is licensed under the BSD 3-clause license. */ -use harm::reloc::Addr; +use harm::reloc::Addr64; use super::Memory; /// Memory that is not intended to be executed immediately, but stored or transferred. pub struct ForeignMemoryBuffer { mem: alloc::vec::Vec, - base_addr: Addr, + base_addr: Addr64, } impl<'mem> ForeignMemoryBuffer { - pub fn new(base_addr: Addr) -> Self { + pub fn new(base_addr: Addr64) -> Self { Self { mem: Vec::new(), base_addr, } } - pub fn with_capacity(base_addr: Addr, capacity: usize) -> Self { + pub fn with_capacity(base_addr: Addr64, capacity: usize) -> Self { Self { mem: Vec::with_capacity(capacity), base_addr, } } - pub fn base_addr(&self) -> Addr { + pub fn base_addr(&self) -> Addr64 { self.base_addr } } @@ -66,14 +66,14 @@ impl Memory for ForeignMemoryBuffer { pub struct ForeignMemory { mem: alloc::vec::Vec, - base_addr: Addr, + base_addr: Addr64, } impl ForeignMemory { - pub fn base_addr(&self) -> Addr { + pub fn base_addr(&self) -> Addr64 { self.base_addr } - pub fn into_inner(self) -> (Addr, alloc::vec::Vec) { + pub fn into_inner(self) -> (Addr64, alloc::vec::Vec) { (self.base_addr, self.mem) } } diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index 825fc39..5169aed 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use crate::labels::LabelRegistry; use harm::instructions::InstructionSeq; -use harm::reloc::{LabelId, Offset, Rel64}; +use harm::reloc::{LabelId, Offset64, Rel64}; // N.N. we keep here internal relocation type, and convert it to external on serialization. #[derive(Default)] @@ -54,7 +54,7 @@ impl Assembler { // TODO can be fused let label_id = self.label_manager.forward_label(); - self.label_manager.define_label(label_id, pos as Offset); + self.label_manager.define_label(label_id, pos as Offset64); label_id } @@ -76,6 +76,6 @@ impl Assembler { pub fn assign_forward_label(&mut self, label_id: LabelId) { let pos = self.memory.len(); - self.label_manager.define_label(label_id, pos as Offset); + self.label_manager.define_label(label_id, pos as Offset64); } } diff --git a/harm/src/reloc.rs b/harm/src/reloc.rs index f2a8c9b..60c6e06 100644 --- a/harm/src/reloc.rs +++ b/harm/src/reloc.rs @@ -24,8 +24,6 @@ mod control; mod data; mod movs; -use ::core::fmt; - use aarchmrs_types::InstructionCode; pub use self::addr::*; @@ -40,14 +38,44 @@ use crate::bits::BitError; #[repr(transparent)] pub struct LabelId(pub usize); -pub type Offset = i64; +// Every offset in an instruction does fit in i32. +// But "[relocation] is sign-extended to 64 bits". +pub type Offset64 = i64; + +pub type Addr64 = u64; + +#[derive(Debug)] +pub enum RelocationError { + CheckedOverflow, + OffsetOverflow, +} + +use ::core::fmt; + +impl fmt::Display for RelocationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use RelocationError::*; + + match self { + CheckedOverflow => write!(f, "Checked relocation overflow"), + OffsetOverflow => write!(f, "Offset overflow"), + } + } +} -pub type Addr = u64; +impl ::core::error::Error for RelocationError {} #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct LabelRef { pub id: LabelId, - pub addend: Offset, + pub addend: Offset64, +} + +// TODO refactor in a separate commit +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Relocation { + pub rel: Rel, + pub label: LabelRef, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -251,6 +279,17 @@ impl Rel64 { pub const fn movw_prel_g3(label: LabelRef) -> Self { Self::new(Rel64Tag::MOVW_PREL_G3, label) } + + #[inline] + pub fn apply( + self, + base: Addr64, + value: Addr64, + memory: &mut [u8], + offset: usize, + ) -> Result<(), Rel64Error> { + self.rel.apply(base, value, memory, offset) + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -348,8 +387,8 @@ impl Rel64Tag { /// location for flexibility: the memory can be moved to real destination later). pub fn apply( self, - base: Addr, - value: Addr, + base: Addr64, + value: Addr64, memory: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -421,7 +460,7 @@ fn get_bytes_mut( /// A function for calculating PC-relative relocation signed difference S - P, where P is `base + offset` (checked) /// and S is `value`. -pub fn calc_delta(base: u64, value: u64, offset: usize) -> Result { +pub fn calc_delta(base: u64, value: u64, offset: usize) -> Result { let offset64 = offset .try_into() .map_err(|_e| Rel64Error::InvalidOffset { offset })?; @@ -437,7 +476,7 @@ pub fn calc_delta(base: u64, value: u64, offset: usize) -> Result Result { +pub fn calc_page_offset(base: u64, value: u64, offset: usize) -> Result { const PAGE_MASK: u64 = !0xfff; let offset64 = offset .try_into() diff --git a/harm/src/reloc/addr.rs b/harm/src/reloc/addr.rs index 7b5f151..490bd56 100644 --- a/harm/src/reloc/addr.rs +++ b/harm/src/reloc/addr.rs @@ -5,7 +5,7 @@ use aarchmrs_types::InstructionCode; -use super::{Addr, Rel64Error, cond_br19_reloc}; +use super::{Addr64, Rel64Error, cond_br19_reloc}; use crate::instructions::dpimm::{AdrOffset, AdrpOffset}; use crate::instructions::ldst::{ScaledOffset16, ScaledOffset32, ScaledOffset64, ScaledOffset128}; use crate::reloc::{calc_delta, calc_page_offset}; @@ -41,8 +41,8 @@ const LDST128_IMM12_WIDTH: u32 = 12u32; #[inline] pub fn ld_prel_lo19_reloc( - base: Addr, - symbol: Addr, + base: Addr64, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -51,8 +51,8 @@ pub fn ld_prel_lo19_reloc( #[inline] pub fn adr_prel_lo21_reloc( - base: Addr, - symbol: Addr, + base: Addr64, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -67,8 +67,8 @@ pub fn adr_prel_lo21_reloc( #[inline] pub fn adr_prel_pg_hi21_reloc( - base: Addr, - symbol: Addr, + base: Addr64, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -85,8 +85,8 @@ pub fn adr_prel_pg_hi21_reloc( #[inline] pub fn adr_prel_pg_hi21_nc_reloc( - base: Addr, - symbol: Addr, + base: Addr64, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -102,7 +102,7 @@ pub fn adr_prel_pg_hi21_nc_reloc( #[inline] pub fn add_abs_lo12_nc_reloc( - symbol: Addr, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -128,7 +128,7 @@ fn patch_adr_adrp(mem: &mut [u8; 4], checked_value: u32) { #[inline] pub fn ldst8_abs_lo12_nc_reloc( - symbol: Addr, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -137,7 +137,7 @@ pub fn ldst8_abs_lo12_nc_reloc( #[inline] pub fn ldst16_abs_lo12_nc_reloc( - symbol: Addr, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -152,7 +152,7 @@ pub fn ldst16_abs_lo12_nc_reloc( #[inline] pub fn ldst32_abs_lo12_nc_reloc( - symbol: Addr, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -167,7 +167,7 @@ pub fn ldst32_abs_lo12_nc_reloc( #[inline] pub fn ldst64_abs_lo12_nc_reloc( - symbol: Addr, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -182,7 +182,7 @@ pub fn ldst64_abs_lo12_nc_reloc( #[inline] pub fn ldst128_abs_lo12_nc_reloc( - symbol: Addr, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { diff --git a/harm/src/reloc/control.rs b/harm/src/reloc/control.rs index b9b36d8..af21c6e 100644 --- a/harm/src/reloc/control.rs +++ b/harm/src/reloc/control.rs @@ -3,7 +3,7 @@ * This document is licensed under the BSD 3-clause license. */ -use super::{Addr, Rel64Error, calc_delta, patch_instruction_bits}; +use super::{Addr64, Rel64Error, calc_delta, patch_instruction_bits}; use crate::instructions::control::{BranchCondOffset, BranchOffset, TestBranchOffset}; use crate::reloc::get_bytes_mut; @@ -18,8 +18,8 @@ const COND_BR_IMM19_WIDTH: u32 = 19u32; #[inline] pub fn jump26_reloc( - base: Addr, - target: Addr, + base: Addr64, + target: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -32,8 +32,8 @@ pub fn jump26_reloc( #[inline] pub fn call26_reloc( - base: Addr, - target: Addr, + base: Addr64, + target: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -42,8 +42,8 @@ pub fn call26_reloc( #[inline] pub fn tst_br14_reloc( - base: Addr, - target: Addr, + base: Addr64, + target: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -56,8 +56,8 @@ pub fn tst_br14_reloc( #[inline] pub fn cond_br19_reloc( - base: Addr, - target: Addr, + base: Addr64, + target: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { diff --git a/harm/src/reloc/data.rs b/harm/src/reloc/data.rs index 319f31e..68892a2 100644 --- a/harm/src/reloc/data.rs +++ b/harm/src/reloc/data.rs @@ -3,7 +3,7 @@ * This document is licensed under the BSD 3-clause license. */ -use super::{Addr, Rel64Error, calc_delta, get_bytes_mut}; +use super::{Addr64, Rel64Error, calc_delta, get_bytes_mut}; #[inline] pub fn abs64_reloc(value: u64, mem: &mut [u8], offset: usize) -> Result<(), Rel64Error> { @@ -30,8 +30,8 @@ pub fn abs16_reloc(value: i64, mem: &mut [u8], offset: usize) -> Result<(), Rel6 #[inline] pub fn prel64_reloc( - base: Addr, - symbol: Addr, + base: Addr64, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -43,8 +43,8 @@ pub fn prel64_reloc( #[inline] pub fn prel32_reloc( - base: Addr, - symbol: Addr, + base: Addr64, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -58,8 +58,8 @@ pub fn prel32_reloc( #[inline] pub fn prel16_reloc( - base: Addr, - symbol: Addr, + base: Addr64, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -73,8 +73,8 @@ pub fn prel16_reloc( #[inline] pub fn plt32_reloc( - base: Addr, - symbol: Addr, + base: Addr64, + symbol: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { diff --git a/harm/src/reloc/movs.rs b/harm/src/reloc/movs.rs index a7b7bb0..f3164ea 100644 --- a/harm/src/reloc/movs.rs +++ b/harm/src/reloc/movs.rs @@ -4,7 +4,7 @@ */ use aarchmrs_types::InstructionCode; -use super::{Addr, Rel64Error, calc_delta, get_bytes_mut}; +use super::{Addr64, Rel64Error, calc_delta, get_bytes_mut}; use crate::bits::SBitValue; const MOV_OPCODE_OFFSET: u32 = 29; @@ -92,8 +92,8 @@ pub fn movw_sabs_g2_reloc(value: i64, mem: &mut [u8], offset: usize) -> Result<( #[inline] pub fn movw_prel_g0_reloc( - base: Addr, - value: Addr, + base: Addr64, + value: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -103,8 +103,8 @@ pub fn movw_prel_g0_reloc( #[inline] pub fn movw_prel_g0_nc_reloc( - base: Addr, - value: Addr, + base: Addr64, + value: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -114,8 +114,8 @@ pub fn movw_prel_g0_nc_reloc( #[inline] pub fn movw_prel_g1_reloc( - base: Addr, - value: Addr, + base: Addr64, + value: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -125,8 +125,8 @@ pub fn movw_prel_g1_reloc( #[inline] pub fn movw_prel_g1_nc_reloc( - base: Addr, - value: Addr, + base: Addr64, + value: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -136,8 +136,8 @@ pub fn movw_prel_g1_nc_reloc( #[inline] pub fn movw_prel_g2_reloc( - base: Addr, - value: Addr, + base: Addr64, + value: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -147,8 +147,8 @@ pub fn movw_prel_g2_reloc( #[inline] pub fn movw_prel_g2_nc_reloc( - base: Addr, - value: Addr, + base: Addr64, + value: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { @@ -158,8 +158,8 @@ pub fn movw_prel_g2_nc_reloc( #[inline] pub fn movw_prel_g3_reloc( - base: Addr, - value: Addr, + base: Addr64, + value: Addr64, mem: &mut [u8], offset: usize, ) -> Result<(), Rel64Error> { From bff64be6af457c8c485c600013c770dabd2f6047 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Tue, 3 Feb 2026 22:24:04 +0100 Subject: [PATCH 05/20] runtime: Assembler::build --- harm-runtime/src/runtime.rs | 47 +++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index 5169aed..cf9d3f0 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -5,52 +5,59 @@ use std::collections::HashMap; +use crate::builder::Builder; use crate::labels::LabelRegistry; +use crate::memory::{FixedMemory, Memory}; use harm::instructions::InstructionSeq; use harm::reloc::{LabelId, Offset64, Rel64}; -// N.N. we keep here internal relocation type, and convert it to external on serialization. +// N.B. we keep here internal relocation type, and convert it to external on serialization. #[derive(Default)] -pub struct Assembler { +pub struct Assembler> { label_manager: LabelRegistry, - memory: Vec, + memory: Mem, relocations: HashMap, + phantom: core::marker::PhantomData, } -impl Assembler { +impl> Assembler { #[inline] - pub fn new() -> Self { - <_>::default() - } - - pub fn build(self) { - todo!() - } - - #[inline] - pub fn with_capacity(cap: usize) -> Self { + pub fn new(mem: Mem) -> Self { Self { label_manager: LabelRegistry::new(), - memory: Vec::with_capacity(cap), + memory: mem, relocations: HashMap::new(), + phantom: core::marker::PhantomData, } } - pub fn insert(&mut self, s: InstSeq) { + pub fn build(self) -> Result { + let mut fixed_memory = self.memory.into_fixed_memory()?; + let base = fixed_memory.as_mut().as_ptr() as u64; + let builder = Builder::new(fixed_memory.as_mut(), base); + builder.build( + self.label_manager.defined_labels().map(|(name, offset)| (name, offset as i64)), + self.relocations.into_iter(), + )?; + fixed_memory.into_executable_memory() + } + + pub fn append(&mut self, s: InstSeq) -> Result<(), Mem::ExtendError> { // TODO align by instruction alignment? for (inst, rel) in s.encode() { - let pos = self.memory.len(); - self.memory.extend(inst.0); + let pos = self.memory.pos(); + self.memory.try_extend(inst.0.iter().cloned())?; if let Some(rel) = rel { self.relocations.insert(pos, rel); } } + Ok(()) } // TODO the label have to be aligned. Except for data labels?.. // For an instruction, it is alwasy 4 bytes, but for data it can be different, from 1 to N bytes. pub fn current_label(&mut self) -> LabelId { - let pos = self.memory.len(); + let pos = self.memory.pos(); // TODO can be fused let label_id = self.label_manager.forward_label(); @@ -74,7 +81,7 @@ impl Assembler { } pub fn assign_forward_label(&mut self, label_id: LabelId) { - let pos = self.memory.len(); + let pos = self.memory.pos(); self.label_manager.define_label(label_id, pos as Offset64); } From 616b0ade79a9b5472ef3b09a23bfc73857fecaef Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 6 Feb 2026 19:53:23 +0100 Subject: [PATCH 06/20] runtime: Fixed memory label manager From f800e54fe063cb5c80134fd93e112482e0d54e4c Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 7 Feb 2026 21:10:14 +0100 Subject: [PATCH 07/20] runtime: no_std From ccca0ec3e7d7148fd1fea594823c5a7d3a44d091 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 7 Feb 2026 21:15:32 +0100 Subject: [PATCH 08/20] Review fixes --- harm-runtime/Cargo.toml | 3 +++ harm-runtime/src/builder.rs | 29 ++++++++++++++++++++--------- harm-runtime/src/labels.rs | 2 +- harm-runtime/src/memory/memmap2.rs | 18 +++++++++++++----- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/harm-runtime/Cargo.toml b/harm-runtime/Cargo.toml index 400172e..266ace8 100644 --- a/harm-runtime/Cargo.toml +++ b/harm-runtime/Cargo.toml @@ -19,3 +19,6 @@ thiserror = "2.0.18" default = ["memmap2", "alloc"] memmap2 = ["dep:memmap2"] alloc = [] + +[dev-dependencies] +clear-cache = "0.1.3" diff --git a/harm-runtime/src/builder.rs b/harm-runtime/src/builder.rs index 49e0fdb..7b18765 100644 --- a/harm-runtime/src/builder.rs +++ b/harm-runtime/src/builder.rs @@ -14,10 +14,7 @@ pub enum BuilderError { #[error("Address overflow: base {0}, offset {1}")] AddressOverflow(u64, usize), #[error("Relocation error: {nested:?} at offset {offset}")] - Relocation { - nested: Rel64Error, - offset: usize, - }, + Relocation { nested: Rel64Error, offset: usize }, } /// Do static relocations: recalculate labels and applies relocations, producing memory ready for execution. @@ -36,7 +33,7 @@ impl<'mem> Builder<'mem> { pub fn build( self, - _labels: impl Iterator, + _label_defs: impl Iterator, relocations: impl Iterator, ) -> Result, BuilderError> { // Recalculate labels. @@ -55,23 +52,33 @@ impl<'mem> Builder<'mem> { #[cfg(test)] mod tests { + use harm::reloc::{LabelId, LabelRef, Rel64Tag}; + use super::*; #[test] fn test_good_offset() { let mut mem = vec![0u8; 4]; let builder = Builder::new(&mut mem, 0); - let relocations = [(0, Rel64::None)]; + let label_ref = LabelRef { + id: LabelId(0), + addend: 0, + }; + let relocations = [(0, Rel64::new(Rel64Tag::NONE, label_ref))]; let res = builder.build([].into_iter(), relocations.into_iter()); - assert!(matches!(res, Ok(_))); + assert!(res.is_ok()); } #[test] fn test_bad_offset() { let mut mem = vec![0u8; 4]; let builder = Builder::new(&mut mem, 0); - let relocations = [(1, Rel64::None)]; + let label_ref = LabelRef { + id: LabelId(0), + addend: 0, + }; + let relocations = [(1, Rel64::new(Rel64Tag::NONE, label_ref))]; let res = builder.build([].into_iter(), relocations.into_iter()); assert!(matches!(res, Err(BuilderError::BadRelocationOffset(_)))); @@ -81,7 +88,11 @@ mod tests { fn test_bad_offset_max() { let mut mem = vec![0u8; 4]; let builder = Builder::new(&mut mem, 0); - let relocations = [(usize::MAX, Rel64::None)]; + let label_ref = LabelRef { + id: LabelId(0), + addend: 0, + }; + let relocations = [(usize::MAX, Rel64::new(Rel64Tag::NONE, label_ref))]; let res = builder.build([].into_iter(), relocations.into_iter()); assert!(matches!(res, Err(BuilderError::BadRelocationOffset(_)))); diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs index a51320f..d8efcf8 100644 --- a/harm-runtime/src/labels.rs +++ b/harm-runtime/src/labels.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; -use harm::reloc::{LabelId, Offset64}; +use harm::reloc::{Addr64, LabelId, Offset64}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum LabelInfo { diff --git a/harm-runtime/src/memory/memmap2.rs b/harm-runtime/src/memory/memmap2.rs index fde4709..3994385 100644 --- a/harm-runtime/src/memory/memmap2.rs +++ b/harm-runtime/src/memory/memmap2.rs @@ -175,19 +175,27 @@ mod tests { #[test] #[cfg(target_arch = "aarch64")] fn test_mmap_execute() { - let mut buf = Mmap2Buffer::allocate(4).expect("mmap failed, system problem"); - buf.try_extend(harm::instructions::control::ret().bytes()) - .unwrap(); + use harm::{ + instructions::{arith::add::add, control::ret}, + register::Reg64::*, + }; + let mut buf = Mmap2Buffer::allocate(8).expect("mmap failed, system problem"); + buf.try_extend(add(X0, X0, X1).bytes()).unwrap(); + buf.try_extend(ret().bytes()).unwrap(); let mem = buf.into_fixed_memory().unwrap(); // Doing relocations... let exec = mem.into_executable_memory().unwrap(); + let res; unsafe { - let func: unsafe extern "C" fn() = std::mem::transmute(exec.as_ptr()); - func(); + clear_cache::clear_cache(exec.as_ptr(), exec.as_ptr().add(exec.len())); + + let func: unsafe extern "C" fn(i64, i64) -> i64 = std::mem::transmute(exec.as_ptr()); + res = func(1, 2); } + assert_eq!(res, 3); } #[test] From 1b07e08ad70bf68b4791e86ea3291b60dc5e2c0f Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Tue, 14 Apr 2026 00:04:37 +0200 Subject: [PATCH 09/20] Misc --- .DS_Store | Bin 6148 -> 6148 bytes harm-runtime/src/builder.rs | 6 +++--- harm-runtime/src/memory.rs | 3 +++ harm-runtime/src/memory/foreign_memory.rs | 1 + harm-runtime/src/memory/memmap2.rs | 7 +++++++ 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.DS_Store b/.DS_Store index 693900ac1a55f1952f09dd1eb7e71c7c1733a0c5..2f998ebe9b329b53263f362e99475a20b3336017 100644 GIT binary patch delta 103 zcmZoMXfc=|#>B!ku~2NHo+2af#(>?7ixpUy7kYnTE1 CwHI3e delta 68 zcmZoMXfc=|#>B)qu~2NHo+2a9#(>?7j69opSc(}pFJVn)+Ss7Vw3(fQp9837vmnQJ W=E?jbjvNd?z{tSBvN=Lz4Kn~&3J { mem: &'mem mut [u8], // real memory - base: u64, // virtual base, on ARM system usually matches with `mem` start + base: Addr64, // virtual base, on ARM system usually matches with `mem` start } impl<'mem> Builder<'mem> { - pub fn new(mem: &'mem mut [u8], base: u64) -> Self { + pub fn new(mem: &'mem mut [u8], base: Addr64) -> Self { Self { mem, base } } diff --git a/harm-runtime/src/memory.rs b/harm-runtime/src/memory.rs index 0264c99..e2233f1 100644 --- a/harm-runtime/src/memory.rs +++ b/harm-runtime/src/memory.rs @@ -6,6 +6,8 @@ #[cfg(feature = "memmap2")] mod memmap2; +use harm::reloc::Addr64; + #[cfg(feature = "memmap2")] pub use self::memmap2::{Mmap2Buffer, Mmap2FixedMemory}; @@ -48,6 +50,7 @@ pub trait FixedMemory: AsMut<[u8]> { type ExecutableMemory; type ExecutableMemoryError; + fn get_base_address(&self) -> Addr64; fn into_executable_memory(self) -> Result; } diff --git a/harm-runtime/src/memory/foreign_memory.rs b/harm-runtime/src/memory/foreign_memory.rs index 80b5cdf..39bad74 100644 --- a/harm-runtime/src/memory/foreign_memory.rs +++ b/harm-runtime/src/memory/foreign_memory.rs @@ -73,6 +73,7 @@ impl ForeignMemory { pub fn base_addr(&self) -> Addr64 { self.base_addr } + pub fn into_inner(self) -> (Addr64, alloc::vec::Vec) { (self.base_addr, self.mem) } diff --git a/harm-runtime/src/memory/memmap2.rs b/harm-runtime/src/memory/memmap2.rs index 3994385..3ec5871 100644 --- a/harm-runtime/src/memory/memmap2.rs +++ b/harm-runtime/src/memory/memmap2.rs @@ -5,6 +5,8 @@ use std::convert::Infallible; +use harm::reloc::Addr64; + use super::{FixedMemory, Memory}; #[derive(thiserror::Error, Debug)] @@ -103,6 +105,11 @@ impl FixedMemory for Mmap2FixedMemory { type ExecutableMemoryError = std::io::Error; + // TODO makes sense only on AArch64. + fn get_base_address(&self) -> Addr64 { + self.0.as_ptr() as Addr64 + } + #[inline] fn into_executable_memory(self) -> Result { self.0.make_exec() From 7b446daab7576158c3abb549ba97fecc3373dcc5 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 24 Apr 2026 09:57:12 +0200 Subject: [PATCH 10/20] Refactor memory buffer traits --- harm-runtime/src/memory.rs | 26 ++++--- harm-runtime/src/memory/foreign_memory.rs | 11 +-- harm-runtime/src/memory/memmap2.rs | 85 +++++++++-------------- harm-runtime/src/runtime.rs | 31 ++++++--- 4 files changed, 78 insertions(+), 75 deletions(-) diff --git a/harm-runtime/src/memory.rs b/harm-runtime/src/memory.rs index e2233f1..1403a97 100644 --- a/harm-runtime/src/memory.rs +++ b/harm-runtime/src/memory.rs @@ -9,16 +9,15 @@ mod memmap2; use harm::reloc::Addr64; #[cfg(feature = "memmap2")] -pub use self::memmap2::{Mmap2Buffer, Mmap2FixedMemory}; +pub use self::memmap2::{MmapBuffer, MmapPositionedMemory}; #[cfg(feature = "alloc")] pub mod foreign_memory; #[cfg(feature = "alloc")] pub use self::foreign_memory::ForeignMemoryBuffer; -pub trait Memory { +pub trait Memory { type ExtendError; - type FixedMemoryError; /// Current writing position. fn pos(&self) -> usize; @@ -31,9 +30,6 @@ pub trait Memory { /// Append data to the memory. Should fail when it reaches memory's capacity. fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError>; - /// Transform into fixed-location memory. - fn into_fixed_memory(self) -> Result; - /// Align position. fn align(&mut self, alignment: usize) -> Result<(), Self::ExtendError> { let pos = self.pos(); @@ -45,12 +41,24 @@ pub trait Memory { } } +pub trait IntoPositionedMemory { + type PositionedMemoryError; + + /// Transform into positioned memory. + fn into_positioned_memory(self) -> Result; +} + /// Memory with fixed location that can be transformed to an executable one after relocations are applied. -pub trait FixedMemory: AsMut<[u8]> { +pub trait PositionedMemory: AsMut<[u8]> { type ExecutableMemory; - type ExecutableMemoryError; fn get_base_address(&self) -> Addr64; +} + +pub trait IntoExecutableMemory { + type ExecutableMemory; + type ExecutableMemoryError; + fn into_executable_memory(self) -> Result; } @@ -61,7 +69,7 @@ mod tests { fn test_align() { use super::*; - let mut data = Vec::::new(); + let mut data = &mut Vec::::new(); Memory::align(&mut data, 8); assert!(data.is_empty()); diff --git a/harm-runtime/src/memory/foreign_memory.rs b/harm-runtime/src/memory/foreign_memory.rs index 39bad74..a306b8a 100644 --- a/harm-runtime/src/memory/foreign_memory.rs +++ b/harm-runtime/src/memory/foreign_memory.rs @@ -5,7 +5,7 @@ use harm::reloc::Addr64; -use super::Memory; +use super::{IntoPositionedMemory, Memory}; /// Memory that is not intended to be executed immediately, but stored or transferred. pub struct ForeignMemoryBuffer { @@ -39,9 +39,8 @@ impl AsRef<[u8]> for ForeignMemoryBuffer { } } -impl Memory for ForeignMemoryBuffer { +impl Memory for ForeignMemoryBuffer { type ExtendError = core::convert::Infallible; - type FixedMemoryError = core::convert::Infallible; fn pos(&self) -> usize { self.mem.len() @@ -55,8 +54,12 @@ impl Memory for ForeignMemoryBuffer { self.mem.extend(bytes); Ok(()) } +} + +impl IntoPositionedMemory for ForeignMemoryBuffer { + type PositionedMemoryError = core::convert::Infallible; - fn into_fixed_memory(self) -> Result { + fn into_positioned_memory(self) -> Result { Ok(ForeignMemory { mem: self.mem, base_addr: self.base_addr, diff --git a/harm-runtime/src/memory/memmap2.rs b/harm-runtime/src/memory/memmap2.rs index 3ec5871..17e7f6b 100644 --- a/harm-runtime/src/memory/memmap2.rs +++ b/harm-runtime/src/memory/memmap2.rs @@ -7,24 +7,25 @@ use std::convert::Infallible; use harm::reloc::Addr64; -use super::{FixedMemory, Memory}; +use super::{IntoPositionedMemory, Memory, PositionedMemory}; #[derive(thiserror::Error, Debug)] -pub enum Map2BufferError { +pub enum MapBufferError { #[error("buffer overflow: {0}")] Overflow(usize), } -pub struct Mmap2Buffer { +pub struct MmapBuffer { pos: usize, memory: memmap2::MmapMut, } -impl Mmap2Buffer { +impl MmapBuffer { #[inline] pub fn new(mmap_mut: memmap2::MmapMut) -> Self { Self { pos: 0, + // N.B. We assume that memory is aligned. memory: mmap_mut, } } @@ -36,10 +37,8 @@ impl Mmap2Buffer { } } -impl Memory for Mmap2Buffer { - type ExtendError = Map2BufferError; - - type FixedMemoryError = Infallible; +impl Memory for MmapBuffer { + type ExtendError = MapBufferError; #[inline] fn pos(&self) -> usize { @@ -55,7 +54,7 @@ impl Memory for Mmap2Buffer { fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError> { for byte in bytes { if self.pos >= self.memory.len() { - return Err(Map2BufferError::Overflow(self.pos)); + return Err(MapBufferError::Overflow(self.pos)); } self.memory[self.pos] = byte; @@ -63,16 +62,21 @@ impl Memory for Mmap2Buffer { } Ok(()) } +} + +impl IntoPositionedMemory for MmapBuffer { + type PositionedMemoryError = Infallible; #[inline] - fn into_fixed_memory(self) -> Result { - Ok(Mmap2FixedMemory::new(self.memory)) + fn into_positioned_memory(self) -> Result { + Ok(MmapPositionedMemory::new(self.memory)) } + } -pub struct Mmap2FixedMemory(memmap2::MmapMut); +pub struct MmapPositionedMemory(memmap2::MmapMut); -impl Mmap2FixedMemory { +impl MmapPositionedMemory { #[inline] pub fn new(mmap_mut: memmap2::MmapMut) -> Self { Self(mmap_mut) @@ -85,21 +89,21 @@ impl Mmap2FixedMemory { } } -impl AsRef<[u8]> for Mmap2FixedMemory { +impl AsRef<[u8]> for MmapPositionedMemory { #[inline] fn as_ref(&self) -> &[u8] { &self.0 } } -impl AsMut<[u8]> for Mmap2FixedMemory { +impl AsMut<[u8]> for MmapPositionedMemory { #[inline] fn as_mut(&mut self) -> &mut [u8] { &mut self.0 } } -impl FixedMemory for Mmap2FixedMemory { +impl PositionedMemory for MmapPositionedMemory { // TODO a wrapper type? type ExecutableMemory = memmap2::Mmap; @@ -109,16 +113,15 @@ impl FixedMemory for Mmap2FixedMemory { fn get_base_address(&self) -> Addr64 { self.0.as_ptr() as Addr64 } - + #[inline] fn into_executable_memory(self) -> Result { self.0.make_exec() } } -impl Memory for Vec { +impl Memory for &mut Vec { type ExtendError = Infallible; - type FixedMemoryError = std::io::Error; #[inline] fn pos(&self) -> usize { @@ -135,38 +138,14 @@ impl Memory for Vec { self.extend(bytes); Ok(()) } - - #[inline] - fn into_fixed_memory(self) -> Result { - let mut mem = Mmap2FixedMemory::allocate(self.len())?; - mem.as_mut().copy_from_slice(&self); - Ok(mem) - } } -impl Memory for &mut Vec { - type ExtendError = Infallible; - type FixedMemoryError = std::io::Error; - - #[inline] - fn pos(&self) -> usize { - self.len() - } - - #[inline] - fn capacity(&self) -> Option { - None - } - - #[inline] - fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError> { - self.extend(bytes); - Ok(()) - } +impl IntoPositionedMemory for &mut Vec { + type PositionedMemoryError = std::io::Error; #[inline] - fn into_fixed_memory(self) -> Result { - let mut mem = Mmap2FixedMemory::allocate(self.len())?; + fn into_positioned_memory(self) -> Result { + let mut mem = MmapPositionedMemory::allocate(self.len())?; // The memmap2 spec doesn't say that the length can be different... mem.as_mut().copy_from_slice(self); Ok(mem) @@ -186,11 +165,11 @@ mod tests { instructions::{arith::add::add, control::ret}, register::Reg64::*, }; - let mut buf = Mmap2Buffer::allocate(8).expect("mmap failed, system problem"); + let mut buf = MmapBuffer::allocate(8).expect("mmap failed, system problem"); buf.try_extend(add(X0, X0, X1).bytes()).unwrap(); buf.try_extend(ret().bytes()).unwrap(); - let mem = buf.into_fixed_memory().unwrap(); + let mem = buf.into_positioned_memory().unwrap(); // Doing relocations... let exec = mem.into_executable_memory().unwrap(); @@ -207,25 +186,25 @@ mod tests { #[test] fn test_try_extend_1023() { - let mut buf = Mmap2Buffer::allocate(1024).expect("mmap failed, system problem"); + let mut buf = MmapBuffer::allocate(1024).expect("mmap failed, system problem"); buf.try_extend(vec![1; 1023].into_iter()).unwrap(); } #[test] fn test_try_extend_1024() { - let mut buf = Mmap2Buffer::allocate(1024).expect("mmap failed, system problem"); + let mut buf = MmapBuffer::allocate(1024).expect("mmap failed, system problem"); buf.try_extend(vec![1; 1024].into_iter()).unwrap(); } #[test] fn test_try_extend_1025() { - let mut buf = Mmap2Buffer::allocate(1024).expect("mmap failed, system problem"); + let mut buf = MmapBuffer::allocate(1024).expect("mmap failed, system problem"); assert!(buf.try_extend(vec![1; 1025].into_iter()).is_err()); } #[test] fn test_try_extend_pair() { - let mut buf = Mmap2Buffer::allocate(1024).expect("mmap failed, system problem"); + let mut buf = MmapBuffer::allocate(1024).expect("mmap failed, system problem"); buf.try_extend(vec![1; 512].into_iter()).unwrap(); buf.try_extend(vec![1; 512].into_iter()).unwrap(); assert_eq!(buf.pos(), 1024); diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index cf9d3f0..de521d4 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -7,38 +7,51 @@ use std::collections::HashMap; use crate::builder::Builder; use crate::labels::LabelRegistry; -use crate::memory::{FixedMemory, Memory}; +use crate::memory::{IntoExecutableMemory, IntoPositionedMemory, Memory, PositionedMemory}; use harm::instructions::InstructionSeq; use harm::reloc::{LabelId, Offset64, Rel64}; // N.B. we keep here internal relocation type, and convert it to external on serialization. #[derive(Default)] -pub struct Assembler> { +pub struct Assembler { label_manager: LabelRegistry, memory: Mem, relocations: HashMap, - phantom: core::marker::PhantomData, } -impl> Assembler { +impl Assembler { #[inline] pub fn new(mem: Mem) -> Self { Self { label_manager: LabelRegistry::new(), memory: mem, relocations: HashMap::new(), - phantom: core::marker::PhantomData, } } - pub fn build(self) -> Result { - let mut fixed_memory = self.memory.into_fixed_memory()?; - let base = fixed_memory.as_mut().as_ptr() as u64; + pub fn build(self) -> Result + where + Mem: IntoPositionedMemory, + FM: PositionedMemory, + { + let mut fixed_memory = self.memory.into_positioned_memory()?; + let base = fixed_memory.get_base_address(); let builder = Builder::new(fixed_memory.as_mut(), base); builder.build( - self.label_manager.defined_labels().map(|(name, offset)| (name, offset as i64)), + self.label_manager + .defined_labels() + .map(|(name, offset)| (name, offset as i64)), self.relocations.into_iter(), )?; + Ok(fixed_memory) + } + + pub fn compile(self) -> Result<::ExecutableMemory, BuilderError> + where + Mem: IntoPositionedMemory, + FM: PositionedMemory + IntoExecutableMemory, + { + let fixed_memory = self.build()?; fixed_memory.into_executable_memory() } From 0dc9bf2a57e2ef7cfca32375613aecc7cb8be81f Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 24 Apr 2026 19:24:26 +0200 Subject: [PATCH 11/20] Misc --- harm-runtime/src/builder.rs | 4 ++-- harm-runtime/src/labels.rs | 9 +++++++ harm-runtime/src/runtime.rs | 48 ++++++++++++++++++++++++++++++++----- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/harm-runtime/src/builder.rs b/harm-runtime/src/builder.rs index 4679c5f..4cf0d5c 100644 --- a/harm-runtime/src/builder.rs +++ b/harm-runtime/src/builder.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; -use harm::reloc::{Addr64, Rel64, Rel64Error}; +use harm::reloc::{Addr64, Offset64, Rel64, Rel64Error}; #[derive(Debug, thiserror::Error)] pub enum BuilderError { @@ -33,7 +33,7 @@ impl<'mem> Builder<'mem> { pub fn build( self, - _label_defs: impl Iterator, + _label_defs: impl Iterator, relocations: impl Iterator, ) -> Result, BuilderError> { // Recalculate labels. diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs index d8efcf8..4b7475b 100644 --- a/harm-runtime/src/labels.rs +++ b/harm-runtime/src/labels.rs @@ -92,4 +92,13 @@ impl LabelRegistry { self.next_id += 1; id } + + pub fn get_defined_labels(&self) -> impl Iterator { + self.named_labels.iter().filter_map(move |(name, id)| { + self.labels.get(id).and_then(|info| match info { + LabelInfo::Offset(offset) => Some((name.as_str(), *offset)), + LabelInfo::Forward => None, + }) + }) + } } diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index de521d4..82255ce 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -5,12 +5,23 @@ use std::collections::HashMap; -use crate::builder::Builder; +use crate::builder::{Builder, BuilderError}; use crate::labels::LabelRegistry; use crate::memory::{IntoExecutableMemory, IntoPositionedMemory, Memory, PositionedMemory}; use harm::instructions::InstructionSeq; use harm::reloc::{LabelId, Offset64, Rel64}; +#[derive(Debug, thiserror::Error)] +pub enum AssemblerError { + #[error("builder error: {0}")] + Builder(#[from] BuilderError), + #[error("memory error: {0}")] + Memory(MemErr), + #[error("positioned memory error: {0}")] + PositionedMemory(PMErr), + #[error("executable memory error: {0}")] + ExecutableMemory(EMErr), +} // N.B. we keep here internal relocation type, and convert it to external on serialization. #[derive(Default)] pub struct Assembler { @@ -29,30 +40,55 @@ impl Assembler { } } - pub fn build(self) -> Result + /// Build the program without making it executable. + pub fn build( + self, + ) -> Result< + FM, + AssemblerError< + Mem::ExtendError, + >::PositionedMemoryError, + E, + >, + > where Mem: IntoPositionedMemory, FM: PositionedMemory, { - let mut fixed_memory = self.memory.into_positioned_memory()?; + let mut fixed_memory = self + .memory + .into_positioned_memory() + .map_err(AssemblerError::PositionedMemory)?; let base = fixed_memory.get_base_address(); let builder = Builder::new(fixed_memory.as_mut(), base); builder.build( self.label_manager - .defined_labels() + .get_defined_labels() .map(|(name, offset)| (name, offset as i64)), self.relocations.into_iter(), )?; Ok(fixed_memory) } - pub fn compile(self) -> Result<::ExecutableMemory, BuilderError> + /// Build the program and make it executable. + pub fn compile( + self, + ) -> Result< + ::ExecutableMemory, + AssemblerError< + Mem::ExtendError, + >::PositionedMemoryError, + ::ExecutableMemoryError, + >, + > where Mem: IntoPositionedMemory, FM: PositionedMemory + IntoExecutableMemory, { let fixed_memory = self.build()?; - fixed_memory.into_executable_memory() + fixed_memory + .into_executable_memory() + .map_err(AssemblerError::ExecutableMemory) } pub fn append(&mut self, s: InstSeq) -> Result<(), Mem::ExtendError> { From 25c2c8d57787ba2ac2b944f6588c319b413836de Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 24 Apr 2026 20:17:21 +0200 Subject: [PATCH 12/20] Fix memmap2 trait definitions --- harm-runtime/src/memory/memmap2.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/harm-runtime/src/memory/memmap2.rs b/harm-runtime/src/memory/memmap2.rs index 17e7f6b..0074953 100644 --- a/harm-runtime/src/memory/memmap2.rs +++ b/harm-runtime/src/memory/memmap2.rs @@ -7,7 +7,7 @@ use std::convert::Infallible; use harm::reloc::Addr64; -use super::{IntoPositionedMemory, Memory, PositionedMemory}; +use super::{IntoExecutableMemory, IntoPositionedMemory, Memory, PositionedMemory}; #[derive(thiserror::Error, Debug)] pub enum MapBufferError { @@ -107,12 +107,15 @@ impl PositionedMemory for MmapPositionedMemory { // TODO a wrapper type? type ExecutableMemory = memmap2::Mmap; - type ExecutableMemoryError = std::io::Error; - // TODO makes sense only on AArch64. fn get_base_address(&self) -> Addr64 { self.0.as_ptr() as Addr64 } +} + +impl IntoExecutableMemory for MmapPositionedMemory { + type ExecutableMemory = memmap2::Mmap; + type ExecutableMemoryError = std::io::Error; #[inline] fn into_executable_memory(self) -> Result { From 7fc2f45e919ecdc78fb52117fc12fa75c98d3621 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 24 Apr 2026 20:17:21 +0200 Subject: [PATCH 13/20] Recalculate labels --- .DS_Store | Bin 6148 -> 6148 bytes harm-runtime/src/builder.rs | 90 ++++++++++++++++++++++++++++-------- harm-runtime/src/labels.rs | 18 +++++--- harm-runtime/src/runtime.rs | 5 +- 4 files changed, 85 insertions(+), 28 deletions(-) diff --git a/.DS_Store b/.DS_Store index 2f998ebe9b329b53263f362e99475a20b3336017..bf55fc4a635c7b6eb333fe3478eff347518195df 100644 GIT binary patch delta 48 zcmZoMXfc@J&&aniU^g=(-)0_`Vn!hrh75*8h9ZXCw3OoHr2PCG#?3XXWlWpdIsWnk E07yv<;Q#;t delta 32 ocmZoMXfc@J&&azmU^g=(?`9sBV#du=Sc{n^Hi&L!=lIJH0HtjT4*&oF diff --git a/harm-runtime/src/builder.rs b/harm-runtime/src/builder.rs index 4cf0d5c..23fde5d 100644 --- a/harm-runtime/src/builder.rs +++ b/harm-runtime/src/builder.rs @@ -5,16 +5,16 @@ use std::collections::HashMap; -use harm::reloc::{Addr64, Offset64, Rel64, Rel64Error}; +use harm::reloc::{Addr64, LabelId, Offset64, Rel64, Rel64Error}; #[derive(Debug, thiserror::Error)] pub enum BuilderError { - #[error("Relocation offset out of range: {0}")] - BadRelocationOffset(usize), #[error("Address overflow: base {0}, offset {1}")] AddressOverflow(u64, usize), #[error("Relocation error: {nested:?} at offset {offset}")] Relocation { nested: Rel64Error, offset: usize }, + #[error("Undefined label: {0:?}")] + UndefinedLabel(LabelId), } /// Do static relocations: recalculate labels and applies relocations, producing memory ready for execution. @@ -23,7 +23,7 @@ pub enum BuilderError { /// and then move data to real position later. pub struct Builder<'mem> { mem: &'mem mut [u8], // real memory - base: Addr64, // virtual base, on ARM system usually matches with `mem` start + base: Addr64, // virtual base, on ARM system usually matches with `mem` start } impl<'mem> Builder<'mem> { @@ -33,20 +33,43 @@ impl<'mem> Builder<'mem> { pub fn build( self, - _label_defs: impl Iterator, + named_labels: impl Iterator, + labels: impl Iterator, relocations: impl Iterator, ) -> Result, BuilderError> { // Recalculate labels. - // todo!(); + let labels: HashMap<_, _> = labels + .map(|(label_id, offset)| { + // TODO is it wrapping? + let addr = self.base.wrapping_add_signed(offset); + (label_id, addr) + }) + .collect(); // Apply relocations to the self.mem. for (offset, rel) in relocations { - let label_addr = todo!(); // Get the address of the label for this relocation. - rel.apply(self.base, label_addr, self.mem, offset) + let label_addr = labels + .get(&rel.label.id) + .copied() + .ok_or_else(|| BuilderError::UndefinedLabel(rel.label.id))?; + // TODO is it wrapping? + let label_ref_addr = label_addr.wrapping_add_signed(rel.label.addend); + rel.apply(self.base, label_ref_addr, self.mem, offset) .map_err(|nested| BuilderError::Relocation { nested, offset })?; } - Ok(Default::default()) + Ok(named_labels + .map(|(name, label_id)| { + let label_addr = labels + .get(&label_id) + .copied() + .ok_or_else(|| BuilderError::UndefinedLabel(label_id))?; + // TODO is it wrapping? + let addend = 0; + let label_ref_addr = label_addr.wrapping_add_signed(addend); + Ok((name, label_ref_addr)) + }) + .collect::>()?) } } @@ -65,9 +88,13 @@ mod tests { addend: 0, }; let relocations = [(0, Rel64::new(Rel64Tag::NONE, label_ref))]; - let res = builder.build([].into_iter(), relocations.into_iter()); + let res = builder.build( + [].into_iter(), + [(LabelId(0), 4)].into_iter(), + relocations.into_iter(), + ); - assert!(res.is_ok()); + assert!(res.is_ok(), "{res:?}"); } #[test] @@ -78,10 +105,24 @@ mod tests { id: LabelId(0), addend: 0, }; - let relocations = [(1, Rel64::new(Rel64Tag::NONE, label_ref))]; - let res = builder.build([].into_iter(), relocations.into_iter()); - - assert!(matches!(res, Err(BuilderError::BadRelocationOffset(_)))); + // N.B. NONE relocation is 0 bytes wide, so 4 doesn't fail. Use 5. + let relocations = [(5, Rel64::new(Rel64Tag::NONE, label_ref))]; + let res = builder.build( + [].into_iter(), + [(LabelId(0), 4)].into_iter(), + relocations.into_iter(), + ); + + assert!( + matches!( + res, + Err(BuilderError::Relocation { + nested: _, + offset: _ + }) + ), + "{res:?}" + ); } #[test] @@ -93,8 +134,21 @@ mod tests { addend: 0, }; let relocations = [(usize::MAX, Rel64::new(Rel64Tag::NONE, label_ref))]; - let res = builder.build([].into_iter(), relocations.into_iter()); - - assert!(matches!(res, Err(BuilderError::BadRelocationOffset(_)))); + let res = builder.build( + [].into_iter(), + [(LabelId(0), 4)].into_iter(), + relocations.into_iter(), + ); + + assert!( + matches!( + res, + Err(BuilderError::Relocation { + nested: _, + offset: _ + }) + ), + "{res:?}" + ); } } diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs index 4b7475b..e736a3a 100644 --- a/harm-runtime/src/labels.rs +++ b/harm-runtime/src/labels.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; -use harm::reloc::{Addr64, LabelId, Offset64}; +use harm::reloc::{LabelId, Offset64}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum LabelInfo { @@ -93,12 +93,16 @@ impl LabelRegistry { id } - pub fn get_defined_labels(&self) -> impl Iterator { - self.named_labels.iter().filter_map(move |(name, id)| { - self.labels.get(id).and_then(|info| match info { - LabelInfo::Offset(offset) => Some((name.as_str(), *offset)), - LabelInfo::Forward => None, - }) + pub fn get_named_labels(&self) -> impl Iterator { + self.named_labels + .iter() + .map(|(name, id)| (name.as_str(), *id)) + } + + pub fn get_defined_labels(&self) -> impl Iterator { + self.labels.iter().filter_map(|(id, info)| match info { + LabelInfo::Offset(offset) => Some((*id, *offset)), + LabelInfo::Forward => None, }) } } diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index 82255ce..6835c7c 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -62,9 +62,8 @@ impl Assembler { let base = fixed_memory.get_base_address(); let builder = Builder::new(fixed_memory.as_mut(), base); builder.build( - self.label_manager - .get_defined_labels() - .map(|(name, offset)| (name, offset as i64)), + self.label_manager.get_named_labels(), + self.label_manager.get_defined_labels(), self.relocations.into_iter(), )?; Ok(fixed_memory) From bf3c148034d01315cbd01df2777d9af6b20a27de Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Tue, 28 Apr 2026 18:19:38 +0200 Subject: [PATCH 14/20] Misc improvements --- harm-runtime/src/builder.rs | 25 +++++++++++++------------ harm-runtime/src/labels.rs | 18 +++++++++++------- harm-runtime/src/runtime.rs | 2 +- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/harm-runtime/src/builder.rs b/harm-runtime/src/builder.rs index 23fde5d..847bf45 100644 --- a/harm-runtime/src/builder.rs +++ b/harm-runtime/src/builder.rs @@ -46,6 +46,17 @@ impl<'mem> Builder<'mem> { }) .collect(); + // Calculate label addresses. + let label_addresses = named_labels + .map(|(name, label_id)| { + let label_addr = labels + .get(&label_id) + .copied() + .ok_or_else(|| BuilderError::UndefinedLabel(label_id))?; + Ok((name, label_addr)) + }) + .collect::>()?; + // Apply relocations to the self.mem. for (offset, rel) in relocations { let label_addr = labels @@ -54,22 +65,12 @@ impl<'mem> Builder<'mem> { .ok_or_else(|| BuilderError::UndefinedLabel(rel.label.id))?; // TODO is it wrapping? let label_ref_addr = label_addr.wrapping_add_signed(rel.label.addend); + rel.apply(self.base, label_ref_addr, self.mem, offset) .map_err(|nested| BuilderError::Relocation { nested, offset })?; } - Ok(named_labels - .map(|(name, label_id)| { - let label_addr = labels - .get(&label_id) - .copied() - .ok_or_else(|| BuilderError::UndefinedLabel(label_id))?; - // TODO is it wrapping? - let addend = 0; - let label_ref_addr = label_addr.wrapping_add_signed(addend); - Ok((name, label_ref_addr)) - }) - .collect::>()?) + Ok(label_addresses) } } diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs index e736a3a..7ee8351 100644 --- a/harm-runtime/src/labels.rs +++ b/harm-runtime/src/labels.rs @@ -28,7 +28,7 @@ impl LabelRegistry { } #[inline] - pub fn forward_named_label(&mut self, name: &str) -> LabelId { + pub fn get_forward_named_label(&mut self, name: &str) -> LabelId { if let Some(id) = self.named_labels.get(name) { *id } else { @@ -46,6 +46,7 @@ impl LabelRegistry { id } + /// Define the label to have its address to be base address plus `offset`. pub fn define_label(&mut self, label_id: LabelId, offset: Offset64) { if let Some(info) = self.labels.get_mut(&label_id) { match info { @@ -61,6 +62,7 @@ impl LabelRegistry { } } + /// Define the label to have its address to be base address plus `offset`. #[inline] pub fn define_named_label(&mut self, name: &str, offset: Offset64) -> LabelId { if let Some(id) = self.named_labels.get(name).copied() { @@ -74,6 +76,7 @@ impl LabelRegistry { } } + /// Turn the label into a named. pub fn name_label(&mut self, id: LabelId, name: &str) { if self.labels.contains_key(&id) { self.named_labels.insert(name.to_string(), id); @@ -82,17 +85,12 @@ impl LabelRegistry { } } + /// Return current label info. #[inline] pub fn label_info(&self, id: LabelId) -> Option<&LabelInfo> { self.labels.get(&id) } - fn next_label(&mut self) -> LabelId { - let id = LabelId(self.next_id); - self.next_id += 1; - id - } - pub fn get_named_labels(&self) -> impl Iterator { self.named_labels .iter() @@ -105,4 +103,10 @@ impl LabelRegistry { LabelInfo::Forward => None, }) } + + fn next_label(&mut self) -> LabelId { + let id = LabelId(self.next_id); + self.next_id += 1; + id + } } diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index 6835c7c..8cb08d9 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -125,7 +125,7 @@ impl Assembler { } pub fn new_forward_named_label(&mut self, name: &str) -> LabelId { - self.label_manager.forward_named_label(name) + self.label_manager.get_forward_named_label(name) } pub fn assign_forward_label(&mut self, label_id: LabelId) { From 102fdc4c71e32b3f82797aa863d77ae2bebf4586 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 1 May 2026 11:09:06 +0200 Subject: [PATCH 15/20] runtime generation test --- harm-runtime/src/memory.rs | 2 - harm-runtime/src/memory/foreign_memory.rs | 8 ++++ harm-runtime/src/memory/memmap2.rs | 3 -- harm-runtime/src/runtime.rs | 46 +++++++++++++++++++++++ 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/harm-runtime/src/memory.rs b/harm-runtime/src/memory.rs index 1403a97..1790ed4 100644 --- a/harm-runtime/src/memory.rs +++ b/harm-runtime/src/memory.rs @@ -50,8 +50,6 @@ pub trait IntoPositionedMemory { /// Memory with fixed location that can be transformed to an executable one after relocations are applied. pub trait PositionedMemory: AsMut<[u8]> { - type ExecutableMemory; - fn get_base_address(&self) -> Addr64; } diff --git a/harm-runtime/src/memory/foreign_memory.rs b/harm-runtime/src/memory/foreign_memory.rs index a306b8a..7fc8aca 100644 --- a/harm-runtime/src/memory/foreign_memory.rs +++ b/harm-runtime/src/memory/foreign_memory.rs @@ -5,6 +5,8 @@ use harm::reloc::Addr64; +use crate::memory::PositionedMemory; + use super::{IntoPositionedMemory, Memory}; /// Memory that is not intended to be executed immediately, but stored or transferred. @@ -93,3 +95,9 @@ impl AsMut<[u8]> for ForeignMemory { &mut self.mem } } + +impl PositionedMemory for ForeignMemory { + fn get_base_address(&self) -> Addr64 { + self.base_addr + } +} \ No newline at end of file diff --git a/harm-runtime/src/memory/memmap2.rs b/harm-runtime/src/memory/memmap2.rs index 0074953..36e54d5 100644 --- a/harm-runtime/src/memory/memmap2.rs +++ b/harm-runtime/src/memory/memmap2.rs @@ -104,9 +104,6 @@ impl AsMut<[u8]> for MmapPositionedMemory { } impl PositionedMemory for MmapPositionedMemory { - // TODO a wrapper type? - type ExecutableMemory = memmap2::Mmap; - // TODO makes sense only on AArch64. fn get_base_address(&self) -> Addr64 { self.0.as_ptr() as Addr64 diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index 8cb08d9..9c643e0 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -134,3 +134,49 @@ impl Assembler { self.label_manager.define_label(label_id, pos as Offset64); } } + +#[cfg(test)] +mod tests { + use harm::{ + instructions::{ + arith::add::add, + control::{b, ret}, + dpimm::movz, + }, + register::Reg64, + reloc::LabelRef, + }; + + use crate::memory::{ForeignMemoryBuffer, foreign_memory::ForeignMemory}; + + use super::*; + + #[test] + fn test_assembler() { + let mem = ForeignMemoryBuffer::new(0x1000); + let mut asm = Assembler::new(mem); + + let finish_label = asm.new_forward_label(); + // TODO constructor + let finish_ref = LabelRef { + id: finish_label, + addend: 0, + }; + + asm.append(add(Reg64::X3, Reg64::X0, Reg64::X1)); + asm.append(b(finish_ref)); + asm.append(movz(Reg64::X3, 0)); + asm.assign_forward_label(finish_label); + asm.append(ret()); + + let fm = asm.build::().unwrap(); + + let mut expected = vec![]; + expected.extend(add(Reg64::X3, Reg64::X0, Reg64::X1).bytes()); + expected.extend(b(8).unwrap().bytes()); + expected.extend(movz(Reg64::X3, 0).bytes()); + expected.extend(ret().bytes()); + + assert_eq!(fm.as_ref(), &*expected); + } +} From 686ad9a529789954a85a4ef7ffa5ebc2d7f30f33 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 1 May 2026 11:09:41 +0200 Subject: [PATCH 16/20] runtime execution test --- harm-runtime/src/builder.rs | 4 +-- harm-runtime/src/runtime.rs | 67 +++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/harm-runtime/src/builder.rs b/harm-runtime/src/builder.rs index 847bf45..92b8307 100644 --- a/harm-runtime/src/builder.rs +++ b/harm-runtime/src/builder.rs @@ -36,7 +36,7 @@ impl<'mem> Builder<'mem> { named_labels: impl Iterator, labels: impl Iterator, relocations: impl Iterator, - ) -> Result, BuilderError> { + ) -> Result, BuilderError> { // Recalculate labels. let labels: HashMap<_, _> = labels .map(|(label_id, offset)| { @@ -53,7 +53,7 @@ impl<'mem> Builder<'mem> { .get(&label_id) .copied() .ok_or_else(|| BuilderError::UndefinedLabel(label_id))?; - Ok((name, label_addr)) + Ok((name.to_owned(), label_addr)) }) .collect::>()?; diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index 9c643e0..0336d9f 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -9,7 +9,7 @@ use crate::builder::{Builder, BuilderError}; use crate::labels::LabelRegistry; use crate::memory::{IntoExecutableMemory, IntoPositionedMemory, Memory, PositionedMemory}; use harm::instructions::InstructionSeq; -use harm::reloc::{LabelId, Offset64, Rel64}; +use harm::reloc::{Addr64, LabelId, Offset64, Rel64}; #[derive(Debug, thiserror::Error)] pub enum AssemblerError { @@ -44,7 +44,7 @@ impl Assembler { pub fn build( self, ) -> Result< - FM, + (FM, HashMap), AssemblerError< Mem::ExtendError, >::PositionedMemoryError, @@ -61,19 +61,22 @@ impl Assembler { .map_err(AssemblerError::PositionedMemory)?; let base = fixed_memory.get_base_address(); let builder = Builder::new(fixed_memory.as_mut(), base); - builder.build( + let labels = builder.build( self.label_manager.get_named_labels(), self.label_manager.get_defined_labels(), self.relocations.into_iter(), )?; - Ok(fixed_memory) + Ok((fixed_memory, labels)) } /// Build the program and make it executable. pub fn compile( self, ) -> Result< - ::ExecutableMemory, + ( + ::ExecutableMemory, + HashMap, + ), AssemblerError< Mem::ExtendError, >::PositionedMemoryError, @@ -84,10 +87,11 @@ impl Assembler { Mem: IntoPositionedMemory, FM: PositionedMemory + IntoExecutableMemory, { - let fixed_memory = self.build()?; - fixed_memory + let (fixed_memory, labels) = self.build()?; + let exec_memory = fixed_memory .into_executable_memory() - .map_err(AssemblerError::ExecutableMemory) + .map_err(AssemblerError::ExecutableMemory)?; + Ok((exec_memory, labels)) } pub fn append(&mut self, s: InstSeq) -> Result<(), Mem::ExtendError> { @@ -152,7 +156,7 @@ mod tests { use super::*; #[test] - fn test_assembler() { + fn test_assembler_build() { let mem = ForeignMemoryBuffer::new(0x1000); let mut asm = Assembler::new(mem); @@ -163,20 +167,55 @@ mod tests { addend: 0, }; - asm.append(add(Reg64::X3, Reg64::X0, Reg64::X1)); + asm.append(add(Reg64::X0, Reg64::X0, Reg64::X1)); asm.append(b(finish_ref)); - asm.append(movz(Reg64::X3, 0)); + asm.append(movz(Reg64::X0, 0)); asm.assign_forward_label(finish_label); asm.append(ret()); - let fm = asm.build::().unwrap(); + let (fm, _) = asm.build::().unwrap(); let mut expected = vec![]; - expected.extend(add(Reg64::X3, Reg64::X0, Reg64::X1).bytes()); + expected.extend(add(Reg64::X0, Reg64::X0, Reg64::X1).bytes()); expected.extend(b(8).unwrap().bytes()); - expected.extend(movz(Reg64::X3, 0).bytes()); + expected.extend(movz(Reg64::X0, 0).bytes()); expected.extend(ret().bytes()); assert_eq!(fm.as_ref(), &*expected); } + + // Execute the code from the `test_assembler_build`. + #[cfg(all(target_arch = "aarch64", feature = "memmap2"))] + #[test] + fn test_assembler_aarch64_execute() { + use crate::memory::MmapBuffer; + + let mem = MmapBuffer::allocate(16).unwrap(); + let mut asm = Assembler::new(mem); + + let finish_label = asm.new_forward_label(); + // TODO constructor + let finish_ref = LabelRef { + id: finish_label, + addend: 0, + }; + + let _start = asm.current_named_label("_start"); + asm.append(add(Reg64::X0, Reg64::X0, Reg64::X1)).unwrap(); + asm.append(b(finish_ref)).unwrap(); + asm.append(movz(Reg64::X0, 0)).unwrap(); + asm.assign_forward_label(finish_label); + asm.append(ret()).unwrap(); + + let (fm, labels) = asm.compile().unwrap(); + let start_addr = labels.get("_start").cloned().unwrap() as usize; + + let res = unsafe { + clear_cache::clear_cache(fm.as_ptr(), fm.as_ptr().add(fm.len())); + + let start: unsafe extern "C" fn(i64, i64) -> i64 = std::mem::transmute(start_addr); + start(42, 8) + }; + assert_eq!(res, 50); + } } From e71b8f836bdb2827ac2f1b453a33e2aae791e287 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 2 May 2026 20:37:35 +0200 Subject: [PATCH 17/20] Fix fmt --- harm-runtime/src/builder.rs | 4 ++-- harm-runtime/src/labels.rs | 2 +- harm-runtime/src/lib.rs | 1 - harm-runtime/src/memory/foreign_memory.rs | 2 +- harm-runtime/src/memory/memmap2.rs | 1 - 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/harm-runtime/src/builder.rs b/harm-runtime/src/builder.rs index 92b8307..0efc8e8 100644 --- a/harm-runtime/src/builder.rs +++ b/harm-runtime/src/builder.rs @@ -56,7 +56,7 @@ impl<'mem> Builder<'mem> { Ok((name.to_owned(), label_addr)) }) .collect::>()?; - + // Apply relocations to the self.mem. for (offset, rel) in relocations { let label_addr = labels @@ -65,7 +65,7 @@ impl<'mem> Builder<'mem> { .ok_or_else(|| BuilderError::UndefinedLabel(rel.label.id))?; // TODO is it wrapping? let label_ref_addr = label_addr.wrapping_add_signed(rel.label.addend); - + rel.apply(self.base, label_ref_addr, self.mem, offset) .map_err(|nested| BuilderError::Relocation { nested, offset })?; } diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs index 7ee8351..f71624c 100644 --- a/harm-runtime/src/labels.rs +++ b/harm-runtime/src/labels.rs @@ -103,7 +103,7 @@ impl LabelRegistry { LabelInfo::Forward => None, }) } - + fn next_label(&mut self) -> LabelId { let id = LabelId(self.next_id); self.next_id += 1; diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs index 2551769..913a824 100644 --- a/harm-runtime/src/lib.rs +++ b/harm-runtime/src/lib.rs @@ -3,7 +3,6 @@ * This document is licensed under the BSD 3-clause license. */ - #[cfg(feature = "alloc")] extern crate alloc; diff --git a/harm-runtime/src/memory/foreign_memory.rs b/harm-runtime/src/memory/foreign_memory.rs index 7fc8aca..bf93ecf 100644 --- a/harm-runtime/src/memory/foreign_memory.rs +++ b/harm-runtime/src/memory/foreign_memory.rs @@ -100,4 +100,4 @@ impl PositionedMemory for ForeignMemory { fn get_base_address(&self) -> Addr64 { self.base_addr } -} \ No newline at end of file +} diff --git a/harm-runtime/src/memory/memmap2.rs b/harm-runtime/src/memory/memmap2.rs index 36e54d5..5078de6 100644 --- a/harm-runtime/src/memory/memmap2.rs +++ b/harm-runtime/src/memory/memmap2.rs @@ -71,7 +71,6 @@ impl IntoPositionedMemory for MmapBuffer { fn into_positioned_memory(self) -> Result { Ok(MmapPositionedMemory::new(self.memory)) } - } pub struct MmapPositionedMemory(memmap2::MmapMut); From be073cf963293b0aa58470a1215ecd389e902a2a Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 9 May 2026 20:44:48 +0200 Subject: [PATCH 18/20] Review fixes --- harm-runtime/src/builder.rs | 20 +++++++++------ harm-runtime/src/lib.rs | 3 --- harm-runtime/src/memory.rs | 30 +++++++++++++++++------ harm-runtime/src/memory/foreign_memory.rs | 8 +++--- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/harm-runtime/src/builder.rs b/harm-runtime/src/builder.rs index 0efc8e8..4f55010 100644 --- a/harm-runtime/src/builder.rs +++ b/harm-runtime/src/builder.rs @@ -9,9 +9,11 @@ use harm::reloc::{Addr64, LabelId, Offset64, Rel64, Rel64Error}; #[derive(Debug, thiserror::Error)] pub enum BuilderError { - #[error("Address overflow: base {0}, offset {1}")] + #[error("Address overflow: base 0x{0:016x}, offset 0x{1:016x}")] AddressOverflow(u64, usize), - #[error("Relocation error: {nested:?} at offset {offset}")] + #[error("Offset overflow: base {0:016x}, offset {1:016x}")] + OffsetOverflow(u64, i64), + #[error("Relocation error: {nested:?} at offset {offset:016x}")] Relocation { nested: Rel64Error, offset: usize }, #[error("Undefined label: {0:?}")] UndefinedLabel(LabelId), @@ -40,11 +42,13 @@ impl<'mem> Builder<'mem> { // Recalculate labels. let labels: HashMap<_, _> = labels .map(|(label_id, offset)| { - // TODO is it wrapping? - let addr = self.base.wrapping_add_signed(offset); - (label_id, addr) + let addr = self + .base + .checked_add_signed(offset) + .ok_or(BuilderError::OffsetOverflow(self.base, offset)); + addr.map(|addr| (label_id, addr)) }) - .collect(); + .collect::>()?; // Calculate label addresses. let label_addresses = named_labels @@ -52,7 +56,7 @@ impl<'mem> Builder<'mem> { let label_addr = labels .get(&label_id) .copied() - .ok_or_else(|| BuilderError::UndefinedLabel(label_id))?; + .ok_or(BuilderError::UndefinedLabel(label_id))?; Ok((name.to_owned(), label_addr)) }) .collect::>()?; @@ -62,7 +66,7 @@ impl<'mem> Builder<'mem> { let label_addr = labels .get(&rel.label.id) .copied() - .ok_or_else(|| BuilderError::UndefinedLabel(rel.label.id))?; + .ok_or(BuilderError::UndefinedLabel(rel.label.id))?; // TODO is it wrapping? let label_ref_addr = label_addr.wrapping_add_signed(rel.label.addend); diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs index 913a824..4af3596 100644 --- a/harm-runtime/src/lib.rs +++ b/harm-runtime/src/lib.rs @@ -3,9 +3,6 @@ * This document is licensed under the BSD 3-clause license. */ -#[cfg(feature = "alloc")] -extern crate alloc; - pub mod builder; pub mod labels; pub mod memory; diff --git a/harm-runtime/src/memory.rs b/harm-runtime/src/memory.rs index 1790ed4..d23367c 100644 --- a/harm-runtime/src/memory.rs +++ b/harm-runtime/src/memory.rs @@ -11,9 +11,7 @@ use harm::reloc::Addr64; #[cfg(feature = "memmap2")] pub use self::memmap2::{MmapBuffer, MmapPositionedMemory}; -#[cfg(feature = "alloc")] pub mod foreign_memory; -#[cfg(feature = "alloc")] pub use self::foreign_memory::ForeignMemoryBuffer; pub trait Memory { @@ -32,10 +30,12 @@ pub trait Memory { /// Align position. fn align(&mut self, alignment: usize) -> Result<(), Self::ExtendError> { - let pos = self.pos(); - let remn = pos % alignment; - if remn != 0 { - self.try_extend(core::iter::repeat(0).take(alignment - remn))?; + if alignment > 1 { + let pos = self.pos(); + let remn = pos % alignment; + if remn != 0 { + self.try_extend(core::iter::repeat_n(0, alignment - remn))?; + } } Ok(()) } @@ -62,7 +62,6 @@ pub trait IntoExecutableMemory { #[cfg(test)] mod tests { - #[cfg(feature = "memmap2")] #[test] fn test_align() { use super::*; @@ -83,4 +82,21 @@ mod tests { Memory::align(&mut data, 8); assert_eq!(data.len(), 16); } + + #[test] + fn test_align_corner_case() { + use super::*; + + let mut data = &mut Vec::::new(); + + Memory::align(&mut data, 0); + assert!(data.is_empty()); + + data.push(1); + Memory::align(&mut data, 0); + assert_eq!(data.len(), 1); + + Memory::align(&mut data, 1); + assert_eq!(data.len(), 1); + } } diff --git a/harm-runtime/src/memory/foreign_memory.rs b/harm-runtime/src/memory/foreign_memory.rs index bf93ecf..e68b5ce 100644 --- a/harm-runtime/src/memory/foreign_memory.rs +++ b/harm-runtime/src/memory/foreign_memory.rs @@ -11,11 +11,11 @@ use super::{IntoPositionedMemory, Memory}; /// Memory that is not intended to be executed immediately, but stored or transferred. pub struct ForeignMemoryBuffer { - mem: alloc::vec::Vec, + mem: std::vec::Vec, base_addr: Addr64, } -impl<'mem> ForeignMemoryBuffer { +impl ForeignMemoryBuffer { pub fn new(base_addr: Addr64) -> Self { Self { mem: Vec::new(), @@ -70,7 +70,7 @@ impl IntoPositionedMemory for ForeignMemoryBuffer { } pub struct ForeignMemory { - mem: alloc::vec::Vec, + mem: std::vec::Vec, base_addr: Addr64, } @@ -79,7 +79,7 @@ impl ForeignMemory { self.base_addr } - pub fn into_inner(self) -> (Addr64, alloc::vec::Vec) { + pub fn into_inner(self) -> (Addr64, std::vec::Vec) { (self.base_addr, self.mem) } } From 7f10680cfe87f1dd09d82a5bd921aa7d74f16115 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sun, 10 May 2026 18:38:09 +0200 Subject: [PATCH 19/20] More review fixes --- harm-runtime/Cargo.toml | 3 +-- harm-runtime/src/memory.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/harm-runtime/Cargo.toml b/harm-runtime/Cargo.toml index 266ace8..6a5dbed 100644 --- a/harm-runtime/Cargo.toml +++ b/harm-runtime/Cargo.toml @@ -16,9 +16,8 @@ memmap2 = { version = "0.9.9", optional = true } thiserror = "2.0.18" [features] -default = ["memmap2", "alloc"] +default = ["memmap2"] memmap2 = ["dep:memmap2"] -alloc = [] [dev-dependencies] clear-cache = "0.1.3" diff --git a/harm-runtime/src/memory.rs b/harm-runtime/src/memory.rs index d23367c..ffce18c 100644 --- a/harm-runtime/src/memory.rs +++ b/harm-runtime/src/memory.rs @@ -68,18 +68,18 @@ mod tests { let mut data = &mut Vec::::new(); - Memory::align(&mut data, 8); + Memory::align(&mut data, 8).unwrap(); assert!(data.is_empty()); data.push(1); - Memory::align(&mut data, 8); + Memory::align(&mut data, 8).unwrap(); assert_eq!(data.len(), 8); - Memory::align(&mut data, 8); + Memory::align(&mut data, 8).unwrap(); assert_eq!(data.len(), 8); data.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7]); - Memory::align(&mut data, 8); + Memory::align(&mut data, 8).unwrap(); assert_eq!(data.len(), 16); } @@ -89,14 +89,14 @@ mod tests { let mut data = &mut Vec::::new(); - Memory::align(&mut data, 0); + Memory::align(&mut data, 0).unwrap(); assert!(data.is_empty()); data.push(1); - Memory::align(&mut data, 0); + Memory::align(&mut data, 0).unwrap(); assert_eq!(data.len(), 1); - Memory::align(&mut data, 1); + Memory::align(&mut data, 1).unwrap(); assert_eq!(data.len(), 1); } } From a41c6665057e5995ffcbb4a89913e67d049f80c9 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sun, 10 May 2026 21:44:24 +0200 Subject: [PATCH 20/20] Atomic `Memory::try_extend` Do not update memory pos if appending fails. --- harm-runtime/src/memory.rs | 9 ++++++--- harm-runtime/src/memory/memmap2.rs | 18 +++++++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/harm-runtime/src/memory.rs b/harm-runtime/src/memory.rs index ffce18c..916794b 100644 --- a/harm-runtime/src/memory.rs +++ b/harm-runtime/src/memory.rs @@ -20,15 +20,18 @@ pub trait Memory { /// Current writing position. fn pos(&self) -> usize; - /// If memory has fixed capacity, return it. + /// If the memory has fixed capacity, return it. /// /// A `Vec` is not considered a memory of fixed capacity because it can grow indefinitely. fn capacity(&self) -> Option; - /// Append data to the memory. Should fail when it reaches memory's capacity. + /// Append data to the memory. + /// + /// Should fail when it reaches memory's capacity. In this case, `self.pos()` must not change, but the memory behind + /// it till the end of the capacity may be modified. fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError>; - /// Align position. + /// Align position. Same guarantees as `try_extend` apply. fn align(&mut self, alignment: usize) -> Result<(), Self::ExtendError> { if alignment > 1 { let pos = self.pos(); diff --git a/harm-runtime/src/memory/memmap2.rs b/harm-runtime/src/memory/memmap2.rs index 5078de6..df0bd5d 100644 --- a/harm-runtime/src/memory/memmap2.rs +++ b/harm-runtime/src/memory/memmap2.rs @@ -52,14 +52,17 @@ impl Memory for MmapBuffer { #[inline] fn try_extend>(&mut self, bytes: I) -> Result<(), Self::ExtendError> { + let mut pos = self.pos; for byte in bytes { - if self.pos >= self.memory.len() { + if pos >= self.memory.len() { return Err(MapBufferError::Overflow(self.pos)); } - self.memory[self.pos] = byte; - self.pos += 1; + self.memory[pos] = byte; + pos += 1; } + // Success, update position. + self.pos = pos; Ok(()) } } @@ -199,6 +202,15 @@ mod tests { fn test_try_extend_1025() { let mut buf = MmapBuffer::allocate(1024).expect("mmap failed, system problem"); assert!(buf.try_extend(vec![1; 1025].into_iter()).is_err()); + assert_eq!(buf.pos(), 0); + } + + #[test] + fn test_try_extend_1020_plus_5() { + let mut buf = MmapBuffer::allocate(1024).expect("mmap failed, system problem"); + buf.try_extend(vec![1; 1020].into_iter()).unwrap(); + assert!(buf.try_extend(vec![1; 5].into_iter()).is_err()); + assert_eq!(buf.pos(), 1020); } #[test]