Skip to main content

clr_assembler/formats/dll/reader/
mod.rs

1mod headers;
2mod metadata;
3mod program;
4mod utils;
5
6use crate::{
7    formats::dll::DllReadConfig,
8    program::{ClrHeader, ClrProgram, ClrVersion, DotNetAssemblyInfo, MetadataHeader, StreamHeader},
9};
10use gaia_types::{GaiaError, SourceLocation};
11use pe_assembler::{
12    helpers::PeReader,
13    types::{PeHeader, PeProgram, SectionHeader},
14};
15use std::io::{Read, Seek};
16
17/// DLL reader for .NET assemblies.
18#[derive(Debug)]
19pub struct DllReader<'config, R> {
20    /// Configuration options for reading.
21    options: &'config DllReadConfig,
22    reader: pe_assembler::formats::dll::reader::DllReader<R>,
23    /// Parsed CLR header information.
24    clr_header: Option<ClrHeader>,
25    /// Parsed metadata header information.
26    metadata_header: Option<MetadataHeader>,
27    /// Metadata stream header list (lazy loaded).
28    stream_headers: Option<Vec<StreamHeader>>,
29    /// Extracted basic assembly information (lazy loaded).
30    assembly_info: Option<DotNetAssemblyInfo>,
31    /// Fully parsed CLR program (lazy loaded).
32    clr_program: Option<ClrProgram>,
33}
34
35impl<'config, R: Read + Seek> PeReader<R> for DllReader<'config, R> {
36    fn get_viewer(&mut self) -> &mut R {
37        self.reader.get_viewer()
38    }
39
40    fn add_diagnostics(&mut self, error: impl Into<GaiaError>) {
41        self.reader.add_diagnostics(error)
42    }
43
44    fn get_section_headers(&mut self) -> Result<&[SectionHeader], GaiaError> {
45        self.reader.get_section_headers()
46    }
47
48    fn get_pe_header(&mut self) -> Result<&PeHeader, GaiaError> {
49        self.reader.get_pe_header()
50    }
51
52    fn get_program(&mut self) -> Result<&PeProgram, GaiaError> {
53        self.reader.get_program()
54    }
55}
56
57impl<'config, R> DllReader<'config, R> {
58    /// Constructs a .NET reader (DLL) using a generic PE reader.
59    ///
60    /// Note: This is a lazy constructor and will not immediately execute the parsing workflow.
61    pub fn new(reader: R, options: &'config DllReadConfig) -> Self {
62        Self {
63            reader: pe_assembler::formats::dll::reader::DllReader::new(reader),
64            clr_header: None,
65            metadata_header: None,
66            stream_headers: None,
67            assembly_info: None,
68            clr_program: None,
69            options,
70        }
71    }
72}
73
74impl<'config, R> DllReader<'config, R>
75where
76    R: Read + Seek,
77{
78    /// Lazily reads basic assembly information.
79    ///
80    /// Only reads basic identification information of the assembly without parsing the full type system.
81    /// Suitable for scenarios where you need to quickly get assembly name, version, etc.
82    ///
83    /// # Returns
84    /// * `Ok(DotNetAssemblyInfo)` - Basic assembly information.
85    /// * `Err(GaiaError)` - Error during the reading process.
86    pub fn get_assembly_info(&mut self) -> Result<DotNetAssemblyInfo, GaiaError> {
87        if self.assembly_info.is_none() {
88            self.ensure_assembly_info_parsed()?;
89        }
90
91        self.assembly_info
92            .as_ref()
93            .cloned()
94            .ok_or_else(|| GaiaError::syntax_error("程序集信息未解析".to_string(), SourceLocation::default()))
95    }
96
97    /// Parses as a full CLR program.
98    ///
99    /// Parses the entire .NET assembly, including all types, methods, fields, and other information.
100    /// This is a heavyweight operation that consumes significant memory and time.
101    ///
102    /// # Returns
103    /// * `Ok(ClrProgram)` - Full representation of the CLR program.
104    /// * `Err(GaiaError)` - Error during the parsing process.
105    pub fn to_clr_program(&mut self) -> Result<ClrProgram, GaiaError> {
106        if let Some(ref program) = self.clr_program {
107            return Ok(program.clone());
108        }
109
110        // 执行完整解析
111        let program = self.parse_full_program()?;
112        self.clr_program = Some(program.clone());
113        Ok(program)
114    }
115
116    /// Validates the integrity of the assembly.
117    ///
118    /// Checks if the parsed .NET assembly contains all required components:
119    /// - CLR Header: Contains runtime information.
120    /// - Metadata Header: Describes the type system.
121    /// - Metadata Streams: Contains the actual metadata.
122    ///
123    /// # Returns
124    /// * `Ok(Vec<String>)` - List of warning messages; an empty list indicates validation success.
125    /// * `Err(GaiaError)` - Error during the validation process.
126    pub fn validate_assembly(&mut self) -> Result<Vec<String>, GaiaError> {
127        let mut warnings = Vec::new();
128
129        // 确保基本信息已解析
130        self.ensure_assembly_info_parsed()?;
131
132        // 验证 CLR 头 - 必需的核心头信息
133        if self.clr_header.is_none() {
134            warnings.push("缺少 CLR 头".to_string());
135        }
136
137        // 验证元数据头 - 描述类型系统的元数据
138        if self.metadata_header.is_none() {
139            warnings.push("缺少元数据头".to_string());
140        }
141
142        // 验证流头 - 包含实际的元数据流
143        if self.stream_headers.as_ref().map_or(true, |h| h.is_empty()) {
144            warnings.push("缺少元数据流".to_string());
145        }
146
147        Ok(warnings)
148    }
149
150    /// Gets a summary of the assembly information.
151    ///
152    /// Returns the basic information of the assembly in a friendly format, suitable for display or logging.
153    /// If the assembly information is unavailable, it returns a corresponding error message.
154    ///
155    /// # Returns
156    /// * `String` - Formatted assembly information containing name, version, culture, public key token, and runtime version.
157    pub fn get_assembly_summary(&mut self) -> String {
158        match self.get_assembly_info() {
159            Ok(info) => {
160                format!(
161                    "Assembly: {}\nVersion: {}\nCulture: {}\nPublic Key Token: {}\nRuntime Version: {}",
162                    info.name,
163                    info.version,
164                    info.culture.as_deref().unwrap_or("neutral"),
165                    info.public_key_token.as_deref().unwrap_or("null"),
166                    info.runtime_version.as_deref().unwrap_or("unknown")
167                )
168            }
169            Err(_) => "Unable to get assembly information".to_string(),
170        }
171    }
172
173    /// Ensures that the assembly information is parsed (lazy loading helper method).
174    fn ensure_assembly_info_parsed(&mut self) -> Result<(), GaiaError> {
175        if self.assembly_info.is_some() {
176            return Ok(());
177        }
178
179        // Execute the parsing workflow on demand
180        self.parse_clr_header_lazy()?;
181        self.parse_metadata_lazy()?;
182        self.extract_assembly_info()?;
183
184        Ok(())
185    }
186
187    /// Parses the CLR header.
188    fn parse_clr_header_lazy(&mut self) -> Result<(), GaiaError> {
189        eprintln!("Starting to parse CLR header");
190        self.clr_header = self.find_and_read_clr_header()?;
191        eprintln!("find_and_read_clr_header returned: {:?}", self.clr_header.is_some());
192        if self.clr_header.is_none() {
193            eprintln!("CLR header is empty, returning error");
194            return Err(GaiaError::syntax_error("Missing CLR header", SourceLocation::default()));
195        }
196        eprintln!("CLR header parsed successfully");
197        Ok(())
198    }
199
200    /// Parses the metadata.
201    fn parse_metadata_lazy(&mut self) -> Result<(), GaiaError> {
202        if let Some(ref clr_header) = self.clr_header {
203            // Convert metadata RVA to file offset
204            let pe_program = self.reader.get_program()?.clone();
205            let metadata_offset = utils::rva_to_file_offset(&pe_program, clr_header.metadata_rva)?;
206            // Read metadata header
207            self.metadata_header = Some(self.read_metadata_header(metadata_offset)?);
208            // Read stream header information
209            self.stream_headers = Some(self.read_stream_headers(metadata_offset)?);
210        }
211
212        Ok(())
213    }
214}