From 98aefbe995c2af45e6e0cacb2c9aa7b222cb17d9 Mon Sep 17 00:00:00 2001 From: an_owl <32559552+an-owl@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:57:02 +1300 Subject: [PATCH 1/4] Added elf library for elf code. --- Cargo.lock | 7 + Cargo.toml | 1 + multiboot2/Cargo.toml | 1 + multiboot2/src/elf_sections.rs | 394 ++++++++++++++++++++++----------- 4 files changed, 269 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e8a9055e..208f7d3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55dd888a213fc57e957abf2aa305ee3e8a28dbe05687a251f33b637cd46b0070" + [[package]] name = "elf_rs" version = "0.3.1" @@ -84,6 +90,7 @@ name = "multiboot2" version = "0.24.1" dependencies = [ "bitflags", + "elf", "log", "multiboot2-common", "ptr_meta", diff --git a/Cargo.toml b/Cargo.toml index cfe37473..153d1b88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ log = { version = "~0.4", default-features = false } ptr_meta = { version = "~0.3", default-features = false } thiserror = { version = "2.0", default-features = false } uefi-raw = { version = "~0.13", default-features = false } +elf = {version = "0.8", default-features = false} # Dependencies for integration tests anyhow = { version = "1.0", default-features = false } diff --git a/multiboot2/Cargo.toml b/multiboot2/Cargo.toml index e21aa17a..9a2db75a 100644 --- a/multiboot2/Cargo.toml +++ b/multiboot2/Cargo.toml @@ -46,6 +46,7 @@ multiboot2-common = { workspace = true } ptr_meta = { workspace = true } thiserror = { workspace = true } uefi-raw = { workspace = true } +elf = {workspace = true} [package.metadata.docs.rs] all-features = true diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index 78096e37..b2200659 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -5,10 +5,16 @@ use core::fmt::{Debug, Formatter}; use core::marker::PhantomData; use core::mem; use core::str::Utf8Error; +use core::cmp::Ordering; +use core::hash::Hasher; use multiboot2_common::{MaybeDynSized, Tag}; +use elf::section::*; #[cfg(feature = "builder")] use {alloc::boxed::Box, multiboot2_common::new_boxed}; +const ELF32_SHDR_SIZE: u32 = size_of::() as u32; +const ELF64_SHDR_SIZE: u32 = size_of::() as u32; + /// This tag contains the section header table from an ELF binary. // The sections iterator is provided via the [`ElfSectionsTag::sections`] // method. @@ -42,9 +48,9 @@ impl ElfSectionsTag { pub const fn sections(&self) -> ElfSectionIter<'_> { let string_section_offset = (self.shndx * self.entry_size) as isize; let string_section_ptr = - unsafe { self.sections.as_ptr().offset(string_section_offset) as *const _ }; + unsafe { ShPointer::from_pointer(self.sections.as_ptr().offset(string_section_offset)) }; ElfSectionIter { - current_section: self.sections.as_ptr(), + current_section: ShPointer::from_pointer(self.sections.as_ptr()), remaining_sections: self.number_of_sections, entry_size: self.entry_size, string_section: string_section_ptr, @@ -104,10 +110,10 @@ impl Debug for ElfSectionsTag { /// An iterator over [`ElfSection`]s. #[derive(Clone)] pub struct ElfSectionIter<'a> { - current_section: *const u8, + current_section: ShPointer, remaining_sections: u32, entry_size: u32, - string_section: *const u8, + string_section: ShPointer, _phantom_data: PhantomData<&'a ()>, } @@ -117,13 +123,14 @@ impl<'a> Iterator for ElfSectionIter<'a> { fn next(&mut self) -> Option> { while self.remaining_sections != 0 { let section = ElfSection { - inner: self.current_section, + // SAFETY: We perform a check to ensure that we are pointing to valid entries above. + inner: SectionHeaderWrapper(unsafe { self.current_section.get(self.entry_size) }), string_section: self.string_section, entry_size: self.entry_size, _phantom: PhantomData, }; - self.current_section = unsafe { self.current_section.offset(self.entry_size as isize) }; + self.current_section = unsafe { self.current_section.offset(self.entry_size,1) }; self.remaining_sections -= 1; if section.section_type() != ElfSectionType::Unused { @@ -172,8 +179,8 @@ impl Debug for ElfSectionIter<'_> { // TODO Shouldn't this be called ElfSectionPtrs, ElfSectionWrapper or so? #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ElfSection<'a> { - inner: *const u8, - string_section: *const u8, + inner: SectionHeaderWrapper, + string_section: ShPointer, entry_size: u32, _phantom: PhantomData<&'a ()>, } @@ -188,55 +195,26 @@ impl Debug for ElfSection<'_> { } } -#[derive(Clone, Copy, Debug)] -#[repr(C, packed)] -struct ElfSectionInner32 { - name_index: u32, - typ: u32, - flags: u32, - addr: u32, - offset: u32, - size: u32, - link: u32, - info: u32, - addralign: u32, - entry_size: u32, -} - -#[derive(Clone, Copy, Debug)] -#[repr(C, packed)] -struct ElfSectionInner64 { - name_index: u32, - typ: u32, - flags: u64, - addr: u64, - offset: u64, - size: u64, - link: u32, - info: u32, - addralign: u64, - entry_size: u64, -} - impl ElfSection<'_> { /// Get the section type as an `ElfSectionType` enum variant. #[must_use] pub fn section_type(&self) -> ElfSectionType { match self.get().typ() { - 0 => ElfSectionType::Unused, - 1 => ElfSectionType::ProgramSection, - 2 => ElfSectionType::LinkerSymbolTable, - 3 => ElfSectionType::StringTable, - 4 => ElfSectionType::RelaRelocation, - 5 => ElfSectionType::SymbolHashTable, - 6 => ElfSectionType::DynamicLinkingTable, - 7 => ElfSectionType::Note, - 8 => ElfSectionType::Uninitialized, - 9 => ElfSectionType::RelRelocation, - 10 => ElfSectionType::Reserved, - 11 => ElfSectionType::DynamicLoaderSymbolTable, - 0x6000_0000..=0x6FFF_FFFF => ElfSectionType::EnvironmentSpecific, - 0x7000_0000..=0x7FFF_FFFF => ElfSectionType::ProcessorSpecific, + elf::abi::SHT_NULL => ElfSectionType::Unused, + elf::abi::SHT_PROGBITS => ElfSectionType::ProgramSection, + elf::abi::SHT_SYMTAB => ElfSectionType::LinkerSymbolTable, + elf::abi::SHT_STRTAB => ElfSectionType::StringTable, + elf::abi::SHT_RELA => ElfSectionType::RelaRelocation, + elf::abi::SHT_HASH => ElfSectionType::SymbolHashTable, + elf::abi::SHT_DYNAMIC => ElfSectionType::DynamicLinkingTable, + elf::abi::SHT_NOTE => ElfSectionType::Note, + elf::abi::SHT_NOBITS => ElfSectionType::Uninitialized, + elf::abi::SHT_REL => ElfSectionType::RelRelocation, + elf::abi::SHT_SHLIB => ElfSectionType::Reserved, + elf::abi::SHT_DYNSYM => ElfSectionType::DynamicLoaderSymbolTable, + elf::abi::SHT_LOOS..=elf::abi::SHT_HIOS => ElfSectionType::EnvironmentSpecific, + elf::abi::SHT_LOPROC..=elf::abi::SHT_HIPROC => ElfSectionType::ProcessorSpecific, + elf::abi::SHT_LOUSER..=elf::abi::SHT_HIUSER => ElfSectionType::ProgramSpecific, e => { log::warn!("Unknown section type {e:x}. Treating as ElfSectionType::Unused"); ElfSectionType::Unused @@ -244,6 +222,11 @@ impl ElfSection<'_> { } } + /// Returns the full section header in a type agnostic format. + pub fn section_raw(&self) -> SectionHeader { + self.inner.0 + } + /// Get the "raw" section type as a `u32` #[must_use] pub fn section_type_raw(&self) -> u32 { @@ -252,20 +235,20 @@ impl ElfSection<'_> { /// Read the name of the section. pub fn name(&self) -> Result<&str, Utf8Error> { - use core::{slice, str}; + use core::ffi::CStr; + // SAFETY: `string_table` returns valid pointer to she start of the string table. + // self.get().name_index() is guaranteed by the multiboot2 spec to not be larger than the string table. let name_ptr = unsafe { self.string_table().offset(self.get().name_index() as isize) }; - // strlen without null byte - let strlen = { - let mut len = 0; - while unsafe { *name_ptr.offset(len) } != 0 { - len += 1; - } - len as usize - }; - - str::from_utf8(unsafe { slice::from_raw_parts(name_ptr, strlen) }) + // SAFETY: The ELF specification guarantees that this is null terminated. + // The section above guarantees that this points to valid memory. + // This memory is 'static not allocated. + // The memory is never mutated. + // We cant guarantee that the terminator is not greater than `isize::MAX` away however + // I think this is a safe assumption. + let t = unsafe { CStr::from_ptr(name_ptr.cast()) }; + t.to_str() } /// Get the physical start address of the section. @@ -312,96 +295,235 @@ impl ElfSection<'_> { } fn get(&self) -> &dyn ElfSectionInner { - match self.entry_size { - 40 => unsafe { &*(self.inner as *const ElfSectionInner32) }, - 64 => unsafe { &*(self.inner as *const ElfSectionInner64) }, - s => panic!("Unexpected entry size: {s}"), - } + &self.inner.0 } - unsafe fn string_table(&self) -> *const u8 { - match self.entry_size { - 40 => { - let ptr = self.string_section.cast::(); - let reference = unsafe { ptr.as_ref().unwrap() }; - reference.addr() as *const u8 - } - 64 => { - let ptr = self.string_section.cast::(); - let reference = unsafe { ptr.as_ref().unwrap() }; - reference.addr() as *const u8 - } - s => panic!("Unexpected entry size: {s}"), - } + fn string_table(&self) -> *const u8 { + // SAFETY: Correctness here is guaranteed by the multiboot2 spec. + // The pointer to the string section is determined by `SelfSectionsTag::sections` + // which guarantees that this pointer points to the valid string section header. + unsafe { self.string_section.get(self.entry_size).addr() as *const u8 } } } -trait ElfSectionInner: Debug { - fn name_index(&self) -> u32; - - fn typ(&self) -> u32; +#[derive(Copy, Clone, PartialEq, Eq)] +struct SectionHeaderWrapper(SectionHeader); + +impl PartialOrd for SectionHeaderWrapper { + fn partial_cmp(&self, other: &Self) -> Option { + macro_rules! actual_partial_comparison { + ($field:ident) => { + match self.0.$field.partial_cmp(&other.0.$field) { + Some(Ordering::Equal) => {}, // fall though + Some(result) => return Some(result), + None => unreachable!() // All fields implement Ord and so cannot return `None` + } + } + } - fn flags(&self) -> u64; + actual_partial_comparison!(sh_name); + actual_partial_comparison!(sh_type); + actual_partial_comparison!(sh_flags); + actual_partial_comparison!(sh_addr); + actual_partial_comparison!(sh_offset); + actual_partial_comparison!(sh_size); + actual_partial_comparison!(sh_link); + actual_partial_comparison!(sh_info); + actual_partial_comparison!(sh_addralign); + actual_partial_comparison!(sh_entsize); + Some(Ordering::Equal) + } +} - fn addr(&self) -> u64; +impl Ord for SectionHeaderWrapper { + fn cmp(&self, other: &Self) -> Ordering { + // Should partial_cmp call this instead? + self.partial_cmp(other).unwrap() + } +} - fn size(&self) -> u64; +impl core::hash::Hash for SectionHeaderWrapper { + fn hash(&self, state: &mut H) { + self.0.sh_name.hash(state); + self.0.sh_type.hash(state); + self.0.sh_flags.hash(state); + self.0.sh_addr.hash(state); + self.0.sh_offset.hash(state); + self.0.sh_size.hash(state); + self.0.sh_link.hash(state); + self.0.sh_info.hash(state); + self.0.sh_addralign.hash(state); + self.0.sh_entsize.hash(state); + } +} - fn addralign(&self) -> u64; +/// Acts as a pointer to either [Elf32_Shdr] or [Elf64_Shdr] which is determined by the `sh_size` in methods +/// +/// # Safety +/// +/// Both variants have the same binary representation, so just accessing a variant is safe. +#[derive(Copy, Clone)] +union ShPointer { + elf32: *const Elf32_Shdr, + elf64: *const Elf64_Shdr, } -impl ElfSectionInner for ElfSectionInner32 { - fn name_index(&self) -> u32 { - self.name_index +impl Debug for ShPointer { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + unsafe { core::write!(f, "{:?}", self.elf64) } } +} - fn typ(&self) -> u32 { - self.typ +impl PartialEq for ShPointer { + fn eq(&self, other: &Self) -> bool { + unsafe { self.elf32 == other.elf32 } } +} - fn flags(&self) -> u64 { - self.flags.into() - } +impl Eq for ShPointer {} - fn addr(&self) -> u64 { - self.addr.into() +impl PartialOrd for ShPointer { + fn partial_cmp(&self, other: &Self) -> Option { + unsafe { self.elf64.partial_cmp(&other.elf64) } } +} - fn size(&self) -> u64 { - self.size.into() +impl Ord for ShPointer { + fn cmp(&self, other: &Self) -> Ordering { + unsafe { self.elf32.cmp(&other.elf32) } } +} - fn addralign(&self) -> u64 { - self.addralign.into() +impl core::hash::Hash for ShPointer { + fn hash(&self, state: &mut H) { + unsafe { self.elf64.hash(state) } } } -impl ElfSectionInner for ElfSectionInner64 { - fn name_index(&self) -> u32 { - self.name_index - } +impl ShPointer { - fn typ(&self) -> u32 { - self.typ + const fn from_pointer(ptr: *const u8) -> Self { + Self { + elf32: ptr as *const Elf32_Shdr, // The variant we use to construct this doesn't matter. + } } - fn flags(&self) -> u64 { - self.flags + /// ShPointer effectively acts as a raw pointer into either [Elf32_Shdr] or [Elf64_Shdr]. + /// + /// This will perform the same operation as [pointer::offset](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset) + /// where the size of `T` is determined by `sh_size`. + // What is the proper way to link to this? + /// + /// # Safety + /// + /// The caller must uphold the same preconditions as [pointer::offset](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset). + /// The caller must also ensure that `sh_size` is the correct value for the pointee variant. + const unsafe fn offset(self, sh_size: u32, index: isize) -> Self { + match sh_size { + ELF32_SHDR_SIZE => { + // SAFETY: Upheld by caller. + unsafe { Self { elf32: self.elf32.offset(index) } } + } + ELF64_SHDR_SIZE => { + unsafe { Self { elf64: self.elf64.offset(index) } } + } + _ => panic!("Unexpected entry size"), // Note: Can't use fmt in const context. + } } - fn addr(&self) -> u64 { - self.addr + /// Returns the pointee as a [SectionHeader] which is a bit-width-agnostic version of an elf section heder + /// + /// # Safety + /// + /// See [core::ptr::read] + unsafe fn get(&self, sh_size: u32) -> SectionHeader { + match sh_size { + ELF32_SHDR_SIZE => { + // SAFETY: Must be upheld by caller. + let sh32 = unsafe { self.elf32.read() }; + // WHY THE FUCK DOESNT THIS IMPLEMENT `Into`. + SectionHeader { + sh_name: sh32.sh_name, + sh_type: sh32.sh_type, + sh_flags: sh32.sh_flags as u64, + sh_addr: sh32.sh_addr as u64, + sh_offset: sh32.sh_offset as u64, + sh_size: sh32.sh_size as u64, + sh_link: sh32.sh_link, + sh_info: sh32.sh_info, + sh_addralign: sh32.sh_addralign as u64, + sh_entsize: sh32.sh_entsize as u64, + } + } + ELF64_SHDR_SIZE => { + // SAFETY: See ELF32_SHDR_SIZE branch. + let sh64 = unsafe { self.elf64.read() }; + SectionHeader { + sh_name: sh64.sh_name, + sh_type: sh64.sh_type, + sh_flags: sh64.sh_flags, + sh_addr: sh64.sh_addr, + sh_offset: sh64.sh_offset, + sh_size: sh64.sh_size, + sh_link: sh64.sh_link, + sh_info: sh64.sh_info, + sh_addralign: sh64.sh_addralign, + sh_entsize: sh64.sh_entsize, + } + } + s => panic!("Unexpected entry size: {s}"), + } } +} - fn size(&self) -> u64 { - self.size - } +// todo: Is this necessary anymore? +trait ElfSectionInner: Debug { + fn name_index(&self) -> u32; - fn addralign(&self) -> u64 { - self.addralign - } + fn typ(&self) -> u32; + + fn flags(&self) -> u64; + + fn addr(&self) -> u64; + + fn size(&self) -> u64; + + fn addralign(&self) -> u64; } +macro_rules! impl_elf { + ($name:ty) => { + impl ElfSectionInner for $name { + fn name_index(&self) -> u32 { + self.sh_name + } + + fn typ(&self) -> u32 { + self.sh_type + } + + fn flags(&self) -> u64 { + self.sh_flags.into() + } + + fn addr(&self) -> u64 { + self.sh_addr.into() + } + + fn size(&self) -> u64 { + self.sh_size.into() + } + + fn addralign(&self) -> u64 { + self.sh_addralign.into() + } + } + }; +} + +impl_elf!(SectionHeader); + + /// An enum abstraction over raw ELF section types. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u32)] @@ -409,55 +531,59 @@ pub enum ElfSectionType { /// This value marks the section header as inactive; it does not have an /// associated section. Other members of the section header have undefined /// values. - Unused = 0, + Unused = elf::abi::SHT_NULL, /// The section holds information defined by the program, whose format and /// meaning are determined solely by the program. - ProgramSection = 1, + ProgramSection = elf::abi::SHT_PROGBITS, /// This section holds a linker symbol table. - LinkerSymbolTable = 2, + LinkerSymbolTable = elf::abi::SHT_SYMTAB, /// The section holds a string table. - StringTable = 3, + StringTable = elf::abi::SHT_STRTAB, /// The section holds relocation entries with explicit addends, such as type /// Elf32_Rela for the 32-bit class of object files. An object file may have /// multiple relocation sections. - RelaRelocation = 4, + RelaRelocation = elf::abi::SHT_RELA, /// The section holds a symbol hash table. - SymbolHashTable = 5, + SymbolHashTable = elf::abi::SHT_HASH, /// The section holds dynamic linking tables. - DynamicLinkingTable = 6, + DynamicLinkingTable = elf::abi::SHT_DYNAMIC, /// This section holds information that marks the file in some way. - Note = 7, + Note = elf::abi::SHT_NOTE, /// A section of this type occupies no space in the file but otherwise resembles /// `ProgramSection`. Although this section contains no bytes, the /// sh_offset member contains the conceptual file offset. - Uninitialized = 8, + Uninitialized = elf::abi::SHT_NOBITS, /// The section holds relocation entries without explicit addends, such as type /// Elf32_Rel for the 32-bit class of object files. An object file may have /// multiple relocation sections. - RelRelocation = 9, + RelRelocation = elf::abi::SHT_REL, /// This section type is reserved but has unspecified semantics. - Reserved = 10, + Reserved = elf::abi::SHT_SHLIB, /// This section holds a dynamic loader symbol table. - DynamicLoaderSymbolTable = 11, + DynamicLoaderSymbolTable = elf::abi::SHT_DYNSYM, /// Values in this inclusive range (`[0x6000_0000, 0x6FFF_FFFF)`) are /// reserved for environment-specific semantics. - EnvironmentSpecific = 0x6000_0000, + EnvironmentSpecific = elf::abi::SHT_LOOS, /// Values in this inclusive range (`[0x7000_0000, 0x7FFF_FFFF)`) are /// reserved for processor-specific semantics. - ProcessorSpecific = 0x7000_0000, + ProcessorSpecific = elf::abi::SHT_LOPROC, + + /// Values in this inclusive range (`[0x7000_0000, 0x7FFF_FFFF)`) are + /// reserved for program-specific semantics. + ProgramSpecific = elf::abi::SHT_LOUSER } bitflags! { From 6fa2ddeab73992ddf2850fabec8aecbae9ac7f4f Mon Sep 17 00:00:00 2001 From: an_owl <32559552+an-owl@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:27:04 +1300 Subject: [PATCH 2/4] Fixed CI and a bit of cleaning --- multiboot2/Cargo.toml | 2 +- multiboot2/src/elf_sections.rs | 52 +++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/multiboot2/Cargo.toml b/multiboot2/Cargo.toml index 9a2db75a..8f3a4444 100644 --- a/multiboot2/Cargo.toml +++ b/multiboot2/Cargo.toml @@ -41,12 +41,12 @@ builder = ["alloc", "multiboot2-common/builder"] [dependencies] bitflags = { workspace = true } +elf = {workspace = true} log = { workspace = true } multiboot2-common = { workspace = true } ptr_meta = { workspace = true } thiserror = { workspace = true } uefi-raw = { workspace = true } -elf = {workspace = true} [package.metadata.docs.rs] all-features = true diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index b2200659..f0828341 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -1,14 +1,13 @@ //! Module for [`ElfSectionsTag`]. use crate::{TagHeader, TagType}; +use core::cmp::Ordering; use core::fmt::{Debug, Formatter}; +use core::hash::Hasher; use core::marker::PhantomData; -use core::mem; use core::str::Utf8Error; -use core::cmp::Ordering; -use core::hash::Hasher; -use multiboot2_common::{MaybeDynSized, Tag}; use elf::section::*; +use multiboot2_common::{MaybeDynSized, Tag}; #[cfg(feature = "builder")] use {alloc::boxed::Box, multiboot2_common::new_boxed}; @@ -47,8 +46,9 @@ impl ElfSectionsTag { #[must_use] pub const fn sections(&self) -> ElfSectionIter<'_> { let string_section_offset = (self.shndx * self.entry_size) as isize; - let string_section_ptr = - unsafe { ShPointer::from_pointer(self.sections.as_ptr().offset(string_section_offset)) }; + let string_section_ptr = unsafe { + ShPointer::from_pointer(self.sections.as_ptr().offset(string_section_offset)) + }; ElfSectionIter { current_section: ShPointer::from_pointer(self.sections.as_ptr()), remaining_sections: self.number_of_sections, @@ -80,7 +80,7 @@ impl ElfSectionsTag { impl MaybeDynSized for ElfSectionsTag { type Header = TagHeader; - const BASE_SIZE: usize = mem::size_of::() + 3 * mem::size_of::(); + const BASE_SIZE: usize = size_of::() + 3 * size_of::(); fn dst_len(header: &TagHeader) -> usize { assert!(header.size as usize >= Self::BASE_SIZE); @@ -130,7 +130,7 @@ impl<'a> Iterator for ElfSectionIter<'a> { _phantom: PhantomData, }; - self.current_section = unsafe { self.current_section.offset(self.entry_size,1) }; + self.current_section = unsafe { self.current_section.offset(self.entry_size, 1) }; self.remaining_sections -= 1; if section.section_type() != ElfSectionType::Unused { @@ -237,7 +237,7 @@ impl ElfSection<'_> { pub fn name(&self) -> Result<&str, Utf8Error> { use core::ffi::CStr; - // SAFETY: `string_table` returns valid pointer to she start of the string table. + // SAFETY: `string_table` returns valid pointer to the start of the string table. // self.get().name_index() is guaranteed by the multiboot2 spec to not be larger than the string table. let name_ptr = unsafe { self.string_table().offset(self.get().name_index() as isize) }; @@ -314,11 +314,11 @@ impl PartialOrd for SectionHeaderWrapper { macro_rules! actual_partial_comparison { ($field:ident) => { match self.0.$field.partial_cmp(&other.0.$field) { - Some(Ordering::Equal) => {}, // fall though + Some(Ordering::Equal) => {} // fall though Some(result) => return Some(result), - None => unreachable!() // All fields implement Ord and so cannot return `None` + None => unreachable!(), // All fields implement Ord and so cannot return `None` } - } + }; } actual_partial_comparison!(sh_name); @@ -401,7 +401,6 @@ impl core::hash::Hash for ShPointer { } impl ShPointer { - const fn from_pointer(ptr: *const u8) -> Self { Self { elf32: ptr as *const Elf32_Shdr, // The variant we use to construct this doesn't matter. @@ -422,16 +421,22 @@ impl ShPointer { match sh_size { ELF32_SHDR_SIZE => { // SAFETY: Upheld by caller. - unsafe { Self { elf32: self.elf32.offset(index) } } - } - ELF64_SHDR_SIZE => { - unsafe { Self { elf64: self.elf64.offset(index) } } + unsafe { + Self { + elf32: self.elf32.offset(index), + } + } } + ELF64_SHDR_SIZE => unsafe { + Self { + elf64: self.elf64.offset(index), + } + }, _ => panic!("Unexpected entry size"), // Note: Can't use fmt in const context. } } - /// Returns the pointee as a [SectionHeader] which is a bit-width-agnostic version of an elf section heder + /// Returns the pointee as a [SectionHeader] which is a bit-width-agnostic version of an elf section header /// /// # Safety /// @@ -440,8 +445,7 @@ impl ShPointer { match sh_size { ELF32_SHDR_SIZE => { // SAFETY: Must be upheld by caller. - let sh32 = unsafe { self.elf32.read() }; - // WHY THE FUCK DOESNT THIS IMPLEMENT `Into`. + let sh32 = unsafe { self.elf32.read_unaligned() }; SectionHeader { sh_name: sh32.sh_name, sh_type: sh32.sh_type, @@ -457,7 +461,10 @@ impl ShPointer { } ELF64_SHDR_SIZE => { // SAFETY: See ELF32_SHDR_SIZE branch. - let sh64 = unsafe { self.elf64.read() }; + // Note this uses `read_unaligned()` because MIRI throws an error otherwise. + // Multiboot2 should *actually* guarantee that this is properly aligned + // elf32 branch uses `read_unaligned` for parity. + let sh64 = unsafe { self.elf64.read_unaligned() }; SectionHeader { sh_name: sh64.sh_name, sh_type: sh64.sh_type, @@ -523,7 +530,6 @@ macro_rules! impl_elf { impl_elf!(SectionHeader); - /// An enum abstraction over raw ELF section types. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u32)] @@ -583,7 +589,7 @@ pub enum ElfSectionType { /// Values in this inclusive range (`[0x7000_0000, 0x7FFF_FFFF)`) are /// reserved for program-specific semantics. - ProgramSpecific = elf::abi::SHT_LOUSER + ProgramSpecific = elf::abi::SHT_LOUSER, } bitflags! { From 5bb8536dc3a54784fe6ce1a0c23f3f0d9fe129dd Mon Sep 17 00:00:00 2001 From: an_owl <32559552+an-owl@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:36:45 +1300 Subject: [PATCH 3/4] Re-ordered dependencies in workspace root --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 153d1b88..ad5fbaf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,11 +23,11 @@ package.license = "MIT/Apache-2.0" [workspace.dependencies] # Dependencies of multiboot2 et al. bitflags = { version = "2.11", default-features = false } +elf = {version = "0.8", default-features = false} log = { version = "~0.4", default-features = false } ptr_meta = { version = "~0.3", default-features = false } thiserror = { version = "2.0", default-features = false } uefi-raw = { version = "~0.13", default-features = false } -elf = {version = "0.8", default-features = false} # Dependencies for integration tests anyhow = { version = "1.0", default-features = false } From 3a2d17e7790bb04da930b55f4828e55f8ff71369 Mon Sep 17 00:00:00 2001 From: an_owl <32559552+an-owl@users.noreply.github.com> Date: Tue, 24 Mar 2026 12:09:49 +1300 Subject: [PATCH 4/4] Fixed CI errors --- multiboot2/src/elf_sections.rs | 49 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index f0828341..f2de47d4 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -223,7 +223,8 @@ impl ElfSection<'_> { } /// Returns the full section header in a type agnostic format. - pub fn section_raw(&self) -> SectionHeader { + #[must_use] + pub const fn section_raw(&self) -> SectionHeader { self.inner.0 } @@ -311,34 +312,32 @@ struct SectionHeaderWrapper(SectionHeader); impl PartialOrd for SectionHeaderWrapper { fn partial_cmp(&self, other: &Self) -> Option { - macro_rules! actual_partial_comparison { - ($field:ident) => { - match self.0.$field.partial_cmp(&other.0.$field) { - Some(Ordering::Equal) => {} // fall though - Some(result) => return Some(result), - None => unreachable!(), // All fields implement Ord and so cannot return `None` - } - }; - } - - actual_partial_comparison!(sh_name); - actual_partial_comparison!(sh_type); - actual_partial_comparison!(sh_flags); - actual_partial_comparison!(sh_addr); - actual_partial_comparison!(sh_offset); - actual_partial_comparison!(sh_size); - actual_partial_comparison!(sh_link); - actual_partial_comparison!(sh_info); - actual_partial_comparison!(sh_addralign); - actual_partial_comparison!(sh_entsize); - Some(Ordering::Equal) + Some(self.cmp(other)) } } impl Ord for SectionHeaderWrapper { fn cmp(&self, other: &Self) -> Ordering { - // Should partial_cmp call this instead? - self.partial_cmp(other).unwrap() + // Compares one field, returns if not equal + macro_rules! partial { + ($field:ident) => { + match self.0.$field.cmp(&other.0.$field) { + Ordering::Equal => {} + result => return result, + } + }; + } + partial!(sh_name); + partial!(sh_type); + partial!(sh_flags); + partial!(sh_addr); + partial!(sh_offset); + partial!(sh_size); + partial!(sh_link); + partial!(sh_info); + partial!(sh_addralign); + partial!(sh_entsize); + Ordering::Equal } } @@ -384,7 +383,7 @@ impl Eq for ShPointer {} impl PartialOrd for ShPointer { fn partial_cmp(&self, other: &Self) -> Option { - unsafe { self.elf64.partial_cmp(&other.elf64) } + Some(self.cmp(other)) } }