1use crate::program::{ClrInstruction, ClrMethod, ClrOpcode, ClrProgram};
2use gaia_types::{GaiaDiagnostics, GaiaError};
3use pe_assembler::{
4 exe_write_path,
5 helpers::PeWriter,
6 types::{
7 tables::{ExportTable, ImportTable},
8 CoffHeader, DataDirectory, DosHeader, NtHeader, OptionalHeader, PeHeader, PeProgram, PeSection, SubsystemType,
9 },
10};
11use std::{
12 io::{Cursor, Seek, Write},
13 path::Path,
14};
15
16#[derive(Debug)]
17pub struct DotNetWriter<W> {
18 writer: W,
19}
20
21impl<W> DotNetWriter<W> {
22 pub fn new(writer: W) -> Self {
23 Self { writer }
24 }
25}
26
27impl<W: Write + Seek> DotNetWriter<W> {
28 pub fn write(mut self, clr: &ClrProgram) -> GaiaDiagnostics<W> {
29 match self.build_pe_program(clr) {
30 Ok(pe_program) => match self.write_pe_program(&pe_program) {
31 Ok(_) => GaiaDiagnostics::success(self.writer),
32 Err(e) => GaiaDiagnostics::failure(e),
33 },
34 Err(e) => GaiaDiagnostics::failure(e),
35 }
36 }
37
38 pub fn write_to_path(clr: &ClrProgram, path: &Path) -> Result<gaia_types::helpers::Url, GaiaError> {
40 let builder: DotNetWriter<std::io::Cursor<Vec<u8>>> = DotNetWriter::new(std::io::Cursor::new(Vec::new()));
42 let pe_program = builder.build_pe_program(clr)?;
43 exe_write_path(&pe_program, path).map_err(|e| GaiaError::custom_error(format!("写入 PE 文件失败: {}", e)))
44 }
45
46 fn build_pe_program(&self, clr: &ClrProgram) -> Result<PeProgram, GaiaError> {
47 let clr_data = self.build_clr_data(clr)?;
49
50 let text_section = PeSection {
52 name: ".text".to_string(),
53 virtual_size: clr_data.len() as u32,
54 virtual_address: 0x2000,
55 size_of_raw_data: align_to(clr_data.len() as u32, 0x200),
56 pointer_to_raw_data: 0x400,
57 pointer_to_relocations: 0,
58 pointer_to_line_numbers: 0,
59 number_of_relocations: 0,
60 number_of_line_numbers: 0,
61 characteristics: 0x60000020, data: clr_data,
63 };
64
65 let pe_header = self.build_pe_header(&text_section)?;
67
68 let pe_program = PeProgram {
70 header: pe_header,
71 sections: vec![text_section],
72 imports: ImportTable::new(),
73 exports: ExportTable::new(),
74 };
75
76 Ok(pe_program)
77 }
78
79 fn write_pe_program(&mut self, pe_program: &PeProgram) -> Result<(), GaiaError> {
80 use pe_assembler::formats::exe::writer::ExeWriter;
81 let mut pe_writer = ExeWriter::new(&mut self.writer);
82 pe_writer.write_program(pe_program)?;
83 Ok(())
84 }
85 fn build_pe_header(&self, text_section: &PeSection) -> Result<PeHeader, GaiaError> {
86 let dos_header = DosHeader::new(0x80); let nt_header = NtHeader {
89 signature: 0x00004550, };
91
92 let coff_header = CoffHeader::new(0x014C, 1) .with_timestamp(0)
94 .with_symbol_table(0, 0)
95 .with_optional_header_size(224) .with_characteristics(0x0102); let optional_header = OptionalHeader::new(
99 0x2000, 0x400000, text_section.size_of_raw_data, 0x400, 0x4000, SubsystemType::Console, );
106
107 Ok(PeHeader { dos_header, nt_header, coff_header, optional_header })
108 }
109
110 fn build_clr_data(&self, clr: &ClrProgram) -> Result<Vec<u8>, GaiaError> {
111 let mut data = Vec::new();
112
113 let clr_header_size = 72;
115 data.resize(clr_header_size, 0);
116
117 let metadata_offset = clr_header_size;
119 let metadata_start = data.len();
120
121 self.write_metadata_to_buffer(&mut data, clr)?;
123 let metadata_size = data.len() - metadata_start;
124
125 while data.len() % 4 != 0 {
127 data.push(0);
128 }
129
130 let code_offset = data.len();
131
132 self.write_code_to_buffer(&mut data, clr)?;
134
135 let base_rva = 0x2000; let metadata_rva = base_rva + metadata_offset as u32;
138 let code_rva = base_rva + code_offset as u32;
139
140 self.write_clr_header_with_offsets(&mut data, clr, metadata_rva, metadata_size as u32)?;
141
142 Ok(data)
143 }
144
145 fn write_clr_header_with_offsets(
146 &self,
147 buffer: &mut Vec<u8>,
148 clr: &ClrProgram,
149 metadata_rva: u32,
150 metadata_size: u32,
151 ) -> Result<(), GaiaError> {
152 let clr_header = ClrHeader::new(
153 clr.version.major as u16,
154 clr.version.minor as u16,
155 metadata_rva,
156 metadata_size,
157 0, );
159
160 let mut cursor = std::io::Cursor::new(&mut buffer[0..72]);
162
163 cursor.write_all(&clr_header.cb.to_le_bytes())?;
165 cursor.write_all(&clr_header.major_runtime_version.to_le_bytes())?;
166 cursor.write_all(&clr_header.minor_runtime_version.to_le_bytes())?;
167 cursor.write_all(&clr_header.metadata_rva.to_le_bytes())?;
168 cursor.write_all(&clr_header.metadata_size.to_le_bytes())?;
169 cursor.write_all(&clr_header.flags.to_le_bytes())?;
170 cursor.write_all(&clr_header.entry_point_token.to_le_bytes())?;
171 cursor.write_all(&clr_header.resources_rva.to_le_bytes())?;
172 cursor.write_all(&clr_header.resources_size.to_le_bytes())?;
173 cursor.write_all(&clr_header.strong_name_signature_rva.to_le_bytes())?;
174 cursor.write_all(&clr_header.strong_name_signature_size.to_le_bytes())?;
175 cursor.write_all(&clr_header.code_manager_table_rva.to_le_bytes())?;
176 cursor.write_all(&clr_header.code_manager_table_size.to_le_bytes())?;
177 cursor.write_all(&clr_header.vtable_fixups_rva.to_le_bytes())?;
178 cursor.write_all(&clr_header.vtable_fixups_size.to_le_bytes())?;
179 cursor.write_all(&clr_header.export_address_table_jumps_rva.to_le_bytes())?;
180 cursor.write_all(&clr_header.export_address_table_jumps_size.to_le_bytes())?;
181 cursor.write_all(&clr_header.managed_native_header_rva.to_le_bytes())?;
182 cursor.write_all(&clr_header.managed_native_header_size.to_le_bytes())?;
183
184 Ok(())
185 }
186
187 fn write_metadata_to_buffer(&self, buffer: &mut Vec<u8>, clr: &ClrProgram) -> Result<(), GaiaError> {
188 buffer.extend_from_slice(b"BSJB");
191
192 buffer.extend_from_slice(&1u16.to_le_bytes()); buffer.extend_from_slice(&1u16.to_le_bytes()); buffer.extend_from_slice(&0u32.to_le_bytes()); let version_str = "v4.0.30319";
199 let version_len = ((version_str.len() + 3) / 4) * 4; buffer.extend_from_slice(&(version_len as u32).to_le_bytes());
201 buffer.extend_from_slice(version_str.as_bytes());
202
203 while buffer.len() % 4 != 0 {
205 buffer.push(0);
206 }
207
208 buffer.extend_from_slice(&0u16.to_le_bytes()); buffer.extend_from_slice(&5u16.to_le_bytes()); self.write_stream_headers(buffer)?;
214
215 self.write_metadata_streams(buffer, clr)?;
217
218 Ok(())
219 }
220
221 fn write_stream_headers(&self, buffer: &mut Vec<u8>) -> Result<(), GaiaError> {
222 buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(b"#~\0\0"); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(b"#Strings\0\0\0\0"); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(b"#US\0"); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(b"#GUID\0\0\0"); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(b"#Blob\0\0\0"); Ok(())
248 }
249
250 fn write_metadata_streams(&self, buffer: &mut Vec<u8>, clr: &ClrProgram) -> Result<(), GaiaError> {
251 self.write_metadata_tables_stream(buffer, clr)?;
255
256 self.write_strings_stream(buffer, clr)?;
258
259 self.write_user_strings_stream(buffer)?;
261
262 self.write_guid_stream(buffer)?;
264
265 self.write_blob_stream(buffer)?;
267
268 Ok(())
269 }
270
271 fn write_metadata_tables_stream(&self, buffer: &mut Vec<u8>, clr: &ClrProgram) -> Result<(), GaiaError> {
272 buffer.extend_from_slice(&0u32.to_le_bytes()); buffer.extend_from_slice(&2u8.to_le_bytes()); buffer.extend_from_slice(&0u8.to_le_bytes()); buffer.extend_from_slice(&0u8.to_le_bytes()); buffer.extend_from_slice(&1u8.to_le_bytes()); buffer.extend_from_slice(&0x01u64.to_le_bytes()); buffer.extend_from_slice(&0x01u64.to_le_bytes()); buffer.extend_from_slice(&1u32.to_le_bytes()); buffer.extend_from_slice(&0u16.to_le_bytes()); buffer.extend_from_slice(&1u16.to_le_bytes()); buffer.extend_from_slice(&1u16.to_le_bytes()); buffer.extend_from_slice(&0u16.to_le_bytes()); buffer.extend_from_slice(&0u16.to_le_bytes()); Ok(())
294 }
295
296 fn write_strings_stream(&self, buffer: &mut Vec<u8>, clr: &ClrProgram) -> Result<(), GaiaError> {
297 buffer.push(0);
299
300 let module_name = clr.name.as_bytes();
302 buffer.extend_from_slice(module_name);
303 buffer.push(0); while buffer.len() % 4 != 0 {
307 buffer.push(0);
308 }
309
310 Ok(())
311 }
312
313 fn write_user_strings_stream(&self, buffer: &mut Vec<u8>) -> Result<(), GaiaError> {
314 buffer.push(0);
316
317 while buffer.len() % 4 != 0 {
319 buffer.push(0);
320 }
321
322 Ok(())
323 }
324
325 fn write_guid_stream(&self, buffer: &mut Vec<u8>) -> Result<(), GaiaError> {
326 let module_guid = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
328 buffer.extend_from_slice(&module_guid);
329
330 Ok(())
331 }
332
333 fn write_blob_stream(&self, buffer: &mut Vec<u8>) -> Result<(), GaiaError> {
334 buffer.push(0);
336
337 while buffer.len() % 4 != 0 {
339 buffer.push(0);
340 }
341
342 Ok(())
343 }
344
345 fn write_code_to_buffer(&self, buffer: &mut Vec<u8>, clr: &ClrProgram) -> Result<(), GaiaError> {
346 for clr_type in &clr.types {
348 for method in &clr_type.methods {
349 self.write_method_code_to_buffer(buffer, method)?;
350 }
351 }
352
353 for method in &clr.global_methods {
355 self.write_method_code_to_buffer(buffer, method)?;
356 }
357 Ok(())
358 }
359
360 fn write_method_code_to_buffer(&self, buffer: &mut Vec<u8>, method: &ClrMethod) -> Result<(), GaiaError> {
361 let mut code_size = 0u32;
363 for instruction in &method.instructions {
364 code_size += self.calculate_instruction_size(instruction)?;
365 }
366
367 if code_size < 64 && method.max_stack <= 8 && method.locals.is_empty() {
369 let header = (code_size << 2) | 0x02; buffer.push(header as u8);
372 }
373 else {
374 let flags: u16 = 0x03; buffer.extend_from_slice(&flags.to_le_bytes());
377 buffer.push(0x30); buffer.extend_from_slice(&method.max_stack.to_le_bytes());
379 buffer.extend_from_slice(&code_size.to_le_bytes());
380 buffer.extend_from_slice(&0u32.to_le_bytes()); }
382
383 for instruction in &method.instructions {
385 self.write_instruction_to_buffer(buffer, instruction)?;
386 }
387
388 while buffer.len() % 4 != 0 {
390 buffer.push(0);
391 }
392
393 Ok(())
394 }
395
396 fn calculate_instruction_size(&self, instruction: &ClrInstruction) -> Result<u32, GaiaError> {
397 match instruction {
398 ClrInstruction::Simple { opcode } => {
399 match opcode {
400 ClrOpcode::Nop | ClrOpcode::Ret => Ok(1),
401 ClrOpcode::Ldstr | ClrOpcode::Call => Ok(5), _ => Ok(1), }
404 }
405 ClrInstruction::WithImmediate { opcode, .. } => {
406 match opcode {
407 ClrOpcode::LdcI4 => Ok(5), _ => Ok(5),
409 }
410 }
411 ClrInstruction::WithImmediate64 { .. } => Ok(9), ClrInstruction::WithFloat32 { .. } => Ok(5), ClrInstruction::WithFloat64 { .. } => Ok(9), ClrInstruction::WithString { .. } => Ok(5), ClrInstruction::WithMethod { .. } => Ok(5), _ => Ok(1),
417 }
418 }
419
420 fn write_instruction_to_buffer(&self, buffer: &mut Vec<u8>, instruction: &ClrInstruction) -> Result<(), GaiaError> {
421 match instruction {
422 ClrInstruction::Simple { opcode } => match opcode {
423 ClrOpcode::Nop => buffer.push(0x00),
424 ClrOpcode::Ldstr => buffer.push(0x72),
425 ClrOpcode::Call => buffer.push(0x28),
426 ClrOpcode::Ret => buffer.push(0x2A),
427 _ => return Err(GaiaError::not_implemented("Unsupported opcode")),
428 },
429 ClrInstruction::WithImmediate { opcode, value } => match opcode {
430 ClrOpcode::LdcI4 => {
431 buffer.push(0x20);
432 buffer.extend_from_slice(&value.to_le_bytes());
433 }
434 _ => return Err(GaiaError::not_implemented("Unsupported opcode with immediate")),
435 },
436 ClrInstruction::WithImmediate64 { opcode, value } => match opcode {
437 ClrOpcode::LdcI8 => {
438 buffer.push(0x21);
439 buffer.extend_from_slice(&value.to_le_bytes());
440 }
441 _ => return Err(GaiaError::not_implemented("Unsupported opcode with immediate64")),
442 },
443 ClrInstruction::WithFloat32 { opcode, value } => match opcode {
444 ClrOpcode::LdcR4 => {
445 buffer.push(0x22);
446 buffer.extend_from_slice(&value.to_le_bytes());
447 }
448 _ => return Err(GaiaError::not_implemented("Unsupported opcode with float32")),
449 },
450 ClrInstruction::WithFloat64 { opcode, value } => match opcode {
451 ClrOpcode::LdcR8 => {
452 buffer.push(0x23);
453 buffer.extend_from_slice(&value.to_le_bytes());
454 }
455 _ => return Err(GaiaError::not_implemented("Unsupported opcode with float64")),
456 },
457 ClrInstruction::WithString { opcode, value } => {
458 match opcode {
459 ClrOpcode::Ldstr => {
460 buffer.push(0x72);
461 buffer.extend_from_slice(&[0x01, 0x00, 0x00, 0x70]);
463 }
464 _ => return Err(GaiaError::not_implemented("Unsupported opcode with string")),
465 }
466 }
467 ClrInstruction::WithMethod { opcode, method_ref } => {
468 match opcode {
469 ClrOpcode::Call => {
470 buffer.push(0x28);
471 buffer.extend_from_slice(&[0x01, 0x00, 0x00, 0x0A]);
473 }
474 _ => return Err(GaiaError::not_implemented("Unsupported opcode with method")),
475 }
476 }
477 _ => return Err(GaiaError::not_implemented("Unsupported instruction type")),
478 }
479 Ok(())
480 }
481}
482
483fn align_to(value: u32, alignment: u32) -> u32 {
485 (value + alignment - 1) & !(alignment - 1)
486}
487
488#[derive(Debug, Copy, Clone)]
490pub struct ClrHeader {
491 cb: u32,
492 major_runtime_version: u16,
493 minor_runtime_version: u16,
494 metadata_rva: u32,
495 metadata_size: u32,
496 flags: u32,
497 entry_point_token: u32,
498 resources_rva: u32,
499 resources_size: u32,
500 strong_name_signature_rva: u32,
501 strong_name_signature_size: u32,
502 code_manager_table_rva: u32,
503 code_manager_table_size: u32,
504 vtable_fixups_rva: u32,
505 vtable_fixups_size: u32,
506 export_address_table_jumps_rva: u32,
507 export_address_table_jumps_size: u32,
508 managed_native_header_rva: u32,
509 managed_native_header_size: u32,
510}
511
512impl ClrHeader {
513 pub fn new(
515 major_runtime_version: u16,
516 minor_runtime_version: u16,
517 metadata_rva: u32,
518 metadata_size: u32,
519 entry_point_token: u32,
520 ) -> Self {
521 Self {
522 cb: 72, major_runtime_version,
524 minor_runtime_version,
525 metadata_rva,
526 metadata_size,
527 flags: 0x01, entry_point_token,
529 resources_rva: 0,
530 resources_size: 0,
531 strong_name_signature_rva: 0,
532 strong_name_signature_size: 0,
533 code_manager_table_rva: 0,
534 code_manager_table_size: 0,
535 vtable_fixups_rva: 0,
536 vtable_fixups_size: 0,
537 export_address_table_jumps_rva: 0,
538 export_address_table_jumps_size: 0,
539 managed_native_header_rva: 0,
540 managed_native_header_size: 0,
541 }
542 }
543}