1use core::slice;
2
3use crate::endian::LittleEndian as LE;
4use crate::pe;
5use crate::read::{Error, ReadError, ReadRef, Result};
6
7use super::{
8 DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory,
9 SectionTable,
10};
11
12/// The table of data directories in a PE file.
13///
14/// Returned by [`ImageNtHeaders::parse`](super::ImageNtHeaders::parse).
15#[derive(Debug, Clone, Copy)]
16pub struct DataDirectories<'data> {
17 entries: &'data [pe::ImageDataDirectory],
18}
19
20impl<'data> DataDirectories<'data> {
21 /// Parse the data directory table.
22 ///
23 /// `data` must be the remaining optional data following the
24 /// [optional header](pe::ImageOptionalHeader64). `number` must be from the
25 /// [`number_of_rva_and_sizes`](pe::ImageOptionalHeader64::number_of_rva_and_sizes)
26 /// field of the optional header.
27 pub fn parse(data: &'data [u8], number: u32) -> Result<Self> {
28 let entries = data
29 .read_slice_at(0, number as usize)
30 .read_error("Invalid PE number of RVA and sizes")?;
31 Ok(DataDirectories { entries })
32 }
33
34 /// The number of data directories.
35 #[allow(clippy::len_without_is_empty)]
36 pub fn len(&self) -> usize {
37 self.entries.len()
38 }
39
40 /// Iterator over the data directories.
41 pub fn iter(&self) -> slice::Iter<'data, pe::ImageDataDirectory> {
42 self.entries.iter()
43 }
44
45 /// Iterator which gives the directories as well as their index (one of the IMAGE_DIRECTORY_ENTRY_* constants).
46 pub fn enumerate(&self) -> core::iter::Enumerate<slice::Iter<'data, pe::ImageDataDirectory>> {
47 self.entries.iter().enumerate()
48 }
49
50 /// Returns the data directory at the given index.
51 ///
52 /// Index should be one of the `IMAGE_DIRECTORY_ENTRY_*` constants.
53 ///
54 /// Returns `None` if the index is larger than the table size,
55 /// or if the entry at the index has a zero virtual address.
56 pub fn get(&self, index: usize) -> Option<&'data pe::ImageDataDirectory> {
57 self.entries
58 .get(index)
59 .filter(|d| d.virtual_address.get(LE) != 0)
60 }
61
62 /// Returns the unparsed export directory.
63 ///
64 /// `data` must be the entire file data.
65 pub fn export_directory<R: ReadRef<'data>>(
66 &self,
67 data: R,
68 sections: &SectionTable<'data>,
69 ) -> Result<Option<&'data pe::ImageExportDirectory>> {
70 let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) {
71 Some(data_dir) => data_dir,
72 None => return Ok(None),
73 };
74 let export_data = data_dir.data(data, sections)?;
75 ExportTable::parse_directory(export_data).map(Some)
76 }
77
78 /// Returns the partially parsed export directory.
79 ///
80 /// `data` must be the entire file data.
81 pub fn export_table<R: ReadRef<'data>>(
82 &self,
83 data: R,
84 sections: &SectionTable<'data>,
85 ) -> Result<Option<ExportTable<'data>>> {
86 let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) {
87 Some(data_dir) => data_dir,
88 None => return Ok(None),
89 };
90 let export_va = data_dir.virtual_address.get(LE);
91 let export_data = data_dir.data(data, sections)?;
92 ExportTable::parse(export_data, export_va).map(Some)
93 }
94
95 /// Returns the partially parsed import directory.
96 ///
97 /// `data` must be the entire file data.
98 pub fn import_table<R: ReadRef<'data>>(
99 &self,
100 data: R,
101 sections: &SectionTable<'data>,
102 ) -> Result<Option<ImportTable<'data>>> {
103 let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_IMPORT) {
104 Some(data_dir) => data_dir,
105 None => return Ok(None),
106 };
107 let import_va = data_dir.virtual_address.get(LE);
108 let (section_data, section_va) = sections
109 .pe_data_containing(data, import_va)
110 .read_error("Invalid import data dir virtual address")?;
111 Ok(Some(ImportTable::new(section_data, section_va, import_va)))
112 }
113
114 /// Returns the partially parsed delay-load import directory.
115 ///
116 /// `data` must be the entire file data.
117 pub fn delay_load_import_table<R: ReadRef<'data>>(
118 &self,
119 data: R,
120 sections: &SectionTable<'data>,
121 ) -> Result<Option<DelayLoadImportTable<'data>>> {
122 let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) {
123 Some(data_dir) => data_dir,
124 None => return Ok(None),
125 };
126 let import_va = data_dir.virtual_address.get(LE);
127 let (section_data, section_va) = sections
128 .pe_data_containing(data, import_va)
129 .read_error("Invalid import data dir virtual address")?;
130 Ok(Some(DelayLoadImportTable::new(
131 section_data,
132 section_va,
133 import_va,
134 )))
135 }
136
137 /// Returns the blocks in the base relocation directory.
138 ///
139 /// `data` must be the entire file data.
140 pub fn relocation_blocks<R: ReadRef<'data>>(
141 &self,
142 data: R,
143 sections: &SectionTable<'data>,
144 ) -> Result<Option<RelocationBlockIterator<'data>>> {
145 let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_BASERELOC) {
146 Some(data_dir) => data_dir,
147 None => return Ok(None),
148 };
149 let reloc_data = data_dir.data(data, sections)?;
150 Ok(Some(RelocationBlockIterator::new(reloc_data)))
151 }
152
153 /// Returns the resource directory.
154 ///
155 /// `data` must be the entire file data.
156 pub fn resource_directory<R: ReadRef<'data>>(
157 &self,
158 data: R,
159 sections: &SectionTable<'data>,
160 ) -> Result<Option<ResourceDirectory<'data>>> {
161 let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_RESOURCE) {
162 Some(data_dir) => data_dir,
163 None => return Ok(None),
164 };
165 let rsrc_data = data_dir.data(data, sections)?;
166 Ok(Some(ResourceDirectory::new(rsrc_data)))
167 }
168}
169
170impl pe::ImageDataDirectory {
171 /// Return the virtual address range of this directory entry.
172 pub fn address_range(&self) -> (u32, u32) {
173 (self.virtual_address.get(LE), self.size.get(LE))
174 }
175
176 /// Return the file offset and size of this directory entry.
177 ///
178 /// This function has some limitations:
179 /// - It requires that the data is contained in a single section.
180 /// - It uses the size field of the directory entry, which is
181 /// not desirable for all data directories.
182 /// - It uses the `virtual_address` of the directory entry as an address,
183 /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`.
184 pub fn file_range(&self, sections: &SectionTable<'_>) -> Result<(u32, u32)> {
185 let (offset, section_size) = sections
186 .pe_file_range_at(self.virtual_address.get(LE))
187 .read_error("Invalid data dir virtual address")?;
188 let size = self.size.get(LE);
189 if size > section_size {
190 return Err(Error("Invalid data dir size"));
191 }
192 Ok((offset, size))
193 }
194
195 /// Get the data referenced by this directory entry.
196 ///
197 /// This function has some limitations:
198 /// - It requires that the data is contained in a single section.
199 /// - It uses the size field of the directory entry, which is
200 /// not desirable for all data directories.
201 /// - It uses the `virtual_address` of the directory entry as an address,
202 /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`.
203 pub fn data<'data, R: ReadRef<'data>>(
204 &self,
205 data: R,
206 sections: &SectionTable<'data>,
207 ) -> Result<&'data [u8]> {
208 sections
209 .pe_data_at(data, self.virtual_address.get(LE))
210 .read_error("Invalid data dir virtual address")?
211 .get(..self.size.get(LE) as usize)
212 .read_error("Invalid data dir size")
213 }
214}
215