1use alloc::{sync::Arc, vec::Vec};
25use x86_64::{
26 structures::paging::{Mapper, Page, Size4KiB},
27 VirtAddr,
28};
29
30use crate::{
31 capability::Capability,
32 memory::address_space::{AddressSpace, VmaFlags, VmaPageSize, VmaType},
33 process::{
34 task::{CpuContext, KernelStack, ResumeKind, SyncUnsafeCell, Task},
35 TaskId, TaskPriority, TaskState,
36 },
37};
38
39const ET_EXEC: u16 = 2;
44const ET_DYN: u16 = 3;
45const PT_LOAD: u32 = 1;
46const PT_DYNAMIC: u32 = 2;
47const PT_INTERP: u32 = 3;
48const PT_TLS: u32 = 7;
49const PF_X: u32 = 1;
50const PF_W: u32 = 2;
51const PF_R: u32 = 4;
52const DT_NULL: i64 = 0;
53const DT_RELA: i64 = 7;
54const DT_RELASZ: i64 = 8;
55const DT_RELAENT: i64 = 9;
56const DT_STRTAB: i64 = 5;
57const DT_SYMTAB: i64 = 6;
58const DT_SYMENT: i64 = 11;
59const DT_JMPREL: i64 = 23;
60const DT_PLTRELSZ: i64 = 2;
61const DT_PLTREL: i64 = 20;
62const DT_RELACOUNT: i64 = 0x6fff_fff9;
63const DT_RELR: i64 = 36;
64const DT_RELRSZ: i64 = 35;
65const DT_RELRENT: i64 = 37;
66const R_X86_64_RELATIVE: u32 = 8;
67const R_X86_64_64: u32 = 1;
68const R_X86_64_COPY: u32 = 5;
69const R_X86_64_GLOB_DAT: u32 = 6;
70const R_X86_64_JUMP_SLOT: u32 = 7;
71const R_X86_64_TPOFF64: u32 = 18;
72const R_X86_64_IRELATIVE: u32 = 37;
73
74pub const USER_ADDR_MAX: u64 = 0x0000_8000_0000_0000;
76const PIE_BASE_ADDR: u64 = 0x0000_0001_0000_0000;
78
79pub const USER_STACK_BASE: u64 = 0x0000_7FFF_F000_0000;
81pub const USER_STACK_PAGES: usize = 16;
83pub const USER_STACK_TOP: u64 = USER_STACK_BASE + (USER_STACK_PAGES as u64) * 4096;
85pub const USER_STACK_GUARD: u64 = USER_STACK_BASE - 4096;
87
88#[derive(Debug, Clone, Copy)]
90pub struct LoadedElfInfo {
91 pub runtime_entry: u64,
92 pub program_entry: u64,
93 pub phdr_vaddr: u64,
94 pub phent: u16,
95 pub phnum: u16,
96 pub interp_base: Option<u64>,
97 pub tls_vaddr: u64,
98 pub tls_filesz: u64,
99 pub tls_memsz: u64,
100 pub tls_align: u64,
101}
102
103#[derive(Debug, Clone, Copy)]
109struct Elf64Header {
110 e_type: u16,
111 e_entry: u64,
112 e_phoff: u64,
113 e_phentsize: u16,
114 e_phnum: u16,
115}
116
117#[repr(C, packed)]
119#[derive(Debug, Clone, Copy)]
120struct Elf64Phdr {
121 p_type: u32,
122 p_flags: u32,
123 p_offset: u64,
124 p_vaddr: u64,
125 p_paddr: u64,
126 p_filesz: u64,
127 p_memsz: u64,
128 p_align: u64,
129}
130
131#[repr(C, packed)]
132#[derive(Debug, Clone, Copy)]
133struct Elf64Dyn {
134 d_tag: i64,
135 d_val: u64,
136}
137
138#[repr(C, packed)]
139#[derive(Debug, Clone, Copy)]
140struct Elf64Rela {
141 r_offset: u64,
142 r_info: u64,
143 r_addend: i64,
144}
145
146#[repr(C, packed)]
147#[derive(Debug, Clone, Copy)]
148struct Elf64Sym {
149 st_name: u32,
150 st_info: u8,
151 st_other: u8,
152 st_shndx: u16,
153 st_value: u64,
154 st_size: u64,
155}
156
157fn parse_header(data: &[u8]) -> Result<Elf64Header, &'static str> {
166 let elf = xmas_elf::ElfFile::new(data).map_err(|_| "Invalid ELF header")?;
167
168 let hdr = elf.header.pt2;
169
170 let e_type = hdr.type_().0;
172 if e_type != ET_EXEC && e_type != ET_DYN {
173 return Err("Unsupported ELF type (expected ET_EXEC or ET_DYN)");
174 }
175
176 let e_entry = hdr.entry_point();
177 if e_entry >= USER_ADDR_MAX {
180 return Err("Entry point outside user address range");
181 }
182
183 let e_phentsize = hdr.ph_entry_size();
184 let e_phoff = hdr.ph_offset();
185 let e_phnum = hdr.ph_count();
186
187 if e_phentsize as usize != core::mem::size_of::<xmas_elf::program::ProgramHeader>() {
189 return Err("Unexpected phentsize");
190 }
191
192 let ph_end = (e_phoff as usize)
193 .checked_add((e_phnum as usize) * (e_phentsize as usize))
194 .ok_or("Program header table overflows")?;
195 if ph_end > data.len() {
196 return Err("Program headers extend past file");
197 }
198
199 Ok(Elf64Header {
200 e_type,
201 e_entry,
202 e_phoff,
203 e_phentsize,
204 e_phnum,
205 })
206}
207
208fn program_headers<'a>(
210 data: &'a [u8],
211 header: &Elf64Header,
212) -> impl Iterator<Item = Elf64Phdr> + 'a {
213 let phoff = header.e_phoff as usize;
214 let phsize = header.e_phentsize as usize;
215 let phnum = header.e_phnum as usize;
216
217 (0..phnum).map(move |i| {
218 let offset = phoff + i * phsize;
219 unsafe { core::ptr::read_unaligned(data.as_ptr().add(offset) as *const Elf64Phdr) }
222 })
223}
224
225fn parse_interp_path<'a>(
227 elf_data: &'a [u8],
228 phdrs: &[Elf64Phdr],
229) -> Result<Option<&'a str>, &'static str> {
230 let Some(interp) = phdrs.iter().find(|ph| ph.p_type == PT_INTERP) else {
231 return Ok(None);
232 };
233 if interp.p_filesz == 0 {
234 return Err("PT_INTERP has empty path");
235 }
236 let start = interp.p_offset as usize;
237 let end = start
238 .checked_add(interp.p_filesz as usize)
239 .ok_or("PT_INTERP range overflow")?;
240 if end > elf_data.len() {
241 return Err("PT_INTERP extends past file");
242 }
243 let raw = &elf_data[start..end];
244 let nul = raw
245 .iter()
246 .position(|&b| b == 0)
247 .ok_or("PT_INTERP path is not NUL terminated")?;
248 let s = core::str::from_utf8(&raw[..nul]).map_err(|_| "PT_INTERP path is not UTF-8")?;
249 if s.is_empty() {
250 return Err("PT_INTERP path is empty");
251 }
252 Ok(Some(s))
253}
254
255fn find_relocated_phdr_vaddr(
257 header: &Elf64Header,
258 phdrs: &[Elf64Phdr],
259 load_bias: u64,
260) -> Result<u64, &'static str> {
261 let phoff = header.e_phoff;
262 for ph in phdrs {
263 if ph.p_type != PT_LOAD || ph.p_filesz == 0 {
264 continue;
265 }
266 let file_start = ph.p_offset;
267 let file_end = ph
268 .p_offset
269 .checked_add(ph.p_filesz)
270 .ok_or("PHDR location overflow")?;
271 if phoff >= file_start && phoff < file_end {
272 let delta = phoff - file_start;
273 let vaddr = ph
274 .p_vaddr
275 .checked_add(delta)
276 .and_then(|v| v.checked_add(load_bias))
277 .ok_or("Relocated PHDR address overflow")?;
278 if vaddr >= USER_ADDR_MAX {
279 return Err("Relocated PHDR outside user address space");
280 }
281 return Ok(vaddr);
282 }
283 }
284 Err("Program headers are not covered by a PT_LOAD segment")
285}
286
287fn read_elf_from_vfs(path: &str) -> Result<Vec<u8>, &'static str> {
289 const MAX_ELF_SIZE: usize = 64 * 1024 * 1024;
290 let resolved_path =
291 crate::vfs::resolve_and_check_path_for_current_task(path, true, false, true)
292 .map_err(|_| "PT_INTERP execute denied")?;
293 let fd = crate::vfs::open(&resolved_path, crate::vfs::OpenFlags::READ)
294 .map_err(|_| "PT_INTERP open failed")?;
295 let mut out = Vec::new();
296 let mut buf = [0u8; 4096];
297
298 let n = match crate::vfs::read(fd, &mut buf) {
300 Ok(0) => {
301 let _ = crate::vfs::close(fd);
302 return Err("PT_INTERP file is empty");
303 }
304 Ok(n) => n,
305 Err(_) => {
306 let _ = crate::vfs::close(fd);
307 return Err("PT_INTERP read failed");
308 }
309 };
310 if n < 4 || buf[..4] != [0x7F, b'E', b'L', b'F'] {
311 let _ = crate::vfs::close(fd);
312 return Err("PT_INTERP file is not an ELF");
313 }
314 out.extend_from_slice(&buf[..n]);
315
316 loop {
318 let n = match crate::vfs::read(fd, &mut buf) {
319 Ok(0) => break,
320 Ok(n) => n,
321 Err(_) => {
322 let _ = crate::vfs::close(fd);
323 return Err("PT_INTERP read failed");
324 }
325 };
326 if out.len().saturating_add(n) > MAX_ELF_SIZE {
327 let _ = crate::vfs::close(fd);
328 return Err("PT_INTERP file too large");
329 }
330 out.extend_from_slice(&buf[..n]);
331 }
332 let _ = crate::vfs::close(fd);
333 Ok(out)
334}
335
336fn compute_load_bounds(phdrs: &[Elf64Phdr]) -> Result<(u64, u64), &'static str> {
338 let mut min_vaddr = u64::MAX;
339 let mut max_vaddr = 0u64;
340 let mut saw_load = false;
341
342 for phdr in phdrs {
343 if phdr.p_type != PT_LOAD {
344 continue;
345 }
346 if phdr.p_memsz == 0 {
347 continue;
348 }
349 saw_load = true;
350
351 if phdr.p_memsz < phdr.p_filesz {
352 return Err("PT_LOAD memsz < filesz");
353 }
354
355 if ((phdr.p_vaddr ^ phdr.p_offset) & 0xFFF) != 0 {
357 return Err("PT_LOAD alignment mismatch (vaddr/offset)");
358 }
359
360 let seg_end = phdr
361 .p_vaddr
362 .checked_add(phdr.p_memsz)
363 .ok_or("PT_LOAD vaddr+memsz overflow")?;
364 if seg_end > USER_ADDR_MAX {
365 return Err("PT_LOAD exceeds user address space");
366 }
367
368 let seg_start_page = phdr.p_vaddr & !0xFFF;
369 let seg_end_page = (seg_end + 0xFFF) & !0xFFF;
370 min_vaddr = min_vaddr.min(seg_start_page);
371 max_vaddr = max_vaddr.max(seg_end_page);
372 }
373
374 if !saw_load {
375 return Err("ELF has no PT_LOAD segments");
376 }
377 Ok((min_vaddr, max_vaddr))
378}
379
380fn compute_load_bias_and_entry(
382 user_as: &AddressSpace,
383 header: &Elf64Header,
384 phdrs: &[Elf64Phdr],
385) -> Result<(u64, u64), &'static str> {
386 let (min_vaddr, max_vaddr) = compute_load_bounds(phdrs)?;
387 let span = max_vaddr
388 .checked_sub(min_vaddr)
389 .ok_or("Invalid PT_LOAD bounds")?;
390
391 let load_bias = if header.e_type == ET_EXEC {
392 0
393 } else {
394 let n_pages = (span as usize).div_ceil(4096);
395 let load_base = user_as
396 .find_free_vma_range(PIE_BASE_ADDR, n_pages, VmaPageSize::Small)
397 .or_else(|| {
398 user_as.find_free_vma_range(0x0000_0000_1000_0000, n_pages, VmaPageSize::Small)
399 })
400 .ok_or("No virtual range for ET_DYN image")?;
401 load_base
402 .checked_sub(min_vaddr)
403 .ok_or("ET_DYN load bias underflow")?
404 };
405
406 let relocated_end = max_vaddr
407 .checked_add(load_bias)
408 .ok_or("Relocated PT_LOAD range overflow")?;
409 if relocated_end > USER_ADDR_MAX {
410 return Err("Relocated PT_LOAD range exceeds user space");
411 }
412
413 let entry_raw = if header.e_type == ET_EXEC && header.e_entry == 0 {
414 let fallback = phdrs
415 .iter()
416 .find(|ph| ph.p_type == PT_LOAD && ph.p_memsz != 0 && (ph.p_flags & PF_X) != 0)
417 .map(|ph| ph.p_vaddr)
418 .ok_or("ET_EXEC has null entry and no executable PT_LOAD")?;
419 log::warn!(
420 "[elf] ET_EXEC has null entry, using fallback executable segment vaddr={:#x}",
421 fallback
422 );
423 fallback
424 } else {
425 header.e_entry
426 };
427
428 let relocated_entry = entry_raw
429 .checked_add(load_bias)
430 .ok_or("Relocated entry overflow")?;
431 if relocated_entry == 0 || relocated_entry >= USER_ADDR_MAX {
432 return Err("Relocated entry outside user space");
433 }
434
435 Ok((load_bias, relocated_entry))
436}
437
438fn apply_segment_permissions(
440 user_as: &AddressSpace,
441 page_start: u64,
442 page_count: usize,
443 flags: VmaFlags,
444) -> Result<(), &'static str> {
445 use x86_64::registers::control::Cr3;
446
447 let pte_flags = flags.to_page_flags();
448 let mut mapper = unsafe { user_as.mapper() };
450 for i in 0..page_count {
451 let vaddr = page_start
452 .checked_add((i as u64) * 4096)
453 .ok_or("Permission update address overflow")?;
454 let page = Page::<Size4KiB>::from_start_address(VirtAddr::new(vaddr))
455 .map_err(|_| "Invalid page while updating segment flags")?;
456 let _ = unsafe {
458 mapper
459 .update_flags(page, pte_flags)
460 .map_err(|_| "Failed to update segment page flags")?
461 };
462 }
464
465 let (current_cr3, _) = Cr3::read();
471 if current_cr3.start_address() == user_as.cr3() {
472 let end = page_start + (page_count as u64) * 4096;
473 crate::arch::x86_64::tlb::local_range(VirtAddr::new(page_start), VirtAddr::new(end));
474 }
475
476 Ok(())
477}
478
479fn read_user_mapped_bytes(
481 user_as: &AddressSpace,
482 mut vaddr: u64,
483 out: &mut [u8],
484) -> Result<(), &'static str> {
485 let end = vaddr
486 .checked_add(out.len() as u64)
487 .ok_or("Read range overflow")?;
488 if end > USER_ADDR_MAX {
489 return Err("Read range outside user space");
490 }
491 let mut copied = 0usize;
492 while copied < out.len() {
493 let page_off = (vaddr & 0xFFF) as usize;
494 let chunk = core::cmp::min(out.len() - copied, 4096 - page_off);
495 let phys = user_as
496 .translate(VirtAddr::new(vaddr))
497 .ok_or("Failed to translate mapped user bytes")?;
498 let paddr = phys.as_u64();
499 if paddr == 0 {
500 return Err("Translated physical address is null");
501 }
502 let src = crate::memory::phys_to_virt(paddr) as *const u8;
503 if src.is_null() {
504 return Err("HHDM-mapped source is null");
505 }
506 unsafe { core::ptr::copy_nonoverlapping(src, out.as_mut_ptr().add(copied), chunk) };
510 copied += chunk;
511 vaddr = vaddr
512 .checked_add(chunk as u64)
513 .ok_or("Virtual address overflow while reading mapped bytes")?;
514 }
515 Ok(())
516}
517
518fn write_user_mapped_bytes(
520 user_as: &AddressSpace,
521 mut vaddr: u64,
522 src: &[u8],
523) -> Result<(), &'static str> {
524 let end = vaddr
525 .checked_add(src.len() as u64)
526 .ok_or("Write range overflow")?;
527 if end > USER_ADDR_MAX {
528 return Err("Write range outside user space");
529 }
530 let mut written = 0usize;
531 while written < src.len() {
532 let page_off = (vaddr & 0xFFF) as usize;
533 let chunk = core::cmp::min(src.len() - written, 4096 - page_off);
534 let phys = user_as
535 .translate(VirtAddr::new(vaddr))
536 .ok_or("Failed to translate relocation target")?;
537 let paddr = phys.as_u64();
538 if paddr == 0 {
539 return Err("Translated physical address is null");
540 }
541 let dst = crate::memory::phys_to_virt(paddr) as *mut u8;
542 if dst.is_null() {
543 return Err("HHDM-mapped destination is null");
544 }
545 unsafe { core::ptr::copy_nonoverlapping(src.as_ptr().add(written), dst, chunk) };
549 written += chunk;
550 vaddr = vaddr
551 .checked_add(chunk as u64)
552 .ok_or("Virtual address overflow while writing mapped bytes")?;
553 }
554 Ok(())
555}
556
557fn read_user_u64(user_as: &AddressSpace, vaddr: u64) -> Result<u64, &'static str> {
559 let mut raw = [0u8; 8];
560 read_user_mapped_bytes(user_as, vaddr, &mut raw)?;
561 Ok(u64::from_le_bytes(raw))
562}
563
564fn write_user_u64(user_as: &AddressSpace, vaddr: u64, value: u64) -> Result<(), &'static str> {
566 write_user_mapped_bytes(user_as, vaddr, &value.to_le_bytes())
567}
568
569fn call_ifunc_resolver(user_as: &AddressSpace, resolver_vaddr: u64) -> Result<u64, &'static str> {
575 if resolver_vaddr >= USER_ADDR_MAX {
576 return Err("IFUNC resolver address outside user space");
577 }
578 let phys = user_as
579 .translate(VirtAddr::new(resolver_vaddr))
580 .ok_or("IFUNC resolver page not mapped")?;
581 let hhdm_ptr = crate::memory::phys_to_virt(phys.as_u64());
582 let resolver: extern "C" fn() -> u64 = unsafe { core::mem::transmute(hhdm_ptr as *const ()) };
587 Ok(resolver())
588}
589
590fn apply_relr_relocations(
592 user_as: &AddressSpace,
593 load_bias: u64,
594 relr_base: u64,
595 relr_size: usize,
596 relr_ent: usize,
597) -> Result<usize, &'static str> {
598 if relr_size == 0 {
599 return Ok(0);
600 }
601 if relr_ent != core::mem::size_of::<u64>() {
602 return Err("Unsupported DT_RELRENT size");
603 }
604 if relr_size % relr_ent != 0 {
605 return Err("DT_RELR table size is not aligned");
606 }
607
608 let count = relr_size / relr_ent;
609 let mut applied = 0usize;
610 let mut where_addr = 0u64;
611
612 for i in 0..count {
613 let entry_addr = relr_base
614 .checked_add((i * relr_ent) as u64)
615 .ok_or("DT_RELR walk overflow")?;
616 let entry = read_user_u64(user_as, entry_addr)?;
617
618 if (entry & 1) == 0 {
619 where_addr = load_bias
620 .checked_add(entry)
621 .ok_or("DT_RELR absolute relocation overflow")?;
622 if where_addr >= USER_ADDR_MAX {
623 return Err("DT_RELR target outside user space");
624 }
625 let cur = read_user_u64(user_as, where_addr)?;
626 write_user_u64(
627 user_as,
628 where_addr,
629 cur.checked_add(load_bias)
630 .ok_or("DT_RELR relocated value overflow")?,
631 )?;
632 where_addr = where_addr
633 .checked_add(8)
634 .ok_or("DT_RELR where pointer overflow")?;
635 applied += 1;
636 } else {
637 let mut bitmap = entry >> 1;
638 for bit in 0..63u64 {
639 if (bitmap & 1) != 0 {
640 let slot = where_addr
641 .checked_add(bit * 8)
642 .ok_or("DT_RELR bitmap target overflow")?;
643 if slot >= USER_ADDR_MAX {
644 return Err("DT_RELR bitmap target outside user space");
645 }
646 let cur = read_user_u64(user_as, slot)?;
647 write_user_u64(
648 user_as,
649 slot,
650 cur.checked_add(load_bias)
651 .ok_or("DT_RELR bitmap relocated value overflow")?,
652 )?;
653 applied += 1;
654 }
655 bitmap >>= 1;
656 if bitmap == 0 {
657 break;
658 }
659 }
660 where_addr = where_addr
661 .checked_add(63 * 8)
662 .ok_or("DT_RELR where advance overflow")?;
663 }
664 }
665 Ok(applied)
666}
667
668fn apply_dynamic_relocations(
670 user_as: &AddressSpace,
671 phdrs: &[Elf64Phdr],
672 elf_type: u16,
673 load_bias: u64,
674) -> Result<(), &'static str> {
675 if elf_type != ET_DYN {
676 return Ok(());
677 }
678
679 let dynamic = phdrs.iter().find(|ph| ph.p_type == PT_DYNAMIC);
680 let Some(dynamic_ph) = dynamic else {
681 return Ok(());
682 };
683 if dynamic_ph.p_filesz == 0 {
684 return Ok(());
685 }
686
687 let dyn_addr = dynamic_ph
688 .p_vaddr
689 .checked_add(load_bias)
690 .ok_or("PT_DYNAMIC relocated address overflow")?;
691 let dyn_file_size = dynamic_ph.p_filesz as usize;
692 let dyn_count = dyn_file_size / core::mem::size_of::<Elf64Dyn>();
693 let mut dyn_buf = alloc::vec![0u8; dyn_file_size];
695 read_user_mapped_bytes(user_as, dyn_addr, &mut dyn_buf)?;
696 let dyn_slice: &[Elf64Dyn] =
697 unsafe { core::slice::from_raw_parts(dyn_buf.as_ptr() as *const Elf64Dyn, dyn_count) };
698
699 let mut rela_addr: Option<u64> = None;
700 let mut rela_size: usize = 0;
701 let mut rela_ent: usize = core::mem::size_of::<Elf64Rela>();
702 let mut jmprel_addr: Option<u64> = None;
703 let mut jmprel_size: usize = 0;
704 let mut pltrel_kind: Option<u64> = None;
705 let mut symtab_addr: Option<u64> = None;
706 let mut sym_ent: usize = core::mem::size_of::<Elf64Sym>();
707 let _strtab_addr: Option<u64> = None;
708 let mut rela_count_hint: Option<usize> = None;
709 let mut relr_addr: Option<u64> = None;
710 let mut relr_size: usize = 0;
711 let mut relr_ent: usize = 0;
712
713 for i in 0..dyn_count {
714 let dyn_entry = &dyn_slice[i];
715
716 match dyn_entry.d_tag {
717 DT_NULL => break,
718 DT_RELA => {
719 rela_addr = Some(
720 dyn_entry
721 .d_val
722 .checked_add(load_bias)
723 .ok_or("DT_RELA relocated address overflow")?,
724 )
725 }
726 DT_RELASZ => rela_size = dyn_entry.d_val as usize,
727 DT_RELAENT => rela_ent = dyn_entry.d_val as usize,
728 DT_RELACOUNT => rela_count_hint = Some(dyn_entry.d_val as usize),
729 DT_JMPREL => {
730 jmprel_addr = Some(
731 dyn_entry
732 .d_val
733 .checked_add(load_bias)
734 .ok_or("DT_JMPREL relocated address overflow")?,
735 )
736 }
737 DT_PLTRELSZ => jmprel_size = dyn_entry.d_val as usize,
738 DT_PLTREL => pltrel_kind = Some(dyn_entry.d_val),
739 DT_SYMTAB => {
740 symtab_addr = Some(
741 dyn_entry
742 .d_val
743 .checked_add(load_bias)
744 .ok_or("DT_SYMTAB relocated address overflow")?,
745 )
746 }
747 DT_SYMENT => sym_ent = dyn_entry.d_val as usize,
748 DT_STRTAB => {
749 let _ = dyn_entry
750 .d_val
751 .checked_add(load_bias)
752 .ok_or("DT_STRTAB relocated address overflow")?;
753 }
754 DT_RELR => {
755 relr_addr = Some(
756 dyn_entry
757 .d_val
758 .checked_add(load_bias)
759 .ok_or("DT_RELR relocated address overflow")?,
760 )
761 }
762 DT_RELRSZ => relr_size = dyn_entry.d_val as usize,
763 DT_RELRENT => relr_ent = dyn_entry.d_val as usize,
764 _ => {}
765 }
766 }
767
768 let mut relr_applied = 0usize;
769 if let Some(relr_base) = relr_addr {
770 relr_applied = apply_relr_relocations(user_as, load_bias, relr_base, relr_size, relr_ent)?;
771 } else if relr_size != 0 || relr_ent != 0 {
772 return Err("DT_RELR metadata present without DT_RELR base");
773 }
774 if rela_ent != core::mem::size_of::<Elf64Rela>() {
775 return Err("Unsupported DT_RELAENT size");
776 }
777 if sym_ent != core::mem::size_of::<Elf64Sym>() {
778 return Err("Unsupported DT_SYMENT size");
779 }
780 if pltrel_kind.is_some() && pltrel_kind != Some(DT_RELA as u64) {
781 return Err("Only DT_PLTREL=DT_RELA is supported");
782 }
783
784 let read_sym_entry = |sym_idx: u32| -> Result<Elf64Sym, &'static str> {
785 let symtab = symtab_addr.ok_or("Missing DT_SYMTAB for symbol relocations")?;
786 let sym_addr = symtab
787 .checked_add((sym_idx as u64) * (sym_ent as u64))
788 .ok_or("Symbol table address overflow")?;
789 let mut raw = [0u8; core::mem::size_of::<Elf64Sym>()];
790 read_user_mapped_bytes(user_as, sym_addr, &mut raw)?;
791 Ok(unsafe { core::ptr::read_unaligned(raw.as_ptr() as *const Elf64Sym) })
792 };
793
794 let resolve_sym =
795 |sym_idx: u32, with_bias: bool, check_def: bool| -> Result<u64, &'static str> {
796 if sym_idx == 0 {
797 return Ok(0);
798 }
799 let sym = read_sym_entry(sym_idx)?;
800 if check_def && sym.st_shndx == 0 {
801 return Err("Undefined symbol relocation not supported");
802 }
803 if with_bias {
804 sym.st_value
805 .checked_add(load_bias)
806 .ok_or("Symbol value relocation overflow")
807 } else {
808 Ok(sym.st_value)
809 }
810 };
811
812 let resolve_size = |sym_idx: u32| -> Result<u64, &'static str> {
813 if sym_idx == 0 {
814 return Ok(0);
815 }
816 let sym = read_sym_entry(sym_idx)?;
817 Ok(sym.st_size)
818 };
819
820 let apply_rela_table = |table_base: u64,
821 table_size: usize,
822 count_hint: Option<usize>|
823 -> Result<usize, &'static str> {
824 if table_size == 0 {
825 return Ok(0);
826 }
827 let count = table_size / rela_ent;
832 if let Some(hint) = count_hint {
833 if hint > count {
834 return Err("DT_RELACOUNT exceeds actual RELA table size");
835 }
836 }
837 let mut applied = 0usize;
838 for i in 0..count {
839 let rela_addr_i = table_base
840 .checked_add((i * rela_ent) as u64)
841 .ok_or("Rela table overflow")?;
842 let mut raw = [0u8; core::mem::size_of::<Elf64Rela>()];
843 read_user_mapped_bytes(user_as, rela_addr_i, &mut raw)?;
844 let rela = unsafe { core::ptr::read_unaligned(raw.as_ptr() as *const Elf64Rela) };
846
847 let r_type = (rela.r_info & 0xffff_ffff) as u32;
848 let r_sym = (rela.r_info >> 32) as u32;
849 let target = rela
850 .r_offset
851 .checked_add(load_bias)
852 .ok_or("Relocation target overflow")?;
853 if target >= USER_ADDR_MAX {
854 return Err("Relocation target outside user space");
855 }
856
857 let value = match r_type {
858 R_X86_64_RELATIVE => {
859 if r_sym != 0 {
860 return Err("R_X86_64_RELATIVE with non-zero symbol");
861 }
862 (load_bias as i128)
863 .checked_add(rela.r_addend as i128)
864 .ok_or("Relocation value overflow")?
865 }
866 R_X86_64_GLOB_DAT | R_X86_64_JUMP_SLOT | R_X86_64_64 => {
867 let sym_val = resolve_sym(r_sym, true, true)? as i128;
868 sym_val
869 .checked_add(rela.r_addend as i128)
870 .ok_or("Relocation value overflow")?
871 }
872 R_X86_64_COPY => {
873 let sym_val = resolve_sym(r_sym, true, true)?;
874 if sym_val == 0 {
875 continue;
876 }
877 let sym_sz = resolve_size(r_sym)?;
878 if sym_sz > 0 && sym_val < USER_ADDR_MAX {
879 let mut tmp = [0u8; 256];
880 let mut off = 0usize;
881 while off < sym_sz as usize {
882 let chunk = core::cmp::min(256, sym_sz as usize - off);
883 read_user_mapped_bytes(
884 user_as,
885 sym_val + off as u64,
886 &mut tmp[..chunk],
887 )?;
888 write_user_mapped_bytes(user_as, target + off as u64, &tmp[..chunk])?;
889 off += chunk;
890 }
891 }
892 applied += 1;
893 continue;
894 }
895 R_X86_64_TPOFF64 => {
896 let sym_val = if r_sym != 0 {
897 resolve_sym(r_sym, false, false)? as i128
898 } else {
899 0i128
900 };
901 sym_val
902 .checked_add(rela.r_addend as i128)
903 .ok_or("TPOFF64 value overflow")?
904 }
905 R_X86_64_IRELATIVE => {
906 let resolver_vaddr = (load_bias as i128)
911 .checked_add(rela.r_addend as i128)
912 .ok_or("IRELATIVE resolver address overflow")?;
913 if resolver_vaddr < 0 || resolver_vaddr as u64 >= USER_ADDR_MAX {
914 return Err("IRELATIVE resolver outside user space");
915 }
916 let resolved = call_ifunc_resolver(user_as, resolver_vaddr as u64)?;
917 resolved as i128
918 }
919 _ => {
920 log::warn!("[elf] Unsupported relocation type {}", r_type);
921 continue;
922 }
923 };
924 if value < 0 || value > u64::MAX as i128 {
925 return Err("Relocation value out of range");
926 }
927 let val_u64 = value as u64;
928 if applied < 5 {
930 let r_addend_copy = rela.r_addend; let mut before = [0u8; 8];
932 let _ = read_user_mapped_bytes(user_as, target, &mut before);
933 let before_val = u64::from_le_bytes(before);
934 crate::e9_println!(
935 "[reloc] [{i}] r_type={} target={:#x} r_addend={:#x} value={:#x} before={:#x}",
936 r_type,
937 target,
938 r_addend_copy,
939 val_u64,
940 before_val
941 );
942 }
943 write_user_mapped_bytes(user_as, target, &val_u64.to_le_bytes())?;
944 if applied < 5 {
946 let mut after = [0u8; 8];
947 let _ = read_user_mapped_bytes(user_as, target, &mut after);
948 let after_val = u64::from_le_bytes(after);
949 crate::e9_println!(
950 "[reloc] [{i}] after_write={:#x} (expected={:#x})",
951 after_val,
952 val_u64
953 );
954 }
955 if val_u64 >= 0xffff_8000_0000_0000 {
957 let r_addend_copy = rela.r_addend;
958 crate::e9_println!(
959 "[reloc-KERNEL-ADDR] [{i}] r_type={} target={:#x} r_addend={:#x} val={:#x} bias={:#x}",
960 r_type, target, r_addend_copy, val_u64, load_bias
961 );
962 }
963 applied += 1;
964 }
965 Ok(applied)
966 };
967
968 let mut total_applied = 0usize;
969 crate::e9_println!(
970 "[reloc] apply_dynamic_relocations: bias={:#x} rela_addr={:?} rela_size={} rela_count={:?}",
971 load_bias,
972 rela_addr,
973 rela_size,
974 rela_count_hint
975 );
976 if let Some(rela_base) = rela_addr {
977 total_applied += apply_rela_table(rela_base, rela_size, rela_count_hint)?;
978 }
979 if let Some(jmprel_base) = jmprel_addr {
980 total_applied += apply_rela_table(jmprel_base, jmprel_size, None)?;
981 }
982
983 if total_applied > 0 {
984 crate::e9_println!(
985 "[reloc] applied {} RELA relocations (bias={:#x})",
986 total_applied,
987 load_bias
988 );
989 }
990 if relr_applied > 0 {
991 log::debug!("[elf] Applied {} RELR relocations", relr_applied);
992 }
993 Ok(())
994}
995
996fn elf_flags_to_vma(p_flags: u32) -> VmaFlags {
1002 VmaFlags {
1003 readable: p_flags & PF_R != 0,
1004 writable: p_flags & PF_W != 0,
1005 executable: p_flags & PF_X != 0,
1006 user_accessible: true,
1007 }
1008}
1009
1010fn load_segment(
1016 user_as: &AddressSpace,
1017 elf_data: &[u8],
1018 phdr: &Elf64Phdr,
1019 load_bias: u64,
1020) -> Result<(), &'static str> {
1021 let vaddr = phdr
1022 .p_vaddr
1023 .checked_add(load_bias)
1024 .ok_or("PT_LOAD relocated vaddr overflow")?;
1025 let memsz = phdr.p_memsz;
1026 let filesz = phdr.p_filesz;
1027 let offset = phdr.p_offset;
1028
1029 if vaddr >= USER_ADDR_MAX {
1031 return Err("PT_LOAD vaddr outside user space");
1032 }
1033 let end = vaddr
1034 .checked_add(memsz)
1035 .ok_or("PT_LOAD vaddr+memsz overflows")?;
1036 if end > USER_ADDR_MAX {
1037 return Err("PT_LOAD segment extends past user space");
1038 }
1039
1040 let file_end = (offset as usize)
1042 .checked_add(filesz as usize)
1043 .ok_or("PT_LOAD offset+filesz overflows")?;
1044 if file_end > elf_data.len() {
1045 return Err("PT_LOAD file data extends past ELF");
1046 }
1047
1048 let page_start = vaddr & !0xFFF;
1050 let page_end = (end + 0xFFF) & !0xFFF;
1051 let page_count = ((page_end - page_start) / 4096) as usize;
1052
1053 let actual_flags = elf_flags_to_vma(phdr.p_flags);
1055 let load_flags = VmaFlags {
1056 readable: true,
1057 writable: true, executable: actual_flags.executable,
1059 user_accessible: true,
1060 };
1061
1062 let vma_type = if actual_flags.executable {
1063 VmaType::Code
1064 } else {
1065 VmaType::Anonymous
1066 };
1067 log::debug!(
1068 "[elf] map PT_LOAD: start={:#x} pages={} filesz={:#x}",
1069 page_start,
1070 page_count,
1071 filesz
1072 );
1073 user_as.map_region(
1074 page_start,
1075 page_count,
1076 load_flags,
1077 vma_type,
1078 VmaPageSize::Small,
1079 )?;
1080
1081 if filesz > 0 {
1084 let src = &elf_data[offset as usize..file_end];
1085 let mut copied = 0usize;
1086
1087 let n_vaddrs = ((page_end - page_start) / 4096) as usize;
1089 let mut phys_pages = alloc::vec::Vec::with_capacity(n_vaddrs);
1090 for i in 0..n_vaddrs {
1091 let vaddr = page_start + (i as u64) * 4096;
1092 let phys = user_as
1093 .translate(VirtAddr::new(vaddr))
1094 .ok_or("Failed to translate user page after mapping")?;
1095 phys_pages.push(phys);
1096 }
1097
1098 while copied < src.len() {
1099 let dst_vaddr = vaddr + copied as u64;
1100 let page_idx = ((dst_vaddr - page_start) / 4096) as usize;
1101 let page_offset = (dst_vaddr & 0xFFF) as usize;
1102 let chunk = core::cmp::min(src.len() - copied, 4096 - page_offset);
1103
1104 let phys = phys_pages[page_idx];
1105 let hhdm_ptr = crate::memory::phys_to_virt(phys.as_u64()) as *mut u8;
1106 unsafe {
1108 core::ptr::copy_nonoverlapping(
1109 src.as_ptr().add(copied),
1110 hhdm_ptr.add(page_offset),
1111 chunk,
1112 );
1113 }
1114 copied += chunk;
1115 }
1116 }
1117
1118 apply_segment_permissions(user_as, page_start, page_count, actual_flags)?;
1120
1121 log::debug!(
1122 " PT_LOAD: {:#x}..{:#x} ({} pages, file {:#x}+{:#x}, flags {:?})",
1123 page_start,
1124 page_end,
1125 page_count,
1126 offset,
1127 filesz,
1128 actual_flags,
1129 );
1130
1131 Ok(())
1132}
1133
1134extern "C" fn elf_ring3_trampoline() -> ! {
1146 use crate::arch::x86_64::gdt;
1147 use core::sync::atomic::Ordering;
1148
1149 crate::e9_println!("[trace][elf] ring3_trampoline before current_task");
1150 let task = crate::process::scheduler::current_task_clone_spin_debug("ring3_trampoline")
1151 .expect("elf_ring3_trampoline: no current task");
1152 crate::e9_println!(
1153 "[trace][elf] ring3_trampoline enter tid={} name={}",
1154 task.id.as_u64(),
1155 task.name
1156 );
1157 crate::serial_println!(
1158 "[trace][elf] ring3_trampoline enter tid={} name={}",
1159 task.id.as_u64(),
1160 task.name
1161 );
1162 task.set_resume_kind(crate::process::task::ResumeKind::IretFrame);
1163
1164 let user_rip = task.trampoline_entry.load(Ordering::Acquire);
1165 let user_rsp = task.trampoline_stack_top.load(Ordering::Acquire);
1166 let user_arg0 = task.trampoline_arg0.load(Ordering::Acquire);
1167 crate::e9_println!(
1168 "[trace][elf] ring3_trampoline args tid={} rip={:#x} rsp={:#x} arg0={:#x}",
1169 task.id.as_u64(),
1170 user_rip,
1171 user_rsp,
1172 user_arg0
1173 );
1174 crate::serial_println!(
1175 "[trace][elf] ring3_trampoline args tid={} rip={:#x} rsp={:#x}",
1176 task.id.as_u64(),
1177 user_rip,
1178 user_rsp
1179 );
1180
1181 {
1186 unsafe {
1188 let as_ref = task.process.address_space_arc();
1189 let task_name: &str = &task.name;
1190 for test_off in [0x12920u64, 0x12928u64, 0x12930u64] {
1191 let vaddr = 0x100000000u64.wrapping_add(test_off);
1192 if let Some(phys) = as_ref.translate(VirtAddr::new(vaddr)) {
1193 let ptr = crate::memory::phys_to_virt(phys.as_u64()) as *const u64;
1194 let val = core::ptr::read_unaligned(ptr);
1195 crate::e9_println!(
1196 "[trampoline-got] tid={} name={} GOT[{:#x}]=phys={:#x} val={:#x}",
1197 task.id.as_u64(),
1198 task_name,
1199 vaddr,
1200 phys.as_u64(),
1201 val
1202 );
1203 } else {
1204 crate::e9_println!(
1205 "[trampoline-got] tid={} name={} GOT[{:#x}]=<not mapped>",
1206 task.id.as_u64(),
1207 task_name,
1208 vaddr
1209 );
1210 }
1211 }
1212 }
1213 }
1214
1215 unsafe {
1218 let as_ref = task.process.address_space_arc();
1219 as_ref.switch_to();
1220 }
1221 crate::e9_println!(
1222 "[trace][elf] ring3_trampoline switch_to done tid={}",
1223 task.id.as_u64()
1224 );
1225 crate::serial_println!(
1226 "[trace][elf] ring3_trampoline switch_to done tid={}",
1227 task.id.as_u64()
1228 );
1229
1230 let user_cs = gdt::user_code_selector().0 as u64;
1231 let user_ss = gdt::user_data_selector().0 as u64;
1232 let user_rflags: u64 = 0x202; crate::e9_println!(
1234 "[trace][elf] ring3_trampoline iret tid={} cs={:#x} ss={:#x} rflags={:#x}",
1235 task.id.as_u64(),
1236 user_cs,
1237 user_ss,
1238 user_rflags
1239 );
1240 crate::serial_println!(
1241 "[trace][elf] ring3_trampoline iret tid={} rip={:#x} rsp={:#x}",
1242 task.id.as_u64(),
1243 user_rip,
1244 user_rsp
1245 );
1246
1247 unsafe {
1251 let lvt = crate::arch::x86_64::apic::read_reg(crate::arch::x86_64::apic::REG_LVT_TIMER);
1252 let init_cnt =
1253 crate::arch::x86_64::apic::read_reg(crate::arch::x86_64::apic::REG_TIMER_INIT);
1254 let cur_cnt =
1255 crate::arch::x86_64::apic::read_reg(crate::arch::x86_64::apic::REG_TIMER_CURRENT);
1256 let rflags_now: u64;
1257 core::arch::asm!("pushfq; pop {}", out(reg) rflags_now, options(nostack));
1258 crate::e9_println!(
1259 "[trace][elf] pre-iret LAPIC: LVT={:#x} init={} cur={} IF={}",
1260 lvt,
1261 init_cnt,
1262 cur_cnt,
1263 (rflags_now >> 9) & 1
1264 );
1265 if lvt & (1 << 16) != 0 {
1266 crate::e9_println!(
1267 "[trace][elf] WARNING: LAPIC timer is MASKED (bit 16 set) : no ticks will fire!"
1268 );
1269 }
1270 if init_cnt == 0 {
1271 crate::e9_println!(
1272 "[trace][elf] WARNING: LAPIC timer init_count=0 : timer not started!"
1273 );
1274 }
1275 }
1276
1277 crate::arch::x86_64::ring3_diag::validate_ring3_state(
1278 user_rip,
1279 user_rsp,
1280 user_cs as u16,
1281 user_ss as u16,
1282 );
1283
1284 crate::e9_println!(
1285 "[elf] PRE-IRETQ tid={} rip={:#x} rsp={:#x} rflags={:#x}",
1286 task.id.as_u64(),
1287 user_rip,
1288 user_rsp,
1289 user_rflags
1290 );
1291
1292 crate::e9_println!(
1296 "E9[0] pre-asm rip={:#x} rsp={:#x} cs={:#x} ss={:#x}",
1297 user_rip,
1298 user_rsp,
1299 user_cs,
1300 user_ss,
1301 );
1302
1303 unsafe {
1313 core::arch::asm!(
1314 "cli",
1318
1319 "push rax",
1323 "mov al, 0x31", "out 0xe9, al",
1325 "pop rax",
1326
1327 "push {ss}",
1335 "push {rsp_val}",
1336 "push {rflags}",
1337 "push {cs}",
1338 "push {rip}",
1339
1340 "push rax",
1342 "mov al, 0x32", "out 0xe9, al",
1344 "pop rax",
1345
1346 "mov rax, {rip}",
1352 "movzx rax, byte ptr [rax]",
1353
1354 "mov rdi, {arg0}",
1356
1357 "push rax",
1359 "mov al, 0x33", "out 0xe9, al",
1361 "pop rax",
1362
1363 "swapgs",
1367
1368 "push rax",
1372 "mov al, 0x34", "out 0xe9, al",
1374 "pop rax",
1375
1376 "iretq",
1378
1379 ss = in(reg) user_ss,
1380 rsp_val = in(reg) user_rsp,
1381 rflags = in(reg) user_rflags,
1382 cs = in(reg) user_cs,
1383 rip = in(reg) user_rip,
1384 arg0 = in(reg) user_arg0,
1385 options(noreturn),
1386 );
1387 }
1388}
1389
1390pub fn load_and_run_elf(elf_data: &[u8], name: &'static str) -> Result<TaskId, &'static str> {
1402 load_and_run_elf_with_caps(elf_data, name, &[])
1403}
1404
1405pub fn load_and_run_elf_with_args(
1409 elf_data: &[u8],
1410 name: &'static str,
1411 extra_args: &[&str],
1412) -> Result<TaskId, &'static str> {
1413 let task = load_elf_task_inner(elf_data, name, extra_args, &[])?;
1414 let task_id = task.id;
1415 crate::process::add_task(task);
1416 Ok(task_id)
1417}
1418
1419pub fn load_elf_task_with_caps(
1421 elf_data: &[u8],
1422 name: &'static str,
1423 seed_caps: &[Capability],
1424) -> Result<Arc<Task>, &'static str> {
1425 load_elf_task_inner(elf_data, name, &[], seed_caps)
1426}
1427
1428pub fn load_and_run_elf_with_caps(
1430 elf_data: &[u8],
1431 name: &'static str,
1432 seed_caps: &[Capability],
1433) -> Result<TaskId, &'static str> {
1434 crate::e9_println!(
1435 "[trace][elf] load_and_run_elf enter name={} size={}",
1436 name,
1437 elf_data.len()
1438 );
1439 let task = load_elf_task_inner(elf_data, name, &[], seed_caps)?;
1440 let task_id = task.id;
1441 let runtime_entry = task
1442 .trampoline_entry
1443 .load(core::sync::atomic::Ordering::Acquire);
1444 crate::e9_println!(
1445 "[trace][elf] load_and_run_elf add_task begin tid={} entry={:#x}",
1446 task_id.as_u64(),
1447 runtime_entry
1448 );
1449 crate::process::add_task(task);
1450 crate::e9_println!(
1451 "[trace][elf] load_and_run_elf add_task done tid={}",
1452 task_id.as_u64()
1453 );
1454
1455 log::info!(
1456 "[elf] Task '{}' created: entry={:#x}, stack_top={:#x}",
1457 name,
1458 runtime_entry,
1459 USER_STACK_TOP,
1460 );
1461
1462 Ok(task_id)
1463}
1464
1465const AT_PHDR: u64 = 3;
1466const AT_PHENT: u64 = 4;
1467const AT_PHNUM: u64 = 5;
1468const AT_PAGESZ: u64 = 6;
1469const AT_BASE: u64 = 7;
1470const AT_ENTRY: u64 = 9;
1471const AT_RANDOM: u64 = 25;
1472
1473fn generate_aux_random_seed() -> [u8; 16] {
1474 let mut seed = [0u8; 16];
1475 crate::entropy::fill_random(&mut seed);
1476 seed
1477}
1478
1479fn push_auxv(user_as: &AddressSpace, sp: &mut u64, tag: u64, val: u64) -> Result<(), &'static str> {
1481 *sp -= 8;
1482 write_user_u64(user_as, *sp, val)?;
1483 *sp -= 8;
1484 write_user_u64(user_as, *sp, tag)?;
1485 Ok(())
1486}
1487
1488fn setup_boot_user_stack(
1501 user_as: &AddressSpace,
1502 name: &str,
1503 extra_args: &[&str],
1504 phdr_vaddr: u64,
1505 phent: u16,
1506 phnum: u16,
1507 program_entry: u64,
1508 interp_base: Option<u64>,
1509) -> Result<u64, &'static str> {
1510 let mut sp = USER_STACK_TOP;
1511
1512 let name_nul_len = (name.len() + 1) as u64;
1514 sp -= name_nul_len;
1515 let argv0_ptr = sp;
1516 write_user_mapped_bytes(user_as, sp, name.as_bytes())?;
1517 write_user_mapped_bytes(user_as, sp + name.len() as u64, &[0])?;
1518
1519 let mut extra_ptrs: alloc::vec::Vec<u64> = alloc::vec::Vec::with_capacity(extra_args.len());
1521 for &arg in extra_args.iter() {
1522 let arg_nul_len = (arg.len() + 1) as u64;
1523 sp -= arg_nul_len;
1524 extra_ptrs.push(sp);
1525 write_user_mapped_bytes(user_as, sp, arg.as_bytes())?;
1526 write_user_mapped_bytes(user_as, sp + arg.len() as u64, &[0])?;
1527 }
1528
1529 sp -= 16;
1530 let random_ptr = sp;
1531 let random_seed = generate_aux_random_seed();
1532 write_user_mapped_bytes(user_as, sp, &random_seed)?;
1533
1534 sp &= !0xF;
1535 let auxv_pairs = if interp_base.is_some() { 8u64 } else { 7u64 };
1536 let stack_words = 4u64 + extra_args.len() as u64 + auxv_pairs * 2;
1538 let align_pad = (0u64.wrapping_sub(stack_words * 8)) & 0xF;
1539 sp -= align_pad;
1540
1541 push_auxv(user_as, &mut sp, 0, 0)?; push_auxv(user_as, &mut sp, AT_RANDOM, random_ptr)?;
1544 push_auxv(user_as, &mut sp, AT_ENTRY, program_entry)?;
1545 if let Some(base) = interp_base {
1546 push_auxv(user_as, &mut sp, AT_BASE, base)?;
1547 }
1548 push_auxv(user_as, &mut sp, AT_PAGESZ, 4096)?;
1549 push_auxv(user_as, &mut sp, AT_PHNUM, phnum as u64)?;
1550 push_auxv(user_as, &mut sp, AT_PHENT, phent as u64)?;
1551 push_auxv(user_as, &mut sp, AT_PHDR, phdr_vaddr)?;
1552
1553 sp -= 8;
1555 write_user_u64(user_as, sp, 0)?;
1556
1557 sp -= 8;
1559 write_user_u64(user_as, sp, 0)?;
1560
1561 for &ptr in extra_ptrs.iter().rev() {
1563 sp -= 8;
1564 write_user_u64(user_as, sp, ptr)?;
1565 }
1566
1567 sp -= 8;
1569 write_user_u64(user_as, sp, argv0_ptr)?;
1570
1571 sp -= 8;
1573 write_user_u64(user_as, sp, 1u64 + extra_args.len() as u64)?;
1574
1575 debug_assert_eq!(sp & 0xF, 0);
1576 Ok(sp)
1577}
1578
1579fn load_elf_task_inner(
1582 elf_data: &[u8],
1583 name: &'static str,
1584 extra_args: &[&str],
1585 seed_caps: &[Capability],
1586) -> Result<Arc<Task>, &'static str> {
1587 crate::e9_println!(
1588 "[trace][elf] load_elf_task enter name={} size={}",
1589 name,
1590 elf_data.len()
1591 );
1592 log::info!("[elf] Loading ELF '{}'...", name);
1593
1594 crate::e9_println!("[trace][elf] load_elf_task parse_header begin");
1596 let header = parse_header(elf_data)?;
1597 crate::e9_println!(
1598 "[trace][elf] load_elf_task parse_header ok type={}",
1599 if header.e_type == ET_DYN {
1600 "ET_DYN"
1601 } else {
1602 "ET_EXEC"
1603 }
1604 );
1605 crate::e9_println!("[trace][elf] load_elf_task user_as begin");
1607 let user_as = Arc::new(AddressSpace::new_user()?);
1608 crate::e9_println!("[trace][elf] load_elf_task user_as done");
1609
1610 let phdrs: Vec<Elf64Phdr> = program_headers(elf_data, &header).collect();
1611 let interp_path = parse_interp_path(elf_data, &phdrs)?;
1612 let (load_bias, entry) = compute_load_bias_and_entry(&user_as, &header, &phdrs)?;
1613 let phdr_vaddr = find_relocated_phdr_vaddr(&header, &phdrs, load_bias)?;
1614
1615 let phnum = header.e_phnum;
1616 crate::e9_println!(
1617 "[trace][elf] load_elf_task layout entry={:#x} bias={:#x} phdrs={}",
1618 entry,
1619 load_bias,
1620 phnum
1621 );
1622 log::info!(
1623 "[elf] ELF '{}': type={}, entry={:#x}, bias={:#x}, {} program headers",
1624 name,
1625 if header.e_type == ET_DYN {
1626 "ET_DYN"
1627 } else {
1628 "ET_EXEC"
1629 },
1630 entry,
1631 load_bias,
1632 phnum,
1633 );
1634
1635 let mut load_count = 0u32;
1637 for phdr in phdrs.iter() {
1638 if phdr.p_type == PT_LOAD && phdr.p_memsz != 0 {
1639 load_segment(&user_as, elf_data, phdr, load_bias)?;
1640 load_count += 1;
1641 }
1642 }
1643 if interp_path.is_none() {
1644 apply_dynamic_relocations(&user_as, &phdrs, header.e_type, load_bias)?;
1645 }
1646
1647 crate::e9_println!(
1648 "[trace][elf] load_elf_task segments_done count={} has_interp={}",
1649 load_count,
1650 interp_path.is_some()
1651 );
1652 log::info!("[elf] Loaded {} PT_LOAD segment(s)", load_count);
1653
1654 let mut runtime_entry = entry;
1655 let mut interp_base: Option<u64> = None;
1656 if let Some(path) = interp_path {
1657 let interp_data = read_elf_from_vfs(path)?;
1658 let interp_header = parse_header(&interp_data)?;
1659 let interp_phdrs: Vec<Elf64Phdr> = program_headers(&interp_data, &interp_header).collect();
1660 if parse_interp_path(&interp_data, &interp_phdrs)?.is_some() {
1661 return Err("Nested PT_INTERP is not supported");
1662 }
1663 let (interp_bias, interp_entry) =
1664 compute_load_bias_and_entry(&user_as, &interp_header, &interp_phdrs)?;
1665 let (interp_min_vaddr, _) = compute_load_bounds(&interp_phdrs)?;
1666 let mut interp_load_count = 0u32;
1667 for phdr in interp_phdrs.iter() {
1668 if phdr.p_type == PT_LOAD && phdr.p_memsz != 0 {
1669 load_segment(&user_as, &interp_data, phdr, interp_bias)?;
1670 interp_load_count += 1;
1671 }
1672 }
1673 apply_dynamic_relocations(&user_as, &interp_phdrs, interp_header.e_type, interp_bias)?;
1674 runtime_entry = interp_entry;
1675 interp_base = Some(interp_min_vaddr.saturating_add(interp_bias));
1676 log::info!(
1677 "[elf] PT_INTERP '{}' loaded: {} PT_LOAD, entry={:#x}",
1678 path,
1679 interp_load_count,
1680 runtime_entry
1681 );
1682 }
1683
1684 let mut user_fs_base_val = 0u64;
1686 if let Some(tls) = phdrs.iter().find(|p| p.p_type == PT_TLS) {
1687 let tls_memsz = tls.p_memsz;
1688 let tls_filesz = tls.p_filesz;
1689 let tls_align = core::cmp::max(tls.p_align, 8).next_power_of_two();
1690 let aligned_memsz = (tls_memsz + tls_align - 1) & !(tls_align - 1);
1691 let total_size = aligned_memsz + 8;
1692 let n_tls_pages = ((total_size + 4095) / 4096) as usize;
1693 let tls_flags = VmaFlags {
1694 readable: true,
1695 writable: true,
1696 executable: false,
1697 user_accessible: true,
1698 };
1699 let tls_base = user_as
1700 .find_free_vma_range(0x7FFF_E000_0000, n_tls_pages, VmaPageSize::Small)
1701 .ok_or("No space for TLS block")?;
1702 user_as.map_region(
1703 tls_base,
1704 n_tls_pages,
1705 tls_flags,
1706 VmaType::Anonymous,
1707 VmaPageSize::Small,
1708 )?;
1709 if tls_filesz > 0 {
1710 let src_off = tls.p_offset as usize;
1711 let src_end = src_off
1712 .checked_add(tls_filesz as usize)
1713 .ok_or("PT_TLS offset+filesz overflows")?;
1714 if src_end > elf_data.len() {
1715 return Err("PT_TLS file data extends past ELF");
1716 }
1717 write_user_mapped_bytes(&user_as, tls_base, &elf_data[src_off..src_end])?;
1718 }
1719 let tp = tls_base + aligned_memsz;
1720 write_user_u64(&user_as, tp, tp)?;
1721 user_fs_base_val = tp;
1722 }
1723
1724 let stack_flags = VmaFlags {
1726 readable: true,
1727 writable: true,
1728 executable: false,
1729 user_accessible: true,
1730 };
1731 user_as.map_region(
1732 USER_STACK_BASE,
1733 USER_STACK_PAGES,
1734 stack_flags,
1735 VmaType::Stack,
1736 VmaPageSize::Small,
1737 )?;
1738 log::debug!(
1742 "[elf] User stack: {:#x}..{:#x} ({} pages), guard at {:#x}",
1743 USER_STACK_BASE,
1744 USER_STACK_TOP,
1745 USER_STACK_PAGES,
1746 USER_STACK_GUARD,
1747 );
1748
1749 let boot_sp = setup_boot_user_stack(
1750 &user_as,
1751 name,
1752 extra_args,
1753 phdr_vaddr,
1754 header.e_phentsize,
1755 header.e_phnum,
1756 entry,
1757 interp_base,
1758 )?;
1759
1760 crate::e9_println!(
1763 "[trace][elf] load_elf_task kstack_begin size={}",
1764 Task::DEFAULT_STACK_SIZE
1765 );
1766 let kernel_stack = KernelStack::allocate(Task::DEFAULT_STACK_SIZE)?;
1767 crate::e9_println!(
1768 "[trace][elf] load_elf_task kstack_done virt={:#x} top={:#x}",
1769 kernel_stack.virt_base.as_u64(),
1770 kernel_stack.virt_base.as_u64() + kernel_stack.size as u64
1771 );
1772 let context = CpuContext::new(elf_ring3_trampoline as *const () as u64, &kernel_stack);
1773 let (pid, tid, tgid) = Task::allocate_process_ids();
1774 let fpu_state = crate::process::task::ExtendedState::new();
1775 let xcr0_mask = fpu_state.xcr0_mask;
1776
1777 let task = Arc::new(Task {
1778 id: TaskId::new(),
1779 pid,
1780 tid,
1781 tgid,
1782 pgid: core::sync::atomic::AtomicU32::new(pid),
1783 sid: core::sync::atomic::AtomicU32::new(pid),
1784 uid: core::sync::atomic::AtomicU32::new(0),
1785 euid: core::sync::atomic::AtomicU32::new(0),
1786 gid: core::sync::atomic::AtomicU32::new(0),
1787 egid: core::sync::atomic::AtomicU32::new(0),
1788 state: core::sync::atomic::AtomicU8::new(TaskState::Ready as u8),
1789 priority: TaskPriority::Normal,
1790 context: SyncUnsafeCell::new(context),
1791 resume_kind: SyncUnsafeCell::new(ResumeKind::RetFrame),
1792 interrupt_rsp: core::sync::atomic::AtomicU64::new(0),
1793 kernel_stack,
1794 user_stack: None,
1795 name,
1796 process: Arc::new(crate::process::process::Process::new(pid, user_as)),
1797 pending_signals: super::signal::SignalSet::new(),
1798 blocked_signals: super::signal::SignalSet::new(),
1799 irq_signal_delivery_blocked: core::sync::atomic::AtomicBool::new(false),
1800 signal_stack: SyncUnsafeCell::new(None),
1801 itimers: super::timer::ITimers::new(),
1802 wake_pending: core::sync::atomic::AtomicBool::new(false),
1803 wake_deadline_ns: core::sync::atomic::AtomicU64::new(0),
1804 trampoline_entry: core::sync::atomic::AtomicU64::new(runtime_entry),
1805 trampoline_stack_top: core::sync::atomic::AtomicU64::new(boot_sp),
1806 trampoline_arg0: core::sync::atomic::AtomicU64::new(0),
1807 ticks: core::sync::atomic::AtomicU64::new(0),
1808 sched_policy: crate::process::task::SyncUnsafeCell::new(Task::default_sched_policy(
1809 TaskPriority::Normal,
1810 )),
1811 home_cpu: core::sync::atomic::AtomicUsize::new(usize::MAX),
1812 vruntime: core::sync::atomic::AtomicU64::new(0),
1813 fair_rq_generation: core::sync::atomic::AtomicU64::new(0),
1814 fair_on_rq: core::sync::atomic::AtomicBool::new(false),
1815 clear_child_tid: core::sync::atomic::AtomicU64::new(0),
1816 robust_list_head: core::sync::atomic::AtomicU64::new(0),
1817 robust_list_len: core::sync::atomic::AtomicUsize::new(0),
1818 user_fs_base: core::sync::atomic::AtomicU64::new(user_fs_base_val),
1819 fpu_state: crate::process::task::SyncUnsafeCell::new(fpu_state),
1820 xcr0_mask: core::sync::atomic::AtomicU64::new(xcr0_mask),
1821 rt_link: intrusive_collections::LinkedListLink::new(),
1822 });
1823
1824 crate::e9_println!(
1825 "[trace][elf] load_elf_task task_built tid={} pid={} entry={:#x} sp={:#x}",
1826 task.id.as_u64(),
1827 task.pid,
1828 runtime_entry,
1829 boot_sp
1830 );
1831 let mut bootstrap_handle: Option<u64> = None;
1833 if !seed_caps.is_empty() {
1834 let caps = unsafe { &mut *task.process.capabilities.get() };
1835 for cap in seed_caps {
1836 let id = caps.insert(cap.clone());
1837 if bootstrap_handle.is_none()
1838 && cap.resource_type == crate::capability::ResourceType::Volume
1839 {
1840 bootstrap_handle = Some(id.as_u64());
1841 }
1842 }
1843 }
1844
1845 {
1848 let fd_table = unsafe { &mut *task.process.fd_table.get() };
1849 crate::vfs::console_scheme::setup_stdio(fd_table);
1850 }
1851
1852 if let Some(h) = bootstrap_handle {
1853 task.trampoline_arg0
1855 .store(h, core::sync::atomic::Ordering::Release);
1856 }
1857
1858 task.seed_interrupt_frame(crate::syscall::SyscallFrame {
1859 r15: 0,
1860 r14: 0,
1861 r13: 0,
1862 r12: 0,
1863 rbp: 0,
1864 rbx: 0,
1865 r11: 0x202,
1866 r10: 0,
1867 r9: 0,
1868 r8: 0,
1869 rsi: 0,
1870 rdi: task
1871 .trampoline_arg0
1872 .load(core::sync::atomic::Ordering::Acquire),
1873 rdx: 0,
1874 rcx: runtime_entry,
1875 rax: 0,
1876 iret_rip: runtime_entry,
1877 iret_cs: crate::arch::x86_64::gdt::user_code_selector().0 as u64,
1878 iret_rflags: 0x202,
1879 iret_rsp: boot_sp,
1880 iret_ss: crate::arch::x86_64::gdt::user_data_selector().0 as u64,
1881 });
1882
1883 {
1884 let arc_data_ptr = alloc::sync::Arc::as_ptr(&task) as usize;
1885 let fpu_ptr = task.fpu_state.get() as usize;
1886 if let Some(cur) = crate::process::scheduler::current_task_clone() {
1887 let cur_data_ptr = alloc::sync::Arc::as_ptr(&cur) as usize;
1888 let cur_strong = alloc::sync::Arc::strong_count(&cur);
1889 log::info!(
1890 "[elf] Task '{}' prepared: entry={:#x}, stack_top={:#x} \
1891 new_arc={:#x} new_fpu={:#x} cur_arc={:#x} cur_strong={}",
1892 name,
1893 runtime_entry,
1894 boot_sp,
1895 arc_data_ptr,
1896 fpu_ptr,
1897 cur_data_ptr,
1898 cur_strong,
1899 );
1900 } else {
1901 log::info!(
1902 "[elf] Task '{}' prepared: entry={:#x}, stack_top={:#x} \
1903 new_arc={:#x} new_fpu={:#x} (no current task)",
1904 name,
1905 runtime_entry,
1906 boot_sp,
1907 arc_data_ptr,
1908 fpu_ptr,
1909 );
1910 }
1911 }
1912
1913 Ok(task)
1914}
1915
1916pub fn load_elf_image(
1919 elf_data: &[u8],
1920 user_as: &AddressSpace,
1921) -> Result<LoadedElfInfo, &'static str> {
1922 let header = parse_header(elf_data)?;
1923 let phdrs: Vec<Elf64Phdr> = program_headers(elf_data, &header).collect();
1924 let interp_path = parse_interp_path(elf_data, &phdrs)?;
1925 let (load_bias, entry) = compute_load_bias_and_entry(user_as, &header, &phdrs)?;
1926 let phdr_vaddr = find_relocated_phdr_vaddr(&header, &phdrs, load_bias)?;
1927
1928 for phdr in phdrs.iter() {
1929 if phdr.p_type == PT_LOAD && phdr.p_memsz != 0 {
1930 load_segment(user_as, elf_data, phdr, load_bias)?;
1931 }
1932 }
1933 if interp_path.is_none() {
1934 apply_dynamic_relocations(user_as, &phdrs, header.e_type, load_bias)?;
1935 }
1936
1937 let (tls_vaddr, tls_filesz, tls_memsz, tls_align) =
1938 if let Some(tls) = phdrs.iter().find(|ph| ph.p_type == PT_TLS) {
1939 let align = core::cmp::max(tls.p_align, 1).next_power_of_two();
1940 (
1941 tls.p_vaddr.saturating_add(load_bias),
1942 tls.p_filesz,
1943 tls.p_memsz,
1944 align,
1945 )
1946 } else {
1947 (0, 0, 0, 1)
1948 };
1949
1950 let mut runtime_entry = entry;
1951 let mut interp_base = None;
1952 if let Some(path) = interp_path {
1953 let interp_data = read_elf_from_vfs(path)?;
1954 let interp_header = parse_header(&interp_data)?;
1955 let interp_phdrs: Vec<Elf64Phdr> = program_headers(&interp_data, &interp_header).collect();
1956 if parse_interp_path(&interp_data, &interp_phdrs)?.is_some() {
1957 return Err("Nested PT_INTERP is not supported");
1958 }
1959 let (interp_bias, interp_entry) =
1960 compute_load_bias_and_entry(user_as, &interp_header, &interp_phdrs)?;
1961 let (interp_min_vaddr, _) = compute_load_bounds(&interp_phdrs)?;
1962 for phdr in interp_phdrs.iter() {
1963 if phdr.p_type == PT_LOAD && phdr.p_memsz != 0 {
1964 load_segment(user_as, &interp_data, phdr, interp_bias)?;
1965 }
1966 }
1967 apply_dynamic_relocations(user_as, &interp_phdrs, interp_header.e_type, interp_bias)?;
1968 runtime_entry = interp_entry;
1969 interp_base = Some(interp_min_vaddr.saturating_add(interp_bias));
1970 }
1971
1972 Ok(LoadedElfInfo {
1973 runtime_entry,
1974 program_entry: entry,
1975 phdr_vaddr,
1976 phent: header.e_phentsize,
1977 phnum: header.e_phnum,
1978 interp_base,
1979 tls_vaddr,
1980 tls_filesz,
1981 tls_memsz,
1982 tls_align,
1983 })
1984}
1985
1986pub fn read_user_mapped_bytes_pub(
1988 user_as: &AddressSpace,
1989 vaddr: u64,
1990 out: &mut [u8],
1991) -> Result<(), &'static str> {
1992 read_user_mapped_bytes(user_as, vaddr, out)
1993}
1994
1995pub fn write_user_mapped_bytes_pub(
1997 user_as: &AddressSpace,
1998 vaddr: u64,
1999 src: &[u8],
2000) -> Result<(), &'static str> {
2001 write_user_mapped_bytes(user_as, vaddr, src)
2002}
2003
2004pub fn write_user_u64_pub(
2006 user_as: &AddressSpace,
2007 vaddr: u64,
2008 value: u64,
2009) -> Result<(), &'static str> {
2010 write_user_u64(user_as, vaddr, value)
2011}