Привет всем; в своем последнем сообщении я создал среду разработки для работы над добавлением поддержки Rust в UEFI. В этом посте я получу ограниченную версию std (по сути, прославленное ядро + alloc) для работы с x86_64 UEFI. Мы начнем с программы no_std
hello_world из предыдущего поста.
Добавьте модуль env для UEFI в std
Добавить модуль uefi
Сначала я добавлю модуль uefi
в library/std/src/sys
. Для этого мы создадим новый файл library/std/src/sys/uefi/mod.rs
. Мы направим все, кроме модуля env
, на неподдерживаемый модуль. Содержимое library/std/src/sys/uefi/mod.rs
приведено ниже:
//! Platform-specific extensions to `std` for UEFI platforms.
//!
//! Provides access to platform-level information on UEFI platforms, and
//! exposes Unix-specific functions that would otherwise be inappropriate as
//! part of the core `std` library.
//!
//! It exposes more ways to deal with platform-specific strings ([`OsStr`],
//! [`OsString`]), allows to set permissions more granularly, extract low-level
//! file descriptors from files and sockets, and has platform-specific helpers
//! for spawning processes.
//!
//! [`OsStr`]: crate::ffi::OsStr
//! [`OsString`]: crate::ffi::OsString
#![deny(unsafe_op_in_unsafe_fn)]
#[path = "../unsupported/alloc.rs"]
pub mod alloc;
#[path = "../unsupported/args.rs"]
pub mod args;
#[path = "../unix/cmath.rs"]
pub mod cmath;
pub mod env;
#[path = "../unsupported/fs.rs"]
pub mod fs;
#[path = "../unsupported/io.rs"]
pub mod io;
#[path = "../unsupported/locks/mod.rs"]
pub mod locks;
#[path = "../unsupported/net.rs"]
pub mod net;
#[path = "../unsupported/os.rs"]
pub mod os;
#[path = "../windows/os_str.rs"]
pub mod os_str;
#[path = "../unix/path.rs"]
pub mod path;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
#[path = "../unsupported/stdio.rs"]
pub mod stdio;
#[path = "../unsupported/thread.rs"]
pub mod thread;
#[path = "../unsupported/thread_local_key.rs"]
pub mod thread_local_key;
#[path = "../unsupported/time.rs"]
pub mod time;
#[path = "../unsupported/common.rs"]
#[deny(unsafe_op_in_unsafe_fn)]
mod common;
pub use common::*;
Как вы можете видеть, cmath
, os_str
и path
указывают на модуль unix
или windows
вместо unsupported
. Это происходит потому, что unsupported
не предоставляет никакого определения для них. API unix-модуля cmath
может быть предоставлен compiler-builtins
, так что это должно сработать. Однако, os_str
и path
пока что являются просто обозначениями.
Внедрение модуля env
Модуль env очень прост, и нам просто нужно определить несколько констант для него. Вот содержимое library/std/src/sys/uefi/env.rs
:
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = "";
pub const DLL_EXTENSION: &str = "";
pub const EXE_SUFFIX: &str = ".efi";
pub const EXE_EXTENSION: &str = "efi";
}
Экспорт содержимого модуля UEFI
Это можно сделать, добавив следующие строки в library/std/src/sys/uefi/mod.rs
:
} else if #[cfg(target_family = "wasm")] {
mod wasm;
pub use self::wasm::*;
+ } else if #[cfg(target_os = "uefi")] {
+ mod uefi;
+ pub use self::uefi::*;
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
mod sgx;
pub use self::sgx::*;
И теперь у нас реализован модуль env
. Эти константы открыты в разделе std::env::consts
.
Сборка новой цепочки инструментов
Теперь нам нужно снова собрать цепочку инструментов:
./x.py build --stage 1
stage1 теперь должен указывать на эту новую цепочку инструментов.
Теперь мы попытаемся вывести эти константы в UEFI очень примитивным способом (поскольку io, строки и т.д. еще не реализованы).
Скомпилируем hello_world с помощью std
Во-первых, мы удалим атрибут no_std
и panic_handler
из src/main.rs
. Затем обновим .cargo/config.toml
со следующим содержимым:
[unstable]
build-std = ["std", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem"]
При попытке скомпилировать проект мы получим следующую ошибку:
error[E0463]: can't find crate for `panic_abort`
error[E0658]: use of unstable library feature 'restricted_std'
|
= help: add `#![feature(restricted_std)]` to the crate attributes to enable
Some errors have detailed explanations: E0463, E0658.
For more information about an error, try `rustc --explain E0463`.
error: could not compile `hello_world` due to 2 previous errors
Первая ошибка – это ошибка в build-std. Он не знает, как обрабатывать panic_abort и panic_unwind, поэтому не использует ни тот, ни другой крейт, что приводит к вышеуказанной ошибке. Ее можно исправить, добавив panic_abort
в .cargo/config.toml
.
Вторая ошибка может быть исправлена добавлением функции restricted_std
в src/main.rs
.
Пытаюсь собрать сейчас и ……….. Это не удается. Мы получаем следующую ошибку:
error: linking with `rust-lld` failed: exit status: 1
|
= note: "rust-lld" "-flavor" "link" "/NOLOGO" "/entry:efi_main" "/subsystem:efi_application" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/hello_world-bf96c91d419b97ff.3aumljakx58twtt1.rcgu.o" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/hello_world-bf96c91d419b97ff.11wx9iybsb4s2x54.rcgu.o" "/LIBPATH:/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps" "/LIBPATH:/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/debug/deps" "/LIBPATH:/var/home/ayush/Documents/Programming/Rust/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/target/lib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libr_efi-44f0e2c98ca397ed.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libstd-0f82fcd1446bb823.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libpanic_abort-6267bb336da2fa77.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/librustc_demangle-12ba4dfb1ce0974e.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libstd_detect-af3466d26b0b584c.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libhashbrown-282ed18a03cc159e.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/librustc_std_workspace_alloc-d47af0fe9b191470.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libunwind-8ad628841136827a.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libcfg_if-f793ff480fd551b6.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/liblibc-249d18e9ef84acfd.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/liballoc-4b1b3794d0343e8a.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/librustc_std_workspace_core-3a7d1ce70363f171.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libcore-73e7a0474be04fb7.rlib" "/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/libcompiler_builtins-4f95060227077e02.rlib" "/NXCOMPAT" "/LIBPATH:/var/home/ayush/Documents/Programming/Rust/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/target/lib" "/OUT:/var/home/ayush/Documents/Programming/Rust/uefi/hello_world/target/target/debug/deps/hello_world-bf96c91d419b97ff.efi" "/OPT:REF,NOICF" "/DEBUG" "/NODEFAULTLIB"
= note: rust-lld: error: undefined symbol: __CxxFrameHandler3
>>> referenced by libstd-0f82fcd1446bb823.rlib(std-0f82fcd1446bb823.std.ea9c30f6-cgu.2.rcgu.o):(.xdata)
>>> referenced by libstd-0f82fcd1446bb823.rlib(std-0f82fcd1446bb823.std.ea9c30f6-cgu.2.rcgu.o):(.xdata)
error: could not compile `hello_world` due to previous error
Эта ошибка, ну, немного странная. Пока что мы можем исправить сборку, предоставив пустую реализацию __CxxFrameHandler3
, но это требует дополнительных исследований. Необходимо добавить следующие строки в src/main.rs
:
#[no_mangle]
pub extern "C" fn __CxxFrameHandler3() {}
Теперь приложение собирается и работает нормально.
Печать константы EXE_EXTENSION
Теперь мы выведем EXE_EXTENSION на консоль. Поскольку у нас еще не реализованы io и string, нам придется сделать это примитивным способом, используя массивы u16
. Окончательный вариант src/main.rs
приведен ниже:
#![no_main]
#![feature(restricted_std)]
use r_efi::efi;
use std::env::consts;
#[no_mangle]
pub extern "C" fn __CxxFrameHandler3() {}
fn print_efi(s: &[u16], st: *mut efi::SystemTable) -> Result<(), r_efi::base::Status> {
let r = unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) };
if r.is_error() {
Err(r)
} else {
Ok(())
}
}
fn print_newline(st: *mut efi::SystemTable) -> Result<(), r_efi::base::Status> {
let mut s = [0;2];
create_const_uefi_str("n", &mut s);
print_efi(&s, st)
}
fn create_const_uefi_str(const_str: &str, uefi_array: &mut [u16]) {
let mut i = 0;
for v in const_str.bytes() {
uefi_array[i] = v as u16;
i += 1;
}
uefi_array[i] = 0x0000u16;
}
fn print_env_constants(st: *mut efi::SystemTable) -> Result<(), r_efi::base::Status> {
let mut exe_extension_heading = [0; 16];
create_const_uefi_str("exe_extension: ", &mut exe_extension_heading);
print_efi(&exe_extension_heading, st)?;
let mut exe_extension = [0; 5];
create_const_uefi_str(consts::EXE_EXTENSION, &mut exe_extension);
print_efi(&exe_extension, st)?;
print_newline(st)
}
#[export_name = "efi_main"]
pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status {
let r = print_env_constants(st);
if let Err(x) = r {
return x;
}
efi::Status::SUCCESS
}
Вот программа, запущенная под qemu:
Таким образом, мы используем наш новый std
для UEFI.
Редактировать
Пустая реализация __CxxFrameHandler3
больше не требуется в ветке master
. Ранее я основывал свои изменения на теге v1.61.0. Однако теперь я буду работать непосредственно над веткой master.
Заключение
Технически, мы теперь используем std
(хотя ничего из этого еще не реализовано). Теперь я начну медленно внедрять части std
, начиная с распределения. Я также хотел найти способ использовать обычную функцию Rust main
вместо текущей efi_main
. Однако это все еще не представляется возможным (см. #29633). Итак, давайте заставим работать выделение и заменим все массивы векторами в этом коде.