Skip to main content

gaia_assembler/backends/pe/
mod.rs

1//! PE (Portable Executable) backend compiler
2//! This backend generates .NET PE files containing IL code, not native machine code
3
4use super::{Backend, GeneratedFiles};
5#[cfg(feature = "clr")]
6use crate::backends::msil::ClrBackend;
7use crate::{config::GaiaConfig, program::GaiaModule};
8#[allow(unused_imports)]
9use gaia_types::{
10    helpers::{AbiCompatible, ApiCompatible, Architecture, CompilationTarget},
11    *,
12};
13#[cfg(feature = "clr")]
14use std::collections::HashMap;
15
16/// PE Backend implementation
17#[derive(Default)]
18pub struct PeBackend {}
19
20impl Backend for PeBackend {
21    fn name(&self) -> &'static str {
22        "Windows PE Assembly"
23    }
24
25    fn primary_target(&self) -> CompilationTarget {
26        CompilationTarget { build: Architecture::CLR, host: AbiCompatible::PE, target: ApiCompatible::ClrRuntime(4) }
27    }
28
29    fn match_score(&self, target: &CompilationTarget) -> f32 {
30        match target.host {
31            AbiCompatible::PE => match target.build {
32                Architecture::CLR => 80.0,
33                // .NET PE is a container for IL, so it can support native archs but with low score
34                Architecture::X86 | Architecture::X86_64 => 5.0,
35                _ => -100.0,
36            },
37            _ => -100.0,
38        }
39    }
40
41    fn generate(&self, _program: &GaiaModule, _config: &GaiaConfig) -> Result<GeneratedFiles> {
42        #[cfg(feature = "clr")]
43        {
44            let mut files = HashMap::new();
45            // 如果存在 main 函数,则输出可执行文件;否则输出 DLL
46            let has_main = _program.functions.iter().any(|f| f.name == "main");
47            let filename = if has_main { "main.exe" } else { "main.dll" };
48            // 使用 CLR 后端按统一设置生成 IL,再打包为 PE
49            let il_code = ClrBackend::generate_with_settings(_program, &_config.setting)?;
50            files.insert(filename.to_string(), generate_dotnet_pe_file(&il_code, &_program.name)?);
51            Ok(GeneratedFiles { files, diagnostics: vec![] })
52        }
53        #[cfg(not(feature = "clr"))]
54        {
55            Err(GaiaError::platform_unsupported("PE", "CLR backend required"))
56        }
57    }
58}
59
60/// Compile Gaia program to .NET PE executable file
61#[cfg(feature = "clr")]
62pub fn compile(program: &GaiaModule) -> Result<Vec<u8>> {
63    // 兼容旧接口:使用默认设置生成 IL 并打包
64    let il_code = ClrBackend::generate(program)?;
65    generate_dotnet_pe_file(&il_code, &program.name)
66}
67
68/// Generate a .NET PE file containing the IL code
69#[cfg(feature = "clr")]
70fn generate_dotnet_pe_file(il_code: &[u8], program_name: &str) -> Result<Vec<u8>> {
71    // Create a minimal .NET PE file structure
72    // This is a simplified implementation that creates a basic .NET executable
73
74    // Convert IL bytecode to string for embedding in PE
75    let il_text = String::from_utf8_lossy(il_code);
76
77    // Create a minimal .NET PE file with the IL code
78    // For now, we'll create a simple wrapper that can be executed by the .NET runtime
79    let pe_content = create_minimal_dotnet_pe(&il_text, program_name)?;
80
81    Ok(pe_content)
82}
83
84/// Create a minimal .NET PE file structure
85#[cfg(feature = "clr")]
86fn create_minimal_dotnet_pe(il_code: &str, program_name: &str) -> Result<Vec<u8>> {
87    // This is a simplified implementation
88    // In a real implementation, we would need to create proper PE headers,
89    // metadata tables, and IL method bodies according to the CLI specification
90
91    // For now, we'll create a basic structure that includes the IL code
92    // and can be recognized as a .NET assembly
93
94    let mut pe_data = Vec::new();
95
96    // Add DOS header (simplified)
97    pe_data.extend_from_slice(b"MZ");
98    pe_data.resize(0x80, 0);
99
100    // Add PE signature
101    pe_data.extend_from_slice(b"PE\0\0");
102
103    // Add basic COFF header for .NET
104    pe_data.extend_from_slice(&[
105        0x4C, 0x01, // Machine: IMAGE_FILE_MACHINE_I386
106        0x03, 0x00, // NumberOfSections: 3
107        0x00, 0x00, 0x00, 0x00, // TimeDateStamp
108        0x00, 0x00, 0x00, 0x00, // PointerToSymbolTable
109        0x00, 0x00, 0x00, 0x00, // NumberOfSymbols
110        0xE0, 0x00, // SizeOfOptionalHeader
111        0x02, 0x01, // Characteristics: IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE
112    ]);
113
114    // Add optional header (simplified for .NET)
115    pe_data.extend_from_slice(&[
116        0x0B, 0x01, // Magic: PE32
117        0x0E, 0x00, // MajorLinkerVersion, MinorLinkerVersion
118    ]);
119
120    // Add more PE structure...
121    // This is a very simplified version. A complete implementation would need
122    // to properly construct all PE sections, metadata, and IL method bodies.
123
124    // For now, embed the IL code as a comment in the PE
125    let il_comment = format!("// IL Code for {}\n{}", program_name, il_code);
126    pe_data.extend_from_slice(il_comment.as_bytes());
127
128    // Pad to minimum size
129    pe_data.resize(1024, 0);
130
131    Ok(pe_data)
132}