gaia_assembler/backends/windows/
x86_64.rs1use 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#[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 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 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.exe".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, custom: None, 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(Vec::new());
132 Ok(writer.finish())
134 }
135}