From dac3e94bea3abc3aa3793ad22fdff6de990fe7e6 Mon Sep 17 00:00:00 2001 From: wyt8 <2253457010@qq.com> Date: Mon, 12 Jan 2026 02:14:02 +0000 Subject: [PATCH] Consider the PIE condition when loading the ELF --- .../process/program_loader/elf/load_elf.rs | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/kernel/src/process/program_loader/elf/load_elf.rs b/kernel/src/process/program_loader/elf/load_elf.rs index 9d8865fda..f0750f0c6 100644 --- a/kernel/src/process/program_loader/elf/load_elf.rs +++ b/kernel/src/process/program_loader/elf/load_elf.rs @@ -19,6 +19,7 @@ use crate::{ process_vm::{AuxKey, AuxVec}, program_loader::check_executable_inode, }, + util::random::getrandom, vm::{ perms::VmPerms, vmar::{VMAR_CAP_ADDR, VMAR_LOWEST_ADDR, Vmar}, @@ -26,6 +27,18 @@ use crate::{ }, }; +/// The base address for PIE (ET_DYN with INTERP) loading. +/// +/// Linux calls this `ELF_ET_DYN_BASE`. It has some intentions: +/// - The base load address for PIE programs (ET_DYN with INTERP). +/// - The heap start address for static PIE programs (ET_DYN without INTERP). +/// +/// References: +/// - x86_64: ELF_ET_DYN_BASE = DEFAULT_MAP_WINDOW / 3 * 2 +/// - riscv64: ELF_ET_DYN_BASE = (DEFAULT_MAP_WINDOW / 3) * 2 +/// - loongarch64: ELF_ET_DYN_BASE = TASK_SIZE / 3 * 2 +const PIE_BASE_ADDR: Vaddr = VMAR_CAP_ADDR / 3 * 2; + pub struct ElfLoadInfo { /// The relocated entry point. pub entry_point: Vaddr, @@ -132,7 +145,7 @@ fn map_vmos_and_build_aux_vec( None }; - let elf_map_range = map_segment_vmos(parsed_elf, vmar, elf_inode)?; + let elf_map_range = map_segment_vmos(parsed_elf, vmar, elf_inode, ldso_load_info.is_some())?; let mut aux_vec = { let ldso_base = ldso_load_info @@ -176,7 +189,7 @@ struct LdsoLoadInfo { } fn load_ldso(vmar: &Vmar, ldso_file: &Path, ldso_elf: &ElfHeaders) -> Result { - let range = map_segment_vmos(ldso_elf, vmar, ldso_file.inode())?; + let range = map_segment_vmos(ldso_elf, vmar, ldso_file.inode(), false)?; let entry_point = range .relocated_addr_of(ldso_elf.entry_point()) .ok_or_else(|| { @@ -199,6 +212,7 @@ fn map_segment_vmos( elf: &ElfHeaders, vmar: &Vmar, elf_inode: &Arc, + has_interpreter: bool, ) -> Result { let elf_va_range = elf.calc_total_vaddr_bounds(); @@ -220,9 +234,41 @@ fn map_segment_vmos( elf_va_range.start.align_down(align)..elf_va_range.end.align_up(align); let map_size = elf_va_range_aligned.len(); - let vmar_map_options = vmar.new_map(map_size, VmPerms::empty())?.align(align); + // There are effectively two types of ET_DYN ELF binaries: + // - PIE programs (ET_DYN with PT_INTERP) and + // - static PIE programs (ET_DYN without PT_INTERP, usually the ELF interpreter itself). + // + // Reference: + let vmar_map_options = if has_interpreter { + // PIE program: map near a dedicated base. + + // Add some random padding. + let nr_pages_padding = { + let mut nr_random_padding_pages: u8 = 0; + getrandom(nr_random_padding_pages.as_bytes_mut()); + nr_random_padding_pages as usize + }; + let offset = (PIE_BASE_ADDR + nr_pages_padding * PAGE_SIZE).align_down(align); + + if offset < VMAR_LOWEST_ADDR { + return_errno_with_message!(Errno::EPERM, "the mapping address is too small"); + } + if VMAR_CAP_ADDR - offset < map_size { + return_errno_with_message!(Errno::ENOMEM, "the mapping address is too large"); + } + vmar.new_map(map_size, VmPerms::empty())? + .align(align) + .offset(offset) + } else { + // Static PIE program: pick an aligned address from the mmap region. + vmar.new_map(map_size, VmPerms::empty())?.align(align) + }; let aligned_range = vmar_map_options.build().map(|addr| addr..addr + map_size)?; + // After acquiring a suitable range, we can remove the mapping and then + // map each segment at the desired address. + vmar.remove_mapping(aligned_range.clone())?; + let start_offset = elf_va_range.start - elf_va_range_aligned.start; let end_offset = elf_va_range_aligned.end - elf_va_range.end; @@ -246,6 +292,10 @@ fn map_segment_vmos( .offset(elf_va_range_aligned.start) .build()?; + // After acquiring a suitable range, we can remove the mapping and then + // map each segment at the desired address. + vmar.remove_mapping(elf_va_range_aligned.clone())?; + elf_va_range.clone() };