use super::context::{RelocationType, X64Context};
#[derive(Debug)]
pub struct X64CodeBuilder {
context: X64Context,
}
impl X64CodeBuilder {
pub fn new() -> Self {
Self { context: X64Context::new() }
}
pub fn context_mut(&mut self) -> &mut X64Context {
&mut self.context
}
pub fn context(&self) -> &X64Context {
&self.context
}
pub fn code(&self) -> &[u8] {
&self.context.code
}
pub fn finish(self) -> X64Context {
self.context
}
pub fn function_prologue(&mut self) -> &mut Self {
let bytes = vec![
0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x20, ];
self.context.emit_bytes(&bytes);
self
}
pub fn function_epilogue(&mut self) -> &mut Self {
let bytes = vec![
0x48, 0x89, 0xEC, 0x5D, 0xC3, ];
self.context.emit_bytes(&bytes);
self
}
pub fn exit_program(&mut self, exit_code: i32) -> &mut Self {
self.context.emit_bytes(&[0xB9]);
self.context.emit_bytes(&exit_code.to_le_bytes());
self.context.add_function_call("ExitProcess", true);
self.context.add_relocation(RelocationType::RipRel32, "ExitProcess");
self.context.emit_bytes(&[0xFF, 0x15, 0x00, 0x00, 0x00, 0x00]);
self
}
pub fn load_immediate(&mut self, value: i64) {
if value >= i32::MIN as i64 && value <= i32::MAX as i64 {
self.context.emit_bytes(&[0xB8]);
self.context.emit_bytes(&(value as i32).to_le_bytes());
}
else {
self.context.emit_bytes(&[0x48, 0xB8]);
self.context.emit_bytes(&value.to_le_bytes());
}
}
pub fn load_string_address(&mut self, string_id: &str) {
self.context.add_string_constant(string_id);
self.context.emit_bytes(&[0x48, 0x8D, 0x05]);
let _offset = self.context.reference_label(&format!("str_{}", string_id));
self.context.emit_bytes(&[0x00, 0x00, 0x00, 0x00]); }
pub fn store_local(&mut self, _offset: i32) {
let stack_offset = self.context.allocate_stack(8);
if stack_offset >= -128 && stack_offset <= 127 {
self.context.emit_bytes(&[0x48, 0x89, 0x45]);
self.context.emit_bytes(&[stack_offset as u8]);
}
else {
self.context.emit_bytes(&[0x48, 0x89, 0x85]);
self.context.emit_bytes(&stack_offset.to_le_bytes());
}
}
pub fn load_local(&mut self, offset: i32) {
if offset >= -128 && offset <= 127 {
self.context.emit_bytes(&[0x48, 0x8B, 0x45]);
self.context.emit_bytes(&[offset as u8]);
}
else {
self.context.emit_bytes(&[0x48, 0x8B, 0x85]);
self.context.emit_bytes(&offset.to_le_bytes());
}
}
pub fn add_operation(&mut self) {
self.context.emit_bytes(&[
0x5B, 0x58, 0x48, 0x01, 0xD8, 0x50, ]);
}
pub fn sub_operation(&mut self) {
self.context.emit_bytes(&[
0x5B, 0x58, 0x48, 0x29, 0xD8, 0x50, ]);
}
pub fn mul_operation(&mut self) {
self.context.emit_bytes(&[
0x5B, 0x58, 0x48, 0x0F, 0xAF, 0xC3, 0x50, ]);
}
pub fn call_printf(&mut self) {
self.context.add_function_call("printf", true);
self.context.emit_bytes(&[0xFF, 0x15, 0x00, 0x00, 0x00, 0x00]);
}
pub fn pop_stack(&mut self) {
self.context.emit_bytes(&[0x58]);
}
pub fn conditional_jump_false(&mut self, label: &str) {
self.context.emit_bytes(&[0x48, 0x85, 0xC0]); self.context.emit_bytes(&[0x0F, 0x84]); let _offset = self.context.reference_label(label);
self.context.emit_bytes(&[0x00, 0x00, 0x00, 0x00]); }
pub fn unconditional_jump(&mut self, label: &str) {
self.context.emit_bytes(&[0xE9]); let _offset = self.context.reference_label(label);
self.context.emit_bytes(&[0x00, 0x00, 0x00, 0x00]); }
pub fn hello_world_program() -> Vec<u8> {
let mut builder = X64CodeBuilder::new();
builder.function_prologue();
builder.load_string_address("Hello, World!\n");
builder.call_printf();
builder.exit_program(0);
builder.finish().code
}
pub fn message_box_program() -> Vec<u8> {
let mut builder = X64CodeBuilder::new();
builder.function_prologue();
builder.context_mut().emit_bytes(&[0x41, 0xB9, 0x00, 0x00, 0x00, 0x00]);
builder.context_mut().emit_bytes(&[0x49, 0xB8]);
builder.context_mut().emit_bytes(&0x2100u64.to_le_bytes());
builder.context_mut().emit_bytes(&[0x48, 0xBA]);
builder.context_mut().emit_bytes(&0x2000u64.to_le_bytes());
builder.context_mut().emit_bytes(&[0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
builder.context_mut().emit_bytes(&[0xFF, 0x15, 0x00, 0x00, 0x00, 0x00]);
builder.exit_program(0);
builder.finish().code
}
}