1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Manipulate ELF binaries.

use crate::error::{Error, Result};
use crate::patch::{Location, Rooted};
use goblin::container::{Container, Ctx, Endian};
use goblin::elf::section_header::{SHT_DYNSYM, SHT_SYMTAB};
use goblin::elf::sym::Sym;
use goblin::elf::{Elf, SectionHeader};
use scroll::ctx::TryFromCtx;
use scroll::Pread;

fn context_from_elf(elf: &Elf) -> Ctx {
    let container = if elf.is_64 {
        Container::Big
    } else {
        Container::Little
    };
    let endian = if elf.little_endian {
        Endian::Little
    } else {
        Endian::Big
    };
    Ctx::new(container, endian)
}

/// An iterator over an ELF symbol table.
pub struct SymtabIter<'a> {
    bytes: &'a [u8],
    ctx: Ctx,
    symoff: usize,
    stroff: usize,
    step: usize,
    count: usize,
    index: usize,
}

impl<'a> SymtabIter<'a> {
    /// Construct a `SymtabIter` from a section header.
    ///
    /// Symbol tables are indicated by `SHT_SYMTAB` or `SHT_DYNSYM` section header types.
    pub fn from_section_header(
        bytes: &'a [u8],
        header: &SectionHeader,
        headers: &[SectionHeader],
        ctx: Ctx,
    ) -> Result<Self> {
        if header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM {
            return Err(Error::WrongSectionHeader(
                "symtab requires sh_type equal to SHT_SYMTAB or SHT_DYNSYM".to_string(),
            ));
        }
        if (header.sh_entsize as usize) < Sym::size(ctx.container) {
            return Err(Error::Malformed("sh_entsize too small".to_string()));
        };
        if (header.sh_link as usize) > headers.len() {
            return Err(Error::Malformed("sh_link too large".to_string()));
        }
        Ok(Self {
            bytes,
            ctx,
            symoff: header.sh_offset as usize,
            stroff: headers[header.sh_link as usize].sh_offset as usize,
            step: header.sh_entsize as usize,
            count: if header.sh_entsize == 0 {
                0
            } else {
                header.sh_size / header.sh_entsize
            } as usize,
            index: 0,
        })
    }

    /// Construct a `SymtabIter` from an ELF binary's static symbol table.
    ///
    /// The static symbol table is in the `SHT_SYMTAB` section.
    pub fn symtab_from_elf(bytes: &'a [u8], elf: &Elf) -> Result<Option<Self>> {
        let ctx = context_from_elf(elf);
        for header in &elf.section_headers {
            if header.sh_type == SHT_SYMTAB {
                return Some(Self::from_section_header(
                    bytes,
                    header,
                    &elf.section_headers,
                    ctx,
                ))
                .transpose();
            }
        }
        Ok(None)
    }

    /// Construct a `SymtabIter` from an ELF binary's dynamic symbol table.
    ///
    /// The dynamic symbol table is in the `SHT_DYNSYM` section.
    pub fn dynsym_from_elf(bytes: &'a [u8], elf: &Elf) -> Result<Option<Self>> {
        let ctx = context_from_elf(elf);
        for header in &elf.section_headers {
            if header.sh_type == SHT_DYNSYM {
                return Some(Self::from_section_header(
                    bytes,
                    header,
                    &elf.section_headers,
                    ctx,
                ))
                .transpose();
            }
        }
        Ok(None)
    }
}

impl<'a> std::iter::Iterator for SymtabIter<'a> {
    type Item = Result<(Option<Rooted<&'a str>>, Rooted<Sym>)>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.index >= self.count {
            None
        } else {
            Some((|| {
                let sym_offset = self.symoff + self.index * self.step;
                self.index += 1;
                let sym = {
                    let (sym, sym_size) = Sym::try_from_ctx(&self.bytes[sym_offset..], self.ctx)?;
                    let location = Location {
                        offset: sym_offset,
                        size: sym_size,
                        ctx: self.ctx,
                    };
                    Rooted::new(location, sym)
                };
                let name = if sym.st_name != 0 {
                    let offset = self.stroff + sym.st_name as usize;
                    let name: &str = self.bytes.pread(offset)?;
                    let location = Location {
                        offset,
                        size: name.len(),
                        ctx: self.ctx,
                    };
                    Some(Rooted::new(location, name))
                } else {
                    None
                };
                Ok((name, sym))
            })())
        }
    }
}