vmbase: Define Bionic TLS from Rust
Move the definition of __bionic_tls to Rust and generalize accessing it
through the TPIDR_EL0 in vmbase::bionic, reducing the amount of ASM we
have and giving typed accesses to the TLS from Rust.
Use the linker script to now define __stack_chk_guard at an offset from
__bionic_tls, which can't be done from Rust.
Note that TPIDR_EL0 can't be configured from rust_entry because the
compiler will dereference it during function entry to access
__stack_chk_guard and Rust doesn't support LLVM's
__attribute__((no_stack_protector)).
Test: atest DebugPolicyHostTests#testNoAdbInDebugPolicy_withDebugLevelNone_boots
Test: atest rialto_test vmbase_example.integration_test
Change-Id: Ife1e43a972758bff1cace87d80319465bb643e6d
diff --git a/vmbase/entry.S b/vmbase/entry.S
index 9f6993a..60930db 100644
--- a/vmbase/entry.S
+++ b/vmbase/entry.S
@@ -73,14 +73,6 @@
.set .L_SMCCC_VERSION_1_1, 0x0101
.set .L_SMCCC_TRNG_VERSION_1_0, 0x0100
-/* Bionic-compatible stack protector */
-.section .data.stack_protector, "aw"
-__bionic_tls:
- .zero 40
-.global __stack_chk_guard
-__stack_chk_guard:
- .quad 0
-
/**
* This macro stores a random value into a register.
* If a TRNG backed is not present or if an error occurs, the value remains unchanged.
@@ -222,7 +214,14 @@
adr x30, vector_table_el1
msr vbar_el1, x30
- /* Set up Bionic-compatible thread-local storage. */
+ /*
+ * Set up Bionic-compatible thread-local storage.
+ *
+ * Note that TPIDR_EL0 can't be configured from rust_entry because the
+ * compiler will dereference it during function entry to access
+ * __stack_chk_guard and Rust doesn't support LLVM's
+ * __attribute__((no_stack_protector)).
+ */
adr_l x30, __bionic_tls
msr tpidr_el0, x30
diff --git a/vmbase/example/src/layout.rs b/vmbase/example/src/layout.rs
index b032a30..fc578bc 100644
--- a/vmbase/example/src/layout.rs
+++ b/vmbase/example/src/layout.rs
@@ -15,7 +15,6 @@
//! Memory layout.
use aarch64_paging::paging::{MemoryRegion, VirtualAddress};
-use core::arch::asm;
use core::ops::Range;
use log::info;
use vmbase::layout;
@@ -55,13 +54,3 @@
boot_stack.end - boot_stack.start
);
}
-
-/// Bionic-compatible thread-local storage entry, at the given offset from TPIDR_EL0.
-pub fn bionic_tls(off: usize) -> u64 {
- let mut base: usize;
- unsafe {
- asm!("mrs {base}, tpidr_el0", base = out(reg) base);
- let ptr = (base + off) as *const u64;
- *ptr
- }
-}
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index 849302d..8aa9f04 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -23,7 +23,7 @@
extern crate alloc;
-use crate::layout::{bionic_tls, boot_stack_range, print_addresses, DEVICE_REGION};
+use crate::layout::{boot_stack_range, print_addresses, DEVICE_REGION};
use crate::pci::{check_pci, get_bar_region};
use aarch64_paging::paging::MemoryRegion;
use aarch64_paging::MapError;
@@ -32,9 +32,9 @@
use libfdt::Fdt;
use log::{debug, error, info, trace, warn, LevelFilter};
use vmbase::{
- configure_heap, cstr,
- layout::{dtb_range, rodata_range, scratch_range, stack_chk_guard, text_range},
- logger, main,
+ bionic, configure_heap, cstr,
+ layout::{dtb_range, rodata_range, scratch_range, text_range},
+ linker, logger, main,
memory::{PageTable, SIZE_64KB},
};
@@ -105,10 +105,18 @@
}
fn check_stack_guard() {
- const BIONIC_TLS_STACK_GRD_OFF: usize = 40;
-
info!("Testing stack guard");
- assert_eq!(bionic_tls(BIONIC_TLS_STACK_GRD_OFF), stack_chk_guard());
+ // SAFETY: No concurrency issue should occur when running these tests.
+ let stack_guard = unsafe { bionic::TLS.stack_guard };
+ assert_ne!(stack_guard, 0);
+ // Check that the TLS and guard are properly accessible from the dedicated register.
+ assert_eq!(stack_guard, bionic::__get_tls().stack_guard);
+ // Check that the LLVM __stack_chk_guard alias is also properly set up.
+ assert_eq!(
+ stack_guard,
+ // SAFETY: No concurrency issue should occur when running these tests.
+ unsafe { linker::__stack_chk_guard },
+ );
}
fn check_data() {
diff --git a/vmbase/sections.ld b/vmbase/sections.ld
index 5232d30..c7ef0ec 100644
--- a/vmbase/sections.ld
+++ b/vmbase/sections.ld
@@ -107,6 +107,9 @@
. = init_stack_pointer;
} >writable_data
+ /* Make our Bionic stack protector compatible with mainline LLVM */
+ __stack_chk_guard = __bionic_tls + 40;
+
/*
* Remove unused sections from the image.
*/
diff --git a/vmbase/src/bionic.rs b/vmbase/src/bionic.rs
index 5af9ebc..2ce0e83 100644
--- a/vmbase/src/bionic.rs
+++ b/vmbase/src/bionic.rs
@@ -23,9 +23,36 @@
use crate::console;
use crate::eprintln;
+use crate::read_sysreg;
const EOF: c_int = -1;
+/// Bionic thread-local storage.
+#[repr(C)]
+pub struct Tls {
+ /// Unused.
+ _unused: [u8; 40],
+ /// Use by the compiler as stack canary value.
+ pub stack_guard: u64,
+}
+
+/// Bionic TLS.
+///
+/// Provides the TLS used by Bionic code. This is unique as vmbase only supports one thread.
+///
+/// Note that the linker script re-exports __bionic_tls.stack_guard as __stack_chk_guard for
+/// compatibility with non-Bionic LLVM.
+#[link_section = ".data.stack_protector"]
+#[export_name = "__bionic_tls"]
+pub static mut TLS: Tls = Tls { _unused: [0; 40], stack_guard: 0 };
+
+/// Gets a reference to the TLS from the dedicated system register.
+pub fn __get_tls() -> &'static mut Tls {
+ let tpidr = read_sysreg!("tpidr_el0");
+ // SAFETY: The register is currently only written to once, from entry.S, with a valid value.
+ unsafe { &mut *(tpidr as *mut Tls) }
+}
+
#[no_mangle]
extern "C" fn __stack_chk_fail() -> ! {
panic!("stack guard check failed");
diff --git a/vmbase/src/lib.rs b/vmbase/src/lib.rs
index e490faa..3d2b1c2 100644
--- a/vmbase/src/lib.rs
+++ b/vmbase/src/lib.rs
@@ -21,14 +21,14 @@
extern crate alloc;
pub mod arch;
-mod bionic;
+pub mod bionic;
pub mod console;
mod entry;
pub mod fdt;
pub mod heap;
mod hvc;
pub mod layout;
-mod linker;
+pub mod linker;
pub mod logger;
pub mod memory;
pub mod power;