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, 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)
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
150/// A trait for generic access to [`elf::ProgramHeader32`] and [`elf::ProgramHeader64`].
151#[allow(missing_docs)]
152pub trait ProgramHeader: Debug + Pod {
153    type Elf: FileHeader<ProgramHeader = Self, Endian = Self::Endian, Word = Self::Word>;
154    type Word: Into<u64>;
155    type Endian: endian::Endian;
156
157    fn p_type(&self, endian: Self::Endian) -> u32;
158    fn p_flags(&self, endian: Self::Endian) -> u32;
159    fn p_offset(&self, endian: Self::Endian) -> Self::Word;
160    fn p_vaddr(&self, endian: Self::Endian) -> Self::Word;
161    fn p_paddr(&self, endian: Self::Endian) -> Self::Word;
162    fn p_filesz(&self, endian: Self::Endian) -> Self::Word;
163    fn p_memsz(&self, endian: Self::Endian) -> Self::Word;
164    fn p_align(&self, endian: Self::Endian) -> Self::Word;
165
166    /// Return the offset and size of the segment in the file.
167    fn file_range(&self, endian: Self::Endian) -> (u64, u64) {
168        (self.p_offset(endian).into(), self.p_filesz(endian).into())
169    }
170
171    /// Return the segment data.
172    ///
173    /// Returns `Err` for invalid values.
174    fn data<'data, R: ReadRef<'data>>(
175        &self,
176        endian: Self::Endian,
177        data: R,
178    ) -> Result<&'data [u8], ()> {
179        let (offset, size) = self.file_range(endian);
180        data.read_bytes_at(offset, size)
181    }
182
183    /// Return the segment data as a slice of the given type.
184    ///
185    /// Allows padding at the end of the data.
186    /// Returns `Ok(&[])` if the segment has no data.
187    /// Returns `Err` for invalid values, including bad alignment.
188    fn data_as_array<'data, T: Pod, R: ReadRef<'data>>(
189        &self,
190        endian: Self::Endian,
191        data: R,
192    ) -> Result<&'data [T], ()> {
193        pod::slice_from_all_bytes(self.data(endian, data)?)
194    }
195
196    /// Return the segment data in the given virtual address range
197    ///
198    /// Returns `Ok(None)` if the segment does not contain the address.
199    /// Returns `Err` for invalid values.
200    fn data_range<'data, R: ReadRef<'data>>(
201        &self,
202        endian: Self::Endian,
203        data: R,
204        address: u64,
205        size: u64,
206    ) -> Result<Option<&'data [u8]>, ()> {
207        Ok(read::util::data_range(
208            self.data(endian, data)?,
209            self.p_vaddr(endian).into(),
210            address,
211            size,
212        ))
213    }
214
215    /// Return entries in a dynamic segment.
216    ///
217    /// Returns `Ok(None)` if the segment is not `PT_DYNAMIC`.
218    /// Returns `Err` for invalid values.
219    fn dynamic<'data, R: ReadRef<'data>>(
220        &self,
221        endian: Self::Endian,
222        data: R,
223    ) -> read::Result<Option<&'data [<Self::Elf as FileHeader>::Dyn]>> {
224        if self.p_type(endian) != elf::PT_DYNAMIC {
225            return Ok(None);
226        }
227        let dynamic = self
228            .data_as_array(endian, data)
229            .read_error("Invalid ELF dynamic segment offset or size")?;
230        Ok(Some(dynamic))
231    }
232
233    /// Return the data in an interpreter segment.
234    ///
235    /// Returns `Ok(None)` if the segment is not `PT_INTERP`.
236    /// Returns `Err` for invalid values.
237    fn interpreter<'data, R: ReadRef<'data>>(
238        &self,
239        endian: Self::Endian,
240        data: R,
241    ) -> read::Result<Option<&'data [u8]>> {
242        if self.p_type(endian) != elf::PT_INTERP {
243            return Ok(None);
244        }
245        let data = self
246            .data(endian, data)
247            .read_error("Invalid ELF interpreter segment offset or size")?;
248        let len = data
249            .iter()
250            .position(|&b| b == 0)
251            .read_error("Invalid ELF interpreter segment data")?;
252        Ok(Some(&data[..len]))
253    }
254
255    /// Return a note iterator for the segment data.
256    ///
257    /// Returns `Ok(None)` if the segment does not contain notes.
258    /// Returns `Err` for invalid values.
259    fn notes<'data, R: ReadRef<'data>>(
260        &self,
261        endian: Self::Endian,
262        data: R,
263    ) -> read::Result<Option<NoteIterator<'data, Self::Elf>>> {
264        if self.p_type(endian) != elf::PT_NOTE {
265            return Ok(None);
266        }
267        let data = self
268            .data(endian, data)
269            .read_error("Invalid ELF note segment offset or size")?;
270        let notes = NoteIterator::new(endian, self.p_align(endian), data)?;
271        Ok(Some(notes))
272    }
273}
274
275impl<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader32<Endian> {
276    type Word = u32;
277    type Endian = Endian;
278    type Elf = elf::FileHeader32<Endian>;
279
280    #[inline]
281    fn p_type(&self, endian: Self::Endian) -> u32 {
282        self.p_type.get(endian)
283    }
284
285    #[inline]
286    fn p_flags(&self, endian: Self::Endian) -> u32 {
287        self.p_flags.get(endian)
288    }
289
290    #[inline]
291    fn p_offset(&self, endian: Self::Endian) -> Self::Word {
292        self.p_offset.get(endian)
293    }
294
295    #[inline]
296    fn p_vaddr(&self, endian: Self::Endian) -> Self::Word {
297        self.p_vaddr.get(endian)
298    }
299
300    #[inline]
301    fn p_paddr(&self, endian: Self::Endian) -> Self::Word {
302        self.p_paddr.get(endian)
303    }
304
305    #[inline]
306    fn p_filesz(&self, endian: Self::Endian) -> Self::Word {
307        self.p_filesz.get(endian)
308    }
309
310    #[inline]
311    fn p_memsz(&self, endian: Self::Endian) -> Self::Word {
312        self.p_memsz.get(endian)
313    }
314
315    #[inline]
316    fn p_align(&self, endian: Self::Endian) -> Self::Word {
317        self.p_align.get(endian)
318    }
319}
320
321impl<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader64<Endian> {
322    type Word = u64;
323    type Endian = Endian;
324    type Elf = elf::FileHeader64<Endian>;
325
326    #[inline]
327    fn p_type(&self, endian: Self::Endian) -> u32 {
328        self.p_type.get(endian)
329    }
330
331    #[inline]
332    fn p_flags(&self, endian: Self::Endian) -> u32 {
333        self.p_flags.get(endian)
334    }
335
336    #[inline]
337    fn p_offset(&self, endian: Self::Endian) -> Self::Word {
338        self.p_offset.get(endian)
339    }
340
341    #[inline]
342    fn p_vaddr(&self, endian: Self::Endian) -> Self::Word {
343        self.p_vaddr.get(endian)
344    }
345
346    #[inline]
347    fn p_paddr(&self, endian: Self::Endian) -> Self::Word {
348        self.p_paddr.get(endian)
349    }
350
351    #[inline]
352    fn p_filesz(&self, endian: Self::Endian) -> Self::Word {
353        self.p_filesz.get(endian)
354    }
355
356    #[inline]
357    fn p_memsz(&self, endian: Self::Endian) -> Self::Word {
358        self.p_memsz.get(endian)
359    }
360
361    #[inline]
362    fn p_align(&self, endian: Self::Endian) -> Self::Word {
363        self.p_align.get(endian)
364    }
365}