Skip to content

Commit 581adb0

Browse files
committed
Add Minimal Std implementation for UEFI
Implemented modules: 1. alloc 2. os_str 3. env 4. math Tracking Issue: rust-lang#100499 API Change Proposal: rust-lang/libs-team#87 This was originally part of rust-lang#100316. Since that PR was becoming too unwieldy and cluttered, and with suggestion from @dvdhrm, I have extracted a minimal std implementation to this PR. Signed-off-by: Ayush Singh <[email protected]>
1 parent ce798a5 commit 581adb0

File tree

24 files changed

+718
-18
lines changed

24 files changed

+718
-18
lines changed

.nlsp-settings/rust_analyzer.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"rust-analyzer.cargo.target": "x86_64-unknown-uefi"
3+
}

Cargo.lock

+23
Original file line numberDiff line numberDiff line change
@@ -2924,6 +2924,27 @@ dependencies = [
29242924
"proc-macro2",
29252925
]
29262926

2927+
[[package]]
2928+
name = "r-efi"
2929+
version = "4.1.0"
2930+
source = "registry+https://github.com/rust-lang/crates.io-index"
2931+
checksum = "9e7345c622833c6745e7b027a28aa95618813dc1f3c3de396206410267dce6f3"
2932+
dependencies = [
2933+
"compiler_builtins",
2934+
"rustc-std-workspace-core",
2935+
]
2936+
2937+
[[package]]
2938+
name = "r-efi-alloc"
2939+
version = "1.0.0"
2940+
source = "registry+https://github.com/rust-lang/crates.io-index"
2941+
checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7"
2942+
dependencies = [
2943+
"compiler_builtins",
2944+
"r-efi",
2945+
"rustc-std-workspace-core",
2946+
]
2947+
29272948
[[package]]
29282949
name = "rand"
29292950
version = "0.8.5"
@@ -4930,6 +4951,8 @@ dependencies = [
49304951
"panic_abort",
49314952
"panic_unwind",
49324953
"profiler_builtins",
4954+
"r-efi",
4955+
"r-efi-alloc",
49334956
"rand",
49344957
"rand_xorshift",
49354958
"rustc-demangle",

compiler/rustc_codegen_ssa/src/base.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,11 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
420420
rust_main_def_id: DefId,
421421
entry_type: EntryFnType,
422422
) -> Bx::Function {
423-
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`,
424-
// depending on whether the target needs `argc` and `argv` to be passed in.
425-
let llfty = if cx.sess().target.main_needs_argc_argv {
423+
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`, or
424+
// `Status efi_main(Handle hd, SystemTable *st)` depending on the target.
425+
let llfty = if cx.sess().target.os.contains("uefi") {
426+
cx.type_func(&[cx.type_ptr(), cx.type_ptr()], cx.type_isize())
427+
} else if cx.sess().target.main_needs_argc_argv {
426428
cx.type_func(&[cx.type_int(), cx.type_ptr()], cx.type_int())
427429
} else {
428430
cx.type_func(&[], cx.type_int())
@@ -485,8 +487,12 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
485487
};
486488

487489
let result = bx.call(start_ty, None, None, start_fn, &args, None);
488-
let cast = bx.intcast(result, cx.type_int(), true);
489-
bx.ret(cast);
490+
if cx.sess().target.os.contains("uefi") {
491+
bx.ret(result);
492+
} else {
493+
let cast = bx.intcast(result, cx.type_int(), true);
494+
bx.ret(cast);
495+
}
490496

491497
llfn
492498
}
@@ -497,7 +503,18 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
497503
cx: &'a Bx::CodegenCx,
498504
bx: &mut Bx,
499505
) -> (Bx::Value, Bx::Value) {
500-
if cx.sess().target.main_needs_argc_argv {
506+
if cx.sess().target.os.contains("uefi") {
507+
// Params for UEFI
508+
let param_handle = bx.get_param(0);
509+
let param_system_table = bx.get_param(1);
510+
let arg_argc = bx.const_int(cx.type_isize(), 2);
511+
let arg_argv = bx.alloca(cx.type_array(cx.type_i8p(), 2), Align::ONE);
512+
bx.store(param_handle, arg_argv, Align::ONE);
513+
let arg_argv_el1 =
514+
bx.gep(cx.type_ptr_to(cx.type_i8()), arg_argv, &[bx.const_int(cx.type_int(), 1)]);
515+
bx.store(param_system_table, arg_argv_el1, Align::ONE);
516+
(arg_argc, arg_argv)
517+
} else if cx.sess().target.main_needs_argc_argv {
501518
// Params from native `main()` used as args for rust start function
502519
let param_argc = bx.get_param(0);
503520
let param_argv = bx.get_param(1);
@@ -549,7 +566,11 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option<AllocatorKind> {
549566
use rustc_middle::middle::dependency_format::Linkage;
550567
list.iter().any(|&linkage| linkage == Linkage::Dynamic)
551568
});
552-
if any_dynamic_crate { None } else { tcx.allocator_kind(()) }
569+
if any_dynamic_crate {
570+
None
571+
} else {
572+
tcx.allocator_kind(())
573+
}
553574
}
554575

555576
pub fn codegen_crate<B: ExtraBackendMethods>(

compiler/rustc_target/src/spec/uefi_msvc_base.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub fn opts() -> TargetOptions {
4646
stack_probes: StackProbeType::Call,
4747
singlethread: true,
4848
linker: Some("rust-lld".into()),
49+
entry_name: "efi_main".into(),
4950
..base
5051
}
5152
}

compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
// The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with
66
// LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features.
77

8-
use crate::spec::Target;
8+
use crate::{abi::call::Conv, spec::Target};
99

1010
pub fn target() -> Target {
1111
let mut base = super::uefi_msvc_base::opts();
1212
base.cpu = "x86-64".into();
1313
base.plt_by_default = false;
1414
base.max_atomic_width = Some(64);
15+
base.entry_abi = Conv::X86_64Win64;
1516

1617
// We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to
1718
// enable these CPU features explicitly before their first use, otherwise their instructions

library/panic_abort/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn BoxMeUp) -> u32 {
4343
libc::abort();
4444
}
4545
} else if #[cfg(any(target_os = "hermit",
46+
target_os = "uefi",
4647
all(target_vendor = "fortanix", target_env = "sgx")
4748
))] {
4849
unsafe fn abort() -> ! {

library/std/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true
4848
[target.'cfg(target_os = "wasi")'.dependencies]
4949
wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
5050

51+
[target.'cfg(target_os = "uefi")'.dependencies]
52+
r-efi = { version = "4.1.0", features = ['rustc-dep-of-std', 'efiapi']}
53+
r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std']}
54+
5155
[features]
5256
backtrace = [
5357
"gimli-symbolize",

library/std/build.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ fn main() {
3737
|| target.contains("nintendo-3ds")
3838
|| target.contains("vita")
3939
|| target.contains("nto")
40+
|| target.contains("uefi")
4041
// See src/bootstrap/synthetic_targets.rs
4142
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
4243
{
@@ -49,7 +50,7 @@ fn main() {
4950
// - mipsel-sony-psp
5051
// - nvptx64-nvidia-cuda
5152
// - arch=avr
52-
// - uefi (x86_64-unknown-uefi, i686-unknown-uefi)
53+
// - tvos (aarch64-apple-tvos, x86_64-apple-tvos)
5354
// - JSON targets
5455
// - Any new targets that have not been explicitly added above.
5556
println!("cargo:rustc-cfg=feature=\"restricted-std\"");

library/std/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@
238238
#![cfg_attr(windows, feature(round_char_boundary))]
239239
//
240240
// Language features:
241-
// tidy-alphabetical-start
242241
#![feature(alloc_error_handler)]
243242
#![feature(allocator_internals)]
244243
#![feature(allow_internal_unsafe)]

library/std/src/os/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ pub mod solid;
140140
#[cfg(target_os = "tvos")]
141141
#[path = "ios/mod.rs"]
142142
pub(crate) mod tvos;
143+
#[cfg(target_os = "uefi")]
144+
pub mod uefi;
143145
#[cfg(target_os = "vita")]
144146
pub mod vita;
145147
#[cfg(target_os = "vxworks")]

library/std/src/os/uefi/env.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//! UEFI-specific extensions to the primitives in `std::env` module
2+
3+
use crate::ffi::c_void;
4+
use crate::ptr::NonNull;
5+
use crate::sync::atomic::{AtomicPtr, Ordering};
6+
use crate::sync::OnceLock;
7+
8+
// Position 0 = SystemTable
9+
// Position 1 = ImageHandle
10+
static GLOBALS: OnceLock<(AtomicPtr<c_void>, AtomicPtr<c_void>)> = OnceLock::new();
11+
12+
/// Initializes the global System Table and Image Handle pointers.
13+
///
14+
/// The standard library requires access to the UEFI System Table and the Application Image Handle
15+
/// to operate. Those are provided to UEFI Applications via their application entry point. By
16+
/// calling `init_globals()`, those pointers are retained by the standard library for future use.
17+
/// The pointers are never exposed to any entity outside of this application and it is guaranteed
18+
/// that, once the application exited, these pointers are never dereferenced again.
19+
///
20+
/// Callers are required to ensure the pointers are valid for the entire lifetime of this
21+
/// application. In particular, UEFI Boot Services must not be exited while an application with the
22+
/// standard library is loaded.
23+
///
24+
/// This function must not be called more than once.
25+
#[unstable(feature = "uefi_std", issue = "100499")]
26+
pub unsafe fn init_globals(handle: NonNull<c_void>, system_table: NonNull<c_void>) {
27+
GLOBALS.set((AtomicPtr::new(system_table.as_ptr()), AtomicPtr::new(handle.as_ptr()))).unwrap()
28+
}
29+
30+
/// Get the SystemTable Pointer.
31+
/// Note: This function panics if the System Table and Image Handle is Not initialized
32+
#[unstable(feature = "uefi_std", issue = "100499")]
33+
pub fn system_table() -> NonNull<c_void> {
34+
try_system_table().unwrap()
35+
}
36+
37+
/// Get the SystemHandle Pointer.
38+
/// Note: This function panics if the System Table and Image Handle is Not initialized
39+
#[unstable(feature = "uefi_std", issue = "100499")]
40+
pub fn image_handle() -> NonNull<c_void> {
41+
try_image_handle().unwrap()
42+
}
43+
44+
/// Get the SystemTable Pointer.
45+
/// This function is mostly intended for places where panic is not an option
46+
pub(crate) fn try_system_table() -> Option<NonNull<crate::ffi::c_void>> {
47+
NonNull::new(GLOBALS.get()?.0.load(Ordering::Acquire))
48+
}
49+
50+
/// Get the SystemHandle Pointer.
51+
/// This function is mostly intended for places where panic is not an option
52+
pub(crate) fn try_image_handle() -> Option<NonNull<crate::ffi::c_void>> {
53+
NonNull::new(GLOBALS.get()?.1.load(Ordering::Acquire))
54+
}

library/std/src/os/uefi/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//! Platform-specific extensions to `std` for UEFI.
2+
3+
#![unstable(feature = "uefi_std", issue = "100499")]
4+
5+
pub mod env;
6+
#[path = "../windows/ffi.rs"]
7+
pub mod ffi;

library/std/src/sys/common/thread_local/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// "static" is for single-threaded platforms where a global static is sufficient.
77

88
cfg_if::cfg_if! {
9-
if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] {
9+
if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] {
1010
#[doc(hidden)]
1111
mod static_local;
1212
#[doc(hidden)]

library/std/src/sys/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ cfg_if::cfg_if! {
4444
} else if #[cfg(target_family = "wasm")] {
4545
mod wasm;
4646
pub use self::wasm::*;
47+
} else if #[cfg(target_os = "uefi")] {
48+
mod uefi;
49+
pub use self::uefi::*;
4750
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
4851
mod sgx;
4952
pub use self::sgx::*;

library/std/src/sys/uefi/alloc.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//! Global Allocator for UEFI.
2+
//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc)
3+
4+
use crate::alloc::{handle_alloc_error, GlobalAlloc, Layout, System};
5+
6+
const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA;
7+
8+
#[stable(feature = "alloc_system_type", since = "1.28.0")]
9+
unsafe impl GlobalAlloc for System {
10+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
11+
let system_table = match crate::os::uefi::env::try_system_table() {
12+
None => return crate::ptr::null_mut(),
13+
Some(x) => x.as_ptr() as *mut _,
14+
};
15+
16+
if layout.size() > 0 {
17+
unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) }
18+
} else {
19+
layout.dangling().as_ptr()
20+
}
21+
}
22+
23+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
24+
let system_table = match crate::os::uefi::env::try_system_table() {
25+
None => handle_alloc_error(layout),
26+
Some(x) => x.as_ptr() as *mut _,
27+
};
28+
if layout.size() > 0 {
29+
unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) }
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)