mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 09:53:24 +00:00
Refine relocations in x86-64 EFI boot
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
68adca4b40
commit
662894ee90
@ -167,7 +167,37 @@ fn install_setup_with_arch(
|
|||||||
let target_dir = std::fs::canonicalize(target_dir).unwrap();
|
let target_dir = std::fs::canonicalize(target_dir).unwrap();
|
||||||
|
|
||||||
let mut cmd = Command::new("cargo");
|
let mut cmd = Command::new("cargo");
|
||||||
cmd.env("RUSTFLAGS", "-Ccode-model=kernel -Crelocation-model=pie -Ctarget-feature=+crt-static -Zplt=yes -Zrelax-elf-relocations=yes -Crelro-level=full");
|
let mut rustflags = vec![
|
||||||
|
"-Cdebuginfo=2",
|
||||||
|
"-Ccode-model=kernel",
|
||||||
|
"-Crelocation-model=pie",
|
||||||
|
"-Zplt=yes",
|
||||||
|
"-Zrelax-elf-relocations=yes",
|
||||||
|
"-Crelro-level=full",
|
||||||
|
];
|
||||||
|
let target_feature_args = match arch {
|
||||||
|
SetupInstallArch::X86_64 => {
|
||||||
|
concat!(
|
||||||
|
"-Ctarget-feature=",
|
||||||
|
"+crt-static",
|
||||||
|
",-adx",
|
||||||
|
",-aes",
|
||||||
|
",-avx",
|
||||||
|
",-avx2",
|
||||||
|
",-fxsr",
|
||||||
|
",-sse",
|
||||||
|
",-sse2",
|
||||||
|
",-sse3",
|
||||||
|
",-sse4.1",
|
||||||
|
",-sse4.2",
|
||||||
|
",-ssse3",
|
||||||
|
",-xsave",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SetupInstallArch::Other(_) => "-Ctarget-feature=+crt-static",
|
||||||
|
};
|
||||||
|
rustflags.push(target_feature_args);
|
||||||
|
cmd.env("RUSTFLAGS", rustflags.join(" "));
|
||||||
cmd.arg("install").arg("linux-bzimage-setup");
|
cmd.arg("install").arg("linux-bzimage-setup");
|
||||||
cmd.arg("--force");
|
cmd.arg("--force");
|
||||||
cmd.arg("--root").arg(install_dir.as_ref());
|
cmd.arg("--root").arg(install_dir.as_ref());
|
||||||
|
@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
use linux_boot_params::BootParams;
|
use linux_boot_params::BootParams;
|
||||||
use uefi::{
|
use uefi::{
|
||||||
|
boot::{exit_boot_services, open_protocol_exclusive},
|
||||||
|
mem::memory_map::{MemoryMap, MemoryMapOwned},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
proto::loaded_image::LoadedImage,
|
proto::loaded_image::LoadedImage,
|
||||||
boot::{exit_boot_services, open_protocol_exclusive},
|
|
||||||
mem::memory_map::{MemoryMapOwned, MemoryMap},
|
|
||||||
};
|
};
|
||||||
use uefi_raw::table::system::SystemTable;
|
use uefi_raw::table::system::SystemTable;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
decoder::decode_payload,
|
decoder::decode_payload,
|
||||||
paging::{Ia32eFlags, PageNumber, PageTableCreator},
|
paging::{Ia32eFlags, PageNumber, PageTableCreator},
|
||||||
relocation::apply_rela_dyn_relocations,
|
relocation::apply_rela_relocations,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Suppress warnings since using todo!.
|
// Suppress warnings since using todo!.
|
||||||
@ -21,10 +21,9 @@ use super::{
|
|||||||
#[allow(clippy::diverging_sub_expression)]
|
#[allow(clippy::diverging_sub_expression)]
|
||||||
#[export_name = "efi_stub_entry"]
|
#[export_name = "efi_stub_entry"]
|
||||||
extern "sysv64" fn efi_stub_entry(handle: Handle, system_table: *const SystemTable) -> ! {
|
extern "sysv64" fn efi_stub_entry(handle: Handle, system_table: *const SystemTable) -> ! {
|
||||||
unsafe {
|
// SAFETY: handle and system_table are valid pointers. It is only called once.
|
||||||
boot::set_image_handle(handle);
|
unsafe { system_init(handle, system_table) };
|
||||||
uefi::table::set_system_table(system_table);
|
|
||||||
}
|
|
||||||
uefi::helpers::init().unwrap();
|
uefi::helpers::init().unwrap();
|
||||||
|
|
||||||
let boot_params = todo!("Use EFI boot services to fill boot params");
|
let boot_params = todo!("Use EFI boot services to fill boot params");
|
||||||
@ -38,10 +37,9 @@ extern "sysv64" fn efi_handover_entry(
|
|||||||
system_table: *const SystemTable,
|
system_table: *const SystemTable,
|
||||||
boot_params_ptr: *mut BootParams,
|
boot_params_ptr: *mut BootParams,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
unsafe {
|
// SAFETY: handle and system_table are valid pointers. It is only called once.
|
||||||
boot::set_image_handle(handle);
|
unsafe { system_init(handle, system_table) };
|
||||||
uefi::table::set_system_table(system_table);
|
|
||||||
}
|
|
||||||
uefi::helpers::init().unwrap();
|
uefi::helpers::init().unwrap();
|
||||||
|
|
||||||
// SAFETY: boot_params is a valid pointer.
|
// SAFETY: boot_params is a valid pointer.
|
||||||
@ -50,18 +48,36 @@ extern "sysv64" fn efi_handover_entry(
|
|||||||
efi_phase_boot(boot_params)
|
efi_phase_boot(boot_params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn efi_phase_boot(boot_params: &mut BootParams) -> ! {
|
/// Initialize the system.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function should be called only once with valid parameters before all
|
||||||
|
/// operations.
|
||||||
|
unsafe fn system_init(handle: Handle, system_table: *const SystemTable) {
|
||||||
// SAFETY: This is the right time to initialize the console and it is only
|
// SAFETY: This is the right time to initialize the console and it is only
|
||||||
// called once here before all console operations.
|
// called once here before all console operations.
|
||||||
unsafe {
|
unsafe {
|
||||||
crate::console::init();
|
crate::console::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: this is the right time to apply relocations.
|
// SAFETY: This is the right time to apply relocations.
|
||||||
unsafe { apply_rela_dyn_relocations() };
|
unsafe { apply_rela_relocations() };
|
||||||
|
|
||||||
|
// SAFETY: The handle and system_table are valid pointers. They are passed
|
||||||
|
// from the UEFI firmware. They are only called once.
|
||||||
|
unsafe {
|
||||||
|
boot::set_image_handle(handle);
|
||||||
|
uefi::table::set_system_table(system_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn efi_phase_boot(boot_params: &mut BootParams) -> ! {
|
||||||
uefi::println!("[EFI stub] Relocations applied.");
|
uefi::println!("[EFI stub] Relocations applied.");
|
||||||
uefi::println!("[EFI stub] Stub loaded at {:#x?}", crate::x86::get_image_loaded_offset());
|
uefi::println!(
|
||||||
|
"[EFI stub] Stub loaded at {:#x?}",
|
||||||
|
crate::x86::get_image_loaded_offset()
|
||||||
|
);
|
||||||
|
|
||||||
// Fill the boot params with the RSDP address if it is not provided.
|
// Fill the boot params with the RSDP address if it is not provided.
|
||||||
if boot_params.acpi_rsdp_addr == 0 {
|
if boot_params.acpi_rsdp_addr == 0 {
|
||||||
@ -84,9 +100,7 @@ fn efi_phase_boot(boot_params: &mut BootParams) -> ! {
|
|||||||
};
|
};
|
||||||
// SAFETY: All allocations in the boot services phase are not used after
|
// SAFETY: All allocations in the boot services phase are not used after
|
||||||
// this point.
|
// this point.
|
||||||
let memory_map = unsafe {
|
let memory_map = unsafe { exit_boot_services(memory_type) };
|
||||||
exit_boot_services(memory_type)
|
|
||||||
};
|
|
||||||
|
|
||||||
efi_phase_runtime(memory_map, boot_params);
|
efi_phase_runtime(memory_map, boot_params);
|
||||||
}
|
}
|
||||||
|
@ -41,14 +41,10 @@ SECTIONS
|
|||||||
*(.eh_frame_hdr .eh_frame_hdr.*)
|
*(.eh_frame_hdr .eh_frame_hdr.*)
|
||||||
}
|
}
|
||||||
|
|
||||||
.rela.dyn : {
|
.rela : {
|
||||||
PROVIDE(__rela_dyn_start = .);
|
PROVIDE(__rela_start = .);
|
||||||
*(.rela.dyn .rela.dyn.*)
|
*(.rela .rela.*)
|
||||||
PROVIDE(__rela_dyn_end = .);
|
PROVIDE(__rela_end = .);
|
||||||
}
|
|
||||||
|
|
||||||
.rela.plt : {
|
|
||||||
*(.rela.plt .rela.plt.*)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment : { *(.comment) }
|
.comment : { *(.comment) }
|
||||||
|
@ -18,6 +18,7 @@ const TABLE_ENTRY_COUNT: usize = 512;
|
|||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct Ia32eFlags: u64 {
|
pub struct Ia32eFlags: u64 {
|
||||||
const PRESENT = 1 << 0;
|
const PRESENT = 1 << 0;
|
||||||
const WRITABLE = 1 << 1;
|
const WRITABLE = 1 << 1;
|
||||||
@ -32,9 +33,11 @@ bitflags::bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
pub struct Ia32eEntry(u64);
|
pub struct Ia32eEntry(u64);
|
||||||
|
|
||||||
/// The table in the IA32E paging specification that occupies a physical page frame.
|
/// The table in the IA32E paging specification that occupies a physical page frame.
|
||||||
|
#[repr(C)]
|
||||||
pub struct Ia32eTable([Ia32eEntry; TABLE_ENTRY_COUNT]);
|
pub struct Ia32eTable([Ia32eEntry; TABLE_ENTRY_COUNT]);
|
||||||
|
|
||||||
/// A page number. It could be either a physical page number or a virtual page number.
|
/// A page number. It could be either a physical page number or a virtual page number.
|
||||||
|
@ -2,62 +2,68 @@
|
|||||||
|
|
||||||
use crate::x86::get_image_loaded_offset;
|
use crate::x86::get_image_loaded_offset;
|
||||||
|
|
||||||
struct Elf64Rela {
|
/// Apply the relocations in the `.rela.*` sections.
|
||||||
r_offset: u64,
|
///
|
||||||
r_info: u64,
|
/// The function will enable dyn Trait objects to work since they rely on
|
||||||
r_addend: i64,
|
/// vtable pointers. Vtable won't work without relocations.
|
||||||
|
///
|
||||||
|
/// We currently support R_X86_64_RELATIVE relocations only. And this type of
|
||||||
|
/// relocation seems to be the only existing type if we compile Rust code to
|
||||||
|
/// PIE ELF binaries.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function will modify the memory pointed by the relocations. And the
|
||||||
|
/// Rust memory safety mechanisms are not aware of these kind of modification.
|
||||||
|
/// Failure to do relocations will cause `dyn Trait` objects to break.
|
||||||
|
pub unsafe fn apply_rela_relocations() {
|
||||||
|
use core::arch::asm;
|
||||||
|
let image_loaded_offset = get_image_loaded_offset();
|
||||||
|
|
||||||
|
let mut start: usize;
|
||||||
|
let end: usize;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
asm!(
|
||||||
|
"lea {}, [rip + __rela_start]",
|
||||||
|
out(reg) start,
|
||||||
|
);
|
||||||
|
asm!(
|
||||||
|
"lea {}, [rip + __rela_end]",
|
||||||
|
out(reg) end,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rela_array() -> &'static [Elf64Rela] {
|
|
||||||
extern "C" {
|
|
||||||
fn __rela_dyn_start();
|
|
||||||
fn __rela_dyn_end();
|
|
||||||
}
|
|
||||||
let start = __rela_dyn_start as *const Elf64Rela;
|
|
||||||
let end = __rela_dyn_end as *const Elf64Rela;
|
|
||||||
let len = unsafe { end.offset_from(start) } as usize;
|
|
||||||
#[cfg(feature = "debug_print")]
|
#[cfg(feature = "debug_print")]
|
||||||
unsafe {
|
unsafe {
|
||||||
use crate::console::{print_hex, print_str};
|
use crate::console::{print_hex, print_str};
|
||||||
print_str("[EFI stub debug] .rela.dyn section size = ");
|
print_str("[EFI stub debug] loaded offset = ");
|
||||||
print_hex(len as u64);
|
print_hex(image_loaded_offset as u64);
|
||||||
print_str("; __rela_dyn_start = ");
|
print_str("\n");
|
||||||
|
print_str("[EFI stub debug] .rela section start = ");
|
||||||
print_hex(start as u64);
|
print_hex(start as u64);
|
||||||
print_str(", __rela_dyn_end = ");
|
print_str(", end = ");
|
||||||
print_hex(end as u64);
|
print_hex(end as u64);
|
||||||
print_str("\n");
|
print_str("\n");
|
||||||
}
|
}
|
||||||
// SAFETY: the linker will ensure that the symbols are valid.
|
|
||||||
unsafe { core::slice::from_raw_parts(start, len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
const R_X86_64_RELATIVE: u32 = 8;
|
#[cfg(feature = "debug_print")]
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
/// Apply the relocations in the `.rela.dyn` section.
|
while start < end {
|
||||||
///
|
let rela = (start as *const Elf64Rela).read_volatile();
|
||||||
/// The function will enable dyn Trait objects to work since they rely on vtable pointers. Vtable
|
|
||||||
/// won't work without relocations.
|
|
||||||
///
|
|
||||||
/// We currently support R_X86_64_RELATIVE relocations only. And this type of relocation seems to
|
|
||||||
/// be the only existing type if we compile Rust code to PIC ELF binaries.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// This function will modify the memory pointed by the relocations. And the Rust memory safety
|
|
||||||
/// mechanisms are not aware of these kind of modification. Failure to do relocations will cause
|
|
||||||
/// dyn Trait objects to break.
|
|
||||||
pub unsafe fn apply_rela_dyn_relocations() {
|
|
||||||
let image_loaded_offset = get_image_loaded_offset();
|
|
||||||
let relas = get_rela_array();
|
|
||||||
for rela in relas {
|
|
||||||
let r_type = (rela.r_info & 0xffffffff) as u32;
|
let r_type = (rela.r_info & 0xffffffff) as u32;
|
||||||
let _r_sym = (rela.r_info >> 32) as usize;
|
let _r_sym = (rela.r_info >> 32) as usize;
|
||||||
let r_addend = rela.r_addend;
|
let r_addend = rela.r_addend as isize;
|
||||||
let r_offset = rela.r_offset as usize;
|
let r_offset = rela.r_offset as isize;
|
||||||
let target = (image_loaded_offset + r_offset as isize) as usize;
|
let target = image_loaded_offset.wrapping_add(r_offset) as usize;
|
||||||
#[cfg(feature = "debug_print")]
|
#[cfg(feature = "debug_print")]
|
||||||
unsafe {
|
unsafe {
|
||||||
use crate::console::{print_hex, print_str};
|
use crate::console::{print_hex, print_str};
|
||||||
print_str("[EFI stub debug] Applying relocation at offset ");
|
count += 1;
|
||||||
|
print_str("[EFI stub debug] Applying relocation #");
|
||||||
|
print_hex(count as u64);
|
||||||
|
print_str(" at offset ");
|
||||||
print_hex(r_offset as u64);
|
print_hex(r_offset as u64);
|
||||||
print_str(", type = ");
|
print_str(", type = ");
|
||||||
print_hex(r_type as u64);
|
print_hex(r_type as u64);
|
||||||
@ -67,12 +73,23 @@ pub unsafe fn apply_rela_dyn_relocations() {
|
|||||||
}
|
}
|
||||||
match r_type {
|
match r_type {
|
||||||
R_X86_64_RELATIVE => {
|
R_X86_64_RELATIVE => {
|
||||||
let value = (image_loaded_offset as i64 + r_addend) as usize;
|
let value = image_loaded_offset.wrapping_add(r_addend) as usize;
|
||||||
*(target as *mut usize) = value;
|
(target as *mut usize).write(value);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("Unknown relocation type: {}", r_type);
|
panic!("Unknown relocation type: {}", r_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
start = start.wrapping_add(core::mem::size_of::<Elf64Rela>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const R_X86_64_RELATIVE: u32 = 8;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Elf64Rela {
|
||||||
|
r_offset: u64,
|
||||||
|
r_info: u64,
|
||||||
|
r_addend: i64,
|
||||||
|
}
|
||||||
|
@ -16,10 +16,23 @@ const START_OF_SETUP32_VA: usize = 0x100000;
|
|||||||
/// The setup is a position-independent executable. We can get the loaded base
|
/// The setup is a position-independent executable. We can get the loaded base
|
||||||
/// address from the symbol.
|
/// address from the symbol.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::fn_to_numeric_cast)]
|
|
||||||
pub fn get_image_loaded_offset() -> isize {
|
pub fn get_image_loaded_offset() -> isize {
|
||||||
extern "C" {
|
let address_of_start: usize;
|
||||||
fn start_of_setup32();
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"lea {}, [rip + start_of_setup32]",
|
||||||
|
out(reg) address_of_start,
|
||||||
|
options(pure, nomem, nostack)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
start_of_setup32 as isize - START_OF_SETUP32_VA as isize
|
#[cfg(target_arch = "x86")]
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"lea {}, [start_of_setup32]",
|
||||||
|
out(reg) address_of_start,
|
||||||
|
options(pure, nomem, nostack)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
address_of_start as isize - START_OF_SETUP32_VA as isize
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user