vmbase: Support generating a Linux image header
Expose a Rust macro to vmbase clients which they can use as opt-in for a
Linux image header to be prepended to their generated binary.
To test this new feature, add an image header to vmbase_example.
Bug: 355696053
Test: m vmbase_example_bin && llvm-objdump -d out/{...}/vmbase_example
Change-Id: I7964d647fb3a068c37edf1beeceed396e879f5a0
diff --git a/libs/libvmbase/example/src/main.rs b/libs/libvmbase/example/src/main.rs
index a01f619..4d535cc 100644
--- a/libs/libvmbase/example/src/main.rs
+++ b/libs/libvmbase/example/src/main.rs
@@ -35,7 +35,7 @@
use libfdt::Fdt;
use log::{debug, error, info, trace, warn, LevelFilter};
use vmbase::{
- bionic, configure_heap,
+ bionic, configure_heap, generate_image_header,
layout::{
crosvm::{FDT_MAX_SIZE, MEM_START},
rodata_range, scratch_range, text_range,
@@ -48,6 +48,7 @@
static mut ZEROED_DATA: [u32; 10] = [0; 10];
static mut MUTABLE_DATA: [u32; 4] = [1, 2, 3, 4];
+generate_image_header!();
main!(main);
configure_heap!(SIZE_64KB);
diff --git a/libs/libvmbase/sections.ld b/libs/libvmbase/sections.ld
index 01b7e39..7d464bc 100644
--- a/libs/libvmbase/sections.ld
+++ b/libs/libvmbase/sections.ld
@@ -34,6 +34,8 @@
* as executable-only.
*/
.text : ALIGN(4096) {
+ KEEP(*(.init.head));
+ *(.init.head)
text_begin = .;
*(.init.entry)
*(.init.*)
diff --git a/libs/libvmbase/src/entry.rs b/libs/libvmbase/src/entry.rs
index ad633ed..99f28fc 100644
--- a/libs/libvmbase/src/entry.rs
+++ b/libs/libvmbase/src/entry.rs
@@ -18,7 +18,7 @@
bionic, console, heap, hyp,
layout::{UART_ADDRESSES, UART_PAGE_ADDR},
logger,
- memory::{SIZE_16KB, SIZE_4KB},
+ memory::{PAGE_SIZE, SIZE_16KB, SIZE_4KB},
power::{reboot, shutdown},
rand,
};
@@ -129,3 +129,37 @@
}
};
}
+
+/// Prepends a Linux kernel header to the generated binary image.
+///
+/// See https://docs.kernel.org/arch/arm64/booting.html
+/// ```
+#[macro_export]
+macro_rules! generate_image_header {
+ () => {
+ #[cfg(not(target_endian = "little"))]
+ compile_error!("Image header uses wrong endianness: bootloaders expect LE!");
+
+ core::arch::global_asm!(
+ // This section gets linked at the start of the image.
+ ".section .init.head, \"ax\"",
+ // This prevents the macro from being called more than once.
+ ".global image_header",
+ "image_header:",
+ // Linux uses a special NOP to be ELF-compatible; we're not.
+ "nop", // code0
+ "b entry", // code1
+ ".quad 0", // text_offset
+ ".quad bin_end - image_header", // image_size
+ ".quad (1 << 1)", // flags (PAGE_SIZE=4KiB)
+ ".quad 0", // res2
+ ".quad 0", // res3
+ ".quad 0", // res4
+ ".ascii \"ARM\x64\"", // magic
+ ".long 0", // res5
+ );
+ };
+}
+
+// If this fails, the image header flags are out-of-sync with PAGE_SIZE!
+static_assertions::const_assert_eq!(PAGE_SIZE, SIZE_4KB);