substrate/symbol/
elf.rs

1use crate::error::{Result, SubstrateError};
2use std::fs::File;
3use std::io::{Read, Seek, SeekFrom};
4
5const ELF_MAGIC: [u8; 4] = [0x7f, b'E', b'L', b'F'];
6const SHT_SYMTAB: u32 = 2;
7const SHT_DYNSYM: u32 = 11;
8const SHT_STRTAB: u32 = 3;
9const STT_FUNC: u8 = 2;
10
11#[repr(C)]
12struct Elf32Ehdr {
13    e_ident: [u8; 16],
14    e_type: u16,
15    e_machine: u16,
16    e_version: u32,
17    e_entry: u32,
18    e_phoff: u32,
19    e_shoff: u32,
20    e_flags: u32,
21    e_ehsize: u16,
22    e_phentsize: u16,
23    e_phnum: u16,
24    e_shentsize: u16,
25    e_shnum: u16,
26    e_shstrndx: u16,
27}
28
29#[repr(C)]
30struct Elf32Shdr {
31    sh_name: u32,
32    sh_type: u32,
33    sh_flags: u32,
34    sh_addr: u32,
35    sh_offset: u32,
36    sh_size: u32,
37    sh_link: u32,
38    sh_info: u32,
39    sh_addralign: u32,
40    sh_entsize: u32,
41}
42
43#[repr(C)]
44pub struct Elf32Sym {
45    st_name: u32,
46    st_value: u32,
47    st_size: u32,
48    st_info: u8,
49    st_other: u8,
50    st_shndx: u16,
51}
52
53pub struct SymbolTable {
54    pub symbols: Vec<Elf32Sym>,
55    pub strings: Vec<u8>,
56}
57
58pub struct ElfSymbols {
59    pub static_symbols: Option<SymbolTable>,
60    pub dynamic_symbols: Option<SymbolTable>,
61}
62
63fn pread(file: &mut File, buf: &mut [u8], offset: u64) -> Result<usize> {
64    file.seek(SeekFrom::Start(offset))?;
65    Ok(file.read(buf)?)
66}
67
68unsafe fn read_struct<T>(file: &mut File, offset: u64) -> Result<T> { unsafe {
69    let mut data = vec![0u8; std::mem::size_of::<T>()];
70    pread(file, &mut data, offset)?;
71    Ok(std::ptr::read(data.as_ptr() as *const T))
72}}
73
74pub fn load_elf_symbols(filename: &str) -> Result<ElfSymbols> {
75    let mut file = File::open(filename)?;
76
77    let ehdr: Elf32Ehdr = unsafe { read_struct(&mut file, 0)? };
78
79    if &ehdr.e_ident[0..4] != &ELF_MAGIC {
80        return Err(SubstrateError::ElfParsing("Not an ELF file".to_string()));
81    }
82
83    if ehdr.e_shentsize as usize != std::mem::size_of::<Elf32Shdr>() {
84        return Err(SubstrateError::ElfParsing("Invalid section header size".to_string()));
85    }
86
87    let mut section_headers = Vec::new();
88    for i in 0..ehdr.e_shnum {
89        let shdr: Elf32Shdr = unsafe {
90            read_struct(
91                &mut file,
92                ehdr.e_shoff as u64 + (i as u64 * ehdr.e_shentsize as u64),
93            )?
94        };
95        section_headers.push(shdr);
96    }
97
98    let mut shstrtab = vec![0u8; section_headers[ehdr.e_shstrndx as usize].sh_size as usize];
99    pread(
100        &mut file,
101        &mut shstrtab,
102        section_headers[ehdr.e_shstrndx as usize].sh_offset as u64,
103    )?;
104
105    let mut symtab_hdr: Option<&Elf32Shdr> = None;
106    let mut strtab_hdr: Option<&Elf32Shdr> = None;
107    let mut dynsym_hdr: Option<&Elf32Shdr> = None;
108    let mut dynstr_hdr: Option<&Elf32Shdr> = None;
109
110    for shdr in &section_headers {
111        match shdr.sh_type {
112            SHT_SYMTAB => {
113                if symtab_hdr.is_some() {
114                    return Err(SubstrateError::ElfParsing("Multiple symbol tables".to_string()));
115                }
116                symtab_hdr = Some(shdr);
117            }
118            SHT_DYNSYM => {
119                if dynsym_hdr.is_some() {
120                    return Err(SubstrateError::ElfParsing("Multiple dynamic symbol tables".to_string()));
121                }
122                dynsym_hdr = Some(shdr);
123            }
124            SHT_STRTAB => {
125                let name_offset = shdr.sh_name as usize;
126                if name_offset < shstrtab.len() {
127                    let name = std::ffi::CStr::from_bytes_until_nul(&shstrtab[name_offset..])
128                        .ok()
129                        .and_then(|s| s.to_str().ok());
130                    if let Some(n) = name {
131                        if n == ".strtab" {
132                            strtab_hdr = Some(shdr);
133                        } else if n == ".dynstr" {
134                            dynstr_hdr = Some(shdr);
135                        }
136                    }
137                }
138            }
139            _ => {}
140        }
141    }
142
143    let static_symbols = if let (Some(symh), Some(strh)) = (symtab_hdr, strtab_hdr) {
144        Some(load_symbol_table(&mut file, symh, strh)?)
145    } else {
146        None
147    };
148
149    let dynamic_symbols = if let (Some(dynh), Some(dstrh)) = (dynsym_hdr, dynstr_hdr) {
150        Some(load_symbol_table(&mut file, dynh, dstrh)?)
151    } else {
152        None
153    };
154
155    Ok(ElfSymbols {
156        static_symbols,
157        dynamic_symbols,
158    })
159}
160
161fn load_symbol_table(
162    file: &mut File,
163    symh: &Elf32Shdr,
164    strh: &Elf32Shdr,
165) -> Result<SymbolTable> {
166    if symh.sh_size % std::mem::size_of::<Elf32Sym>() as u32 != 0 {
167        return Err(SubstrateError::ElfParsing("Invalid symbol table size".to_string()));
168    }
169
170    let num_syms = symh.sh_size as usize / std::mem::size_of::<Elf32Sym>();
171    let mut symbols = Vec::with_capacity(num_syms);
172
173    for i in 0..num_syms {
174        let sym: Elf32Sym = unsafe {
175            read_struct(
176                file,
177                symh.sh_offset as u64 + (i * std::mem::size_of::<Elf32Sym>()) as u64,
178            )?
179        };
180        symbols.push(sym);
181    }
182
183    let mut strings = vec![0u8; strh.sh_size as usize];
184    pread(file, &mut strings, strh.sh_offset as u64)?;
185
186    Ok(SymbolTable { symbols, strings })
187}
188
189pub fn lookup_symbol(symbols: &ElfSymbols, name: &str) -> Option<usize> {
190    if let Some(ref dyn_syms) = symbols.dynamic_symbols {
191        if let Some(addr) = lookup_in_table(dyn_syms, name) {
192            return Some(addr);
193        }
194    }
195
196    if let Some(ref static_syms) = symbols.static_symbols {
197        if let Some(addr) = lookup_in_table(static_syms, name) {
198            return Some(addr);
199        }
200    }
201
202    None
203}
204
205fn lookup_in_table(table: &SymbolTable, name: &str) -> Option<usize> {
206    for sym in &table.symbols {
207        let st_type = sym.st_info & 0xf;
208        if st_type != STT_FUNC {
209            continue;
210        }
211
212        let name_offset = sym.st_name as usize;
213        if name_offset < table.strings.len() {
214            if let Ok(sym_name) = std::ffi::CStr::from_bytes_until_nul(&table.strings[name_offset..])
215            {
216                if let Ok(sym_str) = sym_name.to_str() {
217                    if sym_str == name {
218                        return Some(sym.st_value as usize);
219                    }
220                }
221            }
222        }
223    }
224
225    None
226}