tugger_binary_analysis/
elf.rs1use {
6 crate::UndefinedSymbol,
7 byteorder::ReadBytesExt,
8 std::{ffi::CStr, os::raw::c_char},
9};
10
11#[repr(C)]
12#[derive(Debug, Clone)]
13struct Elf64_Verdef {
14 vd_version: u16,
15 vd_flags: u16,
16 vd_ndx: u16,
17 vd_cnt: u16,
18 vd_hash: u32,
19 vd_aux: u32,
20 vd_next: u32,
21}
22
23#[repr(C)]
24#[derive(Debug, Clone)]
25struct Elf64_Verneed {
26 vn_version: u16,
27 vn_cnt: u16,
28 vn_file: u32,
29 vn_aux: u32,
30 vn_next: u32,
31}
32
33#[repr(C)]
34#[derive(Debug, Clone)]
35struct Elf64_Vernaux {
36 vna_hash: u32,
37 vna_flags: u16,
38 vna_other: u16,
39 vna_name: u32,
40 vna_next: u32,
41}
42
43fn resolve_verneed(
44 verneed_entries: &[(Elf64_Verneed, Vec<Elf64_Vernaux>)],
45 names_data: &[u8],
46 versym: u16,
47) -> (Option<String>, Option<String>) {
48 for (verneed, vernauxes) in verneed_entries {
50 for vernaux in vernauxes {
51 if vernaux.vna_other != versym {
52 continue;
53 }
54
55 let filename_ptr = unsafe { names_data.as_ptr().add(verneed.vn_file as usize) };
56 let filename = unsafe { CStr::from_ptr(filename_ptr as *const c_char) };
57
58 let depend_ptr = unsafe { names_data.as_ptr().add(vernaux.vna_name as usize) };
59 let depend = unsafe { CStr::from_ptr(depend_ptr as *const c_char) };
60
61 return (
62 Some(filename.to_string_lossy().into_owned()),
63 Some(depend.to_string_lossy().into_owned()),
64 );
65 }
66 }
67
68 (None, None)
69}
70
71#[allow(clippy::cast_ptr_alignment)]
75pub fn find_undefined_elf_symbols(buffer: &[u8], elf: &goblin::elf::Elf) -> Vec<UndefinedSymbol> {
76 let mut verneed_entries: Vec<(Elf64_Verneed, Vec<Elf64_Vernaux>)> = Vec::new();
77 let mut versym: Vec<u16> = Vec::new();
78 let mut verneed_names_section: u32 = 0;
79
80 for section_header in &elf.section_headers {
81 match section_header.sh_type {
82 goblin::elf::section_header::SHT_GNU_VERSYM => {
83 let data: &[u8] = &buffer[section_header
84 .file_range()
85 .expect("SHT_GNU_VERSYM missing file range")];
86
87 let mut reader = std::io::Cursor::new(data);
88
89 while let Ok(value) = reader.read_u16::<byteorder::NativeEndian>() {
90 versym.push(value);
91 }
92 }
93 goblin::elf::section_header::SHT_GNU_VERNEED => {
94 verneed_names_section = section_header.sh_link;
95
96 let data: &[u8] = &buffer[section_header
97 .file_range()
98 .expect("SHT_GNU_VERNEED missing file range")];
99
100 let mut ptr = data.as_ptr();
101
102 for _ in 0..elf.dynamic.as_ref().unwrap().info.verneednum {
103 let record: Elf64_Verneed = unsafe { std::ptr::read(ptr as *const _) };
104
105 let next_record = unsafe { ptr.add(record.vn_next as usize) };
107
108 let mut vernaux: Vec<Elf64_Vernaux> = Vec::new();
109
110 ptr = unsafe { ptr.add(record.vn_aux as usize) };
111
112 for _ in 0..record.vn_cnt {
113 let aux: Elf64_Vernaux = unsafe { std::ptr::read(ptr as *const _) };
114 vernaux.push(aux.clone());
115 ptr = unsafe { ptr.add(aux.vna_next as usize) };
116 }
117
118 verneed_entries.push((record.clone(), vernaux));
119
120 ptr = next_record;
121 }
122 }
123 _ => {}
124 }
125 }
126
127 let dynstrtab = &elf.dynstrtab;
128 let verneed_names_data: &[u8] = &buffer[elf.section_headers[verneed_names_section as usize]
129 .file_range()
130 .expect("verneed names section missing file range")];
131
132 let mut res: Vec<UndefinedSymbol> = Vec::new();
133
134 let mut versym_iter = versym.iter();
135
136 for sym in elf.dynsyms.iter() {
137 let versym = *versym_iter.next().unwrap();
138
139 if sym.is_import() {
140 let name = dynstrtab.get_at(sym.st_name).unwrap();
141
142 res.push(if versym > 1 {
143 let (filename, version) =
144 resolve_verneed(&verneed_entries, verneed_names_data, versym);
145
146 UndefinedSymbol {
147 symbol: String::from(name),
148 filename,
149 version,
150 }
151 } else {
152 UndefinedSymbol {
153 symbol: String::from(name),
154 filename: None,
155 version: None,
156 }
157 });
158 }
159 }
160
161 res
162}