Skip to main content

object/read/elf/
dynamic.rs

1use core::convert::TryInto;
2use core::fmt::Debug;
3use core::slice;
4
5use crate::elf;
6use crate::endian;
7use crate::pod::Pod;
8use crate::read::{ReadError, ReadRef, Result, SectionIndex, StringTable};
9
10use super::{FileHeader, SectionHeader, SectionTable};
11
12/// A table of dynamic entries in an ELF file.
13///
14/// Also includes the string table used for the string values.
15///
16/// Returned by [`SectionTable::dynamic_table`].
17#[derive(Debug, Clone, Copy)]
18pub struct DynamicTable<'data, Elf: FileHeader, R = &'data [u8]>
19where
20    R: ReadRef<'data>,
21{
22    endian: Elf::Endian,
23    dynamics: &'data [Elf::Dyn],
24    strings: StringTable<'data, R>,
25}
26
27impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for DynamicTable<'data, Elf, R> {
28    fn default() -> Self {
29        DynamicTable {
30            endian: Default::default(),
31            dynamics: &[],
32            strings: Default::default(),
33        }
34    }
35}
36
37impl<'data, Elf: FileHeader, R: ReadRef<'data>> DynamicTable<'data, Elf, R> {
38    /// Parse the given dynamic table section.
39    pub(crate) fn parse(
40        endian: Elf::Endian,
41        data: R,
42        sections: &SectionTable<'data, Elf, R>,
43        section: &Elf::SectionHeader,
44    ) -> Result<DynamicTable<'data, Elf, R>> {
45        debug_assert!(section.sh_type(endian) == elf::SHT_DYNAMIC);
46
47        let dynamics = section
48            .data_as_array(endian, data)
49            .read_error("Invalid ELF dynamic table data")?;
50
51        let link = SectionIndex(section.sh_link(endian) as usize);
52        let strings = sections.strings(endian, data, link)?;
53
54        Ok(DynamicTable {
55            endian,
56            dynamics,
57            strings,
58        })
59    }
60
61    /// Return the string table used for the dynamic entries that have string values.
62    #[inline]
63    pub fn strings(&self) -> &StringTable<'data, R> {
64        &self.strings
65    }
66
67    /// Return the dynamic entry slice.
68    ///
69    /// This includes the terminating null entry and any following entries, which you will
70    /// usually need to skip.
71    #[inline]
72    pub fn dynamics(&self) -> &'data [Elf::Dyn] {
73        self.dynamics
74    }
75
76    /// Iterate over the dynamic table entries.
77    ///
78    /// Excludes the terminating null entry, and any following entries.
79    #[inline]
80    pub fn iter(&self) -> DynamicIterator<'data, Elf> {
81        DynamicIterator::new(self.endian, self.dynamics)
82    }
83
84    /// Return true if there are no dynamic entries.
85    #[inline]
86    pub fn is_empty(&self) -> bool {
87        self.dynamics.is_empty()
88    }
89
90    /// The number of dynamic entries.
91    ///
92    /// This includes the terminating null entry and any following entries.
93    #[inline]
94    pub fn len(&self) -> usize {
95        self.dynamics.len()
96    }
97
98    /// Return the string value for the given dynamic entry.
99    ///
100    /// Does not check for an appropriate tag.
101    pub fn string(&self, d: Dynamic) -> Result<&'data [u8]> {
102        d.string(&self.strings)
103    }
104}
105
106impl<'a, 'data, Elf: FileHeader, R: ReadRef<'data>> IntoIterator
107    for &'a DynamicTable<'data, Elf, R>
108{
109    type Item = Dynamic;
110    type IntoIter = DynamicIterator<'data, Elf>;
111
112    fn into_iter(self) -> Self::IntoIter {
113        self.iter()
114    }
115}
116
117/// An iterator over the dynamic entries in an ELF file.
118#[derive(Debug)]
119pub struct DynamicIterator<'data, Elf: FileHeader> {
120    endian: Elf::Endian,
121    dynamics: slice::Iter<'data, Elf::Dyn>,
122}
123
124impl<'data, Elf> DynamicIterator<'data, Elf>
125where
126    Elf: FileHeader,
127{
128    fn new(endian: Elf::Endian, dynamics: &'data [Elf::Dyn]) -> Self {
129        DynamicIterator {
130            endian,
131            dynamics: dynamics.iter(),
132        }
133    }
134}
135
136impl<'data, Elf: FileHeader> Iterator for DynamicIterator<'data, Elf> {
137    type Item = Dynamic;
138
139    fn next(&mut self) -> Option<Self::Item> {
140        let d = self.dynamics.next()?;
141        let tag = d.d_tag(self.endian).into();
142        if tag == elf::DT_NULL {
143            self.dynamics = [].iter();
144            return None;
145        }
146        let val = d.d_val(self.endian).into();
147        Some(Dynamic { tag, val })
148    }
149}
150
151/// A parsed dynamic entry in an ELF file.
152#[derive(Debug, Clone, Copy)]
153pub struct Dynamic {
154    /// The entry tag.
155    ///
156    /// One of the `DT_*` constants.
157    pub tag: i64,
158
159    /// The entry value.
160    ///
161    /// This may be an address, a string table offset, or some other value.
162    pub val: u64,
163}
164
165impl Dynamic {
166    /// Return true if the value is an address.
167    pub fn is_address(&self) -> bool {
168        tag_is_address(self.tag)
169    }
170
171    /// Return true if the value is an offset in the dynamic string table.
172    pub fn is_string(&self) -> bool {
173        tag_is_string(self.tag)
174    }
175
176    /// Use the value to get a string in a string table.
177    ///
178    /// Does not check for an appropriate tag.
179    pub fn string<'data, R: ReadRef<'data>>(
180        &self,
181        strings: &StringTable<'data, R>,
182    ) -> Result<&'data [u8]> {
183        self.val
184            .try_into()
185            .ok()
186            .and_then(|val| strings.get(val).ok())
187            .read_error("Invalid ELF dyn string")
188    }
189}
190
191/// A trait for generic access to [`elf::Dyn32`] and [`elf::Dyn64`].
192#[allow(missing_docs)]
193pub trait Dyn: Debug + Pod {
194    type Word: Into<u64>;
195    type Sword: Into<i64>;
196    type Endian: endian::Endian;
197
198    fn d_tag(&self, endian: Self::Endian) -> Self::Sword;
199    fn d_val(&self, endian: Self::Endian) -> Self::Word;
200
201    /// Get the tag as an `i64`.
202    ///
203    /// This will sign-extend for 32-bit ELF.
204    fn tag(&self, endian: Self::Endian) -> i64 {
205        self.d_tag(endian).into()
206    }
207
208    /// Get the value as a `u64`.
209    ///
210    /// This will zero-extend for 32-bit ELF.
211    fn val(&self, endian: Self::Endian) -> u64 {
212        self.d_val(endian).into()
213    }
214
215    /// Try to convert the tag to an `i32`.
216    fn tag32(&self, endian: Self::Endian) -> Option<i32> {
217        self.d_tag(endian).into().try_into().ok()
218    }
219
220    /// Try to convert the value to a `u32`.
221    fn val32(&self, endian: Self::Endian) -> Option<u32> {
222        self.d_val(endian).into().try_into().ok()
223    }
224
225    /// Return true if the value is an offset in the dynamic string table.
226    fn is_string(&self, endian: Self::Endian) -> bool {
227        tag_is_string(self.tag(endian))
228    }
229
230    /// Use the value to get a string in a string table.
231    ///
232    /// Does not check for an appropriate tag.
233    fn string<'data, R: ReadRef<'data>>(
234        &self,
235        endian: Self::Endian,
236        strings: StringTable<'data, R>,
237    ) -> Result<&'data [u8]> {
238        self.val32(endian)
239            .and_then(|val| strings.get(val).ok())
240            .read_error("Invalid ELF dyn string")
241    }
242
243    /// Return true if the value is an address.
244    fn is_address(&self, endian: Self::Endian) -> bool {
245        tag_is_address(self.tag(endian))
246    }
247}
248
249impl<Endian: endian::Endian> Dyn for elf::Dyn32<Endian> {
250    type Word = u32;
251    type Sword = i32;
252    type Endian = Endian;
253
254    #[inline]
255    fn d_tag(&self, endian: Self::Endian) -> Self::Sword {
256        self.d_tag.get(endian)
257    }
258
259    #[inline]
260    fn d_val(&self, endian: Self::Endian) -> Self::Word {
261        self.d_val.get(endian)
262    }
263}
264
265impl<Endian: endian::Endian> Dyn for elf::Dyn64<Endian> {
266    type Word = u64;
267    type Sword = i64;
268    type Endian = Endian;
269
270    #[inline]
271    fn d_tag(&self, endian: Self::Endian) -> Self::Sword {
272        self.d_tag.get(endian)
273    }
274
275    #[inline]
276    fn d_val(&self, endian: Self::Endian) -> Self::Word {
277        self.d_val.get(endian)
278    }
279}
280
281fn tag_is_string(tag: i64) -> bool {
282    match tag {
283        elf::DT_NEEDED
284        | elf::DT_SONAME
285        | elf::DT_RPATH
286        | elf::DT_RUNPATH
287        | elf::DT_AUXILIARY
288        | elf::DT_FILTER => true,
289        _ => false,
290    }
291}
292
293fn tag_is_address(tag: i64) -> bool {
294    // TODO: check architecture specific values. This requires the e_machine value.
295    match tag {
296        elf::DT_PLTGOT
297        | elf::DT_HASH
298        | elf::DT_STRTAB
299        | elf::DT_SYMTAB
300        | elf::DT_RELA
301        | elf::DT_INIT
302        | elf::DT_FINI
303        | elf::DT_REL
304        | elf::DT_DEBUG
305        | elf::DT_JMPREL
306        | elf::DT_FINI_ARRAY
307        | elf::DT_INIT_ARRAY
308        | elf::DT_PREINIT_ARRAY
309        | elf::DT_SYMTAB_SHNDX
310        | elf::DT_VERDEF
311        | elf::DT_VERNEED
312        | elf::DT_VERSYM
313        | elf::DT_ADDRRNGLO..=elf::DT_ADDRRNGHI => true,
314        _ => false,
315    }
316}