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