clr_assembler/formats/exe/reader/
mod.rs

1use crate::program::{
2    ClrAccessFlags, ClrHeader, ClrMethod, ClrProgram, ClrType, ClrTypeReference, ClrVersion, DotNetAssemblyInfo,
3    MetadataHeader, StreamHeader,
4};
5use byteorder::{LittleEndian, ReadBytesExt};
6use gaia_types::{GaiaDiagnostics, GaiaError, SourceLocation};
7use pe_assembler::{
8    helpers::PeReader,
9    types::{PeHeader, PeProgram, SectionHeader},
10};
11use std::{
12    fs,
13    io::{Cursor, Read, Seek, SeekFrom},
14};
15use url::Url;
16
17/// .NET PE 文件惰性读取器
18///
19/// 该类负责读取和解析 .NET 程序集文件,提供以下功能:
20/// - 检查文件是否为有效的 .NET 程序集
21/// - 解析 CLR 头和元数据
22/// - 提取程序集的基本信息
23/// - 验证程序集的完整性
24/// - 支持惰性读取和完整解析两种模式
25#[derive(Debug)]
26pub struct ExeReader<R> {
27    exe_reader: pe_assembler::formats::exe::reader::ExeReader<R>,
28    /// 解析后的 CLR 头信息
29    clr_header: Option<ClrHeader>,
30    /// 解析后的元数据头信息
31    metadata_header: Option<MetadataHeader>,
32    /// 元数据流头信息列表(惰性加载)
33    stream_headers: Option<Vec<StreamHeader>>,
34    /// 提取的程序集基本信息(惰性加载)
35    assembly_info: Option<DotNetAssemblyInfo>,
36    /// 完整解析的 CLR 程序(惰性加载)
37    clr_program: Option<ClrProgram>,
38}
39
40impl<R: Read + Seek> PeReader<R> for ExeReader<R> {
41    fn get_viewer(&mut self) -> &mut R {
42        self.exe_reader.get_viewer()
43    }
44
45    fn add_diagnostics(&mut self, error: impl Into<GaiaError>) {
46        self.exe_reader.add_diagnostics(error)
47    }
48
49    fn get_section_headers(&mut self) -> Result<&[SectionHeader], GaiaError> {
50        self.exe_reader.get_section_headers()
51    }
52
53    fn get_pe_header(&mut self) -> Result<&PeHeader, GaiaError> {
54        self.exe_reader.get_pe_header()
55    }
56
57    fn get_program(&mut self) -> Result<&PeProgram, GaiaError> {
58        self.exe_reader.get_program()
59    }
60}
61
62impl<R> ExeReader<R>
63where
64    R: Read + Seek,
65{
66    /// 使用泛型 PE 读取器构造 .NET 读取器
67    ///
68    /// 注意:这是惰性构造函数,不会立即执行解析工作流程
69    pub fn new(reader: R) -> Self {
70        ExeReader {
71            exe_reader: pe_assembler::formats::exe::reader::ExeReader::new(reader),
72            clr_header: None,
73            metadata_header: None,
74            stream_headers: None,
75            assembly_info: None,
76            clr_program: None,
77        }
78    }
79}
80impl<R> ExeReader<R>
81where
82    R: Read + Seek,
83{
84    /// 从文件读取 .NET 程序集
85    ///
86    /// 该方法读取并解析 .NET 程序集文件,步骤如下:
87    /// 1. 将整个文件读入内存
88    /// 2. 创建 PE 视图以访问 PE 结构
89    /// 3. 创建读取器实例
90    /// 4. 执行解析工作流程
91    ///
92    /// # 参数
93    /// * `file_path` - .NET 程序集文件路径
94    ///
95    /// # 返回
96    /// * `Ok(DotNetReader)` - 成功解析的读取器
97    /// * `Err(GaiaError)` - 读取或解析过程中的错误
98    /// 惰性读取程序集基本信息
99    ///
100    /// 仅读取程序集的基本标识信息,不解析完整的类型系统。
101    /// 适用于快速获取程序集名称、版本等信息的场景。
102    ///
103    /// # 返回
104    /// * `Ok(DotNetAssemblyInfo)` - 程序集基本信息
105    /// * `Err(GaiaError)` - 读取过程中的错误
106    pub fn get_assembly_info(&mut self) -> Result<DotNetAssemblyInfo, GaiaError> {
107        if self.assembly_info.is_none() {
108            self.ensure_assembly_info_parsed()?;
109        }
110
111        self.assembly_info
112            .as_ref()
113            .cloned()
114            .ok_or_else(|| GaiaError::syntax_error("程序集信息未解析".to_string(), SourceLocation::default()))
115    }
116
117    /// 检查文件是否为 .NET 程序集
118    ///
119    /// 快速检查方法,无需完整解析,仅通过检查 PE 数据目录:
120    /// - 读取 PE 文件并创建视图
121    /// - 检查第 15 个数据目录(索引 14)是否为 CLR 运行时头
122    /// - 如果该目录存在且有效,则为 .NET 程序集
123    ///
124    /// # 参数
125    /// * `file_path` - 要检查的 PE 文件路径
126    ///
127    /// # 返回
128    /// * `Ok(true)` - 是 .NET 程序集
129    /// * `Ok(false)` - 不是 .NET 程序集
130    /// * `Err(GaiaError)` - 检查过程中的错误
131    /// 完整解析为 CLR 程序
132    ///
133    /// 解析整个 .NET 程序集,包括所有类型、方法、字段等信息。
134    /// 这是一个重量级操作,会消耗较多内存和时间。
135    ///
136    /// # 返回
137    /// * `Ok(ClrProgram)` - 完整的 CLR 程序表示
138    /// * `Err(GaiaError)` - 解析过程中的错误
139    pub fn to_clr_program(&mut self) -> Result<ClrProgram, GaiaError> {
140        if let Some(ref program) = self.clr_program {
141            return Ok(program.clone());
142        }
143
144        // 执行完整解析
145        let program = self.parse_full_program()?;
146        self.clr_program = Some(program.clone());
147        Ok(program)
148    }
149
150    /// 惰性读取程序集基本信息
151    ///
152    /// 仅读取程序集的基本标识信息,不解析完整的类型系统。
153    /// 适用于快速获取程序集名称、版本等信息的场景。
154    ///
155    /// # 返回
156    /// * `Ok(DotNetAssemblyInfo)` - 程序集基本信息
157    /// * `Err(GaiaError)` - 读取过程中的错误
158    /// 验证程序集完整性
159    ///
160    /// 检查解析后的 .NET 程序集是否包含所有必需的组件:
161    /// - CLR 头:包含运行时信息
162    /// - 元数据头:描述类型系统
163    /// - 元数据流:包含实际的元数据
164    ///
165    /// # 返回
166    /// * `Ok(Vec<String>)` - 警告信息列表,空列表表示验证通过
167    /// * `Err(GaiaError)` - 验证过程中的错误
168    pub fn validate_assembly(&mut self) -> Result<Vec<String>, GaiaError> {
169        let mut warnings = Vec::new();
170
171        // 确保基本信息已解析
172        self.ensure_assembly_info_parsed()?;
173
174        // 验证 CLR 头 - 必需的核心头信息
175        if self.clr_header.is_none() {
176            warnings.push("缺少 CLR 头".to_string());
177        }
178
179        // 验证元数据头 - 描述类型系统的元数据
180        if self.metadata_header.is_none() {
181            warnings.push("缺少元数据头".to_string());
182        }
183
184        // 验证流头 - 包含实际的元数据流
185        if self.stream_headers.as_ref().map_or(true, |h| h.is_empty()) {
186            warnings.push("缺少元数据流".to_string());
187        }
188
189        Ok(warnings)
190    }
191
192    /// 完整解析为 CLR 程序
193    ///
194    /// 解析整个 .NET 程序集,包括所有类型、方法、字段等信息。
195    /// 这是一个重量级操作,会消耗较多内存和时间。
196    ///
197    /// # 返回
198    /// * `Ok(ClrProgram)` - 完整的 CLR 程序表示
199    /// * `Err(GaiaError)` - 解析过程中的错误
200    /// 获取程序集摘要信息
201    ///
202    /// 返回程序集的基本信息摘要,包括名称、版本、文化等。
203    /// 这是一个便捷方法,用于快速获取程序集的关键信息。
204    ///
205    /// # 返回
206    /// * `Ok(String)` - 格式化的程序集摘要信息
207    /// * `Err(GaiaError)` - 获取信息过程中的错误
208    pub fn get_assembly_summary(&mut self) -> Result<String, GaiaError> {
209        match self.get_assembly_info() {
210            Ok(info) => Ok(format!(
211                "程序集: {}, 版本: {}, 文化: {}",
212                info.name,
213                info.version,
214                info.culture.unwrap_or_else(|| "neutral".to_string())
215            )),
216            Err(_) => Ok("程序集信息不可用".to_string()),
217        }
218    }
219
220    /// 验证程序集完整性
221    ///
222    /// 检查解析后的 .NET 程序集是否包含所有必需的组件:
223    /// - CLR 头:包含运行时信息
224    /// - 元数据头:描述类型系统
225    /// - 元数据流:包含实际的元数据
226    ///
227    /// # 返回
228    /// * `Ok(Vec<String>)` - 警告信息列表,空列表表示验证通过
229    /// * `Err(GaiaError)` - 验证过程中的错误
230    /// 解析 CLR 头
231    ///
232    /// 这是解析流程的第一步,负责定位和读取 CLR 头信息。
233    /// CLR 头包含了 .NET 运行时所需的核心信息,如元数据位置、运行时版本等。
234    ///
235    /// # 返回
236    /// * `Ok(())` - 解析成功
237    /// * `Err(GaiaError)` - 解析过程中的错误
238    fn parse_clr_header(&mut self) -> Result<(), GaiaError> {
239        self.clr_header = self.find_and_read_clr_header()?;
240        Ok(())
241    }
242
243    /// 获取程序集摘要信息
244    ///
245    /// 以友好的格式返回程序集的基本信息,适合用于显示或日志记录。
246    /// 如果程序集信息不可用,返回相应的错误消息。
247    ///
248    /// # 返回
249    /// * `String` - 格式化的程序集信息,包含名称、版本、文化、公钥标记和运行时版本
250    /// 解析元数据
251    ///
252    /// 这是解析流程的第二步,在 CLR 头解析成功后执行:
253    /// 1. 使用 CLR 头中的 metadata_rva 定位元数据位置
254    /// 2. 读取元数据头,获取元数据的基本信息
255    /// 3. 读取所有的流头,了解元数据的组织结构
256    ///
257    /// # 返回
258    /// * `Ok(())` - 解析成功(即使没有 CLR 头也不会报错)
259    /// * `Err(GaiaError)` - 解析过程中的错误
260    fn parse_metadata(&mut self) -> Result<(), GaiaError> {
261        if let Some(ref clr_header) = self.clr_header {
262            // 将元数据的 RVA 转换为文件偏移
263            let metadata_offset = self.rva_to_file_offset(clr_header.metadata_rva)?;
264            // 读取元数据头
265            self.metadata_header = Some(self.read_metadata_header(metadata_offset)?);
266            // 读取流头信息
267            self.stream_headers = Some(self.read_stream_headers(metadata_offset)?);
268        }
269
270        Ok(())
271    }
272
273    /// 解析 CLR 头
274    ///
275    /// 这是解析流程的第一步,负责定位和读取 CLR 头信息。
276    /// CLR 头包含了 .NET 运行时所需的核心信息,如元数据位置、运行时版本等。
277    ///
278    /// # 返回
279    /// * `Ok(())` - 解析成功
280
281    /// 确保程序集信息已解析的私有辅助方法
282    fn ensure_assembly_info_parsed(&mut self) -> Result<(), GaiaError> {
283        if self.assembly_info.is_none() {
284            self.parse_clr_header()?;
285            self.parse_metadata()?;
286            self.extract_assembly_info()?;
287        }
288        Ok(())
289    }
290
291    /// 提取程序集信息
292    ///
293    /// 这是解析流程的第三步,负责从元数据中提取程序集级别的信息。
294    /// 这些信息包括程序集名称、版本、文化、公钥标记等,用于标识和版本控制。
295    ///
296    /// # 返回
297    /// * `Ok(())` - 提取成功
298    /// * `Err(GaiaError)` - 提取过程中的错误
299    fn extract_assembly_info(&mut self) -> Result<(), GaiaError> {
300        // TODO: 实现从元数据中提取程序集信息的逻辑
301        // 这需要解析 #Strings 流来获取程序集名称
302        // 解析 Assembly 表来获取版本信息
303        // 解析 Culture 和 PublicKey 信息
304
305        // 临时实现,后续需要完善
306        self.assembly_info = Some(DotNetAssemblyInfo {
307            name: "Unknown".to_string(),
308            version: "0.0.0.0".to_string(),
309            culture: None,
310            public_key_token: None,
311            runtime_version: Some("v4.0.30319".to_string()),
312        });
313
314        Ok(())
315    }
316
317    /// 解析完整的 CLR 程序
318    ///
319    /// 执行完整的程序集解析,包括所有类型、方法、字段等信息。
320    /// 这是一个重量级操作,会解析整个元数据表结构。
321    ///
322    /// # 返回
323    /// * `Ok(ClrProgram)` - 完整的 CLR 程序表示
324    /// * `Err(GaiaError)` - 解析过程中的错误
325    fn parse_full_program(&self) -> Result<ClrProgram, GaiaError> {
326        // 创建基本的 CLR 程序结构
327        let mut program = ClrProgram::new("UnknownAssembly".to_string());
328
329        // 设置版本信息
330        program.version = ClrVersion { major: 1, minor: 0, build: 0, revision: 0 };
331
332        // 设置访问标志
333        program.access_flags =
334            ClrAccessFlags { is_public: true, is_private: false, is_security_transparent: false, is_retargetable: false };
335
336        // TODO: 实现完整的元数据表解析
337        // 这需要解析以下元数据表:
338        // - Assembly 表:程序集信息
339        // - AssemblyRef 表:外部程序集引用
340        // - Module 表:模块信息
341        // - TypeDef 表:类型定义
342        // - MethodDef 表:方法定义
343        // - FieldDef 表:字段定义
344        // - MemberRef 表:成员引用
345        // - TypeRef 表:类型引用
346        // - Param 表:参数信息
347        // - Property 表:属性信息
348        // - Event 表:事件信息
349        // - CustomAttribute 表:自定义属性
350
351        // 临时添加一个示例类型
352        let mut example_type = ClrType::new("ExampleClass".to_string(), Some("ExampleNamespace".to_string()));
353
354        // 添加示例方法
355        let void_type = ClrTypeReference {
356            name: "Void".to_string(),
357            namespace: Some("System".to_string()),
358            assembly: Some("mscorlib".to_string()),
359            is_value_type: true,
360            is_reference_type: false,
361            generic_parameters: Vec::new(),
362        };
363
364        let example_method = ClrMethod::new("ExampleMethod".to_string(), void_type);
365        example_type.add_method(example_method);
366
367        program.add_type(example_type);
368
369        Ok(program)
370    }
371
372    /// 查找并读取 CLR 头
373    ///
374    /// 该方法在 PE 文件中搜索 CLR 头。CLR 头包含:
375    /// - 大小和版本信息
376    /// - 元数据位置(RVA 和大小)
377    /// - 入口点标记
378    /// - 各种标志和配置
379    ///
380    /// # 返回
381    /// * `Ok(Some(ClrHeader))` - 成功找到并读取 CLR 头
382    /// * `Ok(None)` - 未找到 CLR 头(不是 .NET 程序集)
383    /// * `Err(GaiaError)` - 读取过程中的错误
384    fn find_and_read_clr_header(&mut self) -> Result<Option<ClrHeader>, GaiaError> {
385        // 获取 PE 程序以访问数据目录
386        let pe_program = self.get_program()?.clone();
387
388        // 检查 CLR 数据目录是否存在(索引 14 是 CLR 运行时头)
389        if let Some(clr_dir) = pe_program.header.optional_header.data_directories.get(14) {
390            if clr_dir.virtual_address == 0 || clr_dir.size == 0 {
391                return Ok(None);
392            }
393
394            // 将 RVA 转换为文件偏移
395            let file_offset = self.rva_to_file_offset(clr_dir.virtual_address)?;
396
397            // 读取 CLR 头
398            let viewer = self.get_viewer();
399            viewer
400                .seek(SeekFrom::Start(file_offset as u64))
401                .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
402
403            let cb = viewer
404                .read_u32::<LittleEndian>()
405                .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
406            let major_runtime_version = viewer
407                .read_u16::<LittleEndian>()
408                .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
409            let minor_runtime_version = viewer
410                .read_u16::<LittleEndian>()
411                .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
412            let metadata_rva = viewer
413                .read_u32::<LittleEndian>()
414                .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
415            let metadata_size = viewer
416                .read_u32::<LittleEndian>()
417                .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
418            let flags = viewer
419                .read_u32::<LittleEndian>()
420                .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
421
422            Ok(Some(ClrHeader { cb, major_runtime_version, minor_runtime_version, metadata_rva, metadata_size, flags }))
423        }
424        else {
425            Ok(None)
426        }
427    }
428
429    /// 读取元数据头
430    ///
431    /// 该方法从指定的文件偏移位置读取元数据头。
432    /// 元数据头包含关于元数据结构的基本信息:
433    /// - 签名:表示 .NET 元数据的魔数(0x424A5342)
434    /// - 主次版本号
435    /// - 保留字段
436    /// - 版本字符串长度和内容
437    /// - 标志和流数量
438    ///
439    /// # 参数
440    /// * `offset` - 元数据头开始的文件偏移位置
441    ///
442    /// # Returns
443    /// * `Ok(MetadataHeader)` - 成功读取元数据头
444    /// * `Err(GaiaError)` - 读取过程中的错误
445    fn read_metadata_header(&mut self, offset: u32) -> Result<MetadataHeader, GaiaError> {
446        let viewer = self.get_viewer();
447        viewer
448            .seek(SeekFrom::Start(offset as u64))
449            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
450
451        // 读取固定长度的头部字段
452        let signature = viewer
453            .read_u32::<LittleEndian>()
454            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
455        let major_version = viewer
456            .read_u16::<LittleEndian>()
457            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
458        let minor_version = viewer
459            .read_u16::<LittleEndian>()
460            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
461        let reserved = viewer
462            .read_u32::<LittleEndian>()
463            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
464        let version_length = viewer
465            .read_u32::<LittleEndian>()
466            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
467
468        // 读取版本字符串(可变长度)
469        let mut version_bytes = vec![0u8; version_length as usize];
470        viewer
471            .read_exact(&mut version_bytes)
472            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
473        let version_string = String::from_utf8_lossy(&version_bytes).trim_end_matches('\0').to_string();
474
475        // 读取剩余的固定长度字段
476        let flags = viewer
477            .read_u16::<LittleEndian>()
478            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
479        let streams = viewer
480            .read_u16::<LittleEndian>()
481            .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
482
483        // 创建元数据头结构
484        Ok(MetadataHeader { signature, major_version, minor_version, reserved, version_length, version_string, flags, streams })
485    }
486
487    /// 读取流头信息
488    ///
489    /// 从元数据头之后的位置读取所有的流头信息。
490    /// 流头紧跟在元数据头中的可变长度版本字符串之后。
491    ///
492    /// 流头的结构(每个流):
493    /// - offset: 流在元数据中的偏移(4 字节)
494    /// - size: 流的大小(4 字节)
495    /// - name: 流的名称(以 null 结尾的字符串,长度对齐到 4 字节边界)
496    ///
497    /// 常见的流名称:
498    /// - "#Strings": 字符串堆,包含各种名称
499    /// - "#US": 用户字符串,包含字符串字面量
500    /// - "#GUID": GUID 堆,包含 GUID 值
501    /// - "#Blob": Blob 堆,包含二进制数据
502    /// - "#~": 压缩的元数据表流
503    /// - "#-": 未压缩的元数据表流
504    ///
505    /// # 参数
506    /// * `metadata_offset` - 元数据头在文件中的起始偏移
507    ///
508    /// # 返回
509    /// * `Ok(Vec<StreamHeader>)` - 成功读取的流头列表
510    /// * `Err(GaiaError)` - 读取过程中的错误
511    fn read_stream_headers(&mut self, metadata_offset: u32) -> Result<Vec<StreamHeader>, GaiaError> {
512        let mut stream_headers = Vec::new();
513
514        if let Some(ref metadata_header) = self.metadata_header {
515            // 计算流头的起始位置:跳过元数据头的固定部分(20 字节)和版本字符串
516            let stream_start_offset = metadata_offset + 20 + metadata_header.version_length;
517            self.exe_reader
518                .seek(SeekFrom::Start(stream_start_offset as u64))
519                .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
520
521            // 读取每个流的头信息
522            for _ in 0..metadata_header.streams {
523                let offset = self
524                    .exe_reader
525                    .read_u32::<LittleEndian>()
526                    .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
527                let size = self
528                    .exe_reader
529                    .read_u32::<LittleEndian>()
530                    .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
531
532                // 读取流名称(以 null 结尾,对齐到 4 字节边界)
533                let mut name_bytes = Vec::new();
534                loop {
535                    let byte = self
536                        .exe_reader
537                        .read_u8()
538                        .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
539                    if byte == 0 {
540                        break;
541                    }
542                    name_bytes.push(byte);
543                }
544
545                // 对齐到 4 字节边界
546                let current_pos = stream_start_offset + 8 + name_bytes.len() as u32 + 1; // +1 for null terminator
547                let padding = (4 - (current_pos % 4)) % 4;
548                for _ in 0..padding {
549                    self.exe_reader
550                        .read_u8()
551                        .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
552                }
553
554                let name = String::from_utf8_lossy(&name_bytes).to_string();
555                stream_headers.push(StreamHeader { offset, size, name });
556            }
557        }
558
559        Ok(stream_headers)
560    }
561
562    /// 将 RVA(相对虚拟地址)转换为文件偏移
563    ///
564    /// RVA(Relative Virtual Address)是 PE 文件中的重要概念:
565    /// - RVA 是相对于映像基址的偏移量
566    /// - 文件偏移是相对于文件开头的物理位置
567    ///
568    /// 转换过程:
569    /// 1. 在 PE 节表中查找包含目标 RVA 的节
570    /// 2. 计算 RVA 在节内的相对偏移
571    /// 3. 将相对偏移加到节的文件偏移上,得到最终的文件偏移
572    ///
573    /// # 参数
574    /// * `rva` - 要转换的相对虚拟地址
575    ///
576    /// # 返回
577    /// * `Ok(u32)` - 成功转换的文件偏移
578    /// * `Err(GaiaError)` - 找不到包含该 RVA 的节时的错误
579    ///
580    /// # 示例
581    /// ```
582    /// let file_offset = reader.rva_to_file_offset(0x2000)?;
583    /// ```
584    fn rva_to_file_offset(&mut self, rva: u32) -> Result<u32, GaiaError> {
585        // 需要读取完整的 PE 程序来访问节信息
586        let pe_program = self.get_program()?.clone();
587
588        // 在节表中查找包含此 RVA 的节
589        for section in &pe_program.sections {
590            let section_start = section.virtual_address;
591            let section_end = section_start + section.virtual_size;
592
593            // 检查 RVA 是否在该节的地址范围内
594            if rva >= section_start && rva < section_end {
595                // 计算 RVA 在节内的相对偏移
596                let offset_in_section = rva - section_start;
597                // 返回文件偏移 = 节的文件偏移 + 相对偏移
598                return Ok(section.pointer_to_raw_data + offset_in_section);
599            }
600        }
601
602        // 找不到包含该 RVA 的节
603        Err(GaiaError::syntax_error(format!("无法将 RVA 0x{:x} 转换为文件偏移", rva), SourceLocation::default()))
604    }
605}