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 §ion_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}