Skip to main content

object/read/elf/
segment.rs

1use core::fmt::Debug;
2use core::{slice, str};
3
4use crate::elf;
5use crate::endian::{self, Endianness};
6use crate::pod::{self, Pod};
7use crate::read::{self, ObjectSegment, Permissions, ReadError, ReadRef, SegmentFlags};
8
9use super::{ElfFile, FileHeader, NoteIterator};
10
11/// An iterator for the segments in an [`ElfFile32`](super::ElfFile32).
12pub type ElfSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
13    ElfSegmentIterator<'data, 'file, elf::FileHeader32<Endian>, R>;
14/// An iterator for the segments in an [`ElfFile64`](super::ElfFile64).
15pub type ElfSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
16    ElfSegmentIterator<'data, 'file, elf::FileHeader64<Endian>, R>;
17
18/// An iterator for the segments in an [`ElfFile`].
19#[derive(Debug)]
20pub struct ElfSegmentIterator<'data, 'file, Elf, R = &'data [u8]>
21where
22    Elf: FileHeader,
23    R: ReadRef<'data>,
24{
25    pub(super) file: &'file ElfFile<'data, Elf, R>,
26    pub(super) iter: slice::Iter<'data, Elf::ProgramHeader>,
27}
28
29impl<'data, 'file, Elf, R> Iterator for ElfSegmentIterator<'data, 'file, Elf, R>
30where
31    Elf: FileHeader,
32    R: ReadRef<'data>,
33{
34    type Item = ElfSegment<'data, 'file, Elf, R>;
35
36    fn next(&mut self) -> Option<Self::Item> {
37        for segment in self.iter.by_ref() {
38            if segment.p_type(self.file.endian) == elf::PT_LOAD {
39                return Some(ElfSegment {
40                    file: self.file,
41                    segment,
42                });
43            }
44        }
45        None
46    }
47}
48
49/// A segment in an [`ElfFile32`](super::ElfFile32).
50pub type ElfSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
51    ElfSegment<'data, 'file, elf::FileHeader32<Endian>, R>;
52/// A segment in an [`ElfFile64`](super::ElfFile64).
53pub type ElfSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
54    ElfSegment<'data, 'file, elf::FileHeader64<Endian>, R>;
55
56/// A segment in an [`ElfFile`].
57///
58/// Most functionality is provided by the [`ObjectSegment`] trait implementation.
59#[derive(Debug)]
60pub struct ElfSegment<'data, 'file, Elf, R = &'data [u8]>
61where
62    Elf: FileHeader,
63    R: ReadRef<'data>,
64{
65    pub(super) file: &'file ElfFile<'data, Elf, R>,
66    pub(super) segment: &'data Elf::ProgramHeader,
67}
68
69impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSegment<'data, 'file, Elf, R> {
70    /// Get the ELF file containing this segment.
71    pub fn elf_file(&self) -> &'file ElfFile<'data, Elf, R> {
72        self.file
73    }
74
75    /// Get the raw ELF program header for the segment.
76    pub fn elf_program_header(&self) -> &'data Elf::ProgramHeader {
77        self.segment
78    }
79
80    fn bytes(&self) -> read::Result<&'data [u8]> {
81        self.segment
82            .data(self.file.endian, self.file.data.0)
83            .read_error("Invalid ELF segment size or offset")
84    }
85}
86
87impl<'data, 'file, Elf, R> read::private::Sealed for ElfSegment<'data, 'file, Elf, R>
88where
89    Elf: FileHeader,
90    R: ReadRef<'data>,
91{
92}
93
94impl<'data, 'file, Elf, R> ObjectSegment<'data> for ElfSegment<'data, 'file, Elf, R>
95where
96    Elf: FileHeader,
97    R: ReadRef<'data>,
98{
99    #[inline]
100    fn address(&self) -> u64 {
101        self.segment.p_vaddr(self.file.endian).into()
102    }
103
104    #[inline]
105    fn size(&self) -> u64 {
106        self.segment.p_memsz(self.file.endian).into()
107    }
108
109    #[inline]
110    fn align(&self) -> u64 {
111        self.segment.p_align(self.file.endian).into()
112    }
113
114    #[inline]
115    fn file_range(&self) -> (u64, u64) {
116        self.segment.file_range(self.file.endian)
117    }
118
119    #[inline]
120    fn data(&self) -> read::Result<&'data [u8]> {
121        self.bytes()
122    }
123
124    fn data_range(&self, address: u64, size: u64) -> read::Result<Option<&'data [u8]>> {
125        Ok(read::util::data_range(
126            self.bytes()?,
127            self.address(),
128            address,
129            size,
130        ))
131    }
132
133    #[inline]
134    fn name_bytes(&self) -> read::Result<Option<&[u8]>> {
135        Ok(None)
136    }
137
138    #[inline]
139    fn name(&self) -> read::Result<Option<&str>> {
140        Ok(None)
141    }
142
143    #[inline]
144    fn flags(&self) -> SegmentFlags {
145        let p_flags = self.segment.p_flags(self.file.endian);
146        SegmentFlags::Elf { p_flags }
147    }
148
149    #[inline]
150    fn permissions(&self) -> Permissions {
151        let p_flags = self.segment.p_flags(self.file.endian);
152        Permissions::new(
153            p_flags & elf::PF_R != 0,
154            p_flags & elf::PF_W != 0,
155            p_flags & elf::PF_X != 0,
156        )
157    }
158}
159
160/// A trait for generic access to [`elf::ProgramHeader32`] and [`elf::ProgramHeader64`].
161#[allow(missing_docs)]
162pub trait ProgramHeader: Debug + Pod {
163    type Elf: FileHeader<ProgramHeader = Self, Endian = Self::Endian, Word = Self::Word>;
164    type Word: Into<u64>;
165    type Endian: endian::Endian;
166
167    fn p_type(&self, endian: Self::Endian) -> u32;
168    fn p_flags(&self, endian: Self::Endian) -> u32;
169    fn p_offset(&self, endian: Self::Endian) -> Self::Word;
170    fn p_vaddr(&self, endian: Self::Endian) -> Self::Word;
171    fn p_paddr(&self, endian: Self::Endian) -> Self::Word;
172    fn p_filesz(&self, endian: Self::Endian) -> Self::Word;
173    fn p_memsz(&self, endian: Self::Endian) -> Self::Word;
174    fn p_align(&self, endian: Self::Endian) -> Self::Word;
175
176    /// Return the offset and size of the segment in the file.
177    fn file_range(&self, endian: Self::Endian) -> (u64, u64) {
178        (self.p_offset(endian).into(), self.p_filesz(endian).into())
179    }
180
181    /// Return the segment data.
182    ///
183    /// Returns `Err` for invalid values.
184    fn data<'data, R: ReadRef<'data>>(
185        &self,
186        endian: Self::Endian,
187        data: R,
188    ) -> Result<&'data [u8], ()> {
189        let (offset, size) = self.file_range(endian);
190        data.read_bytes_at(offset, size)
191    }
192
193    /// Return the segment data as a slice of the given type.
194    ///
195    /// Allows padding at the end of the data.
196    /// Returns `Ok(&[])` if the segment has no data.
197    /// Returns `Err` for invalid values, including bad alignment.
198    fn data_as_array<'data, T: Pod, R: ReadRef<'data>>(
199        &self,
200        endian: Self::Endian,
201        data: R,
202    ) -> Result<&'data [T], ()> {
203        pod::slice_from_all_bytes(self.data(endian, data)?)
204    }
205
206    /// Return the segment data in the given virtual address range
207    ///
208    /// Returns `Ok(None)` if the segment does not contain the address.
209    /// Returns `Err` for invalid values.
210    fn data_range<'data, R: ReadRef<'data>>(
211        &self,
212        endian: Self::Endian,
213        data: R,
214        address: u64,
215        size: u64,
216    ) -> Result<Option<&'data [u8]>, ()> {
217        Ok(read::util::data_range(
218            self.data(endian, data)?,
219            self.p_vaddr(endian).into(),
220            address,
221            size,
222        ))
223    }
224
225    /// Return entries in a dynamic segment.
226    ///
227    /// Returns `Ok(None)` if the segment is not `PT_DYNAMIC`.
228    /// Returns `Err` for invalid values.
229    fn dynamic<'data, R: ReadRef<'data>>(
230        &self,
231        endian: Self::Endian,
232        data: R,
233    ) -> read::Result<Option<&'data [<Self::Elf as FileHeader>::Dyn]>> {
234        if self.p_type(endian) != elf::PT_DYNAMIC {
235            return Ok(None);
236        }
237        let dynamic = self
238            .data_as_array(endian, data)
239            .read_error("Invalid ELF dynamic segment offset or size")?;
240        Ok(Some(dynamic))
241    }
242
243    /// Return the data in an interpreter segment.
244    ///
245    /// Returns `Ok(None)` if the segment is not `PT_INTERP`.
246    /// Returns `Err` for invalid values.
247    fn interpreter<'data, R: ReadRef<'data>>(
248        &self,
249        endian: Self::Endian,
250        data: R,
251    ) -> read::Result<Option<&'data [u8]>> {
252        if self.p_type(endian) != elf::PT_INTERP {
253            return Ok(None);
254        }
255        let data = self
256            .data(endian, data)
257            .read_error("Invalid ELF interpreter segment offset or size")?;
258        let len = data
259            .iter()
260            .position(|&b| b == 0)
261            .read_error("Invalid ELF interpreter segment data")?;
262        Ok(Some(&data[..len]))
263    }
264
265    /// Return a note iterator for the segment data.
266    ///
267    /// Returns `Ok(None)` if the segment does not contain notes.
268    /// Returns `Err` for invalid values.
269    fn notes<'data, R: ReadRef<'data>>(
270        &self,
271        endian: Self::Endian,
272        data: R,
273    ) -> read::Result<Option<NoteIterator<'data, Self::Elf>>> {
274        if self.p_type(endian) != elf::PT_NOTE {
275            return Ok(None);
276        }
277        let data = self
278            .data(endian, data)
279            .read_error("Invalid ELF note segment offset or size")?;
280        let notes = NoteIterator::new(endian, self.p_align(endian), data)?;
281        Ok(Some(notes))
282    }
283}
284
285impl<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader32<Endian> {
286    type Word = u32;
287    type Endian = Endian;
288    type Elf = elf::FileHeader32<Endian>;
289
290    #[inline]
291    fn p_type(&self, endian: Self::Endian) -> u32 {
292        self.p_type.get(endian)
293    }
294
295    #[inline]
296    fn p_flags(&self, endian: Self::Endian) -> u32 {
297        self.p_flags.get(endian)
298    }
299
300    #[inline]
301    fn p_offset(&self, endian: Self::Endian) -> Self::Word {
302        self.p_offset.get(endian)
303    }
304
305    #[inline]
306    fn p_vaddr(&self, endian: Self::Endian) -> Self::Word {
307        self.p_vaddr.get(endian)
308    }
309
310    #[inline]
311    fn p_paddr(&self, endian: Self::Endian) -> Self::Word {
312        self.p_paddr.get(endian)
313    }
314
315    #[inline]
316    fn p_filesz(&self, endian: Self::Endian) -> Self::Word {
317        self.p_filesz.get(endian)
318    }
319
320    #[inline]
321    fn p_memsz(&self, endian: Self::Endian) -> Self::Word {
322        self.p_memsz.get(endian)
323    }
324
325    #[inline]
326    fn p_align(&self, endian: Self::Endian) -> Self::Word {
327        self.p_align.get(endian)
328    }
329}
330
331impl<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader64<Endian> {
332    type Word = u64;
333    type Endian = Endian;
334    type Elf = elf::FileHeader64<Endian>;
335
336    #[inline]
337    fn p_type(&self, endian: Self::Endian) -> u32 {
338        self.p_type.get(endian)
339    }
340
341    #[inline]
342    fn p_flags(&self, endian: Self::Endian) -> u32 {
343        self.p_flags.get(endian)
344    }
345
346    #[inline]
347    fn p_offset(&self, endian: Self::Endian) -> Self::Word {
348        self.p_offset.get(endian)
349    }
350
351    #[inline]
352    fn p_vaddr(&self, endian: Self::Endian) -> Self::Word {
353        self.p_vaddr.get(endian)
354    }
355
356    #[inline]
357    fn p_paddr(&self, endian: Self::Endian) -> Self::Word {
358        self.p_paddr.get(endian)
359    }
360
361    #[inline]
362    fn p_filesz(&self, endian: Self::Endian) -> Self::Word {
363        self.p_filesz.get(endian)
364    }
365
366    #[inline]
367    fn p_memsz(&self, endian: Self::Endian) -> Self::Word {
368        self.p_memsz.get(endian)
369    }
370
371    #[inline]
372    fn p_align(&self, endian: Self::Endian) -> Self::Word {
373        self.p_align.get(endian)
374    }
375}