Skip to main content

gaia_assembler/backends/windows/
x86_64.rs

1//! Native x86_64 backend compiler for Windows
2//! This backend generates native x86_64 machine code wrapped in a PE container
3
4use crate::{
5    backends::{
6        windows::emitter::{RelocationKind, X64Emitter},
7        Backend, GeneratedFiles,
8    },
9    config::GaiaConfig,
10    instruction::{CoreInstruction, GaiaInstruction, ManagedInstruction},
11    program::{GaiaConstant, GaiaFunction, GaiaModule},
12    types::GaiaType,
13};
14use gaia_types::{
15    helpers::{AbiCompatible, ApiCompatible, Architecture, ArtifactType, CompilationTarget},
16    GaiaError, Result,
17};
18
19#[cfg(feature = "pe-assembler")]
20use pe_assembler::formats::exe::writer::ExeWriter;
21use std::collections::HashMap;
22
23use x86_64_assembler::{
24    encoder::InstructionEncoder,
25    instruction::{Instruction, Operand, Register},
26};
27
28/// Native x86_64 Backend implementation for Windows
29#[derive(Default)]
30pub struct X64Backend {}
31
32impl Backend for X64Backend {
33    fn name(&self) -> &'static str {
34        "Windows (Native x86_64)"
35    }
36
37    fn primary_target(&self) -> CompilationTarget {
38        CompilationTarget { build: Architecture::X86_64, host: AbiCompatible::PE, target: ApiCompatible::MicrosoftVisualC }
39    }
40
41    fn artifact_type(&self) -> ArtifactType {
42        ArtifactType::Executable
43    }
44
45    fn match_score(&self, target: &CompilationTarget) -> f32 {
46        if target.build == Architecture::X86_64 && target.host == AbiCompatible::PE {
47            if target.target == ApiCompatible::MicrosoftVisualC {
48                return 100.0;
49            }
50            return 80.0;
51        }
52        0.0
53    }
54
55    fn generate(&self, program: &GaiaModule, _config: &GaiaConfig) -> Result<GeneratedFiles> {
56        let mut emitter = X64Emitter::new(program);
57        emitter.emit()?;
58        let (instructions, relocations, rdata) = emitter.take_result();
59
60        let mut code = Vec::new();
61        let mut labels = HashMap::new();
62        let mut inst_offsets = Vec::new();
63
64        // Pass 1: Encode and find labels
65        for inst in &instructions {
66            inst_offsets.push(code.len());
67            match inst {
68                Instruction::Label(name) => {
69                    labels.insert(name.clone(), code.len());
70                }
71                _ => {
72                    let bytes = self.encode_inst(inst)?;
73                    code.extend_from_slice(&bytes);
74                }
75            }
76        }
77
78        // Pass 2: Apply relocations (Simplified for now)
79        let mut exit_pos = 0;
80        let mut external_calls: HashMap<String, Vec<usize>> = HashMap::new();
81        let mut string_patches: Vec<(usize, usize)> = Vec::new();
82
83        for reloc in &relocations {
84            let inst_pos = inst_offsets[reloc.instruction_index];
85            match reloc.kind {
86                RelocationKind::Relative32 => {
87                    if let Some(&target_offset) = labels.get(&reloc.target) {
88                        let rel = (target_offset as i32) - (inst_pos as i32 + 5);
89                        code[inst_pos + 1..inst_pos + 5].copy_from_slice(&rel.to_le_bytes());
90                    }
91                }
92                RelocationKind::RipRelative => {
93                    if reloc.target == "ExitProcess" {
94                        exit_pos = inst_pos;
95                    }
96                    external_calls.entry(reloc.target.clone()).or_default().push(inst_pos);
97                }
98                _ => {}
99            }
100        }
101
102        let mut files = HashMap::new();
103        files.insert("main.bin".to_string(), code.clone());
104
105        #[cfg(feature = "pe-assembler")]
106        {
107            let pe_bytes = self.create_pe_exe(&code, program, exit_pos, &external_calls, &rdata, &string_patches)?;
108            files.insert("main.exe".to_string(), pe_bytes);
109        }
110
111        Ok(GeneratedFiles { artifact_type: self.artifact_type(), files, diagnostics: vec![] })
112    }
113}
114
115impl X64Backend {
116    fn encode_inst(&self, inst: &Instruction) -> Result<Vec<u8>> {
117        let encoder = InstructionEncoder::new(Architecture::X86_64);
118        encoder.encode(inst)
119    }
120
121    #[cfg(feature = "pe-assembler")]
122    fn create_pe_exe(
123        &self,
124        code: &[u8],
125        program: &GaiaModule,
126        exit_pos: usize,
127        external_calls: &HashMap<String, Vec<usize>>,
128        rdata: &[u8],
129        string_patches: &[(usize, usize)],
130    ) -> Result<Vec<u8>> {
131        let mut writer = ExeWriter::new();
132        // ... (rest of the PE creation logic)
133        Ok(vec![]) // Simplified for now
134    }
135}