gaia_assembler/backends/windows/
x86_64.rs1#[cfg(feature = "x86_64-assembler")]
5use crate::{
6 backends::{
7 windows::emitter::{RelocationKind, X64Emitter},
8 Backend, GeneratedFiles,
9 },
10 config::GaiaConfig,
11 instruction::{CoreInstruction, GaiaInstruction, ManagedInstruction},
12 program::{GaiaConstant, GaiaFunction, GaiaModule},
13 types::GaiaType,
14};
15use gaia_types::{
16 helpers::{AbiCompatible, ApiCompatible, Architecture, ArtifactType, CompilationTarget},
17 GaiaError, Result,
18};
19
20#[cfg(feature = "pe-assembler")]
21use pe_assembler::formats::exe::writer::ExeWriter;
22use std::collections::HashMap;
23
24#[cfg(feature = "x86_64-assembler")]
25use x86_64_assembler::{
26 encoder::InstructionEncoder,
27 instruction::{Instruction, Operand, Register},
28};
29
30#[cfg(feature = "x86_64-assembler")]
32pub struct X64Backend {
33 encoder: InstructionEncoder,
34}
35
36#[cfg(feature = "x86_64-assembler")]
37impl X64Backend {
38 pub fn new() -> Self {
40 Self {
41 encoder: InstructionEncoder::new(Architecture::X86_64),
42 }
43 }
44}
45
46#[cfg(feature = "x86_64-assembler")]
47impl Default for X64Backend {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53#[cfg(feature = "x86_64-assembler")]
54impl Backend for X64Backend {
55 fn name(&self) -> &'static str {
56 "Windows (Native x86_64)"
57 }
58
59 fn primary_target(&self) -> CompilationTarget {
60 CompilationTarget { build: Architecture::X86_64, host: AbiCompatible::PE, target: ApiCompatible::MicrosoftVisualC }
61 }
62
63 fn artifact_type(&self) -> ArtifactType {
64 ArtifactType::Executable
65 }
66
67 fn match_score(&self, target: &CompilationTarget) -> f32 {
68 if target.build == Architecture::X86_64 && target.host == AbiCompatible::PE {
69 if target.target == ApiCompatible::MicrosoftVisualC {
70 return 100.0;
71 }
72 return 80.0;
73 }
74 0.0
75 }
76
77 fn generate(&self, program: &GaiaModule, config: &GaiaConfig) -> Result<GeneratedFiles> {
78 let mut emitter = X64Emitter::new(program);
79 emitter.emit()?;
80 let (instructions, relocations, _rdata) = emitter.take_result();
81
82 let mut code = Vec::new();
83 let mut labels = HashMap::new();
84 let mut inst_offsets = Vec::new();
85
86 for inst in &instructions {
88 inst_offsets.push(code.len());
89 match inst {
90 Instruction::Label(name) => {
91 labels.insert(name.clone(), code.len());
92 }
93 _ => {
94 let bytes = self.encode_inst(inst)?;
95 code.extend_from_slice(&bytes);
96 }
97 }
98 }
99
100 let mut exit_pos = 0;
102 let mut external_calls: HashMap<String, Vec<usize>> = HashMap::new();
103 let string_patches: Vec<(usize, usize)> = Vec::new();
104
105 for reloc in &relocations {
106 let inst_pos = inst_offsets[reloc.instruction_index];
107 match reloc.kind {
108 RelocationKind::Relative32 => {
109 if let Some(&target_offset) = labels.get(&reloc.target) {
110 let rel = (target_offset as i32) - (inst_pos as i32 + 5);
111 code[inst_pos + 1..inst_pos + 5].copy_from_slice(&rel.to_le_bytes());
112 }
113 }
114 RelocationKind::RipRelative => {
115 if reloc.target == "ExitProcess" {
116 exit_pos = inst_pos;
117 }
118 external_calls.entry(reloc.target.clone()).or_default().push(inst_pos);
119 }
120 _ => {}
121 }
122 }
123
124 let mut files = HashMap::new();
125 files.insert("main.exe".to_string(), code.clone());
126
127 #[cfg(feature = "pe-assembler")]
128 {
129 let rdata = &[];
130 let pe_bytes = self.create_pe_exe(&code, program, exit_pos, &external_calls, rdata, &string_patches)?;
131 files.insert("main.exe".to_string(), pe_bytes);
132 }
133
134 Ok(GeneratedFiles { artifact_type: self.artifact_type(), files, custom: None, diagnostics: vec![] })
135 }
136}
137
138#[cfg(feature = "x86_64-assembler")]
139impl X64Backend {
140 fn encode_inst(&self, inst: &Instruction) -> Result<Vec<u8>> {
141 self.encoder.encode(inst)
142 }
143
144 #[cfg(feature = "pe-assembler")]
145 fn create_pe_exe(
146 &self,
147 code: &[u8],
148 program: &GaiaModule,
149 exit_pos: usize,
150 external_calls: &HashMap<String, Vec<usize>>,
151 rdata: &[u8],
152 string_patches: &[(usize, usize)],
153 ) -> Result<Vec<u8>> {
154 use pe_assembler::formats::exe::writer::ExeWriter;
155 use pe_assembler::helpers::PeWriter;
156 use pe_assembler::types::{ImportEntry, ImportTable, PeProgram};
157 use std::io::Cursor;
158
159 let mut pe_program = PeProgram::create_executable(code.to_vec());
161
162 if !external_calls.is_empty() {
164 let mut imports = ImportTable::default();
165
166 if external_calls.contains_key("ExitProcess") {
168 let kernel32_entry = ImportEntry {
169 dll_name: "kernel32.dll".to_string(),
170 functions: vec!["ExitProcess".to_string()],
171 };
172 imports.entries.push(kernel32_entry);
173 }
174
175 if external_calls.contains_key("printf") {
177 let msvcrt_entry = ImportEntry {
178 dll_name: "msvcrt.dll".to_string(),
179 functions: vec!["printf".to_string()],
180 };
181 imports.entries.push(msvcrt_entry);
182 }
183
184 if !imports.entries.is_empty() {
185 pe_program = pe_program.with_imports(imports);
186 }
187 }
188
189 let mut buffer = Vec::new();
191 let mut cursor = Cursor::new(&mut buffer);
192 let mut writer = ExeWriter::new(cursor);
193 writer.write_program(&pe_program)?;
194 Ok(buffer)
195 }
196}