Использование ограниченного std в UEFI

Привет всем; в своем последнем сообщении я создал среду разработки для работы над добавлением поддержки 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
Enter fullscreen mode Выход из полноэкранного режима

Эта ошибка, ну, немного странная. Пока что мы можем исправить сборку, предоставив пустую реализацию __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). Итак, давайте заставим работать выделение и заменим все массивы векторами в этом коде.

Оцените статью
Procodings.ru
Добавить комментарий