mirror of
https://github.com/obhq/obliteration.git
synced 2024-12-02 16:17:01 +00:00
Exposes kernel page size via ELF note (#934)
This commit is contained in:
parent
30b712b152
commit
0bd471b4c1
@ -1,39 +0,0 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote_spanned;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Error, ItemFn};
|
||||
|
||||
pub fn transform(item: ItemFn) -> syn::Result<TokenStream> {
|
||||
let span = item.span();
|
||||
|
||||
if item.sig.abi.is_some() {
|
||||
return Err(Error::new(span, "expected a function without `extern`"));
|
||||
} else if item.sig.asyncness.is_some() {
|
||||
return Err(Error::new(span, "expected non-async function"));
|
||||
} else if item.sig.constness.is_some() {
|
||||
return Err(Error::new(span, "expected non-const function"));
|
||||
} else if item.sig.generics.lt_token.is_some() {
|
||||
return Err(Error::new(span, "expected non-generic function"));
|
||||
} else if item.sig.variadic.is_some() {
|
||||
return Err(Error::new(span, "expected non-variadic function"));
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let abi = "sysv64";
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let abi = "C";
|
||||
let attrs = item.attrs;
|
||||
let vis = item.vis;
|
||||
let safety = item.sig.unsafety;
|
||||
let name = item.sig.ident;
|
||||
let args = item.sig.inputs;
|
||||
let ret = item.sig.output;
|
||||
let block = item.block;
|
||||
|
||||
Ok(quote_spanned! {span=>
|
||||
#(#attrs)*
|
||||
#vis #safety extern #abi fn #name(#args) #ret {
|
||||
#block
|
||||
}
|
||||
})
|
||||
}
|
131
src/macros/src/elf.rs
Normal file
131
src/macros/src/elf.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::{parse_quote, Error, ItemStatic, LitInt, LitStr, Meta, StaticMutability, Type};
|
||||
|
||||
const OPT_SECTION: &str = "section";
|
||||
const OPT_NAME: &str = "name";
|
||||
const OPT_TY: &str = "ty";
|
||||
|
||||
pub fn transform_note(opts: Options, mut item: ItemStatic) -> syn::Result<TokenStream> {
|
||||
// Forbid "used" and "link_section" attribute.
|
||||
fn unsupported_attr(attr: impl ToTokens) -> syn::Result<TokenStream> {
|
||||
Err(Error::new_spanned(attr, "unsupported attribute"))
|
||||
}
|
||||
|
||||
for attr in &item.attrs {
|
||||
match &attr.meta {
|
||||
Meta::Path(p) => {
|
||||
if p.is_ident("used") {
|
||||
return unsupported_attr(p);
|
||||
}
|
||||
}
|
||||
Meta::List(_) => {}
|
||||
Meta::NameValue(a) => {
|
||||
if a.path.is_ident("link_section") {
|
||||
return unsupported_attr(&a.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow mutable.
|
||||
if let StaticMutability::Mut(t) = &item.mutability {
|
||||
return Err(Error::new_spanned(t, "mutable note is not supported"));
|
||||
}
|
||||
|
||||
// Get section name.
|
||||
let section = match opts.section {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
Span::call_site(),
|
||||
format_args!("missing `{OPT_SECTION}` option"),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Get namespace.
|
||||
let mut name = match opts.name {
|
||||
Some(raw) => {
|
||||
let val = raw.value();
|
||||
|
||||
if val.contains('\0') {
|
||||
return Err(Error::new_spanned(
|
||||
raw,
|
||||
"note name cannot contains NUL character",
|
||||
));
|
||||
}
|
||||
|
||||
val
|
||||
}
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
Span::call_site(),
|
||||
format_args!("missing `{OPT_NAME}` option"),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
name.push('\0');
|
||||
|
||||
// Get type
|
||||
let ty: u32 = match opts.ty {
|
||||
Some(v) => v.base10_parse()?,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
Span::call_site(),
|
||||
format_args!("missing `{OPT_TY}` option"),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Replace type.
|
||||
let nlen = name.len();
|
||||
let dlen = match item.ty.as_ref() {
|
||||
Type::Array(arr) => match arr.elem.as_ref() {
|
||||
Type::Path(elem) if elem.qself.is_none() && elem.path.is_ident("u8") => &arr.len,
|
||||
t => return Err(Error::new_spanned(t, "expect `u8`")),
|
||||
},
|
||||
t => return Err(Error::new_spanned(t, "expect array of `u8`")),
|
||||
};
|
||||
|
||||
item.ty = parse_quote!(crate::imgfmt::elf::Note<#nlen, { #dlen }>);
|
||||
|
||||
// Replace value.
|
||||
let name = name.as_bytes();
|
||||
let desc = item.expr;
|
||||
|
||||
item.expr = parse_quote!(unsafe { crate::imgfmt::elf::Note::new([#(#name),*], #ty, #desc) });
|
||||
|
||||
// Compose.
|
||||
Ok(quote! {
|
||||
#[cfg(not(test))]
|
||||
#[used]
|
||||
#[link_section = #section]
|
||||
#item
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Options {
|
||||
section: Option<LitStr>,
|
||||
name: Option<LitStr>,
|
||||
ty: Option<LitInt>,
|
||||
}
|
||||
|
||||
impl Options {
|
||||
pub fn parse(&mut self, m: ParseNestedMeta) -> syn::Result<()> {
|
||||
if m.path.is_ident(OPT_SECTION) {
|
||||
self.section = Some(m.value()?.parse()?);
|
||||
} else if m.path.is_ident(OPT_NAME) {
|
||||
self.name = Some(m.value()?.parse()?);
|
||||
} else if m.path.is_ident(OPT_TY) {
|
||||
self.ty = Some(m.value()?.parse()?);
|
||||
} else {
|
||||
return Err(m.error("unknown option"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -76,33 +76,27 @@ fn process_variant(variant: &Variant, enum_name: &Ident) -> syn::Result<TokenStr
|
||||
match &variant.fields {
|
||||
Fields::Named(_) => todo!("Named fields are not supported yet"),
|
||||
Fields::Unnamed(fields) => {
|
||||
let ref fields = fields.unnamed;
|
||||
let fields = &fields.unnamed;
|
||||
|
||||
let mut pos = None;
|
||||
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.try_for_each(|(i, field)| {
|
||||
fields.iter().enumerate().try_for_each(|(i, field)| {
|
||||
for attr in field.attrs.iter() {
|
||||
if attr.path().is_ident("source") || attr.path().is_ident("from") {
|
||||
if let Some(_) = pos.replace(i) {
|
||||
if (attr.path().is_ident("source") || attr.path().is_ident("from"))
|
||||
&& pos.replace(i).is_some()
|
||||
{
|
||||
return Err(syn::Error::new_spanned(
|
||||
attr,
|
||||
format!(
|
||||
"multiple fields marked with either #[source] or #[from] found. \
|
||||
Only one field is allowed"
|
||||
),
|
||||
))
|
||||
Only one field is allowed",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
return match pos {
|
||||
match pos {
|
||||
Some(pos) => {
|
||||
let variant_name = &variant.ident;
|
||||
// The field at index `pos` is the one we are interested in
|
||||
@ -117,7 +111,7 @@ fn process_variant(variant: &Variant, enum_name: &Ident) -> syn::Result<TokenStr
|
||||
variant,
|
||||
"no fields of this variant are marked with either #[source] or #[from]",
|
||||
)),
|
||||
};
|
||||
}
|
||||
}
|
||||
Fields::Unit => Err(syn::Error::new_spanned(
|
||||
variant,
|
||||
|
@ -1,17 +1,21 @@
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, Error, ItemEnum, ItemFn, LitStr};
|
||||
use syn::{parse_macro_input, Error, ItemEnum, ItemStatic, LitStr};
|
||||
|
||||
mod cpu_abi;
|
||||
mod elf;
|
||||
mod enum_conversions;
|
||||
mod errno;
|
||||
mod vpath;
|
||||
|
||||
/// Add `extern "sysv64"` on x86-64 or `extern "aapcs"` on AArch64.
|
||||
/// Note will not produced for test target.
|
||||
#[proc_macro_attribute]
|
||||
pub fn cpu_abi(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let item = parse_macro_input!(item as ItemFn);
|
||||
pub fn elf_note(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let item = parse_macro_input!(item as ItemStatic);
|
||||
let mut opts = self::elf::Options::default();
|
||||
let parser = syn::meta::parser(|m| opts.parse(m));
|
||||
|
||||
cpu_abi::transform(item)
|
||||
parse_macro_input!(args with parser);
|
||||
|
||||
self::elf::transform_note(opts, item)
|
||||
.unwrap_or_else(Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
@ -4,5 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
macros = { path = "../macros" }
|
||||
obconf = { path = "../obconf" }
|
||||
obvirt = { path = "../obvirt" }
|
||||
|
@ -4,12 +4,10 @@ fn main() {
|
||||
match target.as_str() {
|
||||
"aarch64-unknown-none-softfloat" => {
|
||||
println!("cargo::rustc-link-arg-bins=--pie");
|
||||
println!("cargo::rustc-link-arg-bins=-zcommon-page-size=0x4000");
|
||||
println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x4000");
|
||||
println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x1000");
|
||||
}
|
||||
"x86_64-unknown-none" => {
|
||||
println!("cargo::rustc-link-arg-bins=-zcommon-page-size=0x4000");
|
||||
println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x4000");
|
||||
println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x1000");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
1
src/obkrnl/src/config/aarch64.rs
Normal file
1
src/obkrnl/src/config/aarch64.rs
Normal file
@ -0,0 +1 @@
|
||||
pub const PAGE_SIZE: usize = 0x1000;
|
@ -1,6 +1,17 @@
|
||||
use core::ptr::null;
|
||||
use macros::elf_note;
|
||||
use obconf::BootEnv;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub use self::aarch64::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use self::x86_64::*;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
mod aarch64;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod x86_64;
|
||||
|
||||
pub fn boot_env() -> &'static BootEnv {
|
||||
// SAFETY: This is safe because the set_boot_env() requirements.
|
||||
unsafe { &*BOOT_ENV }
|
||||
@ -14,3 +25,6 @@ pub unsafe fn set_boot_env(env: &'static BootEnv) {
|
||||
}
|
||||
|
||||
static mut BOOT_ENV: *const BootEnv = null();
|
||||
|
||||
#[elf_note(section = ".note.obkrnl.page-size", name = "obkrnl", ty = 0)]
|
||||
static NOTE_PAGE_SIZE: [u8; size_of::<usize>()] = PAGE_SIZE.to_ne_bytes();
|
||||
|
1
src/obkrnl/src/config/x86_64.rs
Normal file
1
src/obkrnl/src/config/x86_64.rs
Normal file
@ -0,0 +1 @@
|
||||
pub const PAGE_SIZE: usize = 0x1000;
|
60
src/obkrnl/src/imgfmt/elf/mod.rs
Normal file
60
src/obkrnl/src/imgfmt/elf/mod.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use core::ops::Deref;
|
||||
|
||||
/// Single ELF note.
|
||||
#[repr(C)]
|
||||
pub struct Note<const N: usize, const D: usize> {
|
||||
hdr: NoteHdr,
|
||||
name: [u8; N],
|
||||
desc: NoteDesc<D>,
|
||||
}
|
||||
|
||||
impl<const N: usize, const D: usize> Note<N, D> {
|
||||
/// # Safety
|
||||
/// `name` must contains NUL as a last element.
|
||||
pub const unsafe fn new(name: [u8; N], ty: u32, desc: [u8; D]) -> Self {
|
||||
Self {
|
||||
hdr: NoteHdr {
|
||||
name_len: N as _,
|
||||
desc_len: D as _,
|
||||
ty,
|
||||
},
|
||||
name,
|
||||
desc: NoteDesc(desc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `Elf64_Nhdr` and `Elf32_Nhdr` structure.
|
||||
#[repr(C)]
|
||||
pub struct NoteHdr {
|
||||
/// n_namesz.
|
||||
pub name_len: u32,
|
||||
/// n_descsz.
|
||||
pub desc_len: u32,
|
||||
/// n_type.
|
||||
pub ty: u32,
|
||||
}
|
||||
|
||||
/// Note description.
|
||||
#[repr(C, align(4))]
|
||||
pub struct NoteDesc<const L: usize>([u8; L]);
|
||||
|
||||
impl<const L: usize> Deref for NoteDesc<L> {
|
||||
type Target = [u8; L];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core::mem::offset_of;
|
||||
|
||||
#[test]
|
||||
fn note() {
|
||||
assert_eq!(offset_of!(Note::<3, 1>, name), 12);
|
||||
assert_eq!(offset_of!(Note::<3, 1>, desc), 16);
|
||||
}
|
||||
}
|
1
src/obkrnl/src/imgfmt/mod.rs
Normal file
1
src/obkrnl/src/imgfmt/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod elf;
|
@ -8,6 +8,7 @@ use obconf::BootEnv;
|
||||
|
||||
mod config;
|
||||
mod console;
|
||||
mod imgfmt;
|
||||
|
||||
/// Entry point of the kernel.
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user