]> git.proxmox.com Git - rustc.git/blob - vendor/goblin/src/pe/section_table.rs
New upstream version 1.48.0+dfsg1
[rustc.git] / vendor / goblin / src / pe / section_table.rs
1 use crate::error::{self, Error};
2 use crate::pe::relocation;
3 use alloc::string::{String, ToString};
4 use scroll::{ctx, Pread, Pwrite};
5
6 #[repr(C)]
7 #[derive(Debug, PartialEq, Clone, Default)]
8 pub struct SectionTable {
9 pub name: [u8; 8],
10 pub real_name: Option<String>,
11 pub virtual_size: u32,
12 pub virtual_address: u32,
13 pub size_of_raw_data: u32,
14 pub pointer_to_raw_data: u32,
15 pub pointer_to_relocations: u32,
16 pub pointer_to_linenumbers: u32,
17 pub number_of_relocations: u16,
18 pub number_of_linenumbers: u16,
19 pub characteristics: u32,
20 }
21
22 pub const SIZEOF_SECTION_TABLE: usize = 8 * 5;
23
24 // Based on https://github.com/llvm-mirror/llvm/blob/af7b1832a03ab6486c42a40d21695b2c03b2d8a3/lib/Object/COFFObjectFile.cpp#L70
25 // Decodes a string table entry in base 64 (//AAAAAA). Expects string without
26 // prefixed slashes.
27 fn base64_decode_string_entry(s: &str) -> Result<usize, ()> {
28 assert!(s.len() <= 6, "String too long, possible overflow.");
29
30 let mut val = 0;
31 for c in s.bytes() {
32 let v = if b'A' <= c && c <= b'Z' {
33 // 00..=25
34 c - b'A'
35 } else if b'a' <= c && c <= b'z' {
36 // 26..=51
37 c - b'a' + 26
38 } else if b'0' <= c && c <= b'9' {
39 // 52..=61
40 c - b'0' + 52
41 } else if c == b'+' {
42 // 62
43 62
44 } else if c == b'/' {
45 // 63
46 63
47 } else {
48 return Err(());
49 };
50 val = val * 64 + v as usize;
51 }
52 Ok(val)
53 }
54
55 impl SectionTable {
56 pub fn parse(
57 bytes: &[u8],
58 offset: &mut usize,
59 string_table_offset: usize,
60 ) -> error::Result<Self> {
61 let mut table = SectionTable::default();
62 let mut name = [0u8; 8];
63 name.copy_from_slice(bytes.gread_with(offset, 8)?);
64
65 table.name = name;
66 table.virtual_size = bytes.gread_with(offset, scroll::LE)?;
67 table.virtual_address = bytes.gread_with(offset, scroll::LE)?;
68 table.size_of_raw_data = bytes.gread_with(offset, scroll::LE)?;
69 table.pointer_to_raw_data = bytes.gread_with(offset, scroll::LE)?;
70 table.pointer_to_relocations = bytes.gread_with(offset, scroll::LE)?;
71 table.pointer_to_linenumbers = bytes.gread_with(offset, scroll::LE)?;
72 table.number_of_relocations = bytes.gread_with(offset, scroll::LE)?;
73 table.number_of_linenumbers = bytes.gread_with(offset, scroll::LE)?;
74 table.characteristics = bytes.gread_with(offset, scroll::LE)?;
75
76 if let Some(idx) = table.name_offset()? {
77 table.real_name = Some(bytes.pread::<&str>(string_table_offset + idx)?.to_string());
78 }
79 Ok(table)
80 }
81
82 pub fn name_offset(&self) -> error::Result<Option<usize>> {
83 // Based on https://github.com/llvm-mirror/llvm/blob/af7b1832a03ab6486c42a40d21695b2c03b2d8a3/lib/Object/COFFObjectFile.cpp#L1054
84 if self.name[0] == b'/' {
85 let idx: usize = if self.name[1] == b'/' {
86 let b64idx = self.name.pread::<&str>(2)?;
87 base64_decode_string_entry(b64idx).map_err(|_| {
88 Error::Malformed(format!(
89 "Invalid indirect section name //{}: base64 decoding failed",
90 b64idx
91 ))
92 })?
93 } else {
94 let name = self.name.pread::<&str>(1)?;
95 name.parse().map_err(|err| {
96 Error::Malformed(format!("Invalid indirect section name /{}: {}", name, err))
97 })?
98 };
99 Ok(Some(idx))
100 } else {
101 Ok(None)
102 }
103 }
104
105 #[allow(clippy::useless_let_if_seq)]
106 pub fn set_name_offset(&mut self, mut idx: usize) -> error::Result<()> {
107 if idx <= 9_999_999 {
108 // 10^7 - 1
109 // write!(&mut self.name[1..], "{}", idx) without using io::Write.
110 // We write into a temporary since we calculate digits starting at the right.
111 let mut name = [0; 7];
112 let mut len = 0;
113 if idx == 0 {
114 name[6] = b'0';
115 len = 1;
116 } else {
117 while idx != 0 {
118 let rem = (idx % 10) as u8;
119 idx /= 10;
120 name[6 - len] = b'0' + rem;
121 len += 1;
122 }
123 }
124 self.name = [0; 8];
125 self.name[0] = b'/';
126 self.name[1..][..len].copy_from_slice(&name[7 - len..]);
127 Ok(())
128 } else if idx as u64 <= 0xfff_fff_fff {
129 // 64^6 - 1
130 self.name[0] = b'/';
131 self.name[1] = b'/';
132 for i in 0..6 {
133 let rem = (idx % 64) as u8;
134 idx /= 64;
135 let c = match rem {
136 0..=25 => b'A' + rem,
137 26..=51 => b'a' + rem - 26,
138 52..=61 => b'0' + rem - 52,
139 62 => b'+',
140 63 => b'/',
141 _ => unreachable!(),
142 };
143 self.name[7 - i] = c;
144 }
145 Ok(())
146 } else {
147 Err(Error::Malformed(format!(
148 "Invalid section name offset: {}",
149 idx
150 )))
151 }
152 }
153
154 pub fn name(&self) -> error::Result<&str> {
155 match self.real_name.as_ref() {
156 Some(s) => Ok(s),
157 None => Ok(self.name.pread(0)?),
158 }
159 }
160
161 pub fn relocations<'a>(&self, bytes: &'a [u8]) -> error::Result<relocation::Relocations<'a>> {
162 let offset = self.pointer_to_relocations as usize;
163 let number = self.number_of_relocations as usize;
164 relocation::Relocations::parse(bytes, offset, number)
165 }
166 }
167
168 impl ctx::SizeWith<scroll::Endian> for SectionTable {
169 fn size_with(_ctx: &scroll::Endian) -> usize {
170 SIZEOF_SECTION_TABLE
171 }
172 }
173
174 impl ctx::TryIntoCtx<scroll::Endian> for SectionTable {
175 type Error = error::Error;
176 fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
177 let offset = &mut 0;
178 bytes.gwrite(&self.name[..], offset)?;
179 bytes.gwrite_with(self.virtual_size, offset, ctx)?;
180 bytes.gwrite_with(self.virtual_address, offset, ctx)?;
181 bytes.gwrite_with(self.size_of_raw_data, offset, ctx)?;
182 bytes.gwrite_with(self.pointer_to_raw_data, offset, ctx)?;
183 bytes.gwrite_with(self.pointer_to_relocations, offset, ctx)?;
184 bytes.gwrite_with(self.pointer_to_linenumbers, offset, ctx)?;
185 bytes.gwrite_with(self.number_of_relocations, offset, ctx)?;
186 bytes.gwrite_with(self.number_of_linenumbers, offset, ctx)?;
187 bytes.gwrite_with(self.characteristics, offset, ctx)?;
188 Ok(SIZEOF_SECTION_TABLE)
189 }
190 }
191
192 impl ctx::IntoCtx<scroll::Endian> for SectionTable {
193 fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) {
194 bytes.pwrite_with(self, 0, ctx).unwrap();
195 }
196 }
197
198 /// The section should not be padded to the next boundary. This flag is obsolete and is replaced
199 /// by `IMAGE_SCN_ALIGN_1BYTES`. This is valid only for object files.
200 pub const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x0000_0008;
201 /// The section contains executable code.
202 pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020;
203 /// The section contains initialized data.
204 pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040;
205 /// The section contains uninitialized data.
206 pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080;
207 pub const IMAGE_SCN_LNK_OTHER: u32 = 0x0000_0100;
208 /// The section contains comments or other information. The .drectve section has this type.
209 /// This is valid for object files only.
210 pub const IMAGE_SCN_LNK_INFO: u32 = 0x0000_0200;
211 /// The section will not become part of the image. This is valid only for object files.
212 pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x0000_0800;
213 /// The section contains COMDAT data. This is valid only for object files.
214 pub const IMAGE_SCN_LNK_COMDAT: u32 = 0x0000_1000;
215 /// The section contains data referenced through the global pointer (GP).
216 pub const IMAGE_SCN_GPREL: u32 = 0x0000_8000;
217 pub const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x0002_0000;
218 pub const IMAGE_SCN_MEM_16BIT: u32 = 0x0002_0000;
219 pub const IMAGE_SCN_MEM_LOCKED: u32 = 0x0004_0000;
220 pub const IMAGE_SCN_MEM_PRELOAD: u32 = 0x0008_0000;
221
222 pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x0010_0000;
223 pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x0020_0000;
224 pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x0030_0000;
225 pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x0040_0000;
226 pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x0050_0000;
227 pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x0060_0000;
228 pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x0070_0000;
229 pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x0080_0000;
230 pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x0090_0000;
231 pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A0_0000;
232 pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B0_0000;
233 pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C0_0000;
234 pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D0_0000;
235 pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E0_0000;
236 pub const IMAGE_SCN_ALIGN_MASK: u32 = 0x00F0_0000;
237
238 /// The section contains extended relocations.
239 pub const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x0100_0000;
240 /// The section can be discarded as needed.
241 pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x0200_0000;
242 /// The section cannot be cached.
243 pub const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x0400_0000;
244 /// The section is not pageable.
245 pub const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x0800_0000;
246 /// The section can be shared in memory.
247 pub const IMAGE_SCN_MEM_SHARED: u32 = 0x1000_0000;
248 /// The section can be executed as code.
249 pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000;
250 /// The section can be read.
251 pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000;
252 /// The section can be written to.
253 pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000;
254
255 #[cfg(test)]
256 mod tests {
257 use super::*;
258
259 #[test]
260 fn set_name_offset() {
261 let mut section = SectionTable::default();
262 for &(offset, name) in [
263 (0usize, b"/0\0\0\0\0\0\0"),
264 (1, b"/1\0\0\0\0\0\0"),
265 (9_999_999, b"/9999999"),
266 (10_000_000, b"//AAmJaA"),
267 #[cfg(target_pointer_width = "64")]
268 (0xfff_fff_fff, b"////////"),
269 ]
270 .iter()
271 {
272 section.set_name_offset(offset).unwrap();
273 assert_eq!(&section.name, name);
274 assert_eq!(section.name_offset().unwrap(), Some(offset));
275 }
276 #[cfg(target_pointer_width = "64")]
277 assert!(section.set_name_offset(0x1_000_000_000).is_err());
278 }
279 }