1use crate::{TagHeader, TagType};
4use core::fmt::{Debug, Formatter};
5use core::marker::PhantomData;
6use core::mem;
7use core::str::Utf8Error;
8use multiboot2_common::{MaybeDynSized, Tag};
9#[cfg(feature = "builder")]
10use {alloc::boxed::Box, multiboot2_common::new_boxed};
11
12#[derive(ptr_meta::Pointee, PartialEq, Eq)]
16#[repr(C, align(8))]
17pub struct ElfSectionsTag {
18 header: TagHeader,
19 number_of_sections: u32,
20 entry_size: u32,
21 shndx: u32,
22 sections: [u8],
23}
24
25impl ElfSectionsTag {
26 #[cfg(feature = "builder")]
28 #[must_use]
29 pub fn new(number_of_sections: u32, entry_size: u32, shndx: u32, sections: &[u8]) -> Box<Self> {
30 let header = TagHeader::new(Self::ID, 0);
31 let number_of_sections = number_of_sections.to_ne_bytes();
32 let entry_size = entry_size.to_ne_bytes();
33 let shndx = shndx.to_ne_bytes();
34 new_boxed(
35 header,
36 &[&number_of_sections, &entry_size, &shndx, sections],
37 )
38 }
39
40 #[must_use]
42 pub const fn sections(&self) -> ElfSectionIter {
43 let string_section_offset = (self.shndx * self.entry_size) as isize;
44 let string_section_ptr =
45 unsafe { self.sections.as_ptr().offset(string_section_offset) as *const _ };
46 ElfSectionIter {
47 current_section: self.sections.as_ptr(),
48 remaining_sections: self.number_of_sections,
49 entry_size: self.entry_size,
50 string_section: string_section_ptr,
51 _phantom_data: PhantomData,
52 }
53 }
54
55 #[must_use]
57 pub const fn number_of_sections(&self) -> u32 {
58 self.number_of_sections
59 }
60
61 #[must_use]
63 pub const fn entry_size(&self) -> u32 {
64 self.entry_size
65 }
66
67 #[must_use]
69 pub const fn shndx(&self) -> u32 {
70 self.shndx
71 }
72}
73
74impl MaybeDynSized for ElfSectionsTag {
75 type Header = TagHeader;
76
77 const BASE_SIZE: usize = mem::size_of::<TagHeader>() + 3 * mem::size_of::<u32>();
78
79 fn dst_len(header: &TagHeader) -> usize {
80 assert!(header.size as usize >= Self::BASE_SIZE);
81 header.size as usize - Self::BASE_SIZE
82 }
83}
84
85impl Tag for ElfSectionsTag {
86 type IDType = TagType;
87
88 const ID: TagType = TagType::ElfSections;
89}
90
91impl Debug for ElfSectionsTag {
92 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
93 f.debug_struct("ElfSectionsTag")
94 .field("typ", &self.header.typ)
95 .field("size", &self.header.size)
96 .field("number_of_sections", &self.number_of_sections)
97 .field("entry_size", &self.entry_size)
98 .field("shndx", &self.shndx)
99 .field("sections", &self.sections())
100 .finish()
101 }
102}
103
104#[derive(Clone)]
106pub struct ElfSectionIter<'a> {
107 current_section: *const u8,
108 remaining_sections: u32,
109 entry_size: u32,
110 string_section: *const u8,
111 _phantom_data: PhantomData<&'a ()>,
112}
113
114impl<'a> Iterator for ElfSectionIter<'a> {
115 type Item = ElfSection<'a>;
116
117 fn next(&mut self) -> Option<ElfSection<'a>> {
118 while self.remaining_sections != 0 {
119 let section = ElfSection {
120 inner: self.current_section,
121 string_section: self.string_section,
122 entry_size: self.entry_size,
123 _phantom: PhantomData,
124 };
125
126 self.current_section = unsafe { self.current_section.offset(self.entry_size as isize) };
127 self.remaining_sections -= 1;
128
129 if section.section_type() != ElfSectionType::Unused {
130 return Some(section);
131 }
132 }
133 None
134 }
135
136 fn size_hint(&self) -> (usize, Option<usize>) {
137 (
138 self.remaining_sections as usize,
139 Some(self.remaining_sections as usize),
140 )
141 }
142}
143
144impl ExactSizeIterator for ElfSectionIter<'_> {
145 fn len(&self) -> usize {
146 self.remaining_sections as usize
147 }
148}
149
150impl Debug for ElfSectionIter<'_> {
151 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
152 const ELF_SECTIONS_LIMIT: usize = 7;
156
157 let mut debug = f.debug_list();
158
159 self.clone().take(ELF_SECTIONS_LIMIT).for_each(|ref e| {
160 debug.entry(e);
161 });
162
163 if self.clone().len() > ELF_SECTIONS_LIMIT {
164 debug.entry(&"...");
165 }
166
167 debug.finish()
168 }
169}
170
171#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
174pub struct ElfSection<'a> {
175 inner: *const u8,
176 string_section: *const u8,
177 entry_size: u32,
178 _phantom: PhantomData<&'a ()>,
179}
180
181impl Debug for ElfSection<'_> {
182 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
183 let inner = self.get();
184 f.debug_struct("ElfSection")
185 .field("inner", &inner)
186 .field("string_section_ptr", &self.string_section)
187 .finish()
188 }
189}
190
191#[derive(Clone, Copy, Debug)]
192#[repr(C, packed)]
193struct ElfSectionInner32 {
194 name_index: u32,
195 typ: u32,
196 flags: u32,
197 addr: u32,
198 offset: u32,
199 size: u32,
200 link: u32,
201 info: u32,
202 addralign: u32,
203 entry_size: u32,
204}
205
206#[derive(Clone, Copy, Debug)]
207#[repr(C, packed)]
208struct ElfSectionInner64 {
209 name_index: u32,
210 typ: u32,
211 flags: u64,
212 addr: u64,
213 offset: u64,
214 size: u64,
215 link: u32,
216 info: u32,
217 addralign: u64,
218 entry_size: u64,
219}
220
221impl ElfSection<'_> {
222 #[must_use]
224 pub fn section_type(&self) -> ElfSectionType {
225 match self.get().typ() {
226 0 => ElfSectionType::Unused,
227 1 => ElfSectionType::ProgramSection,
228 2 => ElfSectionType::LinkerSymbolTable,
229 3 => ElfSectionType::StringTable,
230 4 => ElfSectionType::RelaRelocation,
231 5 => ElfSectionType::SymbolHashTable,
232 6 => ElfSectionType::DynamicLinkingTable,
233 7 => ElfSectionType::Note,
234 8 => ElfSectionType::Uninitialized,
235 9 => ElfSectionType::RelRelocation,
236 10 => ElfSectionType::Reserved,
237 11 => ElfSectionType::DynamicLoaderSymbolTable,
238 0x6000_0000..=0x6FFF_FFFF => ElfSectionType::EnvironmentSpecific,
239 0x7000_0000..=0x7FFF_FFFF => ElfSectionType::ProcessorSpecific,
240 e => {
241 log::warn!("Unknown section type {e:x}. Treating as ElfSectionType::Unused");
242 ElfSectionType::Unused
243 }
244 }
245 }
246
247 #[must_use]
249 pub fn section_type_raw(&self) -> u32 {
250 self.get().typ()
251 }
252
253 pub fn name(&self) -> Result<&str, Utf8Error> {
255 use core::{slice, str};
256
257 let name_ptr = unsafe { self.string_table().offset(self.get().name_index() as isize) };
258
259 let strlen = {
261 let mut len = 0;
262 while unsafe { *name_ptr.offset(len) } != 0 {
263 len += 1;
264 }
265 len as usize
266 };
267
268 str::from_utf8(unsafe { slice::from_raw_parts(name_ptr, strlen) })
269 }
270
271 #[must_use]
273 pub fn start_address(&self) -> u64 {
274 self.get().addr()
275 }
276
277 #[must_use]
281 pub fn end_address(&self) -> u64 {
282 self.get().addr() + self.get().size()
283 }
284
285 #[must_use]
287 pub fn size(&self) -> u64 {
288 self.get().size()
289 }
290
291 #[must_use]
298 pub fn addralign(&self) -> u64 {
299 self.get().addralign()
300 }
301
302 #[must_use]
304 pub fn flags(&self) -> ElfSectionFlags {
305 ElfSectionFlags::from_bits_truncate(self.get().flags())
306 }
307
308 #[must_use]
310 pub fn is_allocated(&self) -> bool {
311 self.flags().contains(ElfSectionFlags::ALLOCATED)
312 }
313
314 fn get(&self) -> &dyn ElfSectionInner {
315 match self.entry_size {
316 40 => unsafe { &*(self.inner as *const ElfSectionInner32) },
317 64 => unsafe { &*(self.inner as *const ElfSectionInner64) },
318 s => panic!("Unexpected entry size: {s}"),
319 }
320 }
321
322 unsafe fn string_table(&self) -> *const u8 {
323 match self.entry_size {
324 40 => {
325 let ptr = self.string_section.cast::<ElfSectionInner32>();
326 let reference = unsafe { ptr.as_ref().unwrap() };
327 reference.addr() as *const u8
328 }
329 64 => {
330 let ptr = self.string_section.cast::<ElfSectionInner64>();
331 let reference = unsafe { ptr.as_ref().unwrap() };
332 reference.addr() as *const u8
333 }
334 s => panic!("Unexpected entry size: {s}"),
335 }
336 }
337}
338
339trait ElfSectionInner: Debug {
340 fn name_index(&self) -> u32;
341
342 fn typ(&self) -> u32;
343
344 fn flags(&self) -> u64;
345
346 fn addr(&self) -> u64;
347
348 fn size(&self) -> u64;
349
350 fn addralign(&self) -> u64;
351}
352
353impl ElfSectionInner for ElfSectionInner32 {
354 fn name_index(&self) -> u32 {
355 self.name_index
356 }
357
358 fn typ(&self) -> u32 {
359 self.typ
360 }
361
362 fn flags(&self) -> u64 {
363 self.flags.into()
364 }
365
366 fn addr(&self) -> u64 {
367 self.addr.into()
368 }
369
370 fn size(&self) -> u64 {
371 self.size.into()
372 }
373
374 fn addralign(&self) -> u64 {
375 self.addralign.into()
376 }
377}
378
379impl ElfSectionInner for ElfSectionInner64 {
380 fn name_index(&self) -> u32 {
381 self.name_index
382 }
383
384 fn typ(&self) -> u32 {
385 self.typ
386 }
387
388 fn flags(&self) -> u64 {
389 self.flags
390 }
391
392 fn addr(&self) -> u64 {
393 self.addr
394 }
395
396 fn size(&self) -> u64 {
397 self.size
398 }
399
400 fn addralign(&self) -> u64 {
401 self.addralign
402 }
403}
404
405#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
407#[repr(u32)]
408pub enum ElfSectionType {
409 Unused = 0,
413
414 ProgramSection = 1,
417
418 LinkerSymbolTable = 2,
420
421 StringTable = 3,
423
424 RelaRelocation = 4,
428
429 SymbolHashTable = 5,
431
432 DynamicLinkingTable = 6,
434
435 Note = 7,
437
438 Uninitialized = 8,
442
443 RelRelocation = 9,
447
448 Reserved = 10,
450
451 DynamicLoaderSymbolTable = 11,
453
454 EnvironmentSpecific = 0x6000_0000,
457
458 ProcessorSpecific = 0x7000_0000,
461}
462
463bitflags! {
464 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
466 #[repr(transparent)]
467 pub struct ElfSectionFlags: u64 {
468 const WRITABLE = 0x1;
470
471 const ALLOCATED = 0x2;
473
474 const EXECUTABLE = 0x4;
476 }
479}