Rename trojan to wrapper and add docs

This commit is contained in:
Zhang Junyang
2023-12-03 00:07:58 +08:00
committed by Tate, Hongliang Tian
parent 12d01ca1e4
commit e71c2701d6
31 changed files with 134 additions and 87 deletions

View File

@ -0,0 +1,9 @@
[target.x86_64-unknown-none]
rustflags = [
"-Ccode-model=kernel",
"-Crelocation-model=pie",
"-Ctarget-feature=+crt-static",
"-Zplt=yes",
"-Zrelax-elf-relocations=yes",
"-Zrelro-level=full",
]

View File

@ -0,0 +1,23 @@
[package]
name = "aster-boot-wrapper"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cfg-if = "1.0.0"
linux_boot_params = { path = "../linux-boot-params" }
uart_16550 = "0.3.0"
xmas-elf = "0.8.0"
[target.x86_64-unknown-none.dependencies]
bitflags = "2.4.1"
log = "0.4.20"
uefi = "0.26.0"
uefi-services = "0.23.0"
x86_64 = "0.14.11"
[features]
default = []
debug_print = []

View File

@ -0,0 +1,15 @@
use std::path::PathBuf;
fn main() {
let source_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let target_arch = std::env::var("TARGET").unwrap();
let linker_script = if target_arch == "x86_64-unknown-none" {
source_dir.join("src/x86/amd64_efi/linker.ld")
} else if target_arch == "x86_64-i386_pm-none" {
source_dir.join("src/x86/legacy_i386/linker.ld")
} else {
panic!("Unsupported target_arch: {}", target_arch);
};
println!("cargo:rerun-if-changed={}", linker_script.display());
println!("cargo:rustc-link-arg=-T{}", linker_script.display());
}

View File

@ -0,0 +1,89 @@
use core::fmt::{self, Write};
use uart_16550::SerialPort;
struct Stdout {
serial_port: SerialPort,
}
static mut STDOUT: Stdout = Stdout {
serial_port: unsafe { SerialPort::new(0x0) },
};
/// safety: this function must only be called once
pub unsafe fn init() {
STDOUT = Stdout::init();
}
impl Stdout {
/// safety: this function must only be called once
pub unsafe fn init() -> Self {
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
serial_port.init();
Self { serial_port }
}
}
impl Write for Stdout {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.serial_port.write_str(s).unwrap();
Ok(())
}
}
/// This is used when dyn Trait is not supported or fmt::Arguments is fragile to use in PIE.
///
/// Safety: init() must be called before print_str() and there should be no race conditions.
pub unsafe fn print_str(s: &str) {
STDOUT.write_str(s).unwrap();
}
unsafe fn print_char(c: char) {
STDOUT.serial_port.send(c as u8);
}
/// This is used when dyn Trait is not supported or fmt::Arguments is fragile to use in PIE.
///
/// Safety: init() must be called before print_hex() and there should be no race conditions.
pub unsafe fn print_hex(n: u64) {
print_str("0x");
for i in (0..16).rev() {
let digit = (n >> (i * 4)) & 0xf;
if digit < 10 {
print_char((b'0' + digit as u8) as char);
} else {
print_char((b'A' + (digit - 10) as u8) as char);
}
}
}
// TODO: Figure out why fmt::Arguments wont work even if relocations are applied.
// We just settle on simple print functions for now.
/*--------------------------------------------------------------------------------------------------
/// Glue code for print!() and println!() macros.
///
/// Safety: init() must be called before print_fmt() and there should be no race conditions.
pub unsafe fn print_fmt(args: fmt::Arguments) {
STDOUT.write_fmt(args).unwrap();
}
#[macro_export]
macro_rules! print {
($fmt: literal $(, $($arg: tt)+)?) => {
unsafe {
$crate::console::print_fmt(format_args!($fmt $(, $($arg)+)?))
}
}
}
#[macro_export]
macro_rules! println {
($fmt: literal $(, $($arg: tt)+)?) => {
unsafe {
$crate::console::print_fmt(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
}
}
}
*------------------------------------------------------------------------------------------------*/

View File

@ -0,0 +1,61 @@
use xmas_elf::program::{ProgramHeader, SegmentData};
/// TODO: remove this and use copy_from_slice instead
///
/// We use a custom memcpy because the standard library's compiler's builtin memcpy
/// fails for some unknown reason.
unsafe fn memcpy(dst: *mut u8, src: *const u8, size: usize) {
let mut i = 0;
while i < size {
*dst.add(i) = *src.add(i);
i += 1;
}
}
pub fn load_elf(file: &[u8]) {
let elf = xmas_elf::ElfFile::new(file).unwrap();
for ph in elf.program_iter() {
let ProgramHeader::Ph64(program) = ph else {
panic!("[setup] Unexpected program header type! Jinux should be 64-bit ELF binary.");
};
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
load_segment(&elf, program);
}
}
}
fn load_segment(file: &xmas_elf::ElfFile, program: &xmas_elf::program::ProgramHeader64) {
let SegmentData::Undefined(header_data) = program.get_data(&file).unwrap() else {
panic!("[setup] Unexpected segment data type!");
};
// Safety: the physical address from the ELF file is valid
let dst_slice = unsafe {
core::slice::from_raw_parts_mut(program.physical_addr as *mut u8, program.mem_size as usize)
};
/* crate::println!(
"[setup loader debug] loading ELF segment at {:#x}, size = {:#x}",
program.physical_addr,
program.mem_size,
); */
#[cfg(feature = "debug_print")]
unsafe {
use crate::console::{print_hex, print_str};
print_str("[setup loader debug] loading ELF segment at ");
print_hex(program.physical_addr as u64);
print_str(", size = ");
print_hex(program.mem_size as u64);
print_str("\n");
}
// Safety: the ELF file is valid
// dst_slice[..program.file_size as usize].copy_from_slice(header_data);
unsafe {
memcpy(
dst_slice.as_mut_ptr(),
header_data.as_ptr(),
program.file_size as usize,
);
}
let zero_slice = &mut dst_slice[program.file_size as usize..];
zero_slice.fill(0);
}

View File

@ -0,0 +1,42 @@
//! The linux boot wrapper binary.
//!
//! With respect to the format of the bzImage, we design our boot wrapper in the similar
//! role as the setup code in the linux kernel. The setup code is responsible for
//! initializing the machine state, decompressing and loading the kernel image into memory.
//! So does our boot wrapper.
//!
//! The boot wrapper code is concatenated to the bzImage, and it contains both the linux
//! boot header and the PE/COFF header to be a valid UEFI image. The wrapper also supports
//! the legacy 32 bit boot protocol, but the support for the legacy boot protocol does not
//! co-exist with the UEFI boot protocol. Users can choose either one of them. By specifying
//! the target as `x86_64-unknown-none` it supports UEFI protocols. And if the target is
//! `x86_64-i386_pm-none` it supports the legacy boot protocol.
//!
//! The building process of the bzImage and the generation of the PE/COFF header is done
//! by the linux-boot-wrapper-builder crate. And the code of the wrapper is in this crate.
//! You should compile this crate using the functions provided in the builder.
//!
#![no_std]
#![no_main]
use linux_boot_params::BootParams;
mod console;
mod loader;
// Unfortunately, the entrypoint is not defined here in the main.rs file.
// See the exported functions in the x86 module for details.
mod x86;
fn get_payload(boot_params: &BootParams) -> &'static [u8] {
let hdr = &boot_params.hdr;
// The payload_offset field is not recorded in the relocation table, so we need to
// calculate the loaded offset manually.
let loaded_offset = x86::relocation::get_image_loaded_offset();
let payload_offset = (loaded_offset + hdr.payload_offset as isize) as usize;
let payload_length = hdr.payload_length as usize;
// Safety: the payload_offset and payload_length is valid if we assume that the
// boot_params struct is correct.
unsafe { core::slice::from_raw_parts_mut(payload_offset as *mut u8, payload_length as usize) }
}

View File

@ -0,0 +1,131 @@
use uefi::{
data_types::Handle,
proto::loaded_image::LoadedImage,
table::{boot::MemoryMap, Boot, Runtime, SystemTable},
};
use linux_boot_params::BootParams;
#[export_name = "efi_stub_entry"]
extern "sysv64" fn efi_stub_entry(handle: Handle, mut system_table: SystemTable<Boot>) -> ! {
unsafe {
system_table.boot_services().set_image_handle(handle);
}
uefi_services::init(&mut system_table).unwrap();
// Suppress TODO warning.
#[allow(unreachable_code)]
efi_phase_boot(
handle,
system_table,
todo!("Use EFI boot services to fill boot params"),
);
}
#[export_name = "efi_handover_entry"]
extern "sysv64" fn efi_handover_entry(
handle: Handle,
mut system_table: SystemTable<Boot>,
boot_params: *mut BootParams,
) -> ! {
unsafe {
system_table.boot_services().set_image_handle(handle);
}
uefi_services::init(&mut system_table).unwrap();
efi_phase_boot(handle, system_table, boot_params)
}
fn efi_phase_boot(
handle: Handle,
system_table: SystemTable<Boot>,
boot_params: *mut BootParams,
) -> ! {
// Safety: this init function is only called once.
unsafe { crate::console::init() };
// Safety: this is the right time to apply relocations.
unsafe { crate::x86::relocation::apply_rela_dyn_relocations() };
uefi_services::println!("[EFI stub] Relocations applied.");
uefi_services::println!("[EFI stub] Loading payload.");
let payload = unsafe { crate::get_payload(&*boot_params) };
crate::loader::load_elf(payload);
uefi_services::println!("[EFI stub] Exiting EFI boot services.");
let memory_type = {
let boot_services = system_table.boot_services();
let Ok(loaded_image) = boot_services.open_protocol_exclusive::<LoadedImage>(handle) else {
panic!("Failed to open LoadedImage protocol");
};
loaded_image.data_type().clone()
};
let (system_table, memory_map) = system_table.exit_boot_services(memory_type);
efi_phase_runtime(system_table, memory_map, boot_params);
}
fn efi_phase_runtime(
_system_table: SystemTable<Runtime>,
memory_map: MemoryMap<'static>,
boot_params_ptr: *mut BootParams,
) -> ! {
unsafe {
crate::console::print_str("[EFI stub] Entered runtime services.\n");
}
let boot_params = unsafe { &mut *boot_params_ptr };
// Write memory map to e820 table in boot_params.
let e820_table = &mut boot_params.e820_table;
let mut e820_entries = 0;
for md in memory_map.entries() {
if e820_entries >= e820_table.len() || e820_entries >= 128 {
break;
}
e820_table[e820_entries] = linux_boot_params::BootE820Entry {
addr: md.phys_start as u64,
size: md.page_count as u64 * 4096,
typ: match md.ty {
uefi::table::boot::MemoryType::CONVENTIONAL => linux_boot_params::E820Type::Ram,
uefi::table::boot::MemoryType::RESERVED => linux_boot_params::E820Type::Reserved,
uefi::table::boot::MemoryType::ACPI_RECLAIM => linux_boot_params::E820Type::Acpi,
uefi::table::boot::MemoryType::ACPI_NON_VOLATILE => {
linux_boot_params::E820Type::Nvs
}
_ => linux_boot_params::E820Type::Reserved,
},
};
e820_entries += 1;
}
boot_params.e820_entries = e820_entries as u8;
#[cfg(feature = "debug_print")]
unsafe {
use crate::console::{print_hex, print_str};
print_str("[EFI stub debug] Memory map:\n");
for md in memory_map.entries() {
// crate::println!(" [{:#x}] {:#x} ({:#x})", md.ty.0, md.phys_start, md.page_count);
print_str(" [");
print_hex(md.ty.0 as u64);
print_str("]");
print_hex(md.phys_start);
print_str("(size=");
print_hex(md.page_count);
print_str(")");
print_str("{flags=");
print_hex(md.att.bits());
print_str("}\n");
}
}
unsafe {
use crate::console::{print_hex, print_str};
print_str("[EFI stub] Entering Jinux entrypoint at ");
print_hex(super::JINUX_ENTRY_POINT as u64);
print_str("\n");
}
unsafe { super::call_jinux_entrypoint(super::JINUX_ENTRY_POINT, boot_params_ptr as u64) }
}

View File

@ -0,0 +1,59 @@
// The compatibility file for the Linux x86 Boot Protocol.
// See https://www.kernel.org/doc/html/v5.6/x86/boot.html for
// more information on the Linux x86 Boot Protocol.
// Some of the fields filled with a 0xab* values should be filled
// by the torjan builder.
// Asterinas will use only a few of these fields, and some of them
// are filled by the loader and will be read by Asterinas.
.section ".header", "a"
CODE32_START = 0x100000
SETUP_SECTS = 7 # so that the legacy setup could occupy a page
SETUP_SECTS_SIZE = 0x200 * (SETUP_SECTS + 1)
.code16
.org 0x01f1
hdr_start:
setup_sects: .byte SETUP_SECTS
root_flags: .word 1
syssize: .long 0
ram_size: .word 0
vid_mode: .word 0xfffd
root_dev: .word 0
boot_flag: .word 0xAA55
jump: .byte 0xeb
jump_addr: .byte hdr_end-jump_addr
magic: .ascii "HdrS"
.word 0x020f
realmode_swtch: .word 0, 0
start_sys_seg: .word 0
.word 0
type_of_loader: .byte 0
loadflags: .byte (1 << 0)
setup_move_size: .word 0
code32_start: .long CODE32_START
ramdisk_image: .long 0
ramdisk_size: .long 0
bootsect_kludge: .long 0
heap_end_ptr: .word 65535
ext_loader_ver: .byte 0
ext_loader_type: .byte 0
cmd_line_ptr: .long 0
initrd_addr_max: .long 0x7fffffff
kernel_alignment: .long 0x1000000
relocatable_kernel: .byte 0
min_alignment: .byte 0x10
xloadflags: .word 0b01111 # all handover protocols except kexec
cmdline_size: .long 4096-1
hardware_subarch: .long 0
hardware_subarch_data: .quad 0
payload_offset: .long 0xabababab # at 0x248/4, to be filled by the builder
payload_length: .long 0xabababab # at 0x24c/4, to be filled by the builder
setup_data: .quad 0
pref_address: .quad CODE32_START - SETUP_SECTS_SIZE
init_size: .long 0xabababab # at 0x260/4, to be filled by the builder
# The handover_offset should be efi_handover_setup_entry - CODE32_START - 0x200
# But we use ABI workaround to avoid the relocation of efi_handover_setup_entry
handover_offset: .long 0x10
kernel_info_offset: .long 0
hdr_end:

View File

@ -0,0 +1,55 @@
ENTRY(efi_handover_setup_entry)
OUTPUT_ARCH(i386:x86-64)
OUTPUT_FORMAT(elf64-x86-64)
SETUP32_LMA = 0x100000;
BOOTSECT_SIZE = 0x1000;
BOOTSECT_START = SETUP32_LMA - BOOTSECT_SIZE;
LEGACY_STACK_SIZE = 0x1000;
SECTIONS
{
. = BOOTSECT_START;
.header : { KEEP(*(.header)) }
. = SETUP32_LMA;
.setup : { KEEP(*(.setup)) }
.text : { *(.text .text.*) }
.data : { *(.data .data.*) }
.bss : {
PROVIDE(__bss_start = .);
*(.bss .bss.*) *(COMMON)
PROVIDE(__bss_end = .);
}
.got.plt : {
*(.got.plt .got.plt.*)
}
.dynamic : {
*(.dynamic .dynamic.*)
}
.rodata : { *(.rodata .rodata.*) }
.eh_frame : {
*(.eh_frame .eh_frame.*)
}
.eh_frame_hdr : {
*(.eh_frame_hdr .eh_frame_hdr.*)
}
.rela.dyn : {
PROVIDE(__rela_dyn_start = .);
*(.rela.dyn .rela.dyn.*)
PROVIDE(__rela_dyn_end = .);
}
.rela.plt : {
*(.rela.plt .rela.plt.*)
}
.comment : { *(.comment) }
}

View File

@ -0,0 +1,17 @@
mod efi;
use core::arch::{asm, global_asm};
global_asm!(include_str!("header.S"));
global_asm!(include_str!("setup.S"));
pub const ASTER_ENTRY_POINT: u32 = 0x8001200;
unsafe fn call_aster_entrypoint(entrypoint: u64, boot_params_ptr: u64) -> ! {
asm!("mov rsi, {}", in(reg) boot_params_ptr as u64);
asm!("mov rax, {}", in(reg) entrypoint as u64);
asm!("jmp rax");
unreachable!();
}

View File

@ -0,0 +1,41 @@
.section ".setup", "ax"
.code64
// start_of_setup32 should be loaded at CODE32_START, which is our base.
.global start_of_setup32
start_of_setup32:
// `efi_handover_setup_entry64` should be at efi_handover_setup_entry32 + 0x200, but
// we could provide the 32 bit dummy entry point as the 64 bit entry point - 0x200
// since we do not provide 32-bit entry point in the x86_64 specific implementation.
.org 0x210
.global efi_handover_setup_entry
efi_handover_setup_entry:
// The 3 parameters of is stored in rdi, rsi and rdx (sysv64).
// Do not use them.
// Setup the stack.
lea rsp, [rip + setup_stack_top]
lea rax, [rip + halt]
push rax # the return address
mov rbp, rsp
add rbp, -4
push rbp
mov rbp, rsp
.extern efi_handover_entry
lea rax, [rip + efi_handover_entry]
call rax
// Unreachable here.
halt:
hlt
jmp halt
// A small stack for the setup code.
.section .data
.align 0x1000 / 8
.global setup_stack
setup_stack:
.skip 0x1000
.global setup_stack_top
setup_stack_top:

View File

@ -0,0 +1,56 @@
// The compatibility file for the Linux x86 Boot Protocol.
// See https://www.kernel.org/doc/html/v5.6/x86/boot.html for
// more information on the Linux x86 Boot Protocol.
// Some of the fields filled with a 0xab* values should be filled
// by the torjan builder.
// Asterinas will use only a few of these fields, and some of them
// are filled by the loader and will be read by Asterinas.
.section ".header", "a"
CODE32_START = 0x100000
SETUP_SECTS = 7 # so that the legacy setup could occupy a page
.code16
.org 0x01f1
hdr_start:
setup_sects: .byte SETUP_SECTS
root_flags: .word 1
syssize: .long 0
ram_size: .word 0
vid_mode: .word 0xfffd
root_dev: .word 0
boot_flag: .word 0xAA55
jump: .byte 0xeb
jump_addr: .byte hdr_end-jump_addr
magic: .ascii "HdrS"
.word 0x020f
realmode_swtch: .word 0, 0
start_sys_seg: .word 0
.word 0
type_of_loader: .byte 0
loadflags: .byte (1 << 0)
setup_move_size: .word 0
code32_start: .long CODE32_START
ramdisk_image: .long 0
ramdisk_size: .long 0
bootsect_kludge: .long 0
heap_end_ptr: .word 65535
ext_loader_ver: .byte 0
ext_loader_type: .byte 0
cmd_line_ptr: .long 0
initrd_addr_max: .long 0x7fffffff
kernel_alignment: .long 0x1000000
relocatable_kernel: .byte 0
min_alignment: .byte 0x10
xloadflags: .word 0
cmdline_size: .long 4096-1
hardware_subarch: .long 0
hardware_subarch_data: .quad 0
payload_offset: .long 0xabababab # at 0x248/4, to be filled by the builder
payload_length: .long 0xabababab # at 0x24c/4, to be filled by the builder
setup_data: .quad 0
pref_address: .quad CODE32_START - 0x200 * (SETUP_SECTS + 1);
init_size: .long 0xabababab # at 0x260/4, to be filled by the builder
handover_offset: .long 0
kernel_info_offset: .long 0
hdr_end:

View File

@ -0,0 +1,36 @@
ENTRY(start_of_setup32)
OUTPUT_ARCH(i386:x86)
OUTPUT_FORMAT(elf32-i386)
SETUP32_LMA = 0x100000;
BOOTSECT_SIZE = 0x1000;
BOOTSECT_START = SETUP32_LMA - BOOTSECT_SIZE;
LEGACY_STACK_SIZE = 0x1000;
SECTIONS
{
. = BOOTSECT_START;
.header : { KEEP(*(.header)) }
. = SETUP32_LMA;
.setup : { KEEP(*(.setup)) }
.stack : { *(.stack) }
.text : { *(.text .text.*) }
.rodata : { *(.rodata .rodata.*) }
.data : { *(.data .data.*) }
.bss : {
PROVIDE(__bss_start = .);
*(.bss .bss.*) *(COMMON)
PROVIDE(__bss_end = .);
}
.eh_frame : {
*(.eh_frame .eh_frame.*)
}
.eh_frame_hdr : {
*(.eh_frame_hdr .eh_frame_hdr.*)
}
}

View File

@ -0,0 +1,48 @@
use linux_boot_params::BootParams;
use core::arch::{asm, global_asm};
global_asm!(include_str!("header.S"));
global_asm!(include_str!("setup.S"));
use crate::console::{print_hex, print_str};
pub const JINUX_ENTRY_POINT: u32 = 0x8001000;
#[export_name = "_trojan_entry_32"]
extern "cdecl" fn trojan_entry(boot_params_ptr: u32) -> ! {
// Safety: this init function is only called once.
unsafe { crate::console::init() };
// println!("[setup] bzImage loaded at {:#x}", x86::relocation::get_image_loaded_offset());
unsafe {
print_str("[setup] bzImage loaded at ");
print_hex(crate::x86::relocation::get_image_loaded_offset() as u64);
print_str("\n");
}
// Safety: the boot_params_ptr is a valid pointer to be borrowed.
let boot_params = unsafe { &*(boot_params_ptr as *const BootParams) };
// Safety: the payload_offset and payload_length is valid.
let payload = crate::get_payload(boot_params);
crate::loader::load_elf(payload);
// Safety: the entrypoint and the ptr is valid.
unsafe { call_jinux_entrypoint(JINUX_ENTRY_POINT, boot_params_ptr.try_into().unwrap()) };
}
pub const ASTER_ENTRY_POINT: u32 = 0x8001000;
unsafe fn call_aster_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> ! {
asm!("mov esi, {}", in(reg) boot_params_ptr);
asm!("mov eax, {}", in(reg) entrypoint);
asm!("jmp eax");
unreachable!();
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}

View File

@ -0,0 +1,28 @@
// 32-bit setup code starts here, and will be loaded at CODE32_START.
.section ".setup", "ax"
.code32
.global start_of_setup32
start_of_setup32:
mov eax, offset __stack_top
mov esp, eax
mov eax, offset halt
push eax # the return address
mov ebp, esp
add ebp, -4
push ebp
mov ebp, esp
.extern _trojan_entry_32
push esi # the boot_params pointer
call _trojan_entry_32
// Unreachable here.
halt:
hlt
jmp halt
// A small stack for the 32-bit code.
.section ".stack", "aw"
.align 8
.space 0x1000
__stack_top:

View File

@ -0,0 +1,11 @@
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
mod amd64_efi;
} else if #[cfg(target_arch = "x86")] {
mod legacy_i386;
} else {
compile_error!("Unsupported target_arch");
}
}
pub mod relocation;

View File

@ -0,0 +1,106 @@
// This is enforced in the linker script.
const START_OF_SETUP32_VA: usize = 0x100000;
/// The wrapper is a position-independent executable. We can get the loaded base
/// address from the symbol.
#[inline]
pub fn get_image_loaded_offset() -> isize {
extern "C" {
fn start_of_setup32();
}
start_of_setup32 as isize - START_OF_SETUP32_VA as isize
}
#[allow(unused)]
struct Elf64Rela {
r_offset: u64,
r_info: u64,
r_addend: i64,
}
#[allow(unused)]
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;
// FIXME: 2023/11/29
// There should be a Rust compiler bug that makes the calculation of len incorrect.
// The most sound implementation only works in debug mode.
// let len = unsafe { end.offset_from(start) } as usize;
// The inline asm solution is a workaround.
let len = unsafe {
let len: usize;
core::arch::asm!("
mov {len}, {end}
sub {len}, {start}
",
len = out(reg) len,
end = in(reg) end,
start = in(reg) start,
);
len / core::mem::size_of::<Elf64Rela>() as usize
};
#[cfg(feature = "debug_print")]
unsafe {
use crate::console::{print_hex, print_str};
print_str("[EFI stub debug] .rela.dyn section size = ");
print_hex(len as u64);
print_str("; __rela_dyn_start = ");
print_hex(start as u64);
print_str(", __rela_dyn_end = ");
print_hex(end as u64);
print_str("\n");
}
// Safety: the linker will ensure that the symbols are valid.
unsafe { core::slice::from_raw_parts(start as *const Elf64Rela, len) }
}
const R_X86_64_RELATIVE: u32 = 8;
/// Apply the relocations in the `.rela.dyn` section.
///
/// 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.
#[allow(unused)]
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_sym = (rela.r_info >> 32) as usize;
let r_addend = rela.r_addend;
let r_offset = rela.r_offset as usize;
let target = (image_loaded_offset + r_offset as isize) as usize;
#[cfg(feature = "debug_print")]
unsafe {
use crate::console::{print_hex, print_str};
print_str("[EFI stub debug] Applying relocation at offset ");
print_hex(r_offset as u64);
print_str(", type = ");
print_hex(r_type as u64);
print_str(", addend = ");
print_hex(r_addend as u64);
print_str("\n");
}
match r_type {
R_X86_64_RELATIVE => {
let value = (image_loaded_offset as i64 + r_addend) as usize;
*(target as *mut usize) = value;
}
_ => {
panic!("Unknown relocation type: {}", r_type);
}
}
}
}

View File

@ -0,0 +1,20 @@
{
"llvm-target": "i386-unknown-none",
"data-layout": "e-m:e-i32:32-f80:128-n8:16:32-S128-p:32:32",
"cpu": "i386",
"arch": "x86",
"dynamic-linking": false,
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"max-atomic-width": 64,
"position-independent-executables": false,
"disable-redzone": true,
"target-c-int-width": "32",
"target-pointer-width": "32",
"target-endian": "little",
"panic-strategy": "abort",
"os": "none",
"relocation-model": "static",
"features": "+soft-float,-sse,-mmx"
}