use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X64Register {
RAX,
RBX,
RCX,
RDX,
RSI,
RDI,
RBP,
RSP,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
EAX,
EBX,
ECX,
EDX,
ESI,
EDI,
EBP,
ESP,
R8D,
R9D,
R10D,
R11D,
R12D,
R13D,
R14D,
R15D,
}
#[derive(Debug, Clone)]
pub struct Label {
pub name: String,
pub offset: Option<usize>,
pub fixup_locations: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct RelocationInfo {
pub offset: usize,
pub reloc_type: RelocationType,
pub symbol: String,
}
#[derive(Debug, Clone, Copy)]
pub enum RelocationType {
Rel32,
Abs64,
RipRel32,
}
#[derive(Debug, Clone)]
pub struct FunctionCall {
pub name: String,
pub call_offset: usize,
pub is_import: bool,
}
#[derive(Debug)]
pub struct X64Context {
pub code: Vec<u8>,
pub stack_offset: i32,
pub labels: HashMap<String, Label>,
pub relocations: Vec<RelocationInfo>,
pub function_calls: Vec<FunctionCall>,
pub string_constants: HashMap<String, usize>,
pub register_usage: HashMap<X64Register, bool>,
pub stack_size: u32,
}
impl X64Context {
pub fn new() -> Self {
Self {
code: Vec::new(),
stack_offset: 0,
labels: HashMap::new(),
relocations: Vec::new(),
function_calls: Vec::new(),
string_constants: HashMap::new(),
register_usage: HashMap::new(),
stack_size: 0,
}
}
pub fn emit_bytes(&mut self, bytes: &[u8]) {
self.code.extend_from_slice(bytes);
}
pub fn current_position(&self) -> usize {
self.code.len()
}
pub fn define_label(&mut self, name: &str) {
let offset = self.current_position();
if let Some(label) = self.labels.get_mut(name) {
label.offset = Some(offset);
self.fixup_label(name, offset);
}
else {
self.labels
.insert(name.to_string(), Label { name: name.to_string(), offset: Some(offset), fixup_locations: Vec::new() });
}
}
pub fn reference_label(&mut self, name: &str) -> usize {
let current_pos = self.current_position();
if let Some(label) = self.labels.get_mut(name) {
if let Some(offset) = label.offset {
return self.calculate_relative_offset(current_pos, offset);
}
else {
label.fixup_locations.push(current_pos);
}
}
else {
let label = Label { name: name.to_string(), offset: None, fixup_locations: vec![current_pos] };
self.labels.insert(name.to_string(), label);
}
0 }
fn fixup_label(&mut self, name: &str, label_offset: usize) {
if let Some(label) = self.labels.get_mut(name) {
let fixup_positions: Vec<usize> = label.fixup_locations.clone();
for fixup_pos in fixup_positions {
let relative_offset = self.calculate_relative_offset(fixup_pos, label_offset);
let bytes = (relative_offset as i32).to_le_bytes();
for (i, &byte) in bytes.iter().enumerate() {
if fixup_pos + i < self.code.len() {
self.code[fixup_pos + i] = byte;
}
}
}
if let Some(label) = self.labels.get_mut(name) {
label.fixup_locations.clear();
}
}
}
fn calculate_relative_offset(&self, from: usize, to: usize) -> usize {
if to >= from {
to - from
}
else {
0 }
}
pub fn allocate_stack(&mut self, size: u32) -> i32 {
self.stack_offset -= size as i32;
self.stack_size = self.stack_size.max((-self.stack_offset) as u32);
self.stack_offset
}
pub fn add_string_constant(&mut self, value: &str) -> usize {
if let Some(&offset) = self.string_constants.get(value) {
offset
}
else {
let offset = self.string_constants.len() * 8; self.string_constants.insert(value.to_string(), offset);
offset
}
}
pub fn add_function_call(&mut self, name: &str, is_import: bool) {
let call_offset = self.current_position();
self.function_calls.push(FunctionCall { name: name.to_string(), call_offset, is_import });
}
pub fn add_relocation(&mut self, reloc_type: RelocationType, symbol: &str) {
let offset = self.current_position();
self.relocations.push(RelocationInfo { offset, reloc_type, symbol: symbol.to_string() });
}
}
impl Default for X64Context {
fn default() -> Self {
Self::new()
}
}