1#[cfg(feature = "alloc")]
9extern crate alloc;
10
11#[cfg(feature = "alloc")]
12use alloc::{string::String, vec::Vec};
13
14use super::types::{Elf64ProgramHeader, ElfBinary, ProgramType};
15use crate::{error::KernelError, mm::PAGE_SIZE};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[repr(u64)]
25pub enum AuxType {
26 AtNull = 0,
28 AtPhdr = 3,
30 AtPhent = 4,
32 AtPhnum = 5,
34 AtPagesz = 6,
36 AtBase = 7,
38 AtEntry = 9,
40 AtRandom = 25,
42 AtExecfn = 31,
44}
45
46#[derive(Debug, Clone, Copy)]
48pub struct AuxVecEntry {
49 pub type_id: AuxType,
51 pub value: u64,
53}
54
55impl AuxVecEntry {
56 pub fn new(type_id: AuxType, value: u64) -> Self {
58 Self { type_id, value }
59 }
60}
61
62#[cfg(feature = "alloc")]
68pub struct DynamicLinkerInfo {
69 pub interp_path: String,
71 pub interp_base: u64,
73 pub interp_entry: u64,
75 pub aux_vector: Vec<AuxVecEntry>,
77}
78
79#[cfg(feature = "alloc")]
88pub fn find_interpreter(
89 data: &[u8],
90 program_headers: &[Elf64ProgramHeader],
91) -> Result<Option<String>, KernelError> {
92 for ph in program_headers {
93 if ph.p_type == ProgramType::Interp as u32 {
94 let offset = ph.p_offset as usize;
95 let size = ph.p_filesz as usize;
96
97 if size == 0 {
98 return Err(KernelError::InvalidArgument {
99 name: "PT_INTERP",
100 value: "empty interpreter path",
101 });
102 }
103
104 if offset.checked_add(size).is_none() || offset + size > data.len() {
105 return Err(KernelError::InvalidArgument {
106 name: "PT_INTERP",
107 value: "segment extends past end of file",
108 });
109 }
110
111 let interp_bytes = &data[offset..offset + size];
112
113 let path_bytes = if interp_bytes.last() == Some(&0) {
115 &interp_bytes[..size - 1]
116 } else {
117 interp_bytes
118 };
119
120 let path =
121 core::str::from_utf8(path_bytes).map_err(|_| KernelError::InvalidArgument {
122 name: "PT_INTERP",
123 value: "interpreter path is not valid UTF-8",
124 })?;
125
126 return Ok(Some(String::from(path)));
127 }
128 }
129
130 Ok(None)
131}
132
133#[cfg(feature = "alloc")]
149pub fn build_aux_vector(
150 elf_binary: &ElfBinary,
151 phdr_addr: u64,
152 interp_base: u64,
153 random_addr: u64,
154 execfn_addr: u64,
155) -> Vec<AuxVecEntry> {
156 let phdr_count = elf_binary
157 .segments
158 .iter()
159 .filter(|s| s.segment_type == super::types::SegmentType::Phdr)
160 .count();
161
162 let phnum = if phdr_count > 0 {
164 phdr_count as u64
165 } else {
166 elf_binary.segments.len() as u64
167 };
168
169 let phent_size = core::mem::size_of::<Elf64ProgramHeader>() as u64;
170
171 let mut aux = Vec::with_capacity(10);
172
173 aux.push(AuxVecEntry::new(AuxType::AtPhdr, phdr_addr));
174 aux.push(AuxVecEntry::new(AuxType::AtPhent, phent_size));
175 aux.push(AuxVecEntry::new(AuxType::AtPhnum, phnum));
176 aux.push(AuxVecEntry::new(AuxType::AtPagesz, PAGE_SIZE as u64));
177 aux.push(AuxVecEntry::new(AuxType::AtEntry, elf_binary.entry_point));
178
179 if interp_base != 0 {
180 aux.push(AuxVecEntry::new(AuxType::AtBase, interp_base));
181 }
182
183 if random_addr != 0 {
184 aux.push(AuxVecEntry::new(AuxType::AtRandom, random_addr));
185 }
186
187 if execfn_addr != 0 {
188 aux.push(AuxVecEntry::new(AuxType::AtExecfn, execfn_addr));
189 }
190
191 aux.push(AuxVecEntry::new(AuxType::AtNull, 0));
193
194 aux
195}
196
197#[cfg(feature = "alloc")]
216pub fn prepare_dynamic_linking(
217 _elf_data: &[u8],
218 elf_binary: &ElfBinary,
219 load_base: u64,
220) -> Result<Option<DynamicLinkerInfo>, KernelError> {
221 let interp_path = match &elf_binary.interpreter {
223 Some(path) => path.clone(),
224 None => return Ok(None), };
226
227 let interp_elf =
229 super::load_elf_from_file(&interp_path).map_err(|_| KernelError::NotFound {
230 resource: "interpreter",
231 id: 0,
232 })?;
233
234 let interp_base = interp_elf.load_base;
235 let interp_entry = interp_elf.entry_point;
236
237 let phdr_addr = load_base;
241
242 let aux_vector = build_aux_vector(elf_binary, phdr_addr, interp_base, 0, 0);
245
246 Ok(Some(DynamicLinkerInfo {
247 interp_path,
248 interp_base,
249 interp_entry,
250 aux_vector,
251 }))
252}
253
254#[cfg(test)]
255mod tests {
256 use alloc::vec;
257
258 use super::*;
259
260 #[test]
261 fn test_aux_type_values() {
262 assert_eq!(AuxType::AtNull as u64, 0);
263 assert_eq!(AuxType::AtPhdr as u64, 3);
264 assert_eq!(AuxType::AtPhent as u64, 4);
265 assert_eq!(AuxType::AtPhnum as u64, 5);
266 assert_eq!(AuxType::AtPagesz as u64, 6);
267 assert_eq!(AuxType::AtBase as u64, 7);
268 assert_eq!(AuxType::AtEntry as u64, 9);
269 assert_eq!(AuxType::AtRandom as u64, 25);
270 assert_eq!(AuxType::AtExecfn as u64, 31);
271 }
272
273 #[test]
274 fn test_aux_vec_entry_new() {
275 let entry = AuxVecEntry::new(AuxType::AtPagesz, 4096);
276 assert_eq!(entry.type_id, AuxType::AtPagesz);
277 assert_eq!(entry.value, 4096);
278 }
279
280 #[test]
281 fn test_find_interpreter_no_interp() {
282 let headers: Vec<Elf64ProgramHeader> = Vec::new();
284 let data = &[];
285 let result = find_interpreter(data, &headers);
286 assert!(result.is_ok());
287 assert!(result.unwrap().is_none());
288 }
289
290 #[test]
291 fn test_find_interpreter_with_interp() {
292 let interp_path = b"/lib/ld-linux.so.2\0";
293 let mut data = vec![0u8; 128];
294 let offset = 64usize;
295 data[offset..offset + interp_path.len()].copy_from_slice(interp_path);
296
297 let headers = vec![Elf64ProgramHeader {
298 p_type: ProgramType::Interp as u32,
299 p_flags: 4, p_offset: offset as u64,
301 p_vaddr: 0,
302 p_paddr: 0,
303 p_filesz: interp_path.len() as u64,
304 p_memsz: interp_path.len() as u64,
305 p_align: 1,
306 }];
307
308 let result = find_interpreter(&data, &headers);
309 assert!(result.is_ok());
310 let interp = result.unwrap();
311 assert!(interp.is_some());
312 assert_eq!(interp.unwrap(), "/lib/ld-linux.so.2");
313 }
314
315 #[test]
316 fn test_find_interpreter_invalid_offset() {
317 let headers = vec![Elf64ProgramHeader {
318 p_type: ProgramType::Interp as u32,
319 p_flags: 4,
320 p_offset: 1000, p_vaddr: 0,
322 p_paddr: 0,
323 p_filesz: 20,
324 p_memsz: 20,
325 p_align: 1,
326 }];
327
328 let data = &[0u8; 10];
329 let result = find_interpreter(data, &headers);
330 assert!(result.is_err());
331 }
332
333 #[test]
334 fn test_build_aux_vector_terminates_with_null() {
335 use alloc::vec;
336
337 let binary = ElfBinary {
338 entry_point: 0x401000,
339 load_base: 0x400000,
340 load_size: 0x2000,
341 segments: vec![],
342 interpreter: None,
343 dynamic: false,
344 };
345
346 let aux = build_aux_vector(&binary, 0x400040, 0, 0, 0);
347
348 let last = aux.last().expect("aux vector should not be empty");
350 assert_eq!(last.type_id, AuxType::AtNull);
351 assert_eq!(last.value, 0);
352 }
353
354 #[test]
355 fn test_build_aux_vector_contains_essential_entries() {
356 use alloc::vec;
357
358 let binary = ElfBinary {
359 entry_point: 0x401000,
360 load_base: 0x400000,
361 load_size: 0x2000,
362 segments: vec![],
363 interpreter: Some(String::from("/lib/ld.so.1")),
364 dynamic: true,
365 };
366
367 let aux = build_aux_vector(&binary, 0x400040, 0x7F000000, 0xBEEF, 0xCAFE);
368
369 let has = |t: AuxType| aux.iter().any(|e| e.type_id == t);
370 assert!(has(AuxType::AtPhdr));
371 assert!(has(AuxType::AtPhent));
372 assert!(has(AuxType::AtPhnum));
373 assert!(has(AuxType::AtPagesz));
374 assert!(has(AuxType::AtEntry));
375 assert!(has(AuxType::AtBase));
376 assert!(has(AuxType::AtRandom));
377 assert!(has(AuxType::AtExecfn));
378 assert!(has(AuxType::AtNull));
379 }
380}