symtool_backend/
elf.rs

1//! Manipulate ELF binaries.
2
3use crate::error::{Error, Result};
4use crate::patch::{Location, Rooted};
5use goblin::container::{Container, Ctx, Endian};
6use goblin::elf::section_header::{SHT_DYNSYM, SHT_SYMTAB};
7use goblin::elf::sym::Sym;
8use goblin::elf::{Elf, SectionHeader};
9use scroll::ctx::TryFromCtx;
10use scroll::Pread;
11
12fn context_from_elf(elf: &Elf) -> Ctx {
13    let container = if elf.is_64 {
14        Container::Big
15    } else {
16        Container::Little
17    };
18    let endian = if elf.little_endian {
19        Endian::Little
20    } else {
21        Endian::Big
22    };
23    Ctx::new(container, endian)
24}
25
26/// An iterator over an ELF symbol table.
27pub struct SymtabIter<'a> {
28    bytes: &'a [u8],
29    ctx: Ctx,
30    symoff: usize,
31    stroff: usize,
32    step: usize,
33    count: usize,
34    index: usize,
35}
36
37impl<'a> SymtabIter<'a> {
38    /// Construct a `SymtabIter` from a section header.
39    ///
40    /// Symbol tables are indicated by `SHT_SYMTAB` or `SHT_DYNSYM` section header types.
41    pub fn from_section_header(
42        bytes: &'a [u8],
43        header: &SectionHeader,
44        headers: &[SectionHeader],
45        ctx: Ctx,
46    ) -> Result<Self> {
47        if header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM {
48            return Err(Error::WrongSectionHeader(
49                "symtab requires sh_type equal to SHT_SYMTAB or SHT_DYNSYM".to_string(),
50            ));
51        }
52        if (header.sh_entsize as usize) < Sym::size(ctx.container) {
53            return Err(Error::Malformed("sh_entsize too small".to_string()));
54        };
55        if (header.sh_link as usize) > headers.len() {
56            return Err(Error::Malformed("sh_link too large".to_string()));
57        }
58        Ok(Self {
59            bytes,
60            ctx,
61            symoff: header.sh_offset as usize,
62            stroff: headers[header.sh_link as usize].sh_offset as usize,
63            step: header.sh_entsize as usize,
64            count: if header.sh_entsize == 0 {
65                0
66            } else {
67                header.sh_size / header.sh_entsize
68            } as usize,
69            index: 0,
70        })
71    }
72
73    /// Construct a `SymtabIter` from an ELF binary's static symbol table.
74    ///
75    /// The static symbol table is in the `SHT_SYMTAB` section.
76    pub fn symtab_from_elf(bytes: &'a [u8], elf: &Elf) -> Result<Option<Self>> {
77        let ctx = context_from_elf(elf);
78        for header in &elf.section_headers {
79            if header.sh_type == SHT_SYMTAB {
80                return Some(Self::from_section_header(
81                    bytes,
82                    header,
83                    &elf.section_headers,
84                    ctx,
85                ))
86                .transpose();
87            }
88        }
89        Ok(None)
90    }
91
92    /// Construct a `SymtabIter` from an ELF binary's dynamic symbol table.
93    ///
94    /// The dynamic symbol table is in the `SHT_DYNSYM` section.
95    pub fn dynsym_from_elf(bytes: &'a [u8], elf: &Elf) -> Result<Option<Self>> {
96        let ctx = context_from_elf(elf);
97        for header in &elf.section_headers {
98            if header.sh_type == SHT_DYNSYM {
99                return Some(Self::from_section_header(
100                    bytes,
101                    header,
102                    &elf.section_headers,
103                    ctx,
104                ))
105                .transpose();
106            }
107        }
108        Ok(None)
109    }
110}
111
112impl<'a> std::iter::Iterator for SymtabIter<'a> {
113    type Item = Result<(Option<Rooted<&'a str>>, Rooted<Sym>)>;
114
115    fn next(&mut self) -> Option<Self::Item> {
116        if self.index >= self.count {
117            None
118        } else {
119            Some((|| {
120                let sym_offset = self.symoff + self.index * self.step;
121                self.index += 1;
122                let sym = {
123                    let (sym, sym_size) = Sym::try_from_ctx(&self.bytes[sym_offset..], self.ctx)?;
124                    let location = Location {
125                        offset: sym_offset,
126                        size: sym_size,
127                        ctx: self.ctx,
128                    };
129                    Rooted::new(location, sym)
130                };
131                let name = if sym.st_name != 0 {
132                    let offset = self.stroff + sym.st_name as usize;
133                    let name: &str = self.bytes.pread(offset)?;
134                    let location = Location {
135                        offset,
136                        size: name.len(),
137                        ctx: self.ctx,
138                    };
139                    Some(Rooted::new(location, name))
140                } else {
141                    None
142                };
143                Ok((name, sym))
144            })())
145        }
146    }
147}