use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Mutex, RwLock};
use crate::jit_engine::{
GdbJitInterface, JITEngine, JITFunction, JITMemoryManager as EngineJITMemoryManager,
PerfJitInterface, SectionMemoryManager, SymbolResolver,
};
use crate::orc_concurrent::{
CompilationJob, CompilationResult, ConcurrentCompiler, GDBJITDebugRegistrar, JITCodeEntry as GdbCodeEntry,
};
use crate::orc_jit::{
CompileCallbackManager, CompileLayer, CompileOnDemandLayer, ExecutionSession as OrcExecSession,
IRCompileLayer, IRTransformLayer, JITDylib as OrcJITDylib, JITLayer, JITMemoryManager as OrcMemManager,
JITSymbol as OrcSymbol, JITSymbolFlags, LazyCompileLayer, MaterializationUnit as OrcMatUnit,
ORCJITBuilder, ORCJITSession, ObjectLinkingLayer, ResourceTracker as OrcResourceTracker,
SymbolResolver as OrcSymbolResolver, ThreadSafeModule,
};
use crate::module::Module;
use crate::value::ValueRef;
use crate::x86::*;
pub const X86_JIT_MAX_CODE_SIZE: usize = 128 * 1024 * 1024;
pub const X86_JIT_STUB_SIZE: usize = 64;
pub const X86_JIT_PAGE_SIZE: usize = 4096;
pub const X86_JIT_TARGET_TRIPLE: &str = "x86_64-unknown-linux-gnu";
pub const X86_JIT_TARGET_TRIPLE_WIN: &str = "x86_64-pc-windows-msvc";
pub const X86_JIT_MAX_PENDING_LAZY: usize = 1024;
pub const X86_JIT_MAX_CONCURRENT_THREADS: usize = 8;
pub const X86_JIT_DEFAULT_OPT_LEVEL: u8 = 2;
pub struct X86JITFull {
pub orc_jit: X86ORCJIT,
pub mcjit: X86MCJIT,
pub memory_manager: X86JITMemoryManager,
pub linker: X86JITLinker,
pub event_listener: X86JITEventListener,
pub indirect_stubs: X86IndirectStubs,
pub initialized: bool,
pub target_triple: String,
pub opt_level: u8,
pub debug_mode: bool,
pub stats: X86JITStats,
}
#[derive(Debug, Clone, Default)]
pub struct X86JITStats {
pub modules_compiled: u64,
pub functions_compiled: u64,
pub lazy_compilations: u64,
pub code_bytes_emitted: u64,
pub data_bytes_allocated: u64,
pub lookups: u64,
pub relocations_applied: u64,
pub stubs_created: u64,
pub events_fired: u64,
pub errors: u64,
}
impl X86JITStats {
pub fn new() -> Self {
Self::default()
}
pub fn reset(&mut self) {
*self = Self::default();
}
pub fn summary(&self) -> String {
format!(
"JIT Stats: modules={}, funcs={}, lazy={}, code_bytes={}, data_bytes={}, \
lookups={}, relocs={}, stubs={}, events={}, errors={}",
self.modules_compiled,
self.functions_compiled,
self.lazy_compilations,
self.code_bytes_emitted,
self.data_bytes_allocated,
self.lookups,
self.relocations_applied,
self.stubs_created,
self.events_fired,
self.errors,
)
}
}
impl X86JITFull {
pub fn new() -> Self {
Self {
orc_jit: X86ORCJIT::new(),
mcjit: X86MCJIT::new(),
memory_manager: X86JITMemoryManager::new(),
linker: X86JITLinker::new(),
event_listener: X86JITEventListener::new(),
indirect_stubs: X86IndirectStubs::new(),
initialized: false,
target_triple: X86_JIT_TARGET_TRIPLE.to_string(),
opt_level: X86_JIT_DEFAULT_OPT_LEVEL,
debug_mode: false,
stats: X86JITStats::new(),
}
}
pub fn with_target_triple(target_triple: &str) -> Self {
let mut jit = Self::new();
jit.target_triple = target_triple.to_string();
jit
}
pub fn for_windows() -> Self {
Self::with_target_triple(X86_JIT_TARGET_TRIPLE_WIN)
}
pub fn initialize(&mut self) -> Result<(), String> {
if self.initialized {
return Ok(());
}
self.memory_manager.initialize()?;
self.orc_jit.initialize()?;
self.mcjit.initialize(&self.target_triple)?;
self.linker.initialize()?;
self.indirect_stubs.initialize(&mut self.memory_manager)?;
self.event_listener.initialize()?;
self.initialized = true;
self.event_listener.notify_jit_initialized();
Ok(())
}
pub fn set_opt_level(&mut self, level: u8) {
self.opt_level = level.min(3);
self.orc_jit.set_opt_level(self.opt_level);
self.mcjit.set_opt_level(self.opt_level);
}
pub fn set_debug_mode(&mut self, enabled: bool) {
self.debug_mode = enabled;
self.orc_jit.set_debug_mode(enabled);
}
pub fn compile_module_orc(&mut self, module: &Module) -> Result<(), String> {
self.orc_jit.add_module(module)?;
self.stats.modules_compiled += 1;
self.event_listener.notify_module_loaded(module.get_name().unwrap_or("unnamed"));
Ok(())
}
pub fn compile_module_mcjit(&mut self, module: &Module) -> Result<(), String> {
self.mcjit.add_module(module)?;
self.stats.modules_compiled += 1;
self.event_listener.notify_module_loaded(module.get_name().unwrap_or("unnamed"));
Ok(())
}
pub fn lookup_function(&self, name: &str) -> Option<*const u8> {
if let Some(ptr) = self.orc_jit.lookup(name) {
return Some(ptr);
}
self.mcjit.get_function_pointer(name)
}
pub fn orc_lookup(&self, name: &str) -> Option<*const u8> {
self.orc_jit.lookup(name)
}
pub fn mcjit_lookup(&self, name: &str) -> Option<*const u8> {
self.mcjit.get_function_pointer(name)
}
pub fn shutdown(&mut self) {
self.orc_jit.shutdown();
self.mcjit.shutdown();
self.memory_manager.deallocate_all();
self.linker.clear();
self.indirect_stubs.shutdown(&mut self.memory_manager);
self.event_listener.shutdown();
self.initialized = false;
}
pub fn total_code_bytes(&self) -> u64 {
self.stats.code_bytes_emitted
}
pub fn total_data_bytes(&self) -> u64 {
self.stats.data_bytes_allocated
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn orc_engine(&self) -> &X86ORCJIT {
&self.orc_jit
}
pub fn orc_engine_mut(&mut self) -> &mut X86ORCJIT {
&mut self.orc_jit
}
pub fn mcjit_engine(&self) -> &X86MCJIT {
&self.mcjit
}
pub fn mcjit_engine_mut(&mut self) -> &mut X86MCJIT {
&mut self.mcjit
}
pub fn create_lazy_stub(&mut self, function_name: &str, module: &Module) -> Result<*const u8, String> {
let stub_addr = self.indirect_stubs.create_stub(function_name, &mut self.memory_manager)?;
self.orc_jit.register_lazy_function(function_name, module)?;
self.stats.stubs_created += 1;
Ok(stub_addr)
}
pub fn patch_stub(&mut self, function_name: &str, compiled_addr: *const u8) -> Result<(), String> {
self.indirect_stubs.patch_stub(function_name, compiled_addr)?;
self.stats.lazy_compilations += 1;
self.event_listener.notify_function_compiled(function_name, compiled_addr);
Ok(())
}
}
impl Default for X86JITFull {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86JITFull {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86JITFull")
.field("initialized", &self.initialized)
.field("target_triple", &self.target_triple)
.field("opt_level", &self.opt_level)
.field("debug_mode", &self.debug_mode)
.field("stats", &self.stats)
.finish()
}
}
pub struct X86ORCJIT {
pub session: X86ORCExecutionSession,
pub dylibs: HashMap<String, X86JITDylib>,
pub ir_compile_layer: X86IRCompileLayer,
pub object_linking_layer: X86ObjectLinkingLayer,
pub ir_transform_layer: X86IRTransformLayer,
pub object_transform_layer: X86ObjectTransformLayer,
pub resource_tracker: X86JITResourceTracker,
pub lookup_cache: HashMap<String, *const u8>,
pub initialized: bool,
pub opt_level: u8,
pub debug_mode: bool,
}
impl X86ORCJIT {
pub fn new() -> Self {
Self {
session: X86ORCExecutionSession::new(),
dylibs: HashMap::new(),
ir_compile_layer: X86IRCompileLayer::new(),
object_linking_layer: X86ObjectLinkingLayer::new(),
ir_transform_layer: X86IRTransformLayer::new(),
object_transform_layer: X86ObjectTransformLayer::new(),
resource_tracker: X86JITResourceTracker::new(),
lookup_cache: HashMap::new(),
initialized: false,
opt_level: X86_JIT_DEFAULT_OPT_LEVEL,
debug_mode: false,
}
}
pub fn initialize(&mut self) -> Result<(), String> {
if self.initialized {
return Ok(());
}
self.initialized = true;
Ok(())
}
pub fn set_opt_level(&mut self, level: u8) {
self.opt_level = level.min(3);
self.ir_compile_layer.set_opt_level(self.opt_level);
}
pub fn set_debug_mode(&mut self, enabled: bool) {
self.debug_mode = enabled;
self.ir_compile_layer.set_debug_info(enabled);
}
pub fn create_dylib(&mut self, name: &str) -> Result<(), String> {
if self.dylibs.contains_key(name) {
return Err(format!("JITDylib '{}' already exists", name));
}
let dylib = X86JITDylib::new(name);
self.dylibs.insert(name.to_string(), dylib);
Ok(())
}
pub fn remove_dylib(&mut self, name: &str) -> Result<(), String> {
if self.dylibs.remove(name).is_none() {
return Err(format!("JITDylib '{}' not found", name));
}
Ok(())
}
pub fn add_module_to_dylib(&mut self, dylib_name: &str, module: &Module) -> Result<(), String> {
let dylib = self
.dylibs
.get_mut(dylib_name)
.ok_or_else(|| format!("JITDylib '{}' not found", dylib_name))?;
let mu = X86IRMaterializationUnit::new(module);
dylib.add_materialization_unit(mu);
self.ir_compile_layer.compile_module(module)?;
Ok(())
}
pub fn add_module(&mut self, module: &Module) -> Result<(), String> {
let default_name = "__default_dylib";
if !self.dylibs.contains_key(default_name) {
self.create_dylib(default_name)?;
}
self.add_module_to_dylib(default_name, module)
}
pub fn lookup(&self, name: &str) -> Option<*const u8> {
if let Some(&ptr) = self.lookup_cache.get(name) {
return if ptr.is_null() { None } else { Some(ptr) };
}
for dylib in self.dylibs.values() {
if let Some(ptr) = dylib.lookup(name) {
return Some(ptr);
}
}
None
}
pub fn lookup_in_dylib(&self, dylib_name: &str, symbol_name: &str) -> Option<*const u8> {
self.dylibs
.get(dylib_name)
.and_then(|dylib| dylib.lookup(symbol_name))
}
pub fn register_lazy_function(&mut self, function_name: &str, module: &Module) -> Result<(), String> {
let default_name = "__default_dylib";
if !self.dylibs.contains_key(default_name) {
self.create_dylib(default_name)?;
}
let dylib = self.dylibs.get_mut(default_name).unwrap();
dylib.add_lazy_definition(function_name, module)?;
Ok(())
}
pub fn finalize(&mut self) -> Result<(), String> {
self.session.finalize()?;
for dylib in self.dylibs.values_mut() {
dylib.finalize()?;
}
Ok(())
}
pub fn shutdown(&mut self) {
self.dylibs.clear();
self.lookup_cache.clear();
self.resource_tracker.release_all();
self.session.shutdown();
self.initialized = false;
}
pub fn dylib_count(&self) -> usize {
self.dylibs.len()
}
pub fn module_count(&self) -> usize {
self.dylibs.values().map(|d| d.module_count()).sum()
}
pub fn invalidate_cache(&mut self) {
self.lookup_cache.clear();
}
}
impl Default for X86ORCJIT {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86ORCJIT {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86ORCJIT")
.field("dylibs", &self.dylibs.len())
.field("initialized", &self.initialized)
.field("opt_level", &self.opt_level)
.finish()
}
}
pub struct X86ORCExecutionSession {
pub pending_units: Vec<X86MaterializationUnit>,
pub errors: Vec<String>,
pub finalized: bool,
pub session_id: u64,
}
impl X86ORCExecutionSession {
pub fn new() -> Self {
Self {
pending_units: Vec::new(),
errors: Vec::new(),
finalized: false,
session_id: 0,
}
}
pub fn add_materialization_unit(&mut self, mu: X86MaterializationUnit) {
if !self.finalized {
self.pending_units.push(mu);
}
}
pub fn dispatch_materialization(&mut self) -> Result<(), Vec<String>> {
if self.finalized {
return Err(vec!["session is finalized".to_string()]);
}
let mut errors = Vec::new();
for mu in &self.pending_units {
if let Err(e) = mu.verify() {
errors.push(e);
}
}
if !errors.is_empty() {
self.errors.extend(errors.clone());
return Err(errors);
}
self.pending_units.clear();
Ok(())
}
pub fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
for mu in &self.pending_units {
if mu.provides_symbol(name) {
return mu.get_symbol_address(name);
}
}
None
}
pub fn finalize(&mut self) -> Result<(), String> {
if self.finalized {
return Err("session already finalized".to_string());
}
self.finalized = true;
Ok(())
}
pub fn is_finalized(&self) -> bool {
self.finalized
}
pub fn pending_count(&self) -> usize {
self.pending_units.len()
}
pub fn get_errors(&self) -> &[String] {
&self.errors
}
pub fn clear_errors(&mut self) {
self.errors.clear();
}
pub fn shutdown(&mut self) {
self.pending_units.clear();
self.errors.clear();
self.finalized = true;
}
}
impl Default for X86ORCExecutionSession {
fn default() -> Self {
Self::new()
}
}
pub struct X86JITDylib {
pub name: String,
pub materialization_units: Vec<X86MaterializationUnit>,
pub symbols: HashMap<String, X86JITSymbolDef>,
pub lazy_definitions: Vec<X86LazyDefinition>,
pub generators: Vec<Box<dyn X86DefinitionGenerator + Send>>,
pub allow_lazy_compilation: bool,
pub search_order: u32,
pub finalized: bool,
}
#[derive(Debug, Clone)]
pub struct X86JITSymbolDef {
pub address: *const u8,
pub flags: X86JITSymbolFlags,
pub materialized: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct X86JITSymbolFlags {
pub is_callable: bool,
pub is_exported: bool,
pub is_weak: bool,
pub is_common: bool,
pub is_absolute: bool,
pub is_lazy: bool,
pub is_materializing: bool,
}
impl Default for X86JITSymbolFlags {
fn default() -> Self {
Self {
is_callable: true,
is_exported: true,
is_weak: false,
is_common: false,
is_absolute: false,
is_lazy: false,
is_materializing: false,
}
}
}
impl X86JITSymbolFlags {
pub fn exported_function() -> Self {
Self {
is_callable: true,
is_exported: true,
..Default::default()
}
}
pub fn internal_data() -> Self {
Self {
is_callable: false,
is_exported: false,
..Default::default()
}
}
pub fn weak() -> Self {
Self {
is_weak: true,
is_exported: true,
..Default::default()
}
}
pub fn lazy() -> Self {
Self {
is_lazy: true,
is_exported: true,
is_callable: true,
..Default::default()
}
}
}
#[derive(Debug, Clone)]
pub struct X86LazyDefinition {
pub name: String,
pub module_name: String,
pub stub_address: Option<*const u8>,
pub compiling: bool,
}
impl X86JITDylib {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
materialization_units: Vec::new(),
symbols: HashMap::new(),
lazy_definitions: Vec::new(),
generators: Vec::new(),
allow_lazy_compilation: false,
search_order: 0,
finalized: false,
}
}
pub fn add_materialization_unit(&mut self, mu: X86MaterializationUnit) {
self.materialization_units.push(mu);
}
pub fn define_symbol(
&mut self,
name: &str,
address: *const u8,
flags: X86JITSymbolFlags,
) {
self.symbols.insert(
name.to_string(),
X86JITSymbolDef {
address,
flags,
materialized: true,
},
);
}
pub fn lookup(&self, name: &str) -> Option<*const u8> {
if let Some(def) = self.symbols.get(name) {
if def.materialized {
return Some(def.address);
}
}
for mu in &self.materialization_units {
if mu.provides_symbol(name) {
return mu.get_symbol_address(name);
}
}
for gen in &self.generators {
if let Some(addr) = gen.try_to_generate(name) {
return Some(addr);
}
}
None
}
pub fn add_generator<G: X86DefinitionGenerator + Send + 'static>(&mut self, gen: G) {
self.generators.push(Box::new(gen));
}
pub fn add_lazy_definition(
&mut self,
name: &str,
module: &Module,
) -> Result<(), String> {
if self.finalized {
return Err(format!(
"cannot add lazy definition '{}' to finalized dylib '{}'",
name, self.name
));
}
self.lazy_definitions.push(X86LazyDefinition {
name: name.to_string(),
module_name: module.get_name().unwrap_or("unnamed").to_string(),
stub_address: None,
compiling: false,
});
self.allow_lazy_compilation = true;
Ok(())
}
pub fn finalize(&mut self) -> Result<(), String> {
if self.finalized {
return Err(format!("dylib '{}' already finalized", self.name));
}
self.finalized = true;
Ok(())
}
pub fn module_count(&self) -> usize {
self.materialization_units.len()
}
pub fn is_finalized(&self) -> bool {
self.finalized
}
pub fn symbol_names(&self) -> Vec<String> {
let mut names: Vec<String> = self.symbols.keys().cloned().collect();
for mu in &self.materialization_units {
names.extend(mu.symbol_names().iter().cloned());
}
names
}
}
impl fmt::Debug for X86JITDylib {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86JITDylib")
.field("name", &self.name)
.field("symbols", &self.symbols.len())
.field("lazy_definitions", &self.lazy_definitions.len())
.field("units", &self.materialization_units.len())
.field("finalized", &self.finalized)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct X86MaterializationUnit {
pub name: String,
pub symbols: Vec<String>,
pub compiled: bool,
pub linked: bool,
pub kind: X86MaterializationKind,
pub resolved_addresses: HashMap<String, *const u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86MaterializationKind {
IR,
Object,
Binary,
}
impl X86MaterializationUnit {
pub fn new(name: &str, symbols: Vec<String>, kind: X86MaterializationKind) -> Self {
Self {
name: name.to_string(),
symbols,
compiled: false,
linked: false,
kind,
resolved_addresses: HashMap::new(),
}
}
pub fn verify(&self) -> Result<(), String> {
if self.symbols.is_empty() {
return Err(format!("materialization unit '{}' has no symbols", self.name));
}
Ok(())
}
pub fn provides_symbol(&self, name: &str) -> bool {
self.symbols.iter().any(|s| s == name)
}
pub fn get_symbol_address(&self, name: &str) -> Option<*const u8> {
self.resolved_addresses.get(name).copied()
}
pub fn record_address(&mut self, name: &str, address: *const u8) {
self.resolved_addresses.insert(name.to_string(), address);
}
pub fn mark_compiled(&mut self) {
self.compiled = true;
}
pub fn mark_linked(&mut self) {
self.compiled = true;
self.linked = true;
}
pub fn symbol_names(&self) -> &[String] {
&self.symbols
}
pub fn kind(&self) -> &X86MaterializationKind {
&self.kind
}
pub fn is_ir(&self) -> bool {
self.kind == X86MaterializationKind::IR
}
pub fn is_object(&self) -> bool {
self.kind == X86MaterializationKind::Object
}
}
pub struct X86IRMaterializationUnit {
pub unit: X86MaterializationUnit,
pub module_name: String,
pub ir_functions: Vec<String>,
}
impl X86IRMaterializationUnit {
pub fn new(module: &Module) -> Self {
let module_name = module.get_name().unwrap_or("unnamed").to_string();
let mut ir_functions = Vec::new();
if let Ok(iter) = module.get_function_iter() {
for func_ref in iter {
if let Ok(name) = func_ref.get_name() {
ir_functions.push(name);
}
}
}
Self {
unit: X86MaterializationUnit::new(
&module_name,
ir_functions.clone(),
X86MaterializationKind::IR,
),
module_name,
ir_functions,
}
}
pub fn module_name(&self) -> &str {
&self.module_name
}
pub fn functions(&self) -> &[String] {
&self.ir_functions
}
}
impl fmt::Debug for X86IRMaterializationUnit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86IRMaterializationUnit")
.field("module", &self.module_name)
.field("functions", &self.ir_functions.len())
.finish()
}
}
pub struct X86ObjectMaterializationUnit {
pub unit: X86MaterializationUnit,
pub object_data: Vec<u8>,
pub exports: Vec<String>,
pub imports: Vec<String>,
pub relocations: Vec<X86ObjectRelocation>,
}
#[derive(Debug, Clone)]
pub struct X86ObjectRelocation {
pub offset: u64,
pub symbol_name: String,
pub reloc_type: u32,
pub addend: i64,
pub section_index: u32,
}
impl X86ObjectMaterializationUnit {
pub fn new(object_data: Vec<u8>) -> Self {
Self {
unit: X86MaterializationUnit::new(
"object",
Vec::new(),
X86MaterializationKind::Object,
),
object_data,
exports: Vec::new(),
imports: Vec::new(),
relocations: Vec::new(),
}
}
pub fn parse(&mut self) -> Result<(), String> {
if self.object_data.is_empty() {
return Err("empty object data".to_string());
}
Ok(())
}
pub fn size(&self) -> usize {
self.object_data.len()
}
pub fn has_unresolved_relocations(&self) -> bool {
!self.relocations.is_empty()
}
}
impl fmt::Debug for X86ObjectMaterializationUnit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86ObjectMaterializationUnit")
.field("size", &self.object_data.len())
.field("exports", &self.exports.len())
.field("imports", &self.imports.len())
.field("relocations", &self.relocations.len())
.finish()
}
}
pub trait X86DefinitionGenerator: fmt::Debug + Send {
fn try_to_generate(&self, name: &str) -> Option<*const u8>;
fn name(&self) -> &str;
}
#[derive(Debug)]
pub struct X86StaticDefinitionGenerator {
name: String,
symbols: HashMap<String, *const u8>,
}
impl X86StaticDefinitionGenerator {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
symbols: HashMap::new(),
}
}
pub fn register(&mut self, symbol_name: &str, address: *const u8) {
self.symbols.insert(symbol_name.to_string(), address);
}
pub fn register_many(&mut self, symbols: &[(&str, *const u8)]) {
for &(name, addr) in symbols {
self.symbols.insert(name.to_string(), addr);
}
}
pub fn remove(&mut self, symbol_name: &str) {
self.symbols.remove(symbol_name);
}
pub fn contains(&self, name: &str) -> bool {
self.symbols.contains_key(name)
}
}
impl X86DefinitionGenerator for X86StaticDefinitionGenerator {
fn try_to_generate(&self, name: &str) -> Option<*const u8> {
self.symbols.get(name).copied()
}
fn name(&self) -> &str {
&self.name
}
}
#[derive(Debug)]
pub struct X86DynamicDefinitionGenerator {
name: String,
overrides: HashMap<String, *const u8>,
library_paths: Vec<String>,
}
impl X86DynamicDefinitionGenerator {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
overrides: HashMap::new(),
library_paths: Vec::new(),
}
}
pub fn add_library_path(&mut self, path: &str) {
self.library_paths.push(path.to_string());
}
pub fn override_symbol(&mut self, name: &str, address: *const u8) {
self.overrides.insert(name.to_string(), address);
}
}
impl X86DefinitionGenerator for X86DynamicDefinitionGenerator {
fn try_to_generate(&self, name: &str) -> Option<*const u8> {
if let Some(&addr) = self.overrides.get(name) {
return Some(addr);
}
None
}
fn name(&self) -> &str {
&self.name
}
}
#[derive(Debug)]
pub struct X86CustomDefinitionGenerator {
name: String,
resolver: Option<Box<dyn Fn(&str) -> Option<*const u8> + Send + Sync>>,
}
impl X86CustomDefinitionGenerator {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
resolver: None,
}
}
pub fn set_resolver<F: Fn(&str) -> Option<*const u8> + Send + Sync + 'static>(
&mut self,
resolver: F,
) {
self.resolver = Some(Box::new(resolver));
}
}
impl X86DefinitionGenerator for X86CustomDefinitionGenerator {
fn try_to_generate(&self, name: &str) -> Option<*const u8> {
self.resolver.as_ref().and_then(|r| r(name))
}
fn name(&self) -> &str {
&self.name
}
}
pub struct X86IRCompileLayer {
pub target_triple: String,
pub opt_level: u8,
pub debug_info: bool,
pub cache: HashMap<String, Vec<u8>>,
}
impl X86IRCompileLayer {
pub fn new() -> Self {
Self {
target_triple: X86_JIT_TARGET_TRIPLE.to_string(),
opt_level: X86_JIT_DEFAULT_OPT_LEVEL,
debug_info: false,
cache: HashMap::new(),
}
}
pub fn set_opt_level(&mut self, level: u8) {
self.opt_level = level.min(3);
}
pub fn set_debug_info(&mut self, enabled: bool) {
self.debug_info = enabled;
}
pub fn compile_module(&self, module: &Module) -> Result<Vec<u8>, String> {
let module_name = module.get_name().unwrap_or("unnamed");
if let Some(cached) = self.cache.get(module_name) {
return Ok(cached.clone());
}
let object_bytes = self.compile_ir_to_object(module)?;
Ok(object_bytes)
}
fn compile_ir_to_object(&self, _module: &Module) -> Result<Vec<u8>, String> {
let mut object = Vec::new();
object.extend_from_slice(b"\x7fELF");
object.push(2); object.push(1); object.push(1); object.push(0); object.extend_from_slice(&[0u8; 8]); object.extend_from_slice(&[3u8, 0]); object.extend_from_slice(&[0x3eu8, 0]); object.extend_from_slice(&[1u8; 4]); object.extend_from_slice(&[0u8; 24]);
object.extend_from_slice(&[0u8, 0, 0, 0, 64, 0, 64, 0, 0, 0]);
let code: Vec<u8> = vec![
0x55, 0x48, 0x89, 0xe5, 0x48, 0xc7, 0xc0, 0x2a, 0x00, 0x00, 0x00, 0x5d, 0xc3, ];
object.extend_from_slice(&code);
Ok(object)
}
pub fn generate_stub(&self) -> Vec<u8> {
vec![
0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xc3, ]
}
pub fn invalidate_cache(&mut self) {
self.cache.clear();
}
pub fn cache_object(&mut self, key: &str, object: Vec<u8>) {
self.cache.insert(key.to_string(), object);
}
}
impl Default for X86IRCompileLayer {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86IRCompileLayer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86IRCompileLayer")
.field("target", &self.target_triple)
.field("opt_level", &self.opt_level)
.field("debug_info", &self.debug_info)
.field("cache_entries", &self.cache.len())
.finish()
}
}
pub struct X86ObjectLinkingLayer {
pub code_segments: Vec<X86CodeSegment>,
pub linked_symbols: HashMap<String, *const u8>,
pub memory_allocations: Vec<X86JITMemAlloc>,
}
#[derive(Debug, Clone)]
pub struct X86CodeSegment {
pub address: *mut u8,
pub size: usize,
pub is_executable: bool,
pub is_writable: bool,
pub owner_dylib: String,
}
#[derive(Debug, Clone)]
pub struct X86JITMemAlloc {
pub address: *mut u8,
pub size: usize,
pub is_code: bool,
}
impl X86ObjectLinkingLayer {
pub fn new() -> Self {
Self {
code_segments: Vec::new(),
linked_symbols: HashMap::new(),
memory_allocations: Vec::new(),
}
}
pub fn link(
&mut self,
mu: &X86ObjectMaterializationUnit,
dylib_name: &str,
address: *mut u8,
) -> Result<(), String> {
let size = mu.object_data.len();
let segment = X86CodeSegment {
address,
size,
is_executable: true,
is_writable: false,
owner_dylib: dylib_name.to_string(),
};
for reloc in &mu.relocations {
let symbol_addr = self
.linked_symbols
.get(&reloc.symbol_name)
.copied()
.ok_or_else(|| {
format!(
"unresolved symbol '{}' during linking",
reloc.symbol_name
)
})?;
self.apply_relocation(reloc, address, symbol_addr)?;
}
self.code_segments.push(segment);
Ok(())
}
fn apply_relocation(
&self,
reloc: &X86ObjectRelocation,
base: *mut u8,
symbol_addr: *const u8,
) -> Result<(), String> {
let offset = reloc.offset as usize;
let patch_addr = unsafe { base.add(offset) };
match reloc.reloc_type {
2 => {
let value = (symbol_addr as i64)
.wrapping_sub(unsafe { base.add(offset + 4) } as i64);
if value < i32::MIN as i64 || value > i32::MAX as i64 {
return Err("R_X86_64_PC32 relocation overflow".to_string());
}
unsafe {
(patch_addr as *mut i32).write_unaligned(value as i32);
}
}
1 => {
unsafe {
(patch_addr as *mut u64).write_unaligned(symbol_addr as u64);
}
}
10 => {
let addr = symbol_addr as u64;
if addr > u32::MAX as u64 {
return Err("R_X86_64_32 relocation overflow".to_string());
}
unsafe {
(patch_addr as *mut u32).write_unaligned(addr as u32);
}
}
4 => {
let value = (symbol_addr as i64)
.wrapping_sub(unsafe { base.add(offset + 4) } as i64);
if value < i32::MIN as i64 || value > i32::MAX as i64 {
return Err("R_X86_64_PLT32 relocation overflow".to_string());
}
unsafe {
(patch_addr as *mut i32).write_unaligned(value as i32);
}
}
9 => {
let value = (symbol_addr as i64)
.wrapping_sub(unsafe { base.add(offset + 4) } as i64);
if value < i32::MIN as i64 || value > i32::MAX as i64 {
return Err("R_X86_64_GOTPCREL relocation overflow".to_string());
}
unsafe {
(patch_addr as *mut i32).write_unaligned(value as i32);
}
}
_ => {
return Err(format!(
"unsupported relocation type: {}",
reloc.reloc_type
));
}
}
Ok(())
}
pub fn register_symbol(&mut self, name: &str, address: *const u8) {
self.linked_symbols.insert(name.to_string(), address);
}
pub fn record_allocation(&mut self, address: *mut u8, size: usize, is_code: bool) {
self.memory_allocations.push(X86JITMemAlloc {
address,
size,
is_code,
});
}
pub fn find_symbol(&self, name: &str) -> Option<*const u8> {
self.linked_symbols.get(name).copied()
}
pub fn symbol_count(&self) -> usize {
self.linked_symbols.len()
}
pub fn clear(&mut self) {
self.code_segments.clear();
self.linked_symbols.clear();
self.memory_allocations.clear();
}
}
impl Default for X86ObjectLinkingLayer {
fn default() -> Self {
Self::new()
}
}
pub struct X86IRTransformLayer {
pub transforms: Vec<X86IRTransform>,
pub enabled: bool,
}
#[derive(Debug, Clone)]
pub struct X86IRTransform {
pub name: String,
pub priority: u32,
pub enabled: bool,
pub modifies: bool,
}
impl X86IRTransformLayer {
pub fn new() -> Self {
Self {
transforms: Vec::new(),
enabled: true,
}
}
pub fn add_transform(&mut self, name: &str, priority: u32) {
self.transforms.push(X86IRTransform {
name: name.to_string(),
priority,
enabled: true,
modifies: true,
});
}
pub fn add_standard_transforms(&mut self, opt_level: u8) {
self.add_transform("mem2reg", 10);
self.add_transform("instcombine", 20);
if opt_level >= 1 {
self.add_transform("reassociate", 30);
self.add_transform("gvn", 40);
self.add_transform("simplifycfg", 50);
}
if opt_level >= 2 {
self.add_transform("licm", 60);
self.add_transform("indvars", 70);
self.add_transform("loop-rotate", 80);
self.add_transform("loop-unroll", 90);
self.add_transform("sccp", 100);
self.add_transform("slp-vectorizer", 110);
}
if opt_level >= 3 {
self.add_transform("loop-vectorize", 120);
self.add_transform("inline", 130);
self.add_transform("argpromotion", 140);
self.add_transform("ipsccp", 150);
}
}
pub fn apply_transforms(&self, _module: &Module) -> Result<(), String> {
if !self.enabled {
return Ok(());
}
Ok(())
}
pub fn disable_transform(&mut self, name: &str) {
if let Some(t) = self.transforms.iter_mut().find(|t| t.name == name) {
t.enabled = false;
}
}
pub fn enable_transform(&mut self, name: &str) {
if let Some(t) = self.transforms.iter_mut().find(|t| t.name == name) {
t.enabled = true;
}
}
pub fn transform_names(&self) -> Vec<&str> {
self.transforms.iter().map(|t| t.name.as_str()).collect()
}
pub fn sorted_transforms(&self) -> Vec<&X86IRTransform> {
let mut sorted: Vec<&X86IRTransform> = self.transforms.iter().collect();
sorted.sort_by_key(|t| t.priority);
sorted
}
}
impl Default for X86IRTransformLayer {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86IRTransformLayer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86IRTransformLayer")
.field("transforms", &self.transforms.len())
.field("enabled", &self.enabled)
.finish()
}
}
pub struct X86ObjectTransformLayer {
pub transforms: Vec<X86ObjectTransform>,
pub enabled: bool,
}
#[derive(Debug, Clone)]
pub struct X86ObjectTransform {
pub name: String,
pub priority: u32,
pub enabled: bool,
}
impl X86ObjectTransformLayer {
pub fn new() -> Self {
Self {
transforms: Vec::new(),
enabled: false, }
}
pub fn add_transform(&mut self, name: &str, priority: u32) {
self.transforms.push(X86ObjectTransform {
name: name.to_string(),
priority,
enabled: true,
});
}
pub fn add_standard_transforms(&mut self) {
self.add_transform("peephole", 10);
self.add_transform("branch-relaxation", 20);
self.add_transform("nop-padding", 30);
}
pub fn apply_transforms(&self, _object: &mut [u8]) -> Result<(), String> {
if !self.enabled {
return Ok(());
}
Ok(())
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn transform_count(&self) -> usize {
self.transforms.len()
}
}
impl Default for X86ObjectTransformLayer {
fn default() -> Self {
Self::new()
}
}
pub struct X86JITResourceTracker {
pub tracked_addresses: Vec<*mut u8>,
pub tracked_sizes: Vec<usize>,
pub tracked_symbols: Vec<String>,
pub total_allocated: usize,
}
impl X86JITResourceTracker {
pub fn new() -> Self {
Self {
tracked_addresses: Vec::new(),
tracked_sizes: Vec::new(),
tracked_symbols: Vec::new(),
total_allocated: 0,
}
}
pub fn track_memory(&mut self, address: *mut u8, size: usize) {
self.tracked_addresses.push(address);
self.tracked_sizes.push(size);
self.total_allocated += size;
}
pub fn track_symbol(&mut self, name: &str) {
self.tracked_symbols.push(name.to_string());
}
pub fn release_all(&mut self) {
self.tracked_addresses.clear();
self.tracked_sizes.clear();
self.tracked_symbols.clear();
self.total_allocated = 0;
}
pub fn memory_count(&self) -> usize {
self.tracked_addresses.len()
}
pub fn symbol_count(&self) -> usize {
self.tracked_symbols.len()
}
pub fn has_resources(&self) -> bool {
!self.tracked_addresses.is_empty() || !self.tracked_symbols.is_empty()
}
}
impl Default for X86JITResourceTracker {
fn default() -> Self {
Self::new()
}
}
pub struct X86ORCJITLookup {
pub dylibs: Vec<String>,
pub search_process: bool,
pub cache: HashMap<String, Option<*const u8>>,
}
impl X86ORCJITLookup {
pub fn new() -> Self {
Self {
dylibs: Vec::new(),
search_process: true,
cache: HashMap::new(),
}
}
pub fn add_dylib(&mut self, dylib_name: &str) {
if !self.dylibs.iter().any(|d| d == dylib_name) {
self.dylibs.push(dylib_name.to_string());
}
}
pub fn remove_dylib(&mut self, dylib_name: &str) {
self.dylibs.retain(|d| d != dylib_name);
}
pub fn set_known_symbols(&mut self, _symbols: &HashMap<String, *const u8>) {
}
pub fn invalidate_cache(&mut self) {
self.cache.clear();
}
pub fn has_pending(&self) -> bool {
false
}
}
impl Default for X86ORCJITLookup {
fn default() -> Self {
Self::new()
}
}
pub struct X86MCJIT {
pub modules: Vec<X86MCJITModule>,
pub compiled_functions: HashMap<String, *const u8>,
pub memory_manager: X86MCJITMemoryManager,
pub opt_level: u8,
pub lazy_compilation: bool,
pub finalized: bool,
pub target_triple: String,
}
#[derive(Debug, Clone)]
pub struct X86MCJITModule {
pub name: String,
pub compiled: bool,
pub functions: Vec<String>,
pub code_base: Option<*mut u8>,
pub code_size: usize,
}
impl X86MCJIT {
pub fn new() -> Self {
Self {
modules: Vec::new(),
compiled_functions: HashMap::new(),
memory_manager: X86MCJITMemoryManager::new(),
opt_level: X86_JIT_DEFAULT_OPT_LEVEL,
lazy_compilation: false,
finalized: false,
target_triple: X86_JIT_TARGET_TRIPLE.to_string(),
}
}
pub fn initialize(&mut self, target_triple: &str) -> Result<(), String> {
self.target_triple = target_triple.to_string();
self.memory_manager.initialize()?;
Ok(())
}
pub fn set_opt_level(&mut self, level: u8) {
self.opt_level = level.min(3);
}
pub fn set_lazy_compilation(&mut self, lazy: bool) {
self.lazy_compilation = lazy;
}
pub fn add_module(&mut self, module: &Module) -> Result<(), String> {
let module_name = module.get_name().unwrap_or("unnamed").to_string();
let mut function_names = Vec::new();
if let Ok(iter) = module.get_function_iter() {
for func_ref in iter {
if let Ok(name) = func_ref.get_name() {
function_names.push(name);
}
}
}
let mcjit_module = X86MCJITModule {
name: module_name,
compiled: false,
functions: function_names,
code_base: None,
code_size: 0,
};
self.modules.push(mcjit_module);
if !self.lazy_compilation {
self.compile_all()?;
}
Ok(())
}
pub fn compile_all(&mut self) -> Result<(), String> {
for module in &mut self.modules {
if !module.compiled {
self.compile_module_internal(module)?;
}
}
Ok(())
}
fn compile_module_internal(&mut self, module: &mut X86MCJITModule) -> Result<(), String> {
let code = self.compile_module_to_code(module)?;
let code_size = code.len();
let base = self
.memory_manager
.allocate_code_section(code_size)?;
unsafe {
std::ptr::copy_nonoverlapping(code.as_ptr(), base, code_size);
}
for (i, func_name) in module.functions.iter().enumerate() {
let func_offset = i * 64; let func_addr = unsafe { base.add(func_offset) };
self.compiled_functions
.insert(func_name.clone(), func_addr as *const u8);
}
module.code_base = Some(base);
module.code_size = code_size;
module.compiled = true;
Ok(())
}
fn compile_module_to_code(&self, _module: &X86MCJITModule) -> Result<Vec<u8>, String> {
let mut code = Vec::new();
code.extend_from_slice(&[0x55]); code.extend_from_slice(&[0x48, 0x89, 0xe5]); code.extend_from_slice(&[0x48, 0xc7, 0xc0, 0x2a, 0x00, 0x00, 0x00]); code.extend_from_slice(&[0x5d]); code.extend_from_slice(&[0xc3]);
Ok(code)
}
pub fn get_function_pointer(&self, name: &str) -> Option<*const u8> {
if let Some(&ptr) = self.compiled_functions.get(name) {
return Some(ptr);
}
if self.lazy_compilation {
for module in &self.modules {
if module.functions.iter().any(|f| f == name) {
return self.compile_function_lazily(name, module);
}
}
}
None
}
fn compile_function_lazily(
&self,
_name: &str,
_module: &X86MCJITModule,
) -> Option<*const u8> {
None
}
pub fn finalize_memory(&mut self) -> Result<(), String> {
if self.finalized {
return Err("MCJIT memory already finalized".to_string());
}
self.memory_manager.finalize_memory()?;
self.finalized = true;
Ok(())
}
pub fn module_count(&self) -> usize {
self.modules.len()
}
pub fn function_count(&self) -> usize {
self.compiled_functions.len()
}
pub fn function_names(&self) -> Vec<&str> {
self.compiled_functions
.keys()
.map(|s| s.as_str())
.collect()
}
pub fn remove_function(&mut self, name: &str) {
self.compiled_functions.remove(name);
}
pub fn clear(&mut self) {
self.modules.clear();
self.compiled_functions.clear();
self.finalized = false;
}
pub fn shutdown(&mut self) {
self.clear();
self.memory_manager.deallocate_all();
}
pub fn is_finalized(&self) -> bool {
self.finalized
}
}
impl Default for X86MCJIT {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86MCJIT {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86MCJIT")
.field("modules", &self.modules.len())
.field("functions", &self.compiled_functions.len())
.field("opt_level", &self.opt_level)
.field("lazy", &self.lazy_compilation)
.field("finalized", &self.finalized)
.finish()
}
}
pub struct X86MCJITMemoryManager {
pub code_allocations: Vec<X86MCJITAllocation>,
pub rodata_allocations: Vec<X86MCJITAllocation>,
pub rwdata_allocations: Vec<X86MCJITAllocation>,
pub total_allocated: usize,
pub finalized: bool,
}
#[derive(Debug, Clone)]
pub struct X86MCJITAllocation {
pub address: *mut u8,
pub size: usize,
pub permissions: u8, }
impl X86MCJITMemoryManager {
pub fn new() -> Self {
Self {
code_allocations: Vec::new(),
rodata_allocations: Vec::new(),
rwdata_allocations: Vec::new(),
total_allocated: 0,
finalized: false,
}
}
pub fn initialize(&mut self) -> Result<(), String> {
Ok(())
}
pub fn allocate_code_section(&mut self, size: usize) -> Result<*mut u8, String> {
let aligned_size = Self::align_up(size, X86_JIT_PAGE_SIZE);
let address = Self::raw_allocate(aligned_size)?;
self.code_allocations.push(X86MCJITAllocation {
address,
size: aligned_size,
permissions: 7, });
self.total_allocated += aligned_size;
Ok(address)
}
pub fn allocate_data_section(&mut self, size: usize, is_readonly: bool) -> Result<*mut u8, String> {
let aligned_size = Self::align_up(size, X86_JIT_PAGE_SIZE);
let address = Self::raw_allocate(aligned_size)?;
let alloc = X86MCJITAllocation {
address,
size: aligned_size,
permissions: if is_readonly { 1 } else { 3 }, };
if is_readonly {
self.rodata_allocations.push(alloc);
} else {
self.rwdata_allocations.push(alloc);
}
self.total_allocated += aligned_size;
Ok(address)
}
pub fn finalize_memory(&mut self) -> Result<(), String> {
if self.finalized {
return Err("memory already finalized".to_string());
}
for alloc in &mut self.code_allocations {
alloc.permissions = 5; }
self.finalized = true;
Ok(())
}
pub fn deallocate_all(&mut self) {
for alloc in &self.code_allocations {
unsafe {
libc::munmap(alloc.address as *mut libc::c_void, alloc.size);
}
}
for alloc in &self.rodata_allocations {
unsafe {
libc::munmap(alloc.address as *mut libc::c_void, alloc.size);
}
}
for alloc in &self.rwdata_allocations {
unsafe {
libc::munmap(alloc.address as *mut libc::c_void, alloc.size);
}
}
self.code_allocations.clear();
self.rodata_allocations.clear();
self.rwdata_allocations.clear();
self.total_allocated = 0;
self.finalized = false;
}
fn raw_allocate(size: usize) -> Result<*mut u8, String> {
let ptr = unsafe {
libc::mmap(
std::ptr::null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
if ptr == libc::MAP_FAILED {
return Err(format!(
"mmap failed to allocate {} bytes",
size
));
}
Ok(ptr as *mut u8)
}
fn align_up(size: usize, alignment: usize) -> usize {
(size + alignment - 1) & !(alignment - 1)
}
pub fn change_protection(
address: *mut u8,
size: usize,
permissions: u8,
) -> Result<(), String> {
let prot = permissions as i32;
let result = unsafe { libc::mprotect(address as *mut libc::c_void, size, prot) };
if result != 0 {
return Err(format!(
"mprotect failed for {:?} size {} perm {}: {}",
address,
size,
permissions,
std::io::Error::last_os_error()
));
}
Ok(())
}
pub fn total_allocated(&self) -> usize {
self.total_allocated
}
pub fn code_section_count(&self) -> usize {
self.code_allocations.len()
}
pub fn data_section_count(&self) -> usize {
self.rodata_allocations.len() + self.rwdata_allocations.len()
}
}
impl Drop for X86MCJITMemoryManager {
fn drop(&mut self) {
self.deallocate_all();
}
}
impl Default for X86MCJITMemoryManager {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86MCJITMemoryManager {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86MCJITMemoryManager")
.field("code_sections", &self.code_allocations.len())
.field("rodata_sections", &self.rodata_allocations.len())
.field("rwdata_sections", &self.rwdata_allocations.len())
.field("total_allocated", &self.total_allocated)
.field("finalized", &self.finalized)
.finish()
}
}
pub struct X86JITMemoryManager {
pub code_allocations: Vec<*mut u8>,
pub data_allocations: Vec<*mut u8>,
pub section_allocations: HashMap<String, Vec<*mut u8>>,
pub stub_allocations: Vec<*mut u8>,
pub total_code_bytes: usize,
pub total_data_bytes: usize,
pub protect_after_finalize: bool,
pub finalized: bool,
pub initialized: bool,
}
impl X86JITMemoryManager {
pub fn new() -> Self {
Self {
code_allocations: Vec::new(),
data_allocations: Vec::new(),
section_allocations: HashMap::new(),
stub_allocations: Vec::new(),
total_code_bytes: 0,
total_data_bytes: 0,
protect_after_finalize: true,
finalized: false,
initialized: false,
}
}
pub fn initialize(&mut self) -> Result<(), String> {
if self.initialized {
return Ok(());
}
self.initialized = true;
Ok(())
}
pub fn allocate_code(&mut self, size: usize) -> Result<*mut u8, String> {
if self.finalized {
return Err("cannot allocate code: memory finalized".to_string());
}
let addr = self.raw_allocate_rwx(size)?;
self.code_allocations.push(addr);
self.total_code_bytes += size;
Ok(addr)
}
pub fn allocate_data(&mut self, size: usize) -> Result<*mut u8, String> {
if self.finalized {
return Err("cannot allocate data: memory finalized".to_string());
}
let addr = self.raw_allocate_rw(size)?;
self.data_allocations.push(addr);
self.total_data_bytes += size;
Ok(addr)
}
pub fn allocate_section(
&mut self,
section_name: &str,
size: usize,
permissions: &str,
) -> Result<*mut u8, String> {
if self.finalized {
return Err("cannot allocate section: memory finalized".to_string());
}
let addr = match permissions {
"rx" | "code" => self.raw_allocate_rwx(size)?,
"rw" | "data" => self.raw_allocate_rw(size)?,
"r" | "rodata" => self.raw_allocate_rw(size)?, _ => self.raw_allocate_rw(size)?,
};
self.section_allocations
.entry(section_name.to_string())
.or_default()
.push(addr);
self.total_data_bytes += size;
Ok(addr)
}
pub fn allocate_stub_memory(&mut self, count: usize) -> Result<*mut u8, String> {
let size = count * X86_JIT_STUB_SIZE;
let addr = self.raw_allocate_rwx(size)?;
self.stub_allocations.push(addr);
self.total_code_bytes += size;
Ok(addr)
}
pub fn finalize_memory(&mut self) -> Result<(), String> {
if self.finalized {
return Ok(());
}
if self.protect_after_finalize {
for &addr in &self.code_allocations {
Self::set_rx(addr)?;
}
for &addr in &self.stub_allocations {
Self::set_rx(addr)?;
}
for &addr in &self.data_allocations {
Self::set_rw(addr)?;
}
}
self.finalized = true;
Ok(())
}
fn set_rx(addr: *mut u8) -> Result<(), String> {
let page = Self::page_align_down(addr);
let result = unsafe {
libc::mprotect(
page as *mut libc::c_void,
X86_JIT_PAGE_SIZE,
libc::PROT_READ | libc::PROT_EXEC,
)
};
if result != 0 {
return Err(format!("mprotect RX failed: {:?}", std::io::Error::last_os_error()));
}
Ok(())
}
fn set_rw(addr: *mut u8) -> Result<(), String> {
let page = Self::page_align_down(addr);
let result = unsafe {
libc::mprotect(
page as *mut libc::c_void,
X86_JIT_PAGE_SIZE,
libc::PROT_READ | libc::PROT_WRITE,
)
};
if result != 0 {
return Err(format!("mprotect RW failed: {:?}", std::io::Error::last_os_error()));
}
Ok(())
}
pub fn deallocate_all(&mut self) {
for &addr in &self.code_allocations {
unsafe {
libc::munmap(addr as *mut libc::c_void, X86_JIT_PAGE_SIZE);
}
}
for &addr in &self.data_allocations {
unsafe {
libc::munmap(addr as *mut libc::c_void, X86_JIT_PAGE_SIZE);
}
}
for &addr in &self.stub_allocations {
unsafe {
libc::munmap(addr as *mut libc::c_void, X86_JIT_PAGE_SIZE);
}
}
for addrs in self.section_allocations.values() {
for &addr in addrs {
unsafe {
libc::munmap(addr as *mut libc::c_void, X86_JIT_PAGE_SIZE);
}
}
}
self.code_allocations.clear();
self.data_allocations.clear();
self.stub_allocations.clear();
self.section_allocations.clear();
self.total_code_bytes = 0;
self.total_data_bytes = 0;
self.finalized = false;
}
fn raw_allocate_rwx(&self, size: usize) -> Result<*mut u8, String> {
let aligned = Self::align_up(size, X86_JIT_PAGE_SIZE);
let ptr = unsafe {
libc::mmap(
std::ptr::null_mut(),
aligned,
libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
if ptr == libc::MAP_FAILED {
return Err(format!("mmap RWX failed for {} bytes: {:?}", aligned, std::io::Error::last_os_error()));
}
Ok(ptr as *mut u8)
}
fn raw_allocate_rw(&self, size: usize) -> Result<*mut u8, String> {
let aligned = Self::align_up(size, X86_JIT_PAGE_SIZE);
let ptr = unsafe {
libc::mmap(
std::ptr::null_mut(),
aligned,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
if ptr == libc::MAP_FAILED {
return Err(format!("mmap RW failed for {} bytes: {:?}", aligned, std::io::Error::last_os_error()));
}
Ok(ptr as *mut u8)
}
fn page_align_down(addr: *mut u8) -> *mut u8 {
let a = addr as usize;
(a & !(X86_JIT_PAGE_SIZE - 1)) as *mut u8
}
fn align_up(size: usize, alignment: usize) -> usize {
(size + alignment - 1) & !(alignment - 1)
}
pub fn total_allocated(&self) -> usize {
self.total_code_bytes + self.total_data_bytes
}
pub fn is_finalized(&self) -> bool {
self.finalized
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
}
impl Drop for X86JITMemoryManager {
fn drop(&mut self) {
self.deallocate_all();
}
}
impl Default for X86JITMemoryManager {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86JITMemoryManager {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86JITMemoryManager")
.field("code_allocs", &self.code_allocations.len())
.field("data_allocs", &self.data_allocations.len())
.field("stub_allocs", &self.stub_allocations.len())
.field("sections", &self.section_allocations.len())
.field("total_code", &self.total_code_bytes)
.field("total_data", &self.total_data_bytes)
.field("finalized", &self.finalized)
.finish()
}
}
pub struct X86JITLinker {
pub symbol_table: HashMap<String, *const u8>,
pub external_resolver: Option<Box<dyn Fn(&str) -> Option<*const u8> + Send + Sync>>,
pub got_entries: HashMap<String, *mut u8>,
pub plt_entries: HashMap<String, *mut u8>,
pub pending_relocations: Vec<X86JITPendingReloc>,
pub initialized: bool,
}
#[derive(Debug, Clone)]
pub struct X86JITPendingReloc {
pub address: *mut u8,
pub symbol_name: String,
pub reloc_type: u32,
pub addend: i64,
pub is_pc_relative: bool,
}
pub mod x86_reloc {
pub const R_X86_64_NONE: u32 = 0;
pub const R_X86_64_64: u32 = 1;
pub const R_X86_64_PC32: u32 = 2;
pub const R_X86_64_GOT32: u32 = 3;
pub const R_X86_64_PLT32: u32 = 4;
pub const R_X86_64_COPY: u32 = 5;
pub const R_X86_64_GLOB_DAT: u32 = 6;
pub const R_X86_64_JUMP_SLOT: u32 = 7;
pub const R_X86_64_RELATIVE: u32 = 8;
pub const R_X86_64_GOTPCREL: u32 = 9;
pub const R_X86_64_32: u32 = 10;
pub const R_X86_64_32S: u32 = 11;
pub const R_X86_64_16: u32 = 12;
pub const R_X86_64_PC16: u32 = 13;
pub const R_X86_64_8: u32 = 14;
pub const R_X86_64_PC8: u32 = 15;
pub const R_X86_64_GOTPCRELX: u32 = 41;
pub const R_X86_64_REX_GOTPCRELX: u32 = 42;
}
impl X86JITLinker {
pub fn new() -> Self {
Self {
symbol_table: HashMap::new(),
external_resolver: None,
got_entries: HashMap::new(),
plt_entries: HashMap::new(),
pending_relocations: Vec::new(),
initialized: false,
}
}
pub fn initialize(&mut self) -> Result<(), String> {
self.initialized = true;
Ok(())
}
pub fn define_symbol(&mut self, name: &str, address: *const u8) {
self.symbol_table.insert(name.to_string(), address);
self.resolve_pending_for(name);
}
pub fn define_symbols(&mut self, symbols: &[(&str, *const u8)]) {
for &(name, addr) in symbols {
self.define_symbol(name, addr);
}
}
pub fn set_external_resolver<F: Fn(&str) -> Option<*const u8> + Send + Sync + 'static>(
&mut self,
resolver: F,
) {
self.external_resolver = Some(Box::new(resolver));
}
pub fn resolve_symbol(&self, name: &str) -> Option<*const u8> {
if let Some(&addr) = self.symbol_table.get(name) {
return Some(addr);
}
if let Some(ref resolver) = self.external_resolver {
return resolver(name);
}
None
}
pub fn apply_relocation(
&mut self,
address: *mut u8,
symbol_name: &str,
reloc_type: u32,
addend: i64,
) -> Result<(), String> {
let sym_addr = match self.resolve_symbol(symbol_name) {
Some(addr) => addr,
None => {
self.pending_relocations.push(X86JITPendingReloc {
address,
symbol_name: symbol_name.to_string(),
reloc_type,
addend,
is_pc_relative: Self::is_pc_relative(reloc_type),
});
return Ok(());
}
};
self.apply_fixup(address, sym_addr, reloc_type, addend)
}
fn apply_fixup(
&self,
address: *mut u8,
symbol_addr: *const u8,
reloc_type: u32,
addend: i64,
) -> Result<(), String> {
let target = symbol_addr as i64 + addend;
unsafe {
match reloc_type {
x86_reloc::R_X86_64_64 => {
(address as *mut i64).write_unaligned(target);
}
x86_reloc::R_X86_64_PC32 | x86_reloc::R_X86_64_PLT32 | x86_reloc::R_X86_64_GOTPCREL => {
let pc = address as i64 + 4;
let rel = target.wrapping_sub(pc);
if rel < i32::MIN as i64 || rel > i32::MAX as i64 {
return Err(format!("PC-relative relocation overflow: {} bytes", rel));
}
(address as *mut i32).write_unaligned(rel as i32);
}
x86_reloc::R_X86_64_32 | x86_reloc::R_X86_64_32S => {
if target > i32::MAX as i64 || target < i32::MIN as i64 {
return Err("32-bit relocation overflow".to_string());
}
(address as *mut i32).write_unaligned(target as i32);
}
x86_reloc::R_X86_64_16 => {
if target > u16::MAX as i64 || target < i16::MIN as i64 {
return Err("16-bit relocation overflow".to_string());
}
(address as *mut i16).write_unaligned(target as i16);
}
x86_reloc::R_X86_64_8 => {
if target > u8::MAX as i64 || target < i8::MIN as i64 {
return Err("8-bit relocation overflow".to_string());
}
(address as *mut i8).write_unaligned(target as i8);
}
x86_reloc::R_X86_64_RELATIVE => {
(address as *mut i64).write_unaligned(target);
}
_ => return Err(format!("unsupported relocation type: {}", reloc_type)),
}
}
Ok(())
}
fn is_pc_relative(reloc_type: u32) -> bool {
matches!(
reloc_type,
x86_reloc::R_X86_64_PC32
| x86_reloc::R_X86_64_PC16
| x86_reloc::R_X86_64_PC8
| x86_reloc::R_X86_64_PLT32
| x86_reloc::R_X86_64_GOTPCREL
| x86_reloc::R_X86_64_GOTPCRELX
| x86_reloc::R_X86_64_REX_GOTPCRELX
)
}
pub fn allocate_got_entry(
&mut self,
symbol_name: &str,
memory_manager: &mut X86JITMemoryManager,
) -> Result<*mut u8, String> {
let entry = memory_manager.allocate_data(8)?;
self.got_entries.insert(symbol_name.to_string(), entry);
Ok(entry)
}
pub fn allocate_plt_entry(
&mut self,
symbol_name: &str,
memory_manager: &mut X86JITMemoryManager,
) -> Result<*mut u8, String> {
let stub = memory_manager.allocate_code(X86_JIT_STUB_SIZE)?;
self.generate_plt_stub(stub);
self.plt_entries.insert(symbol_name.to_string(), stub);
Ok(stub)
}
fn generate_plt_stub(&self, stub_address: *mut u8) {
let stub: [u8; 6] = [
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, ];
unsafe {
std::ptr::copy_nonoverlapping(stub.as_ptr(), stub_address, stub.len());
}
}
pub fn resolve_weak(
&self,
weak_name: &str,
strong_name: &str,
) -> Option<*const u8> {
self.symbol_table
.get(strong_name)
.or_else(|| self.symbol_table.get(weak_name))
.copied()
}
fn resolve_pending_for(&mut self, symbol_name: &str) {
let addr = match self.symbol_table.get(symbol_name).copied() {
Some(a) => a,
None => return,
};
let mut resolved = Vec::new();
let mut unresolved = Vec::new();
for reloc in self.pending_relocations.drain(..) {
if reloc.symbol_name == symbol_name {
if self.apply_fixup(reloc.address, addr, reloc.reloc_type, reloc.addend).is_ok() {
resolved.push(reloc);
} else {
unresolved.push(reloc);
}
} else {
unresolved.push(reloc);
}
}
self.pending_relocations = unresolved;
}
pub fn apply_all_pending(&mut self) -> Result<usize, String> {
let mut applied = 0;
let mut still_unresolved = Vec::new();
for reloc in self.pending_relocations.drain(..) {
if let Some(addr) = self.resolve_symbol(&reloc.symbol_name) {
self.apply_fixup(reloc.address, addr, reloc.reloc_type, reloc.addend)?;
applied += 1;
} else {
still_unresolved.push(reloc);
}
}
self.pending_relocations = still_unresolved;
Ok(applied)
}
pub fn clear(&mut self) {
self.symbol_table.clear();
self.got_entries.clear();
self.plt_entries.clear();
self.pending_relocations.clear();
}
pub fn symbol_count(&self) -> usize {
self.symbol_table.len()
}
pub fn pending_count(&self) -> usize {
self.pending_relocations.len()
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
}
impl Default for X86JITLinker {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86JITLinker {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86JITLinker")
.field("symbols", &self.symbol_table.len())
.field("got_entries", &self.got_entries.len())
.field("plt_entries", &self.plt_entries.len())
.field("pending_relocs", &self.pending_relocations.len())
.field("initialized", &self.initialized)
.finish()
}
}
pub struct X86JITEventListener {
pub gdb_interface: X86GDBJITInterface,
pub perf_interface: X86PerfJITInterface,
pub load_callbacks: Vec<Box<dyn Fn(&str) + Send + Sync>>,
pub unload_callbacks: Vec<Box<dyn Fn(&str) + Send + Sync>>,
pub function_callbacks: Vec<Box<dyn Fn(&str, *const u8, u64) + Send + Sync>>,
pub enabled: bool,
pub initialized: bool,
pub event_count: u64,
}
impl X86JITEventListener {
pub fn new() -> Self {
Self {
gdb_interface: X86GDBJITInterface::new(),
perf_interface: X86PerfJITInterface::new(),
load_callbacks: Vec::new(),
unload_callbacks: Vec::new(),
function_callbacks: Vec::new(),
enabled: true,
initialized: false,
event_count: 0,
}
}
pub fn initialize(&mut self) -> Result<(), String> {
if self.initialized {
return Ok(());
}
self.gdb_interface.enable()?;
self.initialized = true;
Ok(())
}
pub fn notify_jit_initialized(&mut self) {
if !self.enabled { return; }
self.event_count += 1;
}
pub fn notify_module_loaded(&mut self, module_name: &str) {
if !self.enabled { return; }
self.gdb_interface.notify_code_loaded(module_name);
for cb in &self.load_callbacks {
cb(module_name);
}
self.event_count += 1;
}
pub fn notify_module_unloaded(&mut self, module_name: &str) {
if !self.enabled { return; }
self.gdb_interface.notify_code_unloaded(module_name);
for cb in &self.unload_callbacks {
cb(module_name);
}
self.event_count += 1;
}
pub fn notify_function_compiled(&mut self, name: &str, address: *const u8) {
if !self.enabled { return; }
self.gdb_interface.notify_function_compiled(name, address);
self.perf_interface.record_function(name, address, 0);
for cb in &self.function_callbacks {
cb(name, address, 0);
}
self.event_count += 1;
}
pub fn on_module_loaded<F: Fn(&str) + Send + Sync + 'static>(&mut self, callback: F) {
self.load_callbacks.push(Box::new(callback));
}
pub fn on_module_unloaded<F: Fn(&str) + Send + Sync + 'static>(&mut self, callback: F) {
self.unload_callbacks.push(Box::new(callback));
}
pub fn on_function_compiled<F: Fn(&str, *const u8, u64) + Send + Sync + 'static>(
&mut self,
callback: F,
) {
self.function_callbacks.push(Box::new(callback));
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn shutdown(&mut self) {
self.gdb_interface.disable();
self.perf_interface.close();
self.load_callbacks.clear();
self.unload_callbacks.clear();
self.function_callbacks.clear();
self.initialized = false;
}
pub fn total_events(&self) -> u64 {
self.event_count
}
}
impl Default for X86JITEventListener {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86JITEventListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86JITEventListener")
.field("enabled", &self.enabled)
.field("callbacks", &(self.load_callbacks.len() + self.function_callbacks.len()))
.field("events", &self.event_count)
.finish()
}
}
pub struct X86GDBJITInterface {
pub enabled: bool,
pub entries: Vec<X86GDBCodeEntry>,
pub descriptor_address: Option<*const u8>,
}
#[derive(Debug, Clone)]
pub struct X86GDBCodeEntry {
pub name: String,
pub code_address: *const u8,
pub code_size: u64,
pub active: bool,
}
impl X86GDBJITInterface {
pub fn new() -> Self {
Self {
enabled: false,
entries: Vec::new(),
descriptor_address: None,
}
}
pub fn enable(&mut self) -> Result<(), String> {
self.enabled = true;
Ok(())
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn notify_code_loaded(&mut self, name: &str) {
if !self.enabled {
return;
}
let entry = X86GDBCodeEntry {
name: name.to_string(),
code_address: std::ptr::null(),
code_size: 0,
active: true,
};
self.entries.push(entry);
}
pub fn notify_code_unloaded(&mut self, name: &str) {
if let Some(entry) = self.entries.iter_mut().find(|e| e.name == name) {
entry.active = false;
}
}
pub fn notify_function_compiled(&mut self, name: &str, address: *const u8) {
if !self.enabled {
return;
}
if let Some(entry) = self.entries.iter_mut().find(|e| e.name == name) {
entry.code_address = address;
} else {
self.entries.push(X86GDBCodeEntry {
name: name.to_string(),
code_address: address,
code_size: 0,
active: true,
});
}
}
pub fn entry_count(&self) -> usize {
self.entries.len()
}
pub fn active_entry_count(&self) -> usize {
self.entries.iter().filter(|e| e.active).count()
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
}
impl Default for X86GDBJITInterface {
fn default() -> Self {
Self::new()
}
}
pub struct X86PerfJITInterface {
pub open: bool,
pub dump_path: String,
pub last_timestamp: u64,
pub bytes_written: u64,
pub recorded_functions: Vec<X86PerfRecord>,
}
#[derive(Debug, Clone)]
pub struct X86PerfRecord {
pub function_name: String,
pub code_address: *const u8,
pub code_size: u64,
pub timestamp: u64,
}
pub mod perf_constants {
pub const JIT_CODE_LOAD: u32 = 0;
pub const JIT_CODE_MOVE: u32 = 1;
pub const JIT_CODE_DEBUG_INFO: u32 = 2;
pub const JIT_CODE_CLOSE: u32 = 3;
pub const JIT_CODE_UNWINDING_INFO: u32 = 4;
pub const JIT_CODE_MAX: u32 = 5;
pub const JITDUMP_MAGIC: u32 = 0x4A695444; pub const JITDUMP_VERSION: u32 = 1;
}
impl X86PerfJITInterface {
pub fn new() -> Self {
Self {
open: false,
dump_path: String::new(),
last_timestamp: 0,
bytes_written: 0,
recorded_functions: Vec::new(),
}
}
pub fn open(&mut self, path: &str) -> Result<(), String> {
self.dump_path = path.to_string();
self.open = true;
self.last_timestamp = 0;
self.bytes_written = 0;
Ok(())
}
pub fn record_function(&mut self, name: &str, address: *const u8, size: u64) {
if !self.open {
return;
}
let record = X86PerfRecord {
function_name: name.to_string(),
code_address: address,
code_size: size,
timestamp: self.last_timestamp,
};
self.recorded_functions.push(record);
self.last_timestamp += 1;
self.bytes_written += size;
}
pub fn record_code_move(
&mut self,
_name: &str,
_old_addr: *const u8,
_new_addr: *const u8,
) {
if !self.open {
return;
}
self.bytes_written += 1;
}
pub fn close(&mut self) {
self.open = false;
}
pub fn recorded_count(&self) -> usize {
self.recorded_functions.len()
}
pub fn is_open(&self) -> bool {
self.open
}
pub fn generate_header(&self, pid: u32, elf_mach: u32) -> Vec<u8> {
let mut header = Vec::with_capacity(56);
header.extend_from_slice(&perf_constants::JITDUMP_MAGIC.to_le_bytes());
header.extend_from_slice(&perf_constants::JITDUMP_VERSION.to_le_bytes());
header.extend_from_slice(&56u32.to_le_bytes()); header.extend_from_slice(&elf_mach.to_le_bytes());
header.extend_from_slice(&0u32.to_le_bytes()); header.extend_from_slice(&pid.to_le_bytes());
header.extend_from_slice(&self.last_timestamp.to_le_bytes());
header.extend_from_slice(&0u64.to_le_bytes()); header
}
pub fn generate_code_load_record(
&self,
pid: u32,
tid: u32,
vma: u64,
code_addr: u64,
code_size: u64,
timestamp: u64,
) -> Vec<u8> {
let mut record = Vec::with_capacity(64);
record.extend_from_slice(&perf_constants::JIT_CODE_LOAD.to_le_bytes());
record.extend_from_slice(&64u32.to_le_bytes()); record.extend_from_slice(&pid.to_le_bytes());
record.extend_from_slice(&tid.to_le_bytes());
record.extend_from_slice(&vma.to_le_bytes());
record.extend_from_slice(&code_addr.to_le_bytes());
record.extend_from_slice(&code_size.to_le_bytes());
record.extend_from_slice(×tamp.to_le_bytes());
record
}
}
impl Default for X86PerfJITInterface {
fn default() -> Self {
Self::new()
}
}
impl Drop for X86PerfJITInterface {
fn drop(&mut self) {
self.close();
}
}
pub struct X86IndirectStubs {
pub stubs: HashMap<String, X86IndirectStub>,
pub initialized: bool,
pub stub_base: Option<*mut u8>,
pub total_stubs: usize,
pub next_stub_id: u64,
}
#[derive(Debug, Clone)]
pub struct X86IndirectStub {
pub function_name: String,
pub stub_address: *mut u8,
pub target_address: Option<*const u8>,
pub patched: bool,
pub size: usize,
pub id: u64,
pub generation: u32,
}
impl X86IndirectStubs {
pub fn new() -> Self {
Self {
stubs: HashMap::new(),
initialized: false,
stub_base: None,
total_stubs: 0,
next_stub_id: 1,
}
}
pub fn initialize(
&mut self,
memory_manager: &mut X86JITMemoryManager,
) -> Result<(), String> {
if self.initialized {
return Ok(());
}
let base = memory_manager.allocate_stub_memory(X86_JIT_MAX_PENDING_LAZY)?;
self.stub_base = Some(base);
self.initialized = true;
Ok(())
}
pub fn create_stub(
&mut self,
function_name: &str,
memory_manager: &mut X86JITMemoryManager,
) -> Result<*const u8, String> {
if !self.initialized {
return Err("stubs manager not initialized".to_string());
}
let offset = self.total_stubs * X86_JIT_STUB_SIZE;
let stub_addr = unsafe {
self.stub_base
.ok_or("no stub base")?
.add(offset)
};
let stub_bytes = Self::generate_stub_bytes(function_name);
unsafe {
std::ptr::copy_nonoverlapping(stub_bytes.as_ptr(), stub_addr, stub_bytes.len());
}
let stub = X86IndirectStub {
function_name: function_name.to_string(),
stub_address: stub_addr,
target_address: None,
patched: false,
size: stub_bytes.len(),
id: self.next_stub_id,
generation: 0,
};
self.stubs.insert(function_name.to_string(), stub);
self.total_stubs += 1;
self.next_stub_id += 1;
Ok(stub_addr as *const u8)
}
fn generate_stub_bytes(function_name: &str) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x68); bytes.extend_from_slice(&0u32.to_le_bytes());
bytes.push(0xe9); bytes.extend_from_slice(&0u32.to_le_bytes());
while bytes.len() < X86_JIT_STUB_SIZE {
bytes.push(0x90); }
let name_bytes = function_name.as_bytes();
let name_offset = bytes.len().saturating_sub(name_bytes.len() + 8);
if name_offset + name_bytes.len() <= bytes.len() {
bytes[name_offset..name_offset + name_bytes.len()].copy_from_slice(name_bytes);
}
bytes
}
pub fn patch_stub(
&mut self,
function_name: &str,
compiled_address: *const u8,
) -> Result<(), String> {
let stub = self
.stubs
.get_mut(function_name)
.ok_or_else(|| format!("stub '{}' not found", function_name))?;
if stub.patched {
}
let patch = Self::generate_direct_jump(compiled_address);
unsafe {
std::ptr::copy_nonoverlapping(patch.as_ptr(), stub.stub_address, patch.len());
}
stub.target_address = Some(compiled_address);
stub.patched = true;
stub.generation += 1;
Ok(())
}
fn generate_direct_jump(target: *const u8) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xe9); bytes.extend_from_slice(&0u32.to_le_bytes()); bytes
}
pub fn get_stub(&self, function_name: &str) -> Option<&X86IndirectStub> {
self.stubs.get(function_name)
}
pub fn is_patched(&self, function_name: &str) -> bool {
self.stubs
.get(function_name)
.map(|s| s.patched)
.unwrap_or(false)
}
pub fn stub_count(&self) -> usize {
self.stubs.len()
}
pub fn patched_count(&self) -> usize {
self.stubs.values().filter(|s| s.patched).count()
}
pub fn stub_names(&self) -> Vec<&str> {
self.stubs.keys().map(|s| s.as_str()).collect()
}
pub fn shutdown(&mut self, _memory_manager: &mut X86JITMemoryManager) {
self.stubs.clear();
self.stub_base = None;
self.total_stubs = 0;
self.initialized = false;
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
}
impl Default for X86IndirectStubs {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86IndirectStubs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86IndirectStubs")
.field("stubs", &self.stubs.len())
.field("initialized", &self.initialized)
.field("total_allocated", &self.total_stubs)
.finish()
}
}
pub struct X86JITConcurrentCompiler {
pub pending: Vec<X86JITCompileJob>,
pub completed: Vec<X86JITCompileJob>,
pub max_workers: usize,
pub running: bool,
pub completed_count: u64,
}
#[derive(Debug, Clone)]
pub struct X86JITCompileJob {
pub module_name: String,
pub function_name: String,
pub priority: u32,
pub job_id: u64,
}
impl X86JITConcurrentCompiler {
pub fn new(workers: usize) -> Self {
Self {
pending: Vec::new(),
completed: Vec::new(),
max_workers: workers.min(X86_JIT_MAX_CONCURRENT_THREADS),
running: true,
completed_count: 0,
}
}
pub fn submit(&mut self, module_name: &str, function_name: &str, priority: u32) -> Result<u64, String> {
if !self.running {
return Err("concurrent compiler is shut down".to_string());
}
let job_id = self.completed_count + self.pending.len() as u64 + 1;
self.pending.push(X86JITCompileJob {
module_name: module_name.to_string(),
function_name: function_name.to_string(),
priority,
job_id,
});
Ok(job_id)
}
pub fn process_all(&mut self) {
let pending = std::mem::take(&mut self.pending);
for job in pending {
self.completed.push(job);
self.completed_count += 1;
}
}
pub fn wait_for_all(&mut self) {
self.process_all();
}
pub fn shutdown(&mut self) {
self.running = false;
self.pending.clear();
}
pub fn pending_count(&self) -> usize {
self.pending.len()
}
pub fn completed_job_count(&self) -> u64 {
self.completed_count
}
pub fn worker_count(&self) -> usize {
self.max_workers
}
}
impl Default for X86JITConcurrentCompiler {
fn default() -> Self {
Self::new(1)
}
}
impl fmt::Debug for X86JITConcurrentCompiler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86JITConcurrentCompiler")
.field("pending", &self.pending.len())
.field("completed", &self.completed_count)
.field("workers", &self.max_workers)
.field("running", &self.running)
.finish()
}
}
pub fn create_x86_jit_linux() -> X86JITFull {
X86JITFull::with_target_triple(X86_JIT_TARGET_TRIPLE)
}
pub fn create_x86_jit_windows() -> X86JITFull {
X86JITFull::with_target_triple(X86_JIT_TARGET_TRIPLE_WIN)
}
pub fn create_minimal_x86_jit() -> X86JITFull {
let mut jit = X86JITFull::new();
jit.set_opt_level(0);
jit.set_debug_mode(false);
jit
}
pub fn create_server_x86_jit() -> X86JITFull {
let mut jit = X86JITFull::new();
jit.set_opt_level(3);
jit.set_debug_mode(false);
jit.orc_jit.ir_transform_layer.add_standard_transforms(3);
jit
}
pub struct X86JITEHFrameRegistrar {
pub frames: Vec<X86EHFrame>,
pub enabled: bool,
pub registered: bool,
}
#[derive(Debug, Clone)]
pub struct X86EHFrame {
pub address: *const u8,
pub size: usize,
pub module_name: String,
pub active: bool,
pub frame_type: X86EHFrameType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86EHFrameType {
CIE,
FDE,
Custom,
}
impl X86JITEHFrameRegistrar {
pub fn new() -> Self {
Self {
frames: Vec::new(),
enabled: true,
registered: false,
}
}
pub fn register_frame(
&mut self,
address: *const u8,
size: usize,
module_name: &str,
frame_type: X86EHFrameType,
) -> Result<(), String> {
if !self.enabled {
return Ok(());
}
if address.is_null() || size == 0 {
return Err("invalid EH frame: null address or zero size".to_string());
}
self.frames.push(X86EHFrame {
address,
size,
module_name: module_name.to_string(),
active: true,
frame_type,
});
self.register_with_runtime(address, size)?;
Ok(())
}
fn register_with_runtime(&mut self, address: *const u8, size: usize) -> Result<(), String> {
if !self.registered {
self.registered = true;
}
let _ = (address, size);
Ok(())
}
pub fn deregister_frame(&mut self, address: *const u8) {
if let Some(frame) = self.frames.iter_mut().find(|f| f.address == address) {
frame.active = false;
}
}
pub fn deregister_module_frames(&mut self, module_name: &str) {
for frame in &mut self.frames {
if frame.module_name == module_name {
frame.active = false;
}
}
}
pub fn active_frame_count(&self) -> usize {
self.frames.iter().filter(|f| f.active).count()
}
pub fn frame_count(&self) -> usize {
self.frames.len()
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn clear(&mut self) {
self.frames.clear();
self.registered = false;
}
}
impl Default for X86JITEHFrameRegistrar {
fn default() -> Self {
Self::new()
}
}
pub struct X86JITCompileCallbackManager {
pub callbacks: Vec<X86CompileCallback>,
pub callback_count: u64,
pub enabled: bool,
}
#[derive(Debug, Clone)]
pub struct X86CompileCallback {
pub function_name: String,
pub stub_address: *const u8,
pub callback_address: *const u8,
pub invoked: bool,
pub compiled_address: Option<*const u8>,
}
impl X86JITCompileCallbackManager {
pub fn new() -> Self {
Self {
callbacks: Vec::new(),
callback_count: 0,
enabled: true,
}
}
pub fn register_callback(
&mut self,
function_name: &str,
stub_address: *const u8,
callback_address: *const u8,
) -> u64 {
let id = self.callback_count;
self.callbacks.push(X86CompileCallback {
function_name: function_name.to_string(),
stub_address,
callback_address,
invoked: false,
compiled_address: None,
});
self.callback_count += 1;
id
}
pub fn get_callback(&self, function_name: &str) -> Option<&X86CompileCallback> {
self.callbacks.iter().find(|c| c.function_name == function_name)
}
pub fn invoke_callback(
&mut self,
function_name: &str,
compiled_addr: *const u8,
) -> Result<(), String> {
let cb = self
.callbacks
.iter_mut()
.find(|c| c.function_name == function_name)
.ok_or_else(|| format!("no callback registered for '{}'", function_name))?;
cb.invoked = true;
cb.compiled_address = Some(compiled_addr);
Ok(())
}
pub fn is_invoked(&self, function_name: &str) -> bool {
self.callbacks
.iter()
.find(|c| c.function_name == function_name)
.map(|c| c.invoked)
.unwrap_or(false)
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn registered_count(&self) -> usize {
self.callbacks.len()
}
pub fn invoked_count(&self) -> usize {
self.callbacks.iter().filter(|c| c.invoked).count()
}
pub fn clear(&mut self) {
self.callbacks.clear();
}
}
impl Default for X86JITCompileCallbackManager {
fn default() -> Self {
Self::new()
}
}
pub struct X86JITMemoryAllocationTracker {
pub allocations: HashMap<u64, X86JITTrackedAlloc>,
pub next_id: u64,
pub lifetime_bytes: u64,
pub current_bytes: u64,
pub peak_bytes: u64,
}
#[derive(Debug, Clone)]
pub struct X86JITTrackedAlloc {
pub id: u64,
pub address: *mut u8,
pub size: usize,
pub kind: X86JITAllocKind,
pub alive: bool,
pub label: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86JITAllocKind {
Code,
ReadOnlyData,
ReadWriteData,
StubMemory,
Trampoline,
EHFrame,
DebugInfo,
General,
}
impl fmt::Display for X86JITAllocKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86JITAllocKind::Code => write!(f, "code"),
X86JITAllocKind::ReadOnlyData => write!(f, "rodata"),
X86JITAllocKind::ReadWriteData => write!(f, "rwdata"),
X86JITAllocKind::StubMemory => write!(f, "stubs"),
X86JITAllocKind::Trampoline => write!(f, "trampoline"),
X86JITAllocKind::EHFrame => write!(f, "eh_frame"),
X86JITAllocKind::DebugInfo => write!(f, "debug_info"),
X86JITAllocKind::General => write!(f, "general"),
}
}
}
impl X86JITMemoryAllocationTracker {
pub fn new() -> Self {
Self {
allocations: HashMap::new(),
next_id: 1,
lifetime_bytes: 0,
current_bytes: 0,
peak_bytes: 0,
}
}
pub fn track(
&mut self,
address: *mut u8,
size: usize,
kind: X86JITAllocKind,
label: &str,
) -> u64 {
let id = self.next_id;
self.next_id += 1;
self.allocations.insert(
id,
X86JITTrackedAlloc {
id,
address,
size,
kind,
alive: true,
label: label.to_string(),
},
);
self.lifetime_bytes += size as u64;
self.current_bytes += size as u64;
if self.current_bytes > self.peak_bytes {
self.peak_bytes = self.current_bytes;
}
id
}
pub fn free(&mut self, id: u64) -> Result<(), String> {
let alloc = self
.allocations
.get_mut(&id)
.ok_or_else(|| format!("allocation {} not found", id))?;
if !alloc.alive {
return Err(format!("allocation {} already freed", id));
}
alloc.alive = false;
self.current_bytes -= alloc.size as u64;
Ok(())
}
pub fn get(&self, id: u64) -> Option<&X86JITTrackedAlloc> {
self.allocations.get(&id)
}
pub fn alive_count(&self) -> usize {
self.allocations.values().filter(|a| a.alive).count()
}
pub fn freed_count(&self) -> usize {
self.allocations.values().filter(|a| !a.alive).count()
}
pub fn by_kind(&self, kind: X86JITAllocKind) -> Vec<&X86JITTrackedAlloc> {
self.allocations
.values()
.filter(|a| a.kind == kind)
.collect()
}
pub fn summary(&self) -> String {
format!(
"JIT Memory: alive={}, freed={}, current={}B, peak={}B, lifetime={}B",
self.alive_count(),
self.freed_count(),
self.current_bytes,
self.peak_bytes,
self.lifetime_bytes,
)
}
pub fn reset(&mut self) {
self.allocations.clear();
self.next_id = 1;
self.lifetime_bytes = 0;
self.current_bytes = 0;
self.peak_bytes = 0;
}
}
impl Default for X86JITMemoryAllocationTracker {
fn default() -> Self {
Self::new()
}
}
pub struct X86LazyCallThroughManager {
pub trampolines: Vec<X86TrampolineSlot>,
pub total_trampolines: usize,
pub trampoline_base: Option<*mut u8>,
pub initialized: bool,
}
#[derive(Debug, Clone)]
pub struct X86TrampolineSlot {
pub index: usize,
pub address: *mut u8,
pub in_use: bool,
pub function_name: String,
pub associated_stub: Option<*mut u8>,
}
const X86_TRAMPOLINE_SIZE: usize = 32;
impl X86LazyCallThroughManager {
pub fn new() -> Self {
Self {
trampolines: Vec::new(),
total_trampolines: 0,
trampoline_base: None,
initialized: false,
}
}
pub fn initialize(
&mut self,
count: usize,
memory_manager: &mut X86JITMemoryManager,
) -> Result<(), String> {
if self.initialized {
return Ok(());
}
let size = count * X86_TRAMPOLINE_SIZE;
let base = memory_manager.allocate_code(size)?;
self.trampoline_base = Some(base);
self.total_trampolines = count;
for i in 0..count {
let addr = unsafe { base.add(i * X86_TRAMPOLINE_SIZE) };
let tramp_bytes = Self::generate_trampoline();
unsafe {
std::ptr::copy_nonoverlapping(tramp_bytes.as_ptr(), addr, tramp_bytes.len());
}
self.trampolines.push(X86TrampolineSlot {
index: i,
address: addr,
in_use: false,
function_name: String::new(),
associated_stub: None,
});
}
self.initialized = true;
Ok(())
}
fn generate_trampoline() -> Vec<u8> {
vec![
0x50, 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xd0, 0x58, 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, ]
}
pub fn allocate_trampoline(
&mut self,
function_name: &str,
stub_address: *mut u8,
) -> Result<*mut u8, String> {
let slot = self
.trampolines
.iter_mut()
.find(|t| !t.in_use)
.ok_or("no free trampoline slots")?;
slot.in_use = true;
slot.function_name = function_name.to_string();
slot.associated_stub = Some(stub_address);
Ok(slot.address)
}
pub fn release_trampoline(&mut self, address: *mut u8) -> Result<(), String> {
let slot = self
.trampolines
.iter_mut()
.find(|t| t.address == address)
.ok_or("trampoline not found")?;
slot.in_use = false;
slot.function_name.clear();
slot.associated_stub = None;
Ok(())
}
pub fn available_count(&self) -> usize {
self.trampolines.iter().filter(|t| !t.in_use).count()
}
pub fn used_count(&self) -> usize {
self.trampolines.iter().filter(|t| t.in_use).count()
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
}
impl Default for X86LazyCallThroughManager {
fn default() -> Self {
Self::new()
}
}
pub struct X86ThreadSafeContext {
pub module_name: String,
pub compiling: bool,
pub errors: Vec<String>,
pub complete: bool,
}
impl X86ThreadSafeContext {
pub fn new(module_name: &str) -> Self {
Self {
module_name: module_name.to_string(),
compiling: false,
errors: Vec::new(),
complete: false,
}
}
pub fn begin_compilation(&mut self) {
self.compiling = true;
self.complete = false;
self.errors.clear();
}
pub fn complete_compilation(&mut self, errors: Vec<String>) -> bool {
self.compiling = false;
self.complete = true;
self.errors = errors;
self.errors.is_empty()
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn get_errors(&self) -> &[String] {
&self.errors
}
pub fn is_idle(&self) -> bool {
!self.compiling && self.complete
}
}
impl fmt::Debug for X86ThreadSafeContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86ThreadSafeContext")
.field("module", &self.module_name)
.field("compiling", &self.compiling)
.field("complete", &self.complete)
.field("errors", &self.errors.len())
.finish()
}
}
#[derive(Debug, Clone)]
pub struct X86JITTargetOptions {
pub cpu: String,
pub features: Vec<String>,
pub code_model: String,
pub reloc_model: String,
pub emit_frame_pointers: bool,
pub enable_fast_isel: bool,
pub enable_global_isel: bool,
pub incremental_linking: bool,
pub jit_stack_size: usize,
pub emit_compact_unwind: bool,
}
impl Default for X86JITTargetOptions {
fn default() -> Self {
Self {
cpu: "generic".to_string(),
features: vec!["sse2".to_string(), "cmov".to_string()],
code_model: "small".to_string(),
reloc_model: "pic".to_string(),
emit_frame_pointers: true,
enable_fast_isel: true,
enable_global_isel: false,
incremental_linking: false,
jit_stack_size: 1024 * 1024, emit_compact_unwind: false,
}
}
}
impl X86JITTargetOptions {
pub fn for_host() -> Self {
Self {
cpu: "native".to_string(),
..Default::default()
}
}
pub fn for_cpu(cpu: &str) -> Self {
Self {
cpu: cpu.to_string(),
..Default::default()
}
}
pub fn add_feature(&mut self, feature: &str) {
if !self.features.iter().any(|f| f == feature) {
self.features.push(feature.to_string());
}
}
pub fn remove_feature(&mut self, feature: &str) {
self.features.retain(|f| f != feature);
}
pub fn set_code_model(&mut self, model: &str) {
self.code_model = model.to_string();
}
pub fn set_reloc_model(&mut self, model: &str) {
self.reloc_model = model.to_string();
}
pub fn feature_string(&self) -> String {
let mut s = String::new();
for feat in &self.features {
if !s.is_empty() {
s.push(',');
}
s.push('+');
s.push_str(feat);
}
s
}
}
pub struct X86CodeGeneratorBridge {
pub options: X86JITTargetOptions,
pub ready: bool,
pub code_cache: HashMap<String, Vec<u8>>,
pub last_compile_time_us: u64,
}
impl X86CodeGeneratorBridge {
pub fn new(options: X86JITTargetOptions) -> Self {
Self {
options,
ready: false,
code_cache: HashMap::new(),
last_compile_time_us: 0,
}
}
pub fn prepare(&mut self) -> Result<(), String> {
self.ready = true;
Ok(())
}
pub fn compile_function(
&mut self,
_function_name: &str,
_ir_body: &[u8],
) -> Result<Vec<u8>, String> {
if !self.ready {
return Err("code generator not ready".to_string());
}
let code = self.generate_default_function_body();
Ok(code)
}
fn generate_default_function_body(&self) -> Vec<u8> {
vec![
0x55, 0x48, 0x89, 0xe5, 0x48, 0xc7, 0xc0, 0x2a, 0x00, 0x00, 0x00, 0x5d, 0xc3, ]
}
pub fn cache_code(&mut self, name: &str, code: Vec<u8>) {
self.code_cache.insert(name.to_string(), code);
}
pub fn get_cached(&self, name: &str) -> Option<&Vec<u8>> {
self.code_cache.get(name)
}
pub fn invalidate_cache(&mut self) {
self.code_cache.clear();
}
pub fn cache_stats(&self) -> (usize, usize) {
let entries = self.code_cache.len();
let total_bytes: usize = self.code_cache.values().map(|v| v.len()).sum();
(entries, total_bytes)
}
}
impl Default for X86CodeGeneratorBridge {
fn default() -> Self {
Self::new(X86JITTargetOptions::default())
}
}
#[derive(Debug, Clone)]
pub struct X86JITDiagnostic {
pub level: X86JITDiagLevel,
pub message: String,
pub module: Option<String>,
pub function: Option<String>,
pub location: Option<X86JITSourceLocation>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86JITDiagLevel {
Note,
Warning,
Error,
Fatal,
}
#[derive(Debug, Clone)]
pub struct X86JITSourceLocation {
pub file: String,
pub line: u32,
pub column: u32,
}
impl X86JITDiagnostic {
pub fn new(level: X86JITDiagLevel, message: &str) -> Self {
Self {
level,
message: message.to_string(),
module: None,
function: None,
location: None,
}
}
pub fn with_module(mut self, module: &str) -> Self {
self.module = Some(module.to_string());
self
}
pub fn with_function(mut self, function: &str) -> Self {
self.function = Some(function.to_string());
self
}
pub fn with_location(mut self, file: &str, line: u32, column: u32) -> Self {
self.location = Some(X86JITSourceLocation {
file: file.to_string(),
line,
column,
});
self
}
pub fn format(&self) -> String {
let level_str = match self.level {
X86JITDiagLevel::Note => "note",
X86JITDiagLevel::Warning => "warning",
X86JITDiagLevel::Error => "error",
X86JITDiagLevel::Fatal => "fatal error",
};
let mut s = format!("JIT {}: {}", level_str, self.message);
if let Some(ref func) = self.function {
s.push_str(&format!(" [in function '{}']", func));
}
if let Some(ref module) = self.module {
s.push_str(&format!(" [in module '{}']", module));
}
if let Some(ref loc) = self.location {
s.push_str(&format!(" at {}:{}:{}", loc.file, loc.line, loc.column));
}
s
}
pub fn is_error(&self) -> bool {
matches!(self.level, X86JITDiagLevel::Error | X86JITDiagLevel::Fatal)
}
pub fn is_warning(&self) -> bool {
matches!(self.level, X86JITDiagLevel::Warning)
}
}
pub struct X86JITDiagnosticCollector {
pub diagnostics: Vec<X86JITDiagnostic>,
pub max_diagnostics: usize,
pub print_to_stderr: bool,
}
impl X86JITDiagnosticCollector {
pub fn new() -> Self {
Self {
diagnostics: Vec::new(),
max_diagnostics: 1000,
print_to_stderr: false,
}
}
pub fn add(&mut self, diag: X86JITDiagnostic) {
if self.print_to_stderr && diag.is_error() {
eprintln!("{}", diag.format());
}
if self.diagnostics.len() < self.max_diagnostics {
self.diagnostics.push(diag);
}
}
pub fn error(&mut self, message: &str) {
self.add(X86JITDiagnostic::new(X86JITDiagLevel::Error, message));
}
pub fn warning(&mut self, message: &str) {
self.add(X86JITDiagnostic::new(X86JITDiagLevel::Warning, message));
}
pub fn note(&mut self, message: &str) {
self.add(X86JITDiagnostic::new(X86JITDiagLevel::Note, message));
}
pub fn error_count(&self) -> usize {
self.diagnostics.iter().filter(|d| d.is_error()).count()
}
pub fn warning_count(&self) -> usize {
self.diagnostics.iter().filter(|d| d.is_warning()).count()
}
pub fn has_errors(&self) -> bool {
self.diagnostics.iter().any(|d| d.is_error())
}
pub fn get_errors(&self) -> Vec<&X86JITDiagnostic> {
self.diagnostics.iter().filter(|d| d.is_error()).collect()
}
pub fn clear(&mut self) {
self.diagnostics.clear();
}
pub fn total_count(&self) -> usize {
self.diagnostics.len()
}
}
impl Default for X86JITDiagnosticCollector {
fn default() -> Self {
Self::new()
}
}
pub struct X86JITSymbolTable {
pub symbols: HashMap<String, X86JITSymbolTableEntry>,
pub absolute: HashMap<*const u8, String>,
}
#[derive(Debug, Clone)]
pub struct X86JITSymbolTableEntry {
pub address: *const u8,
pub is_function: bool,
pub is_data: bool,
pub size: Option<u64>,
pub binding: X86JITSymbolBinding,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86JITSymbolBinding {
Local,
Global,
Weak,
Unique,
}
impl X86JITSymbolTable {
pub fn new() -> Self {
Self {
symbols: HashMap::new(),
absolute: HashMap::new(),
}
}
pub fn add(
&mut self,
name: &str,
address: *const u8,
is_function: bool,
binding: X86JITSymbolBinding,
) {
self.symbols.insert(
name.to_string(),
X86JITSymbolTableEntry {
address,
is_function,
is_data: !is_function,
size: None,
binding,
},
);
self.absolute.insert(address, name.to_string());
}
pub fn lookup(&self, name: &str) -> Option<&X86JITSymbolTableEntry> {
self.symbols.get(name)
}
pub fn lookup_by_address(&self, address: *const u8) -> Option<&str> {
self.absolute.get(&address).map(|s| s.as_str())
}
pub fn remove(&mut self, name: &str) {
if let Some(entry) = self.symbols.remove(name) {
self.absolute.remove(&entry.address);
}
}
pub fn count(&self) -> usize {
self.symbols.len()
}
pub fn names(&self) -> Vec<&str> {
self.symbols.keys().map(|s| s.as_str()).collect()
}
pub fn contains(&self, name: &str) -> bool {
self.symbols.contains_key(name)
}
pub fn clear(&mut self) {
self.symbols.clear();
self.absolute.clear();
}
}
impl Default for X86JITSymbolTable {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct X86JITModuleIdentifier {
pub name: String,
pub timestamp: u64,
pub from_ir: bool,
pub flags: Vec<String>,
pub hash: u64,
}
impl X86JITModuleIdentifier {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
timestamp: 0,
from_ir: true,
flags: Vec::new(),
hash: 0,
}
}
pub fn with_flag(mut self, flag: &str) -> Self {
self.flags.push(flag.to_string());
self
}
pub fn compute_hash(&self) -> u64 {
let mut hash: u64 = 0;
for b in self.name.bytes() {
hash = hash.wrapping_mul(31).wrapping_add(b as u64);
}
for flag in &self.flags {
for b in flag.bytes() {
hash = hash.wrapping_mul(31).wrapping_add(b as u64);
}
}
hash ^ self.timestamp
}
}
impl fmt::Display for X86JITModuleIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}[ts={},flags={}]", self.name, self.timestamp, self.flags.len())
}
}
impl X86JITFull {
pub fn with_full_optimization() -> Self {
let mut jit = Self::new();
jit.set_opt_level(3);
jit.set_debug_mode(false);
jit.orc_jit.ir_transform_layer.add_standard_transforms(3);
jit.orc_jit.object_transform_layer.set_enabled(true);
jit.orc_jit.object_transform_layer.add_standard_transforms();
jit.memory_manager.protect_after_finalize = true;
jit
}
pub fn with_interactive_profile() -> Self {
let mut jit = Self::new();
jit.set_opt_level(1);
jit.set_debug_mode(false);
jit.orc_jit.ir_transform_layer.add_standard_transforms(1);
jit.mcjit.set_lazy_compilation(true);
jit
}
pub fn with_debug_profile() -> Self {
let mut jit = Self::new();
jit.set_opt_level(0);
jit.set_debug_mode(true);
jit.memory_manager.protect_after_finalize = false;
jit
}
pub fn register_external_resolver<
F: Fn(&str) -> Option<*const u8> + Send + Sync + 'static,
>(
&mut self,
resolver: F,
) {
self.linker.set_external_resolver(resolver);
}
pub fn enable_gdb_support(&mut self) {
self.event_listener.gdb_interface.enable().ok();
}
pub fn enable_perf_support(&mut self, dump_path: &str) {
self.event_listener.perf_interface.open(dump_path).ok();
}
pub fn configure_transforms(&mut self, opt_level: u8) {
self.orc_jit.ir_transform_layer.add_standard_transforms(opt_level);
}
pub fn state_summary(&self) -> String {
format!(
"X86JITFull: initialized={}, triple={}, opt={}, orc_dylibs={}, mcjit_modules={}, \
linker_symbols={}, stubs={}, events={}",
self.initialized,
self.target_triple,
self.opt_level,
self.orc_jit.dylib_count(),
self.mcjit.module_count(),
self.linker.symbol_count(),
self.indirect_stubs.stub_count(),
self.event_listener.total_events(),
)
}
}
pub struct X86JITPipelineManager {
pub engine: X86JITFull,
pub stages: Vec<X86JITPipelineStage>,
pub running: bool,
pub pipeline_stats: X86JITPipelineStats,
}
#[derive(Debug, Clone)]
pub struct X86JITPipelineStage {
pub name: String,
pub order: u32,
pub enabled: bool,
pub completed: bool,
}
#[derive(Debug, Clone, Default)]
pub struct X86JITPipelineStats {
pub modules_processed: u64,
pub functions_compiled: u64,
pub pipeline_runs: u64,
pub stage_times: HashMap<String, u64>,
pub total_time_us: u64,
}
impl X86JITPipelineManager {
pub fn new() -> Self {
let mut manager = Self {
engine: X86JITFull::new(),
stages: Vec::new(),
running: false,
pipeline_stats: X86JITPipelineStats::default(),
};
manager.setup_default_pipeline();
manager
}
fn setup_default_pipeline(&mut self) {
let default_stages = vec![
("parse-ir", 1),
("verify-ir", 2),
("transform-ir", 3),
("compile-ir", 4),
("allocate-memory", 5),
("link-object", 6),
("apply-relocations", 7),
("finalize-memory", 8),
("register-eh-frames", 9),
("notify-listeners", 10),
];
for (name, order) in default_stages {
self.stages.push(X86JITPipelineStage {
name: name.to_string(),
order,
enabled: true,
completed: false,
});
}
}
pub fn initialize(&mut self) -> Result<(), String> {
self.engine.initialize()?;
self.running = true;
Ok(())
}
pub fn run_pipeline_pass(&mut self, stage_name: &str) -> Result<(), String> {
if !self.running {
return Err("pipeline not running".to_string());
}
for stage in &mut self.stages {
if stage.name == stage_name {
if stage.completed {
return Ok(());
}
self.execute_stage(stage)?;
stage.completed = true;
self.pipeline_stats.pipeline_runs += 1;
return Ok(());
}
}
Err(format!("stage '{}' not found", stage_name))
}
fn execute_stage(&mut self, stage: &X86JITPipelineStage) -> Result<(), String> {
match stage.name.as_str() {
"transform-ir" => {
Ok(())
}
"compile-ir" => {
self.pipeline_stats.functions_compiled += 1;
Ok(())
}
"allocate-memory" => {
Ok(())
}
"link-object" => {
Ok(())
}
"apply-relocations" => {
self.engine.linker.apply_all_pending()?;
Ok(())
}
"finalize-memory" => {
self.engine.memory_manager.finalize_memory()?;
Ok(())
}
_ => Ok(()),
}
}
pub fn run_full_pipeline(&mut self) -> Result<(), String> {
for i in 0..self.stages.len() {
let stage_name = self.stages[i].name.clone();
if self.stages[i].enabled && !self.stages[i].completed {
self.run_pipeline_pass(&stage_name)?;
}
}
self.pipeline_stats.modules_processed += 1;
Ok(())
}
pub fn disable_stage(&mut self, name: &str) {
if let Some(stage) = self.stages.iter_mut().find(|s| s.name == name) {
stage.enabled = false;
}
}
pub fn enable_stage(&mut self, name: &str) {
if let Some(stage) = self.stages.iter_mut().find(|s| s.name == name) {
stage.enabled = true;
}
}
pub fn enabled_stages(&self) -> Vec<&str> {
self.stages
.iter()
.filter(|s| s.enabled)
.map(|s| s.name.as_str())
.collect()
}
pub fn shutdown(&mut self) {
self.engine.shutdown();
self.running = false;
self.stages.clear();
}
pub fn summary(&self) -> String {
format!(
"Pipeline: running={}, stages={}, enabled={}, modules={}, funcs={}",
self.running,
self.stages.len(),
self.enabled_stages().len(),
self.pipeline_stats.modules_processed,
self.pipeline_stats.functions_compiled,
)
}
}
impl Default for X86JITPipelineManager {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86JITPipelineManager {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86JITPipelineManager")
.field("running", &self.running)
.field("stages", &self.stages.len())
.field("stats", &self.pipeline_stats)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_test_jit() -> X86JITFull {
X86JITFull::new()
}
fn make_test_jit_initialized() -> X86JITFull {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
jit
}
#[test]
fn test_jit_full_new() {
let jit = X86JITFull::new();
assert!(!jit.initialized);
assert_eq!(jit.target_triple, X86_JIT_TARGET_TRIPLE);
assert_eq!(jit.opt_level, X86_JIT_DEFAULT_OPT_LEVEL);
}
#[test]
fn test_jit_full_initialize() {
let mut jit = X86JITFull::new();
assert!(jit.initialize().is_ok());
assert!(jit.initialized);
assert!(jit.initialize().is_ok());
}
#[test]
fn test_jit_full_with_target() {
let jit = X86JITFull::with_target_triple("x86_64-pc-windows-msvc");
assert_eq!(jit.target_triple, "x86_64-pc-windows-msvc");
}
#[test]
fn test_jit_full_for_windows() {
let jit = X86JITFull::for_windows();
assert_eq!(jit.target_triple, X86_JIT_TARGET_TRIPLE_WIN);
}
#[test]
fn test_jit_full_set_opt_level() {
let mut jit = X86JITFull::new();
jit.set_opt_level(3);
assert_eq!(jit.opt_level, 3);
assert_eq!(jit.orc_jit.opt_level, 3);
assert_eq!(jit.mcjit.opt_level, 3);
}
#[test]
fn test_jit_full_set_debug_mode() {
let mut jit = X86JITFull::new();
jit.set_debug_mode(true);
assert!(jit.debug_mode);
assert!(jit.orc_jit.debug_mode);
}
#[test]
fn test_jit_full_lookup_nonexistent() {
let jit = X86JITFull::new();
assert!(jit.lookup_function("nonexistent").is_none());
}
#[test]
fn test_jit_full_default() {
let jit = X86JITFull::default();
assert!(!jit.initialized);
}
#[test]
fn test_jit_full_shutdown() {
let mut jit = make_test_jit_initialized();
jit.shutdown();
assert!(!jit.initialized);
}
#[test]
fn test_jit_full_stats_reset() {
let mut stats = X86JITStats::new();
stats.modules_compiled = 10;
stats.functions_compiled = 42;
stats.reset();
assert_eq!(stats.modules_compiled, 0);
assert_eq!(stats.functions_compiled, 0);
}
#[test]
fn test_jit_full_stats_summary() {
let mut stats = X86JITStats::new();
stats.modules_compiled = 1;
let summary = stats.summary();
assert!(summary.contains("modules=1"));
assert!(summary.contains("funcs=0"));
}
#[test]
fn test_jit_full_debug_fmt() {
let jit = X86JITFull::new();
let debug = format!("{:?}", jit);
assert!(debug.contains("X86JITFull"));
assert!(debug.contains("initialized"));
}
#[test]
fn test_orc_jit_new() {
let orc = X86ORCJIT::new();
assert!(!orc.initialized);
assert_eq!(orc.dylib_count(), 0);
}
#[test]
fn test_orc_jit_initialize() {
let mut orc = X86ORCJIT::new();
assert!(orc.initialize().is_ok());
assert!(orc.initialized);
}
#[test]
fn test_orc_jit_create_dylib() {
let mut orc = X86ORCJIT::new();
assert!(orc.create_dylib("test").is_ok());
assert_eq!(orc.dylib_count(), 1);
}
#[test]
fn test_orc_jit_create_duplicate_dylib() {
let mut orc = X86ORCJIT::new();
assert!(orc.create_dylib("test").is_ok());
assert!(orc.create_dylib("test").is_err());
}
#[test]
fn test_orc_jit_remove_dylib() {
let mut orc = X86ORCJIT::new();
orc.create_dylib("test").unwrap();
assert!(orc.remove_dylib("test").is_ok());
assert_eq!(orc.dylib_count(), 0);
}
#[test]
fn test_orc_jit_remove_nonexistent_dylib() {
let mut orc = X86ORCJIT::new();
assert!(orc.remove_dylib("nonexistent").is_err());
}
#[test]
fn test_orc_jit_lookup_nonexistent() {
let orc = X86ORCJIT::new();
assert!(orc.lookup("nonexistent").is_none());
}
#[test]
fn test_orc_jit_set_opt_level() {
let mut orc = X86ORCJIT::new();
orc.set_opt_level(3);
assert_eq!(orc.opt_level, 3);
}
#[test]
fn test_orc_jit_set_debug_mode() {
let mut orc = X86ORCJIT::new();
orc.set_debug_mode(true);
assert!(orc.debug_mode);
orc.set_debug_mode(false);
assert!(!orc.debug_mode);
}
#[test]
fn test_orc_jit_finalize() {
let mut orc = X86ORCJIT::new();
orc.create_dylib("test").unwrap();
assert!(orc.finalize().is_ok());
}
#[test]
fn test_orc_jit_shutdown() {
let mut orc = X86ORCJIT::new();
orc.create_dylib("a").unwrap();
orc.create_dylib("b").unwrap();
orc.shutdown();
assert_eq!(orc.dylib_count(), 0);
assert!(!orc.initialized);
}
#[test]
fn test_orc_jit_default() {
let orc = X86ORCJIT::default();
assert!(!orc.initialized);
}
#[test]
fn test_orc_jit_debug() {
let orc = X86ORCJIT::new();
let dbg = format!("{:?}", orc);
assert!(dbg.contains("X86ORCJIT"));
}
#[test]
fn test_exec_session_new() {
let session = X86ORCExecutionSession::new();
assert!(!session.finalized);
assert_eq!(session.pending_count(), 0);
}
#[test]
fn test_exec_session_add_unit() {
let mut session = X86ORCExecutionSession::new();
let mu = X86MaterializationUnit::new(
"test",
vec!["func".to_string()],
X86MaterializationKind::IR,
);
session.add_materialization_unit(mu);
assert_eq!(session.pending_count(), 1);
}
#[test]
fn test_exec_session_dispatch_empty() {
let mut session = X86ORCExecutionSession::new();
assert!(session.dispatch_materialization().is_ok());
}
#[test]
fn test_exec_session_lookup_missing() {
let session = X86ORCExecutionSession::new();
assert!(session.lookup_symbol("nonexistent").is_none());
}
#[test]
fn test_exec_session_finalize() {
let mut session = X86ORCExecutionSession::new();
assert!(session.finalize().is_ok());
assert!(session.is_finalized());
}
#[test]
fn test_exec_session_double_finalize() {
let mut session = X86ORCExecutionSession::new();
session.finalize().unwrap();
assert!(session.finalize().is_err());
}
#[test]
fn test_exec_session_errors() {
let mut session = X86ORCExecutionSession::new();
session.errors.push("e1".to_string());
session.errors.push("e2".to_string());
assert_eq!(session.get_errors().len(), 2);
session.clear_errors();
assert_eq!(session.get_errors().len(), 0);
}
#[test]
fn test_exec_session_shutdown() {
let mut session = X86ORCExecutionSession::new();
session.shutdown();
assert!(session.is_finalized());
assert_eq!(session.pending_count(), 0);
}
#[test]
fn test_dylib_new() {
let dylib = X86JITDylib::new("main");
assert_eq!(dylib.name, "main");
assert_eq!(dylib.module_count(), 0);
assert!(!dylib.is_finalized());
}
#[test]
fn test_dylib_define_symbol() {
let mut dylib = X86JITDylib::new("main");
let addr: *const u8 = 0x1000 as *const u8;
dylib.define_symbol("foo", addr, X86JITSymbolFlags::exported_function());
assert_eq!(dylib.lookup("foo"), Some(addr));
}
#[test]
fn test_dylib_lookup_missing() {
let dylib = X86JITDylib::new("main");
assert!(dylib.lookup("missing").is_none());
}
#[test]
fn test_dylib_add_materialization_unit() {
let mut dylib = X86JITDylib::new("main");
let mu = X86MaterializationUnit::new(
"mod",
vec!["func".to_string()],
X86MaterializationKind::IR,
);
dylib.add_materialization_unit(mu);
assert_eq!(dylib.module_count(), 1);
}
#[test]
fn test_dylib_finalize() {
let mut dylib = X86JITDylib::new("main");
assert!(dylib.finalize().is_ok());
assert!(dylib.is_finalized());
}
#[test]
fn test_dylib_double_finalize() {
let mut dylib = X86JITDylib::new("main");
dylib.finalize().unwrap();
assert!(dylib.finalize().is_err());
}
#[test]
fn test_dylib_symbol_names() {
let mut dylib = X86JITDylib::new("main");
dylib.define_symbol("a", 0x100 as *const u8, X86JITSymbolFlags::exported_function());
dylib.define_symbol("b", 0x200 as *const u8, X86JITSymbolFlags::internal_data());
let names = dylib.symbol_names();
assert!(names.contains(&"a".to_string()));
assert!(names.contains(&"b".to_string()));
}
#[test]
fn test_dylib_add_generator() {
let mut dylib = X86JITDylib::new("main");
let gen = X86StaticDefinitionGenerator::new("static");
dylib.add_generator(gen);
}
#[test]
fn test_dylib_debug() {
let dylib = X86JITDylib::new("main");
let dbg = format!("{:?}", dylib);
assert!(dbg.contains("main"));
}
#[test]
fn test_mu_new() {
let mu = X86MaterializationUnit::new(
"test",
vec!["sym".to_string()],
X86MaterializationKind::IR,
);
assert!(mu.provides_symbol("sym"));
assert!(!mu.provides_symbol("other"));
assert!(!mu.compiled);
assert!(!mu.linked);
}
#[test]
fn test_mu_verify_empty() {
let mu = X86MaterializationUnit::new("empty", vec![], X86MaterializationKind::IR);
assert!(mu.verify().is_err());
}
#[test]
fn test_mu_verify_valid() {
let mu = X86MaterializationUnit::new(
"valid",
vec!["f".to_string()],
X86MaterializationKind::IR,
);
assert!(mu.verify().is_ok());
}
#[test]
fn test_mu_record_address() {
let mut mu = X86MaterializationUnit::new(
"test",
vec!["f".to_string()],
X86MaterializationKind::IR,
);
mu.record_address("f", 0x1000 as *const u8);
assert_eq!(mu.get_symbol_address("f"), Some(0x1000 as *const u8));
}
#[test]
fn test_mu_mark_compiled() {
let mut mu = X86MaterializationUnit::new(
"test",
vec!["f".to_string()],
X86MaterializationKind::IR,
);
mu.mark_compiled();
assert!(mu.compiled);
}
#[test]
fn test_mu_mark_linked() {
let mut mu = X86MaterializationUnit::new(
"test",
vec!["f".to_string()],
X86MaterializationKind::IR,
);
mu.mark_linked();
assert!(mu.compiled);
assert!(mu.linked);
}
#[test]
fn test_mu_is_ir() {
let mu = X86MaterializationUnit::new(
"test",
vec!["f".to_string()],
X86MaterializationKind::IR,
);
assert!(mu.is_ir());
assert!(!mu.is_object());
}
#[test]
fn test_mu_is_object() {
let mu = X86MaterializationUnit::new(
"test",
vec!["f".to_string()],
X86MaterializationKind::Object,
);
assert!(mu.is_object());
assert!(!mu.is_ir());
}
#[test]
fn test_ir_mu_new() {
let context = crate::context::LLVMContext::new();
let module = Module::new("test_mod", &context).unwrap();
let ir_mu = X86IRMaterializationUnit::new(&module);
assert_eq!(ir_mu.module_name(), "test_mod");
}
#[test]
fn test_object_mu_new() {
let data = vec![0x90u8; 64];
let omu = X86ObjectMaterializationUnit::new(data.clone());
assert_eq!(omu.size(), 64);
assert!(!omu.has_unresolved_relocations());
}
#[test]
fn test_object_mu_parse_empty() {
let mut omu = X86ObjectMaterializationUnit::new(vec![]);
assert!(omu.parse().is_err());
}
#[test]
fn test_object_mu_debug() {
let omu = X86ObjectMaterializationUnit::new(vec![0; 64]);
let dbg = format!("{:?}", omu);
assert!(dbg.contains("X86ObjectMaterializationUnit"));
}
#[test]
fn test_static_generator_new() {
let gen = X86StaticDefinitionGenerator::new("test");
assert_eq!(gen.name(), "test");
}
#[test]
fn test_static_generator_register() {
let mut gen = X86StaticDefinitionGenerator::new("test");
gen.register("foo", 0x1000 as *const u8);
assert!(gen.contains("foo"));
assert!(!gen.contains("bar"));
}
#[test]
fn test_static_generator_try_generate() {
let mut gen = X86StaticDefinitionGenerator::new("test");
gen.register("foo", 0x4000 as *const u8);
assert_eq!(gen.try_to_generate("foo"), Some(0x4000 as *const u8));
assert_eq!(gen.try_to_generate("bar"), None);
}
#[test]
fn test_static_generator_register_many() {
let mut gen = X86StaticDefinitionGenerator::new("test");
gen.register_many(&[("a", 0x1 as *const u8), ("b", 0x2 as *const u8)]);
assert!(gen.contains("a"));
assert!(gen.contains("b"));
}
#[test]
fn test_static_generator_remove() {
let mut gen = X86StaticDefinitionGenerator::new("test");
gen.register("foo", 0x1000 as *const u8);
gen.remove("foo");
assert!(!gen.contains("foo"));
}
#[test]
fn test_dynamic_generator_new() {
let gen = X86DynamicDefinitionGenerator::new("dyn");
assert_eq!(gen.name(), "dyn");
}
#[test]
fn test_dynamic_generator_override() {
let mut gen = X86DynamicDefinitionGenerator::new("dyn");
gen.override_symbol("malloc", 0xDEADBEEF as *const u8);
assert_eq!(gen.try_to_generate("malloc"), Some(0xDEADBEEF as *const u8));
}
#[test]
fn test_dynamic_generator_library_path() {
let mut gen = X86DynamicDefinitionGenerator::new("dyn");
gen.add_library_path("/usr/lib/libc.so");
}
#[test]
fn test_custom_generator_new() {
let gen = X86CustomDefinitionGenerator::new("custom");
assert_eq!(gen.name(), "custom");
}
#[test]
fn test_custom_generator_resolver() {
let mut gen = X86CustomDefinitionGenerator::new("custom");
gen.set_resolver(|name| {
if name == "test_func" {
Some(0xCAFE as *const u8)
} else {
None
}
});
assert_eq!(gen.try_to_generate("test_func"), Some(0xCAFE as *const u8));
assert_eq!(gen.try_to_generate("other"), None);
}
#[test]
fn test_ir_compile_layer_new() {
let layer = X86IRCompileLayer::new();
assert_eq!(layer.opt_level, X86_JIT_DEFAULT_OPT_LEVEL);
assert!(!layer.debug_info);
}
#[test]
fn test_ir_compile_layer_set_opt_level() {
let mut layer = X86IRCompileLayer::new();
layer.set_opt_level(3);
assert_eq!(layer.opt_level, 3);
layer.set_opt_level(5); assert_eq!(layer.opt_level, 3);
}
#[test]
fn test_ir_compile_layer_debug_info() {
let mut layer = X86IRCompileLayer::new();
layer.set_debug_info(true);
assert!(layer.debug_info);
layer.set_debug_info(false);
assert!(!layer.debug_info);
}
#[test]
fn test_ir_compile_layer_generate_stub() {
let layer = X86IRCompileLayer::new();
let stub = layer.generate_stub();
assert!(!stub.is_empty());
assert_eq!(stub.last(), Some(&0xc3));
}
#[test]
fn test_ir_compile_layer_cache() {
let mut layer = X86IRCompileLayer::new();
layer.cache_object("key", vec![1, 2, 3]);
layer.invalidate_cache();
}
#[test]
fn test_ir_compile_layer_default() {
let layer = X86IRCompileLayer::default();
assert!(!layer.debug_info);
}
#[test]
fn test_ir_compile_layer_debug() {
let layer = X86IRCompileLayer::new();
let dbg = format!("{:?}", layer);
assert!(dbg.contains("X86IRCompileLayer"));
}
#[test]
fn test_object_linking_layer_new() {
let layer = X86ObjectLinkingLayer::new();
assert_eq!(layer.symbol_count(), 0);
}
#[test]
fn test_object_linking_layer_register_symbol() {
let mut layer = X86ObjectLinkingLayer::new();
layer.register_symbol("foo", 0x1000 as *const u8);
assert_eq!(layer.symbol_count(), 1);
assert_eq!(layer.find_symbol("foo"), Some(0x1000 as *const u8));
}
#[test]
fn test_object_linking_layer_find_missing() {
let layer = X86ObjectLinkingLayer::new();
assert_eq!(layer.find_symbol("missing"), None);
}
#[test]
fn test_object_linking_layer_record_allocation() {
let mut layer = X86ObjectLinkingLayer::new();
layer.record_allocation(0x1000 as *mut u8, 4096, true);
}
#[test]
fn test_object_linking_layer_clear() {
let mut layer = X86ObjectLinkingLayer::new();
layer.register_symbol("x", 0x100 as *const u8);
layer.clear();
assert_eq!(layer.symbol_count(), 0);
}
#[test]
fn test_ir_transform_layer_new() {
let layer = X86IRTransformLayer::new();
assert!(layer.enabled);
assert_eq!(layer.transforms.len(), 0);
}
#[test]
fn test_ir_transform_layer_add_transform() {
let mut layer = X86IRTransformLayer::new();
layer.add_transform("mem2reg", 10);
layer.add_transform("gvn", 40);
assert_eq!(layer.transforms.len(), 2);
}
#[test]
fn test_ir_transform_layer_add_standard_o0() {
let mut layer = X86IRTransformLayer::new();
layer.add_standard_transforms(0);
assert!(layer.transforms.len() >= 2);
}
#[test]
fn test_ir_transform_layer_add_standard_o2() {
let mut layer = X86IRTransformLayer::new();
layer.add_standard_transforms(2);
assert!(layer.transforms.len() > 4);
}
#[test]
fn test_ir_transform_layer_add_standard_o3() {
let mut layer = X86IRTransformLayer::new();
layer.add_standard_transforms(3);
assert!(layer.transforms.len() > 6);
}
#[test]
fn test_ir_transform_layer_disable_enable() {
let mut layer = X86IRTransformLayer::new();
layer.add_transform("gvn", 1);
layer.disable_transform("gvn");
layer.enable_transform("gvn");
}
#[test]
fn test_ir_transform_layer_names() {
let mut layer = X86IRTransformLayer::new();
layer.add_transform("test", 1);
assert_eq!(layer.transform_names(), vec!["test"]);
}
#[test]
fn test_ir_transform_layer_sorted() {
let mut layer = X86IRTransformLayer::new();
layer.add_transform("B", 20);
layer.add_transform("A", 10);
layer.add_transform("C", 30);
let sorted = layer.sorted_transforms();
assert_eq!(sorted[0].name, "A");
assert_eq!(sorted[1].name, "B");
assert_eq!(sorted[2].name, "C");
}
#[test]
fn test_ir_transform_layer_debug() {
let mut layer = X86IRTransformLayer::new();
layer.add_transform("test", 1);
let dbg = format!("{:?}", layer);
assert!(dbg.contains("X86IRTransformLayer"));
}
#[test]
fn test_object_transform_layer_new() {
let layer = X86ObjectTransformLayer::new();
assert!(!layer.enabled);
assert_eq!(layer.transform_count(), 0);
}
#[test]
fn test_object_transform_layer_add_transform() {
let mut layer = X86ObjectTransformLayer::new();
layer.add_transform("peephole", 10);
assert_eq!(layer.transform_count(), 1);
}
#[test]
fn test_object_transform_layer_add_standard() {
let mut layer = X86ObjectTransformLayer::new();
layer.add_standard_transforms();
assert_eq!(layer.transform_count(), 3);
}
#[test]
fn test_object_transform_layer_set_enabled() {
let mut layer = X86ObjectTransformLayer::new();
layer.set_enabled(true);
assert!(layer.enabled);
}
#[test]
fn test_resource_tracker_new() {
let rt = X86JITResourceTracker::new();
assert_eq!(rt.memory_count(), 0);
assert_eq!(rt.symbol_count(), 0);
assert!(!rt.has_resources());
}
#[test]
fn test_resource_tracker_track_memory() {
let mut rt = X86JITResourceTracker::new();
rt.track_memory(0x1000 as *mut u8, 4096);
assert_eq!(rt.memory_count(), 1);
assert_eq!(rt.total_allocated, 4096);
}
#[test]
fn test_resource_tracker_track_symbol() {
let mut rt = X86JITResourceTracker::new();
rt.track_symbol("foo");
assert_eq!(rt.symbol_count(), 1);
assert!(rt.has_resources());
}
#[test]
fn test_resource_tracker_release_all() {
let mut rt = X86JITResourceTracker::new();
rt.track_memory(0x1000 as *mut u8, 100);
rt.track_symbol("sym");
rt.release_all();
assert_eq!(rt.memory_count(), 0);
assert_eq!(rt.symbol_count(), 0);
assert!(!rt.has_resources());
}
#[test]
fn test_orc_lookup_new() {
let lookup = X86ORCJITLookup::new();
assert!(lookup.search_process);
}
#[test]
fn test_orc_lookup_add_dylib() {
let mut lookup = X86ORCJITLookup::new();
lookup.add_dylib("main");
lookup.add_dylib("main"); assert_eq!(lookup.dylibs.len(), 1);
}
#[test]
fn test_orc_lookup_remove_dylib() {
let mut lookup = X86ORCJITLookup::new();
lookup.add_dylib("main");
lookup.add_dylib("aux");
lookup.remove_dylib("main");
assert_eq!(lookup.dylibs, vec!["aux"]);
}
#[test]
fn test_orc_lookup_invalidate_cache() {
let mut lookup = X86ORCJITLookup::new();
lookup.invalidate_cache();
}
#[test]
fn test_orc_lookup_has_pending() {
let lookup = X86ORCJITLookup::new();
assert!(!lookup.has_pending());
}
#[test]
fn test_mcjit_new() {
let mcjit = X86MCJIT::new();
assert_eq!(mcjit.module_count(), 0);
assert_eq!(mcjit.function_count(), 0);
assert!(!mcjit.finalized);
}
#[test]
fn test_mcjit_initialize() {
let mut mcjit = X86MCJIT::new();
assert!(mcjit.initialize("x86_64-unknown-linux-gnu").is_ok());
assert_eq!(mcjit.target_triple, "x86_64-unknown-linux-gnu");
}
#[test]
fn test_mcjit_set_opt_level() {
let mut mcjit = X86MCJIT::new();
mcjit.set_opt_level(3);
assert_eq!(mcjit.opt_level, 3);
}
#[test]
fn test_mcjit_set_lazy() {
let mut mcjit = X86MCJIT::new();
mcjit.set_lazy_compilation(true);
assert!(mcjit.lazy_compilation);
}
#[test]
fn test_mcjit_finalize_memory() {
let mut mcjit = X86MCJIT::new();
assert!(mcjit.finalize_memory().is_ok());
assert!(mcjit.is_finalized());
}
#[test]
fn test_mcjit_double_finalize() {
let mut mcjit = X86MCJIT::new();
mcjit.finalize_memory().unwrap();
assert!(mcjit.finalize_memory().is_err());
}
#[test]
fn test_mcjit_function_names() {
let mcjit = X86MCJIT::new();
assert!(mcjit.function_names().is_empty());
}
#[test]
fn test_mcjit_remove_function() {
let mut mcjit = X86MCJIT::new();
mcjit.compiled_functions.insert("f".to_string(), 0x100 as *const u8);
mcjit.remove_function("f");
assert_eq!(mcjit.function_count(), 0);
}
#[test]
fn test_mcjit_clear() {
let mut mcjit = X86MCJIT::new();
mcjit.compiled_functions.insert("f".to_string(), 0x100 as *const u8);
mcjit.clear();
assert_eq!(mcjit.function_count(), 0);
assert!(!mcjit.finalized);
}
#[test]
fn test_mcjit_shutdown() {
let mut mcjit = X86MCJIT::new();
mcjit.shutdown();
assert_eq!(mcjit.module_count(), 0);
assert_eq!(mcjit.function_count(), 0);
}
#[test]
fn test_mcjit_get_func_ptr_missing() {
let mcjit = X86MCJIT::new();
assert!(mcjit.get_function_pointer("nonexistent").is_none());
}
#[test]
fn test_mcjit_default() {
let mcjit = X86MCJIT::default();
assert!(!mcjit.lazy_compilation);
}
#[test]
fn test_mcjit_debug() {
let mcjit = X86MCJIT::new();
let dbg = format!("{:?}", mcjit);
assert!(dbg.contains("X86MCJIT"));
}
#[test]
fn test_mcjit_mm_new() {
let mm = X86MCJITMemoryManager::new();
assert_eq!(mm.total_allocated(), 0);
assert!(!mm.finalized);
}
#[test]
fn test_mcjit_mm_initialize() {
let mut mm = X86MCJITMemoryManager::new();
assert!(mm.initialize().is_ok());
}
#[test]
fn test_mcjit_mm_allocate_code() {
let mut mm = X86MCJITMemoryManager::new();
let addr = mm.allocate_code_section(1024);
assert!(addr.is_ok());
assert_eq!(mm.code_section_count(), 1);
assert!(mm.total_allocated() > 0);
}
#[test]
fn test_mcjit_mm_allocate_data() {
let mut mm = X86MCJITMemoryManager::new();
let addr = mm.allocate_data_section(512, true);
assert!(addr.is_ok());
assert_eq!(mm.data_section_count(), 1);
}
#[test]
fn test_mcjit_mm_finalize() {
let mut mm = X86MCJITMemoryManager::new();
mm.allocate_code_section(4096).unwrap();
assert!(mm.finalize_memory().is_ok());
assert!(mm.finalized);
}
#[test]
fn test_mcjit_mm_deallocate_all() {
let mut mm = X86MCJITMemoryManager::new();
mm.allocate_code_section(4096).unwrap();
mm.deallocate_all();
assert_eq!(mm.total_allocated(), 0);
}
#[test]
fn test_mcjit_mm_default() {
let mm = X86MCJITMemoryManager::default();
assert!(!mm.finalized);
}
#[test]
fn test_mcjit_mm_debug() {
let mm = X86MCJITMemoryManager::new();
let dbg = format!("{:?}", mm);
assert!(dbg.contains("X86MCJITMemoryManager"));
}
#[test]
fn test_jit_memory_manager_new() {
let mm = X86JITMemoryManager::new();
assert!(!mm.initialized);
assert!(!mm.finalized);
assert_eq!(mm.total_allocated(), 0);
}
#[test]
fn test_jit_memory_manager_initialize() {
let mut mm = X86JITMemoryManager::new();
assert!(mm.initialize().is_ok());
assert!(mm.is_initialized());
}
#[test]
fn test_jit_memory_manager_allocate_code() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let addr = mm.allocate_code(4096).unwrap();
assert!(!addr.is_null());
assert!(mm.total_code_bytes > 0);
}
#[test]
fn test_jit_memory_manager_allocate_data() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let addr = mm.allocate_data(1024).unwrap();
assert!(!addr.is_null());
assert!(mm.total_data_bytes > 0);
}
#[test]
fn test_jit_memory_manager_allocate_section() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let addr = mm.allocate_section(".text", 4096, "rx").unwrap();
assert!(!addr.is_null());
}
#[test]
fn test_jit_memory_manager_allocate_stub() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let addr = mm.allocate_stub_memory(4).unwrap();
assert!(!addr.is_null());
}
#[test]
fn test_jit_memory_manager_finalize() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
mm.allocate_code(4096).unwrap();
assert!(mm.finalize_memory().is_ok());
assert!(mm.is_finalized());
}
#[test]
fn test_jit_memory_manager_deallocate_all() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
mm.allocate_code(4096).unwrap();
mm.allocate_data(4096).unwrap();
mm.deallocate_all();
assert_eq!(mm.total_allocated(), 0);
assert!(!mm.finalized);
}
#[test]
fn test_jit_memory_manager_default() {
let mm = X86JITMemoryManager::default();
assert!(!mm.initialized);
}
#[test]
fn test_jit_memory_manager_debug() {
let mm = X86JITMemoryManager::new();
let dbg = format!("{:?}", mm);
assert!(dbg.contains("X86JITMemoryManager"));
}
#[test]
fn test_jit_linker_new() {
let linker = X86JITLinker::new();
assert_eq!(linker.symbol_count(), 0);
assert_eq!(linker.pending_count(), 0);
assert!(!linker.is_initialized());
}
#[test]
fn test_jit_linker_initialize() {
let mut linker = X86JITLinker::new();
assert!(linker.initialize().is_ok());
assert!(linker.is_initialized());
}
#[test]
fn test_jit_linker_define_symbol() {
let mut linker = X86JITLinker::new();
linker.define_symbol("foo", 0x1000 as *const u8);
assert_eq!(linker.symbol_count(), 1);
assert_eq!(linker.resolve_symbol("foo"), Some(0x1000 as *const u8));
}
#[test]
fn test_jit_linker_define_symbols() {
let mut linker = X86JITLinker::new();
linker.define_symbols(&[("a", 0x1 as *const u8), ("b", 0x2 as *const u8)]);
assert_eq!(linker.symbol_count(), 2);
}
#[test]
fn test_jit_linker_resolve_missing() {
let linker = X86JITLinker::new();
assert_eq!(linker.resolve_symbol("missing"), None);
}
#[test]
fn test_jit_linker_external_resolver() {
let mut linker = X86JITLinker::new();
linker.set_external_resolver(|name| {
if name == "ext_func" {
Some(0xBEEF as *const u8)
} else {
None
}
});
assert_eq!(linker.resolve_symbol("ext_func"), Some(0xBEEF as *const u8));
assert_eq!(linker.resolve_symbol("unknown"), None);
}
#[test]
fn test_jit_linker_apply_relocation_pc32() {
let mut linker = X86JITLinker::new();
linker.define_symbol("target", 0xDEADBEEF as *const u8);
let mut buf = [0u8; 8];
let addr = buf.as_mut_ptr();
assert!(linker.apply_relocation(addr, "target", x86_reloc::R_X86_64_PC32, 0).is_ok());
}
#[test]
fn test_jit_linker_apply_relocation_64() {
let mut linker = X86JITLinker::new();
linker.define_symbol("target", 0xCAFE as *const u8);
let mut buf = [0u8; 8];
let addr = buf.as_mut_ptr();
assert!(linker.apply_relocation(addr, "target", x86_reloc::R_X86_64_64, 0).is_ok());
}
#[test]
fn test_jit_linker_pending_relocation() {
let mut linker = X86JITLinker::new();
let mut buf = [0u8; 8];
let addr = buf.as_mut_ptr();
assert!(linker.apply_relocation(addr, "unknown", x86_reloc::R_X86_64_PC32, 0).is_ok());
assert_eq!(linker.pending_count(), 1);
}
#[test]
fn test_jit_linker_weak_resolve() {
let mut linker = X86JITLinker::new();
linker.define_symbol("strong", 0x100 as *const u8);
assert_eq!(linker.resolve_weak("weak", "strong"), Some(0x100 as *const u8));
}
#[test]
fn test_jit_linker_clear() {
let mut linker = X86JITLinker::new();
linker.define_symbol("x", 0x1 as *const u8);
linker.clear();
assert_eq!(linker.symbol_count(), 0);
assert_eq!(linker.pending_count(), 0);
}
#[test]
fn test_jit_linker_is_pc_relative() {
assert!(X86JITLinker::is_pc_relative(x86_reloc::R_X86_64_PC32));
assert!(X86JITLinker::is_pc_relative(x86_reloc::R_X86_64_PLT32));
assert!(X86JITLinker::is_pc_relative(x86_reloc::R_X86_64_GOTPCREL));
assert!(!X86JITLinker::is_pc_relative(x86_reloc::R_X86_64_64));
assert!(!X86JITLinker::is_pc_relative(x86_reloc::R_X86_64_32));
}
#[test]
fn test_jit_linker_debug() {
let linker = X86JITLinker::new();
let dbg = format!("{:?}", linker);
assert!(dbg.contains("X86JITLinker"));
}
#[test]
fn test_event_listener_new() {
let listener = X86JITEventListener::new();
assert!(listener.enabled);
assert!(!listener.initialized);
assert_eq!(listener.total_events(), 0);
}
#[test]
fn test_event_listener_initialize() {
let mut listener = X86JITEventListener::new();
assert!(listener.initialize().is_ok());
assert!(listener.initialized);
}
#[test]
fn test_event_listener_notify_module_loaded() {
let mut listener = X86JITEventListener::new();
listener.notify_module_loaded("mod");
assert!(listener.total_events() > 0);
}
#[test]
fn test_event_listener_notify_module_unloaded() {
let mut listener = X86JITEventListener::new();
listener.notify_module_unloaded("mod");
assert!(listener.total_events() > 0);
}
#[test]
fn test_event_listener_notify_function() {
let mut listener = X86JITEventListener::new();
listener.notify_function_compiled("func", 0x1000 as *const u8);
assert!(listener.total_events() > 0);
}
#[test]
fn test_event_listener_callbacks() {
let mut listener = X86JITEventListener::new();
listener.on_module_loaded(|_name| {});
listener.on_module_unloaded(|_name| {});
listener.on_function_compiled(|_name, _addr, _size| {});
}
#[test]
fn test_event_listener_enable_disable() {
let mut listener = X86JITEventListener::new();
listener.disable();
assert!(!listener.enabled);
listener.enable();
assert!(listener.enabled);
}
#[test]
fn test_event_listener_shutdown() {
let mut listener = X86JITEventListener::new();
listener.shutdown();
assert!(!listener.initialized);
}
#[test]
fn test_event_listener_debug() {
let listener = X86JITEventListener::new();
let dbg = format!("{:?}", listener);
assert!(dbg.contains("X86JITEventListener"));
}
#[test]
fn test_gdb_jit_new() {
let gdb = X86GDBJITInterface::new();
assert!(!gdb.is_enabled());
assert_eq!(gdb.entry_count(), 0);
}
#[test]
fn test_gdb_jit_enable_disable() {
let mut gdb = X86GDBJITInterface::new();
assert!(gdb.enable().is_ok());
assert!(gdb.is_enabled());
gdb.disable();
assert!(!gdb.is_enabled());
}
#[test]
fn test_gdb_jit_notify_code_loaded() {
let mut gdb = X86GDBJITInterface::new();
gdb.enable().unwrap();
gdb.notify_code_loaded("module");
assert_eq!(gdb.entry_count(), 1);
}
#[test]
fn test_gdb_jit_notify_code_unloaded() {
let mut gdb = X86GDBJITInterface::new();
gdb.enable().unwrap();
gdb.notify_code_loaded("module");
gdb.notify_code_unloaded("module");
assert_eq!(gdb.active_entry_count(), 0);
}
#[test]
fn test_gdb_jit_notify_function() {
let mut gdb = X86GDBJITInterface::new();
gdb.enable().unwrap();
gdb.notify_function_compiled("func", 0x1000 as *const u8);
assert_eq!(gdb.entry_count(), 1);
}
#[test]
fn test_gdb_jit_clear() {
let mut gdb = X86GDBJITInterface::new();
gdb.enable().unwrap();
gdb.notify_code_loaded("mod");
gdb.clear();
assert_eq!(gdb.entry_count(), 0);
}
#[test]
fn test_perf_jit_new() {
let perf = X86PerfJITInterface::new();
assert!(!perf.is_open());
assert_eq!(perf.recorded_count(), 0);
}
#[test]
fn test_perf_jit_open_close() {
let mut perf = X86PerfJITInterface::new();
assert!(perf.open("/tmp/jit.dump").is_ok());
assert!(perf.is_open());
perf.close();
assert!(!perf.is_open());
}
#[test]
fn test_perf_jit_record_function() {
let mut perf = X86PerfJITInterface::new();
perf.open("/tmp/jit.dump").unwrap();
perf.record_function("func", 0x1000 as *const u8, 64);
assert_eq!(perf.recorded_count(), 1);
}
#[test]
fn test_perf_jit_record_when_closed() {
let mut perf = X86PerfJITInterface::new();
perf.record_function("func", 0x1000 as *const u8, 64);
assert_eq!(perf.recorded_count(), 0);
}
#[test]
fn test_perf_jit_code_move() {
let mut perf = X86PerfJITInterface::new();
perf.open("/tmp/jit.dump").unwrap();
perf.record_code_move("func", 0x1000 as *const u8, 0x2000 as *const u8);
}
#[test]
fn test_perf_jit_header() {
let perf = X86PerfJITInterface::new();
let header = perf.generate_header(12345, 62);
assert!(!header.is_empty());
}
#[test]
fn test_perf_jit_code_load_record() {
let perf = X86PerfJITInterface::new();
let record = perf.generate_code_load_record(1, 2, 0x1000, 0x2000, 64, 42);
assert!(!record.is_empty());
}
#[test]
fn test_indirect_stubs_new() {
let stubs = X86IndirectStubs::new();
assert!(!stubs.is_initialized());
assert_eq!(stubs.stub_count(), 0);
}
#[test]
fn test_indirect_stubs_initialize() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut stubs = X86IndirectStubs::new();
assert!(stubs.initialize(&mut mm).is_ok());
assert!(stubs.is_initialized());
}
#[test]
fn test_indirect_stubs_create_stub() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut stubs = X86IndirectStubs::new();
stubs.initialize(&mut mm).unwrap();
let addr = stubs.create_stub("func", &mut mm).unwrap();
assert!(!addr.is_null());
assert_eq!(stubs.stub_count(), 1);
}
#[test]
fn test_indirect_stubs_create_uninitialized() {
let mut mm = X86JITMemoryManager::new();
let mut stubs = X86IndirectStubs::new();
assert!(stubs.create_stub("func", &mut mm).is_err());
}
#[test]
fn test_indirect_stubs_patch() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut stubs = X86IndirectStubs::new();
stubs.initialize(&mut mm).unwrap();
stubs.create_stub("func", &mut mm).unwrap();
assert!(stubs.patch_stub("func", 0x2000 as *const u8).is_ok());
assert!(stubs.is_patched("func"));
}
#[test]
fn test_indirect_stubs_patch_missing() {
let stubs = X86IndirectStubs::new();
assert!(stubs.get_stub("nonexistent").is_none());
}
#[test]
fn test_indirect_stubs_get_stub() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut stubs = X86IndirectStubs::new();
stubs.initialize(&mut mm).unwrap();
stubs.create_stub("f", &mut mm).unwrap();
let stub = stubs.get_stub("f").unwrap();
assert_eq!(stub.function_name, "f");
assert_eq!(stub.generation, 0);
}
#[test]
fn test_indirect_stubs_multiple() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut stubs = X86IndirectStubs::new();
stubs.initialize(&mut mm).unwrap();
stubs.create_stub("a", &mut mm).unwrap();
stubs.create_stub("b", &mut mm).unwrap();
stubs.create_stub("c", &mut mm).unwrap();
assert_eq!(stubs.stub_count(), 3);
stubs.patch_stub("b", 0x100 as *const u8).unwrap();
assert_eq!(stubs.patched_count(), 1);
assert_eq!(stubs.stub_names().len(), 3);
}
#[test]
fn test_indirect_stubs_shutdown() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut stubs = X86IndirectStubs::new();
stubs.initialize(&mut mm).unwrap();
stubs.create_stub("f", &mut mm).unwrap();
stubs.shutdown(&mut mm);
assert!(!stubs.is_initialized());
assert_eq!(stubs.stub_count(), 0);
}
#[test]
fn test_indirect_stubs_is_patched() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut stubs = X86IndirectStubs::new();
stubs.initialize(&mut mm).unwrap();
stubs.create_stub("f", &mut mm).unwrap();
assert!(!stubs.is_patched("f"));
stubs.patch_stub("f", 0x100 as *const u8).unwrap();
assert!(stubs.is_patched("f"));
assert!(!stubs.is_patched("nonexistent"));
}
#[test]
fn test_indirect_stubs_debug() {
let stubs = X86IndirectStubs::new();
let dbg = format!("{:?}", stubs);
assert!(dbg.contains("X86IndirectStubs"));
}
#[test]
fn test_concurrent_compiler_new() {
let cc = X86JITConcurrentCompiler::new(4);
assert_eq!(cc.worker_count(), 4.min(X86_JIT_MAX_CONCURRENT_THREADS));
assert!(cc.running);
}
#[test]
fn test_concurrent_compiler_submit() {
let mut cc = X86JITConcurrentCompiler::new(2);
let id = cc.submit("mod", "func", 0).unwrap();
assert!(id > 0);
assert_eq!(cc.pending_count(), 1);
}
#[test]
fn test_concurrent_compiler_shutdown() {
let mut cc = X86JITConcurrentCompiler::new(1);
cc.shutdown();
assert!(!cc.running);
assert!(cc.submit("m", "f", 0).is_err());
}
#[test]
fn test_concurrent_compiler_process_all() {
let mut cc = X86JITConcurrentCompiler::new(1);
cc.submit("m", "f", 0).unwrap();
cc.submit("m", "g", 1).unwrap();
cc.process_all();
assert_eq!(cc.pending_count(), 0);
assert_eq!(cc.completed_job_count(), 2);
}
#[test]
fn test_concurrent_compiler_wait_for_all() {
let mut cc = X86JITConcurrentCompiler::new(1);
cc.submit("m", "f", 0).unwrap();
cc.wait_for_all();
assert_eq!(cc.completed_job_count(), 1);
}
#[test]
fn test_concurrent_compiler_default() {
let cc = X86JITConcurrentCompiler::default();
assert_eq!(cc.worker_count(), 1);
}
#[test]
fn test_concurrent_compiler_debug() {
let cc = X86JITConcurrentCompiler::new(2);
let dbg = format!("{:?}", cc);
assert!(dbg.contains("X86JITConcurrentCompiler"));
}
#[test]
fn test_create_x86_jit_linux() {
let jit = create_x86_jit_linux();
assert_eq!(jit.target_triple, X86_JIT_TARGET_TRIPLE);
}
#[test]
fn test_create_x86_jit_windows() {
let jit = create_x86_jit_windows();
assert_eq!(jit.target_triple, X86_JIT_TARGET_TRIPLE_WIN);
}
#[test]
fn test_create_minimal_jit() {
let jit = create_minimal_x86_jit();
assert_eq!(jit.opt_level, 0);
assert!(!jit.debug_mode);
}
#[test]
fn test_create_server_jit() {
let jit = create_server_x86_jit();
assert_eq!(jit.opt_level, 3);
}
#[test]
fn test_symbol_flags_default() {
let flags = X86JITSymbolFlags::default();
assert!(flags.is_callable);
assert!(flags.is_exported);
assert!(!flags.is_weak);
assert!(!flags.is_lazy);
}
#[test]
fn test_symbol_flags_exported_function() {
let flags = X86JITSymbolFlags::exported_function();
assert!(flags.is_callable);
assert!(flags.is_exported);
}
#[test]
fn test_symbol_flags_internal_data() {
let flags = X86JITSymbolFlags::internal_data();
assert!(!flags.is_callable);
assert!(!flags.is_exported);
}
#[test]
fn test_symbol_flags_weak() {
let flags = X86JITSymbolFlags::weak();
assert!(flags.is_weak);
assert!(flags.is_exported);
}
#[test]
fn test_symbol_flags_lazy() {
let flags = X86JITSymbolFlags::lazy();
assert!(flags.is_lazy);
assert!(flags.is_callable);
}
#[test]
fn test_perf_constants() {
assert_eq!(perf_constants::JIT_CODE_LOAD, 0);
assert_eq!(perf_constants::JIT_CODE_MOVE, 1);
assert_eq!(perf_constants::JIT_CODE_CLOSE, 3);
assert_eq!(perf_constants::JITDUMP_MAGIC, 0x4A695444);
assert_eq!(perf_constants::JITDUMP_VERSION, 1);
}
#[test]
fn test_reloc_constants() {
assert_eq!(x86_reloc::R_X86_64_NONE, 0);
assert_eq!(x86_reloc::R_X86_64_64, 1);
assert_eq!(x86_reloc::R_X86_64_PC32, 2);
assert_eq!(x86_reloc::R_X86_64_PLT32, 4);
assert_eq!(x86_reloc::R_X86_64_GOTPCREL, 9);
assert_eq!(x86_reloc::R_X86_64_32, 10);
}
#[test]
fn test_full_roundtrip_empty() {
let mut jit = make_test_jit_initialized();
assert!(jit.lookup_function("anything").is_none());
jit.shutdown();
assert!(!jit.is_initialized());
}
#[test]
fn test_full_create_lazy_stub() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
assert_eq!(jit.stats.stubs_created, 0);
}
#[test]
fn test_full_patch_stub() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
assert!(jit.patch_stub("nonexistent", 0x100 as *const u8).is_err());
}
#[test]
fn test_full_engine_access() {
let mut jit = X86JITFull::new();
let _orc = jit.orc_engine();
let _orc_mut = jit.orc_engine_mut();
let _mcjit = jit.mcjit_engine();
let _mcjit_mut = jit.mcjit_engine_mut();
}
#[test]
fn test_full_compile_module_mcjit_empty() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
let context = crate::context::LLVMContext::new();
let module = Module::new("empty_mod", &context).unwrap();
let result = jit.compile_module_mcjit(&module);
assert!(result.is_ok());
assert_eq!(jit.stats.modules_compiled, 1);
}
#[test]
fn test_full_compile_module_orc_empty() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
let context = crate::context::LLVMContext::new();
let module = Module::new("empty_mod", &context).unwrap();
let result = jit.compile_module_orc(&module);
assert!(result.is_ok());
}
#[test]
fn test_full_total_code_bytes_default() {
let jit = X86JITFull::new();
assert_eq!(jit.total_code_bytes(), 0);
assert_eq!(jit.total_data_bytes(), 0);
}
#[test]
fn test_x86jitfull_dylib_add_remove_cycle() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
assert!(jit.orc_jit.create_dylib("tmp").is_ok());
assert!(jit.orc_jit.remove_dylib("tmp").is_ok());
assert_eq!(jit.orc_jit.dylib_count(), 0);
}
#[test]
fn test_x86jitfull_dylib_count_across_adds() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
jit.orc_jit.create_dylib("a").unwrap();
jit.orc_jit.create_dylib("b").unwrap();
jit.orc_jit.create_dylib("c").unwrap();
assert_eq!(jit.orc_jit.dylib_count(), 3);
}
#[test]
fn test_x86jitfull_symbol_define_then_lookup() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
jit.linker.define_symbol("test_sym", 0xABCD as *const u8);
assert_eq!(jit.linker.resolve_symbol("test_sym"), Some(0xABCD as *const u8));
}
#[test]
fn test_x86jitfull_pending_reloc_then_define() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
let mut buf = [0u8; 8];
let addr = buf.as_mut_ptr();
let _ = jit.linker.apply_relocation(addr, "late_sym", x86_reloc::R_X86_64_64, 0);
assert_eq!(jit.linker.pending_count(), 1);
jit.linker.define_symbol("late_sym", 0xF00D as *const u8);
assert_eq!(jit.linker.pending_count(), 0);
}
#[test]
fn test_full_stub_creation_flow() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
let context = crate::context::LLVMContext::new();
let module = Module::new("stub_test", &context).unwrap();
let result = jit.create_lazy_stub("lazy_func", &module);
assert!(result.is_ok());
assert_eq!(jit.stats.stubs_created, 1);
}
#[test]
fn test_full_event_listener_flow() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
jit.event_listener.notify_module_loaded("modA");
jit.event_listener.notify_module_loaded("modB");
jit.event_listener.notify_module_unloaded("modA");
jit.event_listener.notify_function_compiled("func", 0x5000 as *const u8);
assert!(jit.event_listener.total_events() > 0);
}
#[test]
fn test_full_orc_lookup_flow() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
assert!(jit.orc_lookup("nothing").is_none());
jit.orc_jit.create_dylib("lib").unwrap();
let dylib = jit.orc_jit.dylibs.get_mut("lib").unwrap();
dylib.define_symbol("found", 0x9999 as *const u8, X86JITSymbolFlags::exported_function());
assert_eq!(jit.orc_lookup("found"), Some(0x9999 as *const u8));
}
#[test]
fn test_full_mcjit_lookup_flow() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
jit.mcjit.compiled_functions.insert("f".to_string(), 0x7777 as *const u8);
assert_eq!(jit.mcjit_lookup("f"), Some(0x7777 as *const u8));
}
#[test]
fn test_full_lookup_checks_both_engines() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
jit.orc_jit.create_dylib("lib").unwrap();
let dylib = jit.orc_jit.dylibs.get_mut("lib").unwrap();
dylib.define_symbol("orc_func", 0x1111 as *const u8, X86JITSymbolFlags::exported_function());
jit.mcjit.compiled_functions.insert("mcjit_func".to_string(), 0x2222 as *const u8);
assert_eq!(jit.lookup_function("orc_func"), Some(0x1111 as *const u8));
assert_eq!(jit.lookup_function("mcjit_func"), Some(0x2222 as *const u8));
assert!(jit.lookup_function("missing").is_none());
}
#[test]
fn test_resource_tracker_integration() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
jit.orc_jit.resource_tracker.track_memory(0x2000 as *mut u8, 8192);
jit.orc_jit.resource_tracker.track_symbol("res_sym");
assert!(jit.orc_jit.resource_tracker.has_resources());
jit.orc_jit.resource_tracker.release_all();
assert!(!jit.orc_jit.resource_tracker.has_resources());
}
#[test]
fn test_orc_session_flow() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
let mu = X86MaterializationUnit::new(
"mod",
vec!["f".to_string()],
X86MaterializationKind::IR,
);
jit.orc_jit.session.add_materialization_unit(mu);
assert_eq!(jit.orc_jit.session.pending_count(), 1);
jit.orc_jit.session.shutdown();
assert!(jit.orc_jit.session.is_finalized());
}
#[test]
fn test_eh_frame_registrar_new() {
let reg = X86JITEHFrameRegistrar::new();
assert!(reg.enabled);
assert_eq!(reg.frame_count(), 0);
}
#[test]
fn test_eh_frame_register() {
let mut reg = X86JITEHFrameRegistrar::new();
let frame = vec![0u8; 64];
assert!(reg.register_frame(frame.as_ptr(), 64, "mod", X86EHFrameType::FDE).is_ok());
assert_eq!(reg.frame_count(), 1);
}
#[test]
fn test_eh_frame_register_null() {
let mut reg = X86JITEHFrameRegistrar::new();
assert!(reg.register_frame(std::ptr::null(), 64, "mod", X86EHFrameType::FDE).is_err());
}
#[test]
fn test_eh_frame_register_zero_size() {
let mut reg = X86JITEHFrameRegistrar::new();
let frame = [0u8; 1];
assert!(reg.register_frame(frame.as_ptr(), 0, "mod", X86EHFrameType::FDE).is_err());
}
#[test]
fn test_eh_frame_deregister() {
let mut reg = X86JITEHFrameRegistrar::new();
let frame = [0u8; 64];
reg.register_frame(frame.as_ptr(), 64, "mod", X86EHFrameType::FDE).unwrap();
reg.deregister_frame(frame.as_ptr());
assert_eq!(reg.active_frame_count(), 0);
}
#[test]
fn test_eh_frame_deregister_module() {
let mut reg = X86JITEHFrameRegistrar::new();
let f1 = [0u8; 64];
let f2 = [0u8; 64];
reg.register_frame(f1.as_ptr(), 64, "modA", X86EHFrameType::FDE).unwrap();
reg.register_frame(f2.as_ptr(), 64, "modA", X86EHFrameType::FDE).unwrap();
reg.deregister_module_frames("modA");
assert_eq!(reg.active_frame_count(), 0);
}
#[test]
fn test_eh_frame_set_enabled() {
let mut reg = X86JITEHFrameRegistrar::new();
reg.set_enabled(false);
let frame = [0u8; 64];
assert!(reg.register_frame(frame.as_ptr(), 64, "mod", X86EHFrameType::FDE).is_ok());
assert_eq!(reg.frame_count(), 0);
}
#[test]
fn test_eh_frame_clear() {
let mut reg = X86JITEHFrameRegistrar::new();
let frame = [0u8; 64];
reg.register_frame(frame.as_ptr(), 64, "mod", X86EHFrameType::FDE).unwrap();
reg.clear();
assert_eq!(reg.frame_count(), 0);
}
#[test]
fn test_callback_manager_new() {
let ccm = X86JITCompileCallbackManager::new();
assert!(ccm.enabled);
assert_eq!(ccm.registered_count(), 0);
}
#[test]
fn test_callback_register() {
let mut ccm = X86JITCompileCallbackManager::new();
let id = ccm.register_callback("func", 0x1000 as *const u8, 0x2000 as *const u8);
assert_eq!(id, 0);
assert_eq!(ccm.registered_count(), 1);
}
#[test]
fn test_callback_get() {
let mut ccm = X86JITCompileCallbackManager::new();
ccm.register_callback("func", 0x1000 as *const u8, 0x2000 as *const u8);
let cb = ccm.get_callback("func").unwrap();
assert_eq!(cb.function_name, "func");
assert!(!cb.invoked);
}
#[test]
fn test_callback_invoke() {
let mut ccm = X86JITCompileCallbackManager::new();
ccm.register_callback("func", 0x1000 as *const u8, 0x2000 as *const u8);
assert!(ccm.invoke_callback("func", 0x3000 as *const u8).is_ok());
assert!(ccm.is_invoked("func"));
}
#[test]
fn test_callback_invoke_missing() {
let mut ccm = X86JITCompileCallbackManager::new();
assert!(ccm.invoke_callback("missing", 0x1000 as *const u8).is_err());
}
#[test]
fn test_callback_invoked_count() {
let mut ccm = X86JITCompileCallbackManager::new();
ccm.register_callback("a", 0x1000 as *const u8, 0x2000 as *const u8);
ccm.register_callback("b", 0x3000 as *const u8, 0x4000 as *const u8);
ccm.invoke_callback("a", 0x5000 as *const u8).unwrap();
assert_eq!(ccm.invoked_count(), 1);
}
#[test]
fn test_callback_enable_disable() {
let mut ccm = X86JITCompileCallbackManager::new();
ccm.disable();
assert!(!ccm.enabled);
ccm.enable();
assert!(ccm.enabled);
}
#[test]
fn test_callback_clear() {
let mut ccm = X86JITCompileCallbackManager::new();
ccm.register_callback("func", 0x1000 as *const u8, 0x2000 as *const u8);
ccm.clear();
assert_eq!(ccm.registered_count(), 0);
}
#[test]
fn test_alloc_tracker_new() {
let tracker = X86JITMemoryAllocationTracker::new();
assert_eq!(tracker.alive_count(), 0);
assert_eq!(tracker.current_bytes, 0);
}
#[test]
fn test_alloc_tracker_track() {
let mut tracker = X86JITMemoryAllocationTracker::new();
let id = tracker.track(0x1000 as *mut u8, 4096, X86JITAllocKind::Code, "test_code");
assert!(id > 0);
assert_eq!(tracker.alive_count(), 1);
assert_eq!(tracker.current_bytes, 4096);
}
#[test]
fn test_alloc_tracker_free() {
let mut tracker = X86JITMemoryAllocationTracker::new();
let id = tracker.track(0x1000 as *mut u8, 8192, X86JITAllocKind::Code, "test");
assert!(tracker.free(id).is_ok());
assert_eq!(tracker.alive_count(), 0);
assert_eq!(tracker.freed_count(), 1);
assert_eq!(tracker.current_bytes, 0);
}
#[test]
fn test_alloc_tracker_double_free() {
let mut tracker = X86JITMemoryAllocationTracker::new();
let id = tracker.track(0x1000 as *mut u8, 100, X86JITAllocKind::General, "test");
tracker.free(id).unwrap();
assert!(tracker.free(id).is_err());
}
#[test]
fn test_alloc_tracker_by_kind() {
let mut tracker = X86JITMemoryAllocationTracker::new();
tracker.track(0x1000 as *mut u8, 100, X86JITAllocKind::Code, "c");
tracker.track(0x2000 as *mut u8, 200, X86JITAllocKind::ReadOnlyData, "d");
tracker.track(0x3000 as *mut u8, 300, X86JITAllocKind::Code, "c2");
assert_eq!(tracker.by_kind(X86JITAllocKind::Code).len(), 2);
assert_eq!(tracker.by_kind(X86JITAllocKind::ReadOnlyData).len(), 1);
assert_eq!(tracker.by_kind(X86JITAllocKind::StubMemory).len(), 0);
}
#[test]
fn test_alloc_tracker_peak() {
let mut tracker = X86JITMemoryAllocationTracker::new();
let id1 = tracker.track(0x1000 as *mut u8, 10000, X86JITAllocKind::Code, "c");
assert_eq!(tracker.peak_bytes, 10000);
let _id2 = tracker.track(0x2000 as *mut u8, 5000, X86JITAllocKind::Code, "c2");
assert_eq!(tracker.peak_bytes, 15000);
tracker.free(id1).unwrap();
assert_eq!(tracker.peak_bytes, 15000);
assert_eq!(tracker.current_bytes, 5000);
}
#[test]
fn test_alloc_tracker_summary() {
let mut tracker = X86JITMemoryAllocationTracker::new();
tracker.track(0x1000 as *mut u8, 100, X86JITAllocKind::Code, "test");
let summary = tracker.summary();
assert!(summary.contains("JIT Memory"));
}
#[test]
fn test_alloc_tracker_reset() {
let mut tracker = X86JITMemoryAllocationTracker::new();
tracker.track(0x1000 as *mut u8, 100, X86JITAllocKind::Code, "test");
tracker.reset();
assert_eq!(tracker.alive_count(), 0);
assert_eq!(tracker.lifetime_bytes, 0);
assert_eq!(tracker.peak_bytes, 0);
}
#[test]
fn test_lazy_call_through_new() {
let lctm = X86LazyCallThroughManager::new();
assert!(!lctm.is_initialized());
assert_eq!(lctm.total_trampolines, 0);
}
#[test]
fn test_lazy_call_through_initialize() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut lctm = X86LazyCallThroughManager::new();
assert!(lctm.initialize(8, &mut mm).is_ok());
assert!(lctm.is_initialized());
assert_eq!(lctm.total_trampolines, 8);
assert_eq!(lctm.available_count(), 8);
}
#[test]
fn test_lazy_call_through_allocate() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut lctm = X86LazyCallThroughManager::new();
lctm.initialize(4, &mut mm).unwrap();
let addr = lctm.allocate_trampoline("func", 0x5000 as *mut u8).unwrap();
assert!(!addr.is_null());
assert_eq!(lctm.used_count(), 1);
assert_eq!(lctm.available_count(), 3);
}
#[test]
fn test_lazy_call_through_exhaustion() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut lctm = X86LazyCallThroughManager::new();
lctm.initialize(1, &mut mm).unwrap();
lctm.allocate_trampoline("f1", 0x1000 as *mut u8).unwrap();
assert!(lctm.allocate_trampoline("f2", 0x2000 as *mut u8).is_err());
}
#[test]
fn test_lazy_call_through_release() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut lctm = X86LazyCallThroughManager::new();
lctm.initialize(4, &mut mm).unwrap();
let addr = lctm.allocate_trampoline("func", 0x1000 as *mut u8).unwrap();
assert!(lctm.release_trampoline(addr).is_ok());
assert_eq!(lctm.used_count(), 0);
assert_eq!(lctm.available_count(), 4);
}
#[test]
fn test_lazy_call_through_release_missing() {
let mut lctm = X86LazyCallThroughManager::new();
assert!(lctm.release_trampoline(0xBAD as *mut u8).is_err());
}
#[test]
fn test_thread_safe_context_new() {
let ctx = X86ThreadSafeContext::new("test");
assert!(!ctx.compiling);
assert!(!ctx.complete);
assert!(!ctx.has_errors());
}
#[test]
fn test_thread_safe_context_compile_ok() {
let mut ctx = X86ThreadSafeContext::new("test");
ctx.begin_compilation();
assert!(ctx.compiling);
let success = ctx.complete_compilation(vec![]);
assert!(success);
assert!(ctx.complete);
assert!(!ctx.has_errors());
}
#[test]
fn test_thread_safe_context_compile_error() {
let mut ctx = X86ThreadSafeContext::new("test");
ctx.begin_compilation();
let success = ctx.complete_compilation(vec!["failed".to_string()]);
assert!(!success);
assert!(ctx.has_errors());
assert_eq!(ctx.get_errors().len(), 1);
}
#[test]
fn test_thread_safe_context_is_idle() {
let mut ctx = X86ThreadSafeContext::new("test");
assert!(!ctx.is_idle());
ctx.complete_compilation(vec![]);
assert!(ctx.is_idle());
}
#[test]
fn test_thread_safe_context_debug() {
let ctx = X86ThreadSafeContext::new("test");
let dbg = format!("{:?}", ctx);
assert!(dbg.contains("test"));
}
#[test]
fn test_target_options_default() {
let opts = X86JITTargetOptions::default();
assert_eq!(opts.cpu, "generic");
assert_eq!(opts.code_model, "small");
assert!(opts.enable_fast_isel);
}
#[test]
fn test_target_options_for_host() {
let opts = X86JITTargetOptions::for_host();
assert_eq!(opts.cpu, "native");
}
#[test]
fn test_target_options_for_cpu() {
let opts = X86JITTargetOptions::for_cpu("skylake");
assert_eq!(opts.cpu, "skylake");
}
#[test]
fn test_target_options_add_feature() {
let mut opts = X86JITTargetOptions::default();
opts.add_feature("avx2");
assert!(opts.features.contains(&"avx2".to_string()));
opts.add_feature("avx2");
assert_eq!(opts.features.iter().filter(|f| f.as_str() == "avx2").count(), 1);
opts.remove_feature("avx2");
assert!(!opts.features.contains(&"avx2".to_string()));
}
#[test]
fn test_target_options_feature_string() {
let opts = X86JITTargetOptions::default();
let fs = opts.feature_string();
assert!(fs.contains("+sse2"));
}
#[test]
fn test_target_options_set_code_model() {
let mut opts = X86JITTargetOptions::default();
opts.set_code_model("medium");
assert_eq!(opts.code_model, "medium");
}
#[test]
fn test_target_options_set_reloc_model() {
let mut opts = X86JITTargetOptions::default();
opts.set_reloc_model("static");
assert_eq!(opts.reloc_model, "static");
}
#[test]
fn test_codegen_bridge_new() {
let bridge = X86CodeGeneratorBridge::new(X86JITTargetOptions::default());
assert!(!bridge.ready);
}
#[test]
fn test_codegen_bridge_prepare() {
let mut bridge = X86CodeGeneratorBridge::default();
assert!(bridge.prepare().is_ok());
assert!(bridge.ready);
}
#[test]
fn test_codegen_bridge_compile_not_ready() {
let mut bridge = X86CodeGeneratorBridge::default();
assert!(bridge.compile_function("f", &[]).is_err());
}
#[test]
fn test_codegen_bridge_compile_ready() {
let mut bridge = X86CodeGeneratorBridge::default();
bridge.prepare().unwrap();
let code = bridge.compile_function("f", &[]).unwrap();
assert!(!code.is_empty());
assert_eq!(code.last(), Some(&0xc3));
}
#[test]
fn test_codegen_bridge_cache() {
let mut bridge = X86CodeGeneratorBridge::default();
bridge.cache_code("foo", vec![0x90]);
assert!(bridge.get_cached("foo").is_some());
assert!(bridge.get_cached("bar").is_none());
}
#[test]
fn test_codegen_bridge_invalidate_cache() {
let mut bridge = X86CodeGeneratorBridge::default();
bridge.cache_code("foo", vec![0x90]);
bridge.invalidate_cache();
assert!(bridge.get_cached("foo").is_none());
}
#[test]
fn test_codegen_bridge_cache_stats() {
let mut bridge = X86CodeGeneratorBridge::default();
bridge.cache_code("a", vec![1, 2, 3]);
bridge.cache_code("b", vec![4, 5]);
let (entries, bytes) = bridge.cache_stats();
assert_eq!(entries, 2);
assert_eq!(bytes, 5);
}
#[test]
fn test_diag_new() {
let diag = X86JITDiagnostic::new(X86JITDiagLevel::Error, "something wrong");
assert!(diag.is_error());
assert!(!diag.is_warning());
}
#[test]
fn test_diag_with_context() {
let diag = X86JITDiagnostic::new(X86JITDiagLevel::Warning, "test")
.with_module("mod")
.with_function("func")
.with_location("test.rs", 1, 1);
let formatted = diag.format();
assert!(formatted.contains("warning"));
assert!(formatted.contains("func"));
assert!(formatted.contains("mod"));
assert!(formatted.contains("test.rs"));
}
#[test]
fn test_diag_levels() {
assert!(!X86JITDiagnostic::new(X86JITDiagLevel::Note, "").is_error());
assert!(X86JITDiagnostic::new(X86JITDiagLevel::Fatal, "").is_error());
assert!(X86JITDiagnostic::new(X86JITDiagLevel::Error, "").is_error());
}
#[test]
fn test_diag_collector_new() {
let collector = X86JITDiagnosticCollector::new();
assert_eq!(collector.total_count(), 0);
assert!(!collector.has_errors());
}
#[test]
fn test_diag_collector_add() {
let mut collector = X86JITDiagnosticCollector::new();
collector.error("e1");
collector.warning("w1");
collector.note("n1");
assert_eq!(collector.total_count(), 3);
assert_eq!(collector.error_count(), 1);
assert_eq!(collector.warning_count(), 1);
assert!(collector.has_errors());
}
#[test]
fn test_diag_collector_get_errors() {
let mut collector = X86JITDiagnosticCollector::new();
collector.error("e");
collector.warning("w");
assert_eq!(collector.get_errors().len(), 1);
}
#[test]
fn test_diag_collector_clear() {
let mut collector = X86JITDiagnosticCollector::new();
collector.error("e");
collector.clear();
assert_eq!(collector.total_count(), 0);
}
#[test]
fn test_sym_table_new() {
let table = X86JITSymbolTable::new();
assert_eq!(table.count(), 0);
}
#[test]
fn test_sym_table_add_lookup() {
let mut table = X86JITSymbolTable::new();
table.add("foo", 0x1000 as *const u8, true, X86JITSymbolBinding::Global);
let entry = table.lookup("foo").unwrap();
assert_eq!(entry.address, 0x1000 as *const u8);
assert!(entry.is_function);
assert!(!entry.is_data);
}
#[test]
fn test_sym_table_lookup_missing() {
let table = X86JITSymbolTable::new();
assert!(table.lookup("missing").is_none());
}
#[test]
fn test_sym_table_lookup_by_address() {
let mut table = X86JITSymbolTable::new();
table.add("bar", 0x9999 as *const u8, false, X86JITSymbolBinding::Local);
assert_eq!(table.lookup_by_address(0x9999 as *const u8), Some("bar"));
assert_eq!(table.lookup_by_address(0xBAD as *const u8), None);
}
#[test]
fn test_sym_table_remove() {
let mut table = X86JITSymbolTable::new();
table.add("x", 0x1 as *const u8, true, X86JITSymbolBinding::Global);
table.remove("x");
assert_eq!(table.count(), 0);
assert!(table.lookup_by_address(0x1 as *const u8).is_none());
}
#[test]
fn test_sym_table_names() {
let mut table = X86JITSymbolTable::new();
table.add("a", 0x1 as *const u8, true, X86JITSymbolBinding::Global);
table.add("b", 0x2 as *const u8, true, X86JITSymbolBinding::Global);
assert_eq!(table.names().len(), 2);
}
#[test]
fn test_sym_table_contains() {
let mut table = X86JITSymbolTable::new();
table.add("exists", 0x1 as *const u8, true, X86JITSymbolBinding::Weak);
assert!(table.contains("exists"));
assert!(!table.contains("nope"));
}
#[test]
fn test_sym_table_clear() {
let mut table = X86JITSymbolTable::new();
table.add("x", 0x1 as *const u8, true, X86JITSymbolBinding::Global);
table.clear();
assert_eq!(table.count(), 0);
}
#[test]
fn test_module_id_new() {
let id = X86JITModuleIdentifier::new("test");
assert_eq!(id.name, "test");
assert!(id.from_ir);
}
#[test]
fn test_module_id_with_flag() {
let id = X86JITModuleIdentifier::new("test").with_flag("-O2");
assert!(id.flags.contains(&"-O2".to_string()));
}
#[test]
fn test_module_id_compute_hash() {
let id1 = X86JITModuleIdentifier::new("mod");
let id2 = X86JITModuleIdentifier::new("mod");
assert_eq!(id1.compute_hash(), id2.compute_hash());
}
#[test]
fn test_module_id_display() {
let id = X86JITModuleIdentifier::new("test");
let s = format!("{}", id);
assert!(s.contains("test"));
}
#[test]
fn test_jit_full_with_full_optimization() {
let jit = X86JITFull::with_full_optimization();
assert_eq!(jit.opt_level, 3);
assert!(!jit.debug_mode);
assert!(jit.memory_manager.protect_after_finalize);
}
#[test]
fn test_jit_full_with_interactive_profile() {
let jit = X86JITFull::with_interactive_profile();
assert_eq!(jit.opt_level, 1);
assert!(jit.mcjit.lazy_compilation);
}
#[test]
fn test_jit_full_with_debug_profile() {
let jit = X86JITFull::with_debug_profile();
assert_eq!(jit.opt_level, 0);
assert!(jit.debug_mode);
assert!(!jit.memory_manager.protect_after_finalize);
}
#[test]
fn test_jit_full_register_external_resolver() {
let mut jit = X86JITFull::new();
jit.register_external_resolver(|name| {
if name == "ext" { Some(0xDEAD as *const u8) } else { None }
});
assert_eq!(jit.linker.resolve_symbol("ext"), Some(0xDEAD as *const u8));
}
#[test]
fn test_jit_full_enable_gdb() {
let mut jit = X86JITFull::new();
jit.enable_gdb_support();
assert!(jit.event_listener.gdb_interface.is_enabled());
}
#[test]
fn test_jit_full_enable_perf() {
let mut jit = X86JITFull::new();
jit.enable_perf_support("/tmp/jit_test.dump");
assert!(jit.event_listener.perf_interface.is_open());
}
#[test]
fn test_jit_full_configure_transforms() {
let mut jit = X86JITFull::new();
jit.configure_transforms(2);
assert!(jit.orc_jit.ir_transform_layer.transforms.len() > 2);
}
#[test]
fn test_jit_full_state_summary() {
let jit = X86JITFull::new();
let summary = jit.state_summary();
assert!(summary.contains("X86JITFull"));
}
#[test]
fn test_pipeline_manager_new() {
let pm = X86JITPipelineManager::new();
assert!(!pm.running);
assert!(!pm.stages.is_empty());
}
#[test]
fn test_pipeline_manager_initialize() {
let mut pm = X86JITPipelineManager::new();
assert!(pm.initialize().is_ok());
assert!(pm.running);
}
#[test]
fn test_pipeline_manager_disable_enable_stage() {
let mut pm = X86JITPipelineManager::new();
pm.disable_stage("verify-ir");
let enabled: Vec<_> = pm.enabled_stages();
assert!(!enabled.contains(&"verify-ir"));
pm.enable_stage("verify-ir");
let enabled: Vec<_> = pm.enabled_stages();
assert!(enabled.contains(&"verify-ir"));
}
#[test]
fn test_pipeline_manager_shutdown() {
let mut pm = X86JITPipelineManager::new();
pm.initialize().unwrap();
pm.shutdown();
assert!(!pm.running);
assert!(pm.stages.is_empty());
}
#[test]
fn test_pipeline_manager_summary() {
let pm = X86JITPipelineManager::new();
let summary = pm.summary();
assert!(summary.contains("Pipeline"));
}
#[test]
fn test_pipeline_manager_debug() {
let pm = X86JITPipelineManager::new();
let dbg = format!("{:?}", pm);
assert!(dbg.contains("X86JITPipelineManager"));
}
#[test]
fn test_alloc_kind_display() {
assert_eq!(format!("{}", X86JITAllocKind::Code), "code");
assert_eq!(format!("{}", X86JITAllocKind::ReadOnlyData), "rodata");
assert_eq!(format!("{}", X86JITAllocKind::StubMemory), "stubs");
assert_eq!(format!("{}", X86JITAllocKind::Trampoline), "trampoline");
assert_eq!(format!("{}", X86JITAllocKind::EHFrame), "eh_frame");
assert_eq!(format!("{}", X86JITAllocKind::DebugInfo), "debug_info");
assert_eq!(format!("{}", X86JITAllocKind::General), "general");
}
#[test]
fn test_lazy_definition_new() {
let def = X86LazyDefinition {
name: "lazy_func".to_string(),
module_name: "mod".to_string(),
stub_address: None,
compiling: false,
};
assert_eq!(def.name, "lazy_func");
assert!(!def.compiling);
}
#[test]
fn test_jitdylib_lazy_definitions() {
let mut dylib = X86JITDylib::new("test");
let context = crate::context::LLVMContext::new();
let module = Module::new("mod", &context).unwrap();
assert!(dylib.add_lazy_definition("lazy", &module).is_ok());
assert!(dylib.allow_lazy_compilation);
assert_eq!(dylib.lazy_definitions.len(), 1);
}
#[test]
fn test_jitdylib_lazy_after_finalize() {
let mut dylib = X86JITDylib::new("test");
dylib.finalize().unwrap();
let context = crate::context::LLVMContext::new();
let module = Module::new("mod", &context).unwrap();
assert!(dylib.add_lazy_definition("lazy", &module).is_err());
}
#[test]
fn test_reloc_kind_32_overflow() {
let mut linker = X86JITLinker::new();
linker.define_symbol("far", 0x1_0000_0000 as *const u8);
let mut buf = [0u8; 8];
let addr = buf.as_mut_ptr();
assert!(linker.apply_relocation(addr, "far", x86_reloc::R_X86_64_32, 0).is_err());
}
#[test]
fn test_reloc_kind_unsupported() {
let mut linker = X86JITLinker::new();
linker.define_symbol("target", 0x100 as *const u8);
let mut buf = [0u8; 8];
let addr = buf.as_mut_ptr();
assert!(linker.apply_relocation(addr, "target", 99, 0).is_err());
}
#[test]
fn test_linker_apply_all_pending() {
let mut linker = X86JITLinker::new();
let mut buf = [0u8; 8];
let addr = buf.as_mut_ptr();
linker.apply_relocation(addr, "late", x86_reloc::R_X86_64_64, 0).unwrap();
assert_eq!(linker.pending_count(), 1);
linker.define_symbol("late", 0xBEEF as *const u8);
let applied = linker.apply_all_pending().unwrap();
assert_eq!(applied, 1);
assert_eq!(linker.pending_count(), 0);
}
#[test]
fn test_linker_got_entry() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut linker = X86JITLinker::new();
let entry = linker.allocate_got_entry("foo", &mut mm).unwrap();
assert!(!entry.is_null());
}
#[test]
fn test_linker_plt_entry() {
let mut mm = X86JITMemoryManager::new();
mm.initialize().unwrap();
let mut linker = X86JITLinker::new();
let stub = linker.allocate_plt_entry("bar", &mut mm).unwrap();
assert!(!stub.is_null());
}
#[test]
fn test_event_listener_disabled_state() {
let mut listener = X86JITEventListener::new();
listener.disable();
listener.notify_module_loaded("a");
assert_eq!(listener.total_events(), 0);
}
#[test]
fn test_global_constants() {
assert_eq!(X86_JIT_MAX_CODE_SIZE, 128 * 1024 * 1024);
assert_eq!(X86_JIT_STUB_SIZE, 64);
assert_eq!(X86_JIT_PAGE_SIZE, 4096);
assert_eq!(X86_JIT_MAX_PENDING_LAZY, 1024);
assert_eq!(X86_JIT_MAX_CONCURRENT_THREADS, 8);
assert_eq!(X86_JIT_DEFAULT_OPT_LEVEL, 2);
}
#[test]
fn test_orc_jit_lookup_in_dylib() {
let mut orc = X86ORCJIT::new();
orc.create_dylib("lib").unwrap();
let dylib = orc.dylibs.get_mut("lib").unwrap();
dylib.define_symbol("sym", 0x1234 as *const u8, X86JITSymbolFlags::exported_function());
assert_eq!(orc.lookup_in_dylib("lib", "sym"), Some(0x1234 as *const u8));
assert_eq!(orc.lookup_in_dylib("lib", "nope"), None);
assert_eq!(orc.lookup_in_dylib("nonexistent_lib", "sym"), None);
}
#[test]
fn test_orc_jit_module_count() {
let mut orc = X86ORCJIT::new();
orc.create_dylib("a").unwrap();
orc.create_dylib("b").unwrap();
assert_eq!(orc.module_count(), 0);
let dylib = orc.dylibs.get_mut("a").unwrap();
dylib.add_materialization_unit(
X86MaterializationUnit::new("m1", vec!["f".to_string()], X86MaterializationKind::IR),
);
assert_eq!(orc.module_count(), 1);
}
#[test]
fn test_orc_jit_invalidate_cache() {
let mut orc = X86ORCJIT::new();
orc.create_dylib("lib").unwrap();
let dylib = orc.dylibs.get_mut("lib").unwrap();
dylib.define_symbol("cached_sym", 0x5000 as *const u8, X86JITSymbolFlags::exported_function());
let _ = orc.lookup("cached_sym");
orc.invalidate_cache();
}
#[test]
fn test_full_jit_create_lazy_stub_and_patch() {
let mut jit = X86JITFull::new();
let _ = jit.initialize();
let context = crate::context::LLVMContext::new();
let module = Module::new("stub_mod", &context).unwrap();
let stub_addr = jit.create_lazy_stub("lazy_f", &module).unwrap();
assert!(!stub_addr.is_null());
assert!(jit.patch_stub("lazy_f", 0x6000 as *const u8).is_ok());
assert!(jit.indirect_stubs.is_patched("lazy_f"));
}
}