Skip to content

Commit

Permalink
Merge pull request #45 from hermit-os/hermit-version
Browse files Browse the repository at this point in the history
feat: add Hermit version to kernel image
  • Loading branch information
mkroening authored Jan 18, 2025
2 parents a45d202 + e15f062 commit 104ea03
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
align-address = "0.3"
const_parse = "1"
goblin = { version = "0.9", default-features = false, features = ["elf64"], optional = true }
log = { version = "0.4", optional = true }
plain = { version = "0.2", optional = true }
Expand Down
65 changes: 57 additions & 8 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use log::{info, warn};
use plain::Plain;

use crate::boot_info::{LoadInfo, TlsInfo};
use crate::HermitVersion;

// See https://refspecs.linuxbase.org/elf/x86_64-abi-0.98.pdf
#[cfg(target_arch = "x86_64")]
Expand Down Expand Up @@ -67,8 +68,12 @@ pub struct KernelObject<'a> {

/// Symbol table for relocations
dynsyms: &'a [Sym],

/// The kernel's Hermit version if any.
hermit_version: Option<HermitVersion>,
}

#[derive(Clone)]
struct NoteIterator<'a> {
bytes: &'a [u8],
align: usize,
Expand Down Expand Up @@ -104,6 +109,36 @@ fn iter_notes(bytes: &[u8], align: usize) -> NoteIterator<'_> {
NoteIterator { bytes, align }
}

#[derive(Debug)]
struct ParseHermitVersionError;

impl TryFrom<Note<'_>> for HermitVersion {
type Error = ParseHermitVersionError;

fn try_from(value: Note<'_>) -> Result<Self, Self::Error> {
if value.name != "GNU" {
return Err(ParseHermitVersionError);
}

if value.ty != crate::NT_GNU_ABI_TAG {
return Err(ParseHermitVersionError);
}

let data = <[u8; 16]>::try_from(value.desc).map_err(|_| ParseHermitVersionError)?;
let data = unsafe { mem::transmute::<[u8; 16], [u32; 4]>(data) };

if data[0] != crate::ELF_NOTE_OS_HERMIT {
return Err(ParseHermitVersionError);
}

Ok(Self {
major: data[1],
minor: data[2],
patch: data[3],
})
}
}

/// An error returned when parsing a kernel ELF fails.
#[derive(Debug)]
pub struct ParseKernelError(&'static str);
Expand Down Expand Up @@ -138,6 +173,22 @@ impl KernelObject<'_> {
SectionHeader::slice_from_bytes_len(&elf[start..], len).unwrap()
};

let note_section = phs
.iter()
.find(|ph| ph.p_type == program_header::PT_NOTE)
.ok_or(ParseKernelError("Kernel does not have note section"))?;
let mut note_iter = iter_notes(
&elf[note_section.p_offset as usize..][..note_section.p_filesz as usize],
note_section.p_align as usize,
);

let hermit_version = note_iter
.clone()
.find_map(|note| HermitVersion::try_from(note).ok());
if let Some(hermit_version) = hermit_version {
info!("Found Hermit version {hermit_version}");
}

// General compatibility checks
{
let class = header.e_ident[header::EI_CLASS];
Expand All @@ -153,14 +204,6 @@ impl KernelObject<'_> {
warn!("Kernel is not a hermit application");
}

let note_section = phs
.iter()
.find(|ph| ph.p_type == program_header::PT_NOTE)
.ok_or(ParseKernelError("Kernel does not have note section"))?;
let mut note_iter = iter_notes(
&elf[note_section.p_offset as usize..][..note_section.p_filesz as usize],
note_section.p_align as usize,
);
let note = note_iter
.find(|note| note.name == "HERMIT" && note.ty == crate::NT_HERMIT_ENTRY_VERSION)
.ok_or(ParseKernelError(
Expand Down Expand Up @@ -222,9 +265,15 @@ impl KernelObject<'_> {
phs,
relas,
dynsyms,
hermit_version,
})
}

/// Returns the Hermit version of this kernel if present.
pub fn hermit_version(&self) -> Option<HermitVersion> {
self.hermit_version
}

/// Required memory size for loading.
pub fn mem_size(&self) -> usize {
let first_ph = self
Expand Down
35 changes: 34 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ pub mod elf;
#[cfg(feature = "kernel")]
mod note;

use core::fmt;

#[doc(hidden)]
pub use const_parse::parse_u128 as _parse_u128;
#[cfg(feature = "kernel")]
#[doc(hidden)]
pub use note::_Note;
pub use note::{_AbiTag, _Note};

/// Kernel entry point.
///
Expand Down Expand Up @@ -71,3 +75,32 @@ pub mod fc {
pub const CMD_LINE_PTR_OFFSET: usize = 55;
pub const CMD_LINE_SIZE_OFFSET: usize = 71;
}

#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
const NT_GNU_ABI_TAG: u32 = 1;
#[cfg_attr(not(any(feature = "loader", feature = "kernel")), expect(dead_code))]
const ELF_NOTE_OS_HERMIT: u32 = 6;

/// A Hermit version.
#[derive(Clone, Copy, Debug)]
pub struct HermitVersion {
/// The major version of Hermit.
pub major: u32,

/// The minor version of Hermit.
pub minor: u32,

/// The patch version of Hermit.
pub patch: u32,
}

impl fmt::Display for HermitVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
major,
minor,
patch,
} = self;
write!(f, "{major}.{minor}.{patch}")
}
}
49 changes: 49 additions & 0 deletions src/note.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use core::mem;

use crate::HermitVersion;

/// Defines the hermit entry version in the note section.
///
/// This macro must be used in a module that is guaranteed to be linked.
Expand Down Expand Up @@ -39,3 +43,48 @@ struct Nhdr32 {
n_descsz: u32,
n_type: u32,
}

/// Defines the current Hermit kernel version in the note section.
///
/// The version is saved in `.note.ABI-tag` in accordance with [LSB].
///
/// [LSB]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/noteabitag.html
#[macro_export]
macro_rules! define_abi_tag {
() => {
#[used]
#[link_section = ".note.ABI-tag"]
static ABI_TAG: $crate::_AbiTag = $crate::_AbiTag::new($crate::HermitVersion {
major: $crate::_parse_u128(::core::env!("CARGO_PKG_VERSION_MAJOR")) as u32,
minor: $crate::_parse_u128(::core::env!("CARGO_PKG_VERSION_MINOR")) as u32,
patch: $crate::_parse_u128(::core::env!("CARGO_PKG_VERSION_PATCH")) as u32,
});
};
}

#[repr(C)]
#[doc(hidden)]
pub struct _AbiTag {
header: Nhdr32,
name: [u8; 4],
data: [u32; 4],
}

impl _AbiTag {
pub const fn new(version: HermitVersion) -> Self {
Self {
header: Nhdr32 {
n_namesz: mem::size_of::<[u8; 4]>() as u32,
n_descsz: mem::size_of::<[u32; 4]>() as u32,
n_type: crate::NT_GNU_ABI_TAG,
},
name: *b"GNU\0",
data: [
crate::ELF_NOTE_OS_HERMIT,
version.major,
version.minor,
version.patch,
],
}
}
}

0 comments on commit 104ea03

Please sign in to comment.