diff --git a/Cargo.lock b/Cargo.lock index 13eab3e1..fc5a7308 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 fe76c50f..ebda4961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ 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 } diff --git a/integration-test/bins/multiboot2_payload/src/verify/mod.rs b/integration-test/bins/multiboot2_payload/src/verify/mod.rs index 517c0a97..407edb81 100644 --- a/integration-test/bins/multiboot2_payload/src/verify/mod.rs +++ b/integration-test/bins/multiboot2_payload/src/verify/mod.rs @@ -3,7 +3,7 @@ mod grub; use alloc::format; use alloc::vec::Vec; -use multiboot2::BootInformation; +use multiboot2::{BootInformation, ElfSectionExt}; pub fn run(mbi: &BootInformation) -> anyhow::Result<()> { println!("MBI: {mbi:#x?}"); @@ -52,20 +52,32 @@ pub(self) fn print_elf_info(mbi: &BootInformation) -> anyhow::Result<()> { .ok_or("Should have elf sections") .map(|tag| tag.sections()) .map_err(anyhow::Error::msg)?; + let string_table = mbi + .elf_sections_tag() + .ok_or("Should have elf sections") + .map(|tag| tag.string_table()) + .map_err(anyhow::Error::msg)? + .ok_or("String table section should be present") + .map_err(anyhow::Error::msg)?; + println!("ELF sections:"); for s in sections_iter { - let typ = format!("{:?}", s.section_type()); + let typ = format!("{:?}", s.sh_type); let flags = format!("{:?}", s.flags()); - let name = s.name().map_err(anyhow::Error::msg)?; + let name = s + .name_from_string_table(string_table) + .map_err(anyhow::Error::msg)? + .to_str() + .map_err(anyhow::Error::msg)?; println!( " {:<13} {:<17} {:<22} 0x{:010x} 0x{:010x} {:>5.2} MiB align={}", name, typ, flags, - s.start_address(), - s.end_address(), - s.size() as f32 / 1024.0, - s.addralign(), + s.sh_addr, + s.sh_addr + s.sh_size, + s.sh_size as f32 / 1024.0, + s.sh_addralign, ); } println!(); diff --git a/multiboot2/Cargo.toml b/multiboot2/Cargo.toml index e21aa17a..46cf61eb 100644 --- a/multiboot2/Cargo.toml +++ b/multiboot2/Cargo.toml @@ -41,6 +41,7 @@ builder = ["alloc", "multiboot2-common/builder"] [dependencies] bitflags = { workspace = true } +elf = { workspace = true } log = { workspace = true } multiboot2-common = { workspace = true } ptr_meta = { workspace = true } diff --git a/multiboot2/src/builder.rs b/multiboot2/src/builder.rs index 95d6322e..bdf61c99 100644 --- a/multiboot2/src/builder.rs +++ b/multiboot2/src/builder.rs @@ -348,7 +348,7 @@ mod tests { 8, FramebufferType::Text, )) - .elf_sections(ElfSectionsTag::new(0, 32, 0, &[])) + .elf_sections(ElfSectionsTag::new(0, 40, 0, &[])) .apm(ApmTag::new(0, 0, 0, 0, 0, 0, 0, 0, 0)) .efi32(EFISdt32Tag::new(0x1000)) .efi64(EFISdt64Tag::new(0x1000)) diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index 78096e37..dafcde2e 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -1,10 +1,11 @@ //! Module for [`ElfSectionsTag`]. use crate::{TagHeader, TagType}; +use core::ffi::{CStr, FromBytesUntilNulError}; use core::fmt::{Debug, Formatter}; -use core::marker::PhantomData; use core::mem; -use core::str::Utf8Error; +use elf::endian::NativeEndian; +use elf::section::{SectionHeader, SectionHeaderTable}; use multiboot2_common::{MaybeDynSized, Tag}; #[cfg(feature = "builder")] use {alloc::boxed::Box, multiboot2_common::new_boxed}; @@ -22,6 +23,9 @@ pub struct ElfSectionsTag { sections: [u8], } +/// Iterator over the ELF section header table. +pub type ElfSectionIter<'a> = elf::parse::ParsingIterator<'a, NativeEndian, SectionHeader>; + impl ElfSectionsTag { /// Create a new ElfSectionsTag with the given data. #[cfg(feature = "builder")] @@ -39,19 +43,50 @@ impl ElfSectionsTag { /// Get an iterator over the ELF sections. #[must_use] - 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 _ }; - ElfSectionIter { - current_section: self.sections.as_ptr(), - remaining_sections: self.number_of_sections, - entry_size: self.entry_size, - string_section: string_section_ptr, - _phantom_data: PhantomData, + pub fn sections(&self) -> ElfSectionIter<'_> { + SectionHeaderTable::new(NativeEndian, self.class(), &self.sections).into_iter() + } + + const fn class(&self) -> elf::file::Class { + use elf::section::{Elf32_Shdr, Elf64_Shdr}; + const SHDR_ELF32_SIZE: usize = size_of::(); + const SHDR_ELF64_SIZE: usize = size_of::(); + + match self.entry_size as usize { + SHDR_ELF32_SIZE => elf::file::Class::ELF32, + SHDR_ELF64_SIZE => elf::file::Class::ELF64, + _ => { + panic!("Unknown ELF section entry size"); + } } } + /// Returns the string table data, if it's present. + #[must_use] + pub fn string_table(&self) -> Option<&[u8]> { + let shdr_table: SectionHeaderTable = self.into(); + + // Info for this here + // https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-43405.html @ `e_shstrndx` + let strtab_index = match self.shndx as u16 { + elf::abi::SHN_UNDEF => return None, + elf::abi::SHN_XINDEX => shdr_table.get(0).unwrap().sh_link as usize, + i => i as usize, + }; + + let strtab_hdr = shdr_table.get(strtab_index).ok()?; + // todo: Should this check that `strtab_hdr.sh_type == elf::abi::SHT_STRTAB`? + + // SAFETY: The multiboot2 spec defines that sections are always loaded at `sh_addr`. + // Casting through `usize` will not truncate data on 32bit systems because the multiboot2 loads all sections below u32::MAX + Some(unsafe { + core::slice::from_raw_parts( + strtab_hdr.sh_addr as usize as *const u8, + strtab_hdr.sh_size as usize, + ) + }) + } + /// Returns the amount of sections. #[must_use] pub const fn number_of_sections(&self) -> u32 { @@ -71,6 +106,12 @@ impl ElfSectionsTag { } } +impl<'a> From<&'a ElfSectionsTag> for SectionHeaderTable<'a, NativeEndian> { + fn from(value: &'a ElfSectionsTag) -> Self { + SectionHeaderTable::new(NativeEndian, value.class(), &value.sections) + } +} + impl MaybeDynSized for ElfSectionsTag { type Header = TagHeader; @@ -101,304 +142,57 @@ impl Debug for ElfSectionsTag { } } -/// An iterator over [`ElfSection`]s. -#[derive(Clone)] -pub struct ElfSectionIter<'a> { - current_section: *const u8, - remaining_sections: u32, - entry_size: u32, - string_section: *const u8, - _phantom_data: PhantomData<&'a ()>, -} - -impl<'a> Iterator for ElfSectionIter<'a> { - type Item = ElfSection<'a>; - - fn next(&mut self) -> Option> { - while self.remaining_sections != 0 { - let section = ElfSection { - inner: self.current_section, - 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.remaining_sections -= 1; - - if section.section_type() != ElfSectionType::Unused { - return Some(section); - } - } - None - } - - fn size_hint(&self) -> (usize, Option) { - ( - self.remaining_sections as usize, - Some(self.remaining_sections as usize), - ) - } -} - -impl ExactSizeIterator for ElfSectionIter<'_> { - fn len(&self) -> usize { - self.remaining_sections as usize - } -} - -impl Debug for ElfSectionIter<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - /// Limit how many Elf-Sections should be debug-formatted. - /// Can be thousands of sections for a Rust binary => this is useless output. - /// If the user really wants this, they should debug-format the field directly. - const ELF_SECTIONS_LIMIT: usize = 7; - - let mut debug = f.debug_list(); - - self.clone().take(ELF_SECTIONS_LIMIT).for_each(|ref e| { - debug.entry(e); - }); - - if self.clone().len() > ELF_SECTIONS_LIMIT { - debug.entry(&"..."); - } - - debug.finish() - } -} - -/// A single generic ELF Section. -// 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, - entry_size: u32, - _phantom: PhantomData<&'a ()>, -} - -impl Debug for ElfSection<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let inner = self.get(); - f.debug_struct("ElfSection") - .field("inner", &inner) - .field("string_section_ptr", &self.string_section) - .finish() - } -} - -#[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<'_> { +/// Extension trait for [SectionHeader] containing getters for rust-native types +pub trait ElfSectionExt { /// 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, - e => { - log::warn!("Unknown section type {e:x}. Treating as ElfSectionType::Unused"); - ElfSectionType::Unused - } - } - } - - /// Get the "raw" section type as a `u32` - #[must_use] - pub fn section_type_raw(&self) -> u32 { - self.get().typ() - } - - /// Read the name of the section. - pub fn name(&self) -> Result<&str, Utf8Error> { - use core::{slice, str}; - - 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) }) - } - - /// Get the physical start address of the section. - #[must_use] - pub fn start_address(&self) -> u64 { - self.get().addr() - } - - /// Get the physical end address of the section. - /// - /// This is the same as doing `section.start_address() + section.size()` - #[must_use] - pub fn end_address(&self) -> u64 { - self.get().addr() + self.get().size() - } - - /// Get the section's size in bytes. - #[must_use] - pub fn size(&self) -> u64 { - self.get().size() - } - - /// Get the section's address alignment constraints. - /// - /// That is, the value of `start_address` must be congruent to 0, - /// modulo the value of `addrlign`. Currently, only 0 and positive - /// integral powers of two are allowed. Values 0 and 1 mean the section has no - /// alignment constraints. - #[must_use] - pub fn addralign(&self) -> u64 { - self.get().addralign() - } + fn section_type(&self) -> ElfSectionType; /// Get the section's flags. #[must_use] - pub fn flags(&self) -> ElfSectionFlags { - ElfSectionFlags::from_bits_truncate(self.get().flags()) - } - - /// Check if the `ALLOCATED` flag is set in the section flags. - #[must_use] - pub fn is_allocated(&self) -> bool { - self.flags().contains(ElfSectionFlags::ALLOCATED) - } + fn flags(&self) -> ElfSectionFlags; - 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}"), - } - } + /// Fetches the section name from the string table. + fn name_from_string_table<'a>( + &self, + name: &'a [u8], + ) -> Result<&'a CStr, FromBytesUntilNulError>; +} - 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 +impl ElfSectionExt for SectionHeader { + fn section_type(&self) -> ElfSectionType { + match self.sh_type { + 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::UserDefined, + e => { + log::warn!("Unknown section type {e:x}. Treating as ElfSectionType::Unused"); + ElfSectionType::Unused } - s => panic!("Unexpected entry size: {s}"), } } -} - -trait ElfSectionInner: Debug { - fn name_index(&self) -> u32; - - fn typ(&self) -> u32; - - fn flags(&self) -> u64; - - fn addr(&self) -> u64; - - fn size(&self) -> u64; - - fn addralign(&self) -> u64; -} - -impl ElfSectionInner for ElfSectionInner32 { - fn name_index(&self) -> u32 { - self.name_index - } - fn typ(&self) -> u32 { - self.typ + fn flags(&self) -> ElfSectionFlags { + ElfSectionFlags::from_bits_retain(self.sh_flags) } - fn flags(&self) -> u64 { - self.flags.into() - } - - fn addr(&self) -> u64 { - self.addr.into() - } - - fn size(&self) -> u64 { - self.size.into() - } - - fn addralign(&self) -> u64 { - self.addralign.into() - } -} - -impl ElfSectionInner for ElfSectionInner64 { - fn name_index(&self) -> u32 { - self.name_index - } - - fn typ(&self) -> u32 { - self.typ - } - - fn flags(&self) -> u64 { - self.flags - } - - fn addr(&self) -> u64 { - self.addr - } - - fn size(&self) -> u64 { - self.size - } - - fn addralign(&self) -> u64 { - self.addralign + fn name_from_string_table<'a>( + &self, + name: &'a [u8], + ) -> Result<&'a CStr, FromBytesUntilNulError> { + CStr::from_bytes_until_nul(&name[self.sh_name as usize..]) } } @@ -409,55 +203,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 (`[0x8000_0000, 0x8FFF_FFFF)`) are + /// reserved for user-specific semantics. + UserDefined = elf::abi::SHT_LOUSER, } bitflags! { @@ -466,13 +264,22 @@ bitflags! { #[repr(transparent)] pub struct ElfSectionFlags: u64 { /// The section contains data that should be writable during program execution. - const WRITABLE = 0x1; + const WRITABLE = elf::abi::SHF_WRITE as u64; /// The section occupies memory during the process execution. - const ALLOCATED = 0x2; + const ALLOCATED = elf::abi::SHF_ALLOC as u64; /// The section contains executable machine instructions. - const EXECUTABLE = 0x4; + const EXECUTABLE = elf::abi::SHF_EXECINSTR as u64; + + /// Identifies a section that consists of null-terminated character strings. + /// The size of each character is specified in the section header's `sh_entsize` field. + const STRINGS = elf::abi::SHF_STRINGS as u64; + + /// This section holds thread-local storage. Each thread within a process has a distinct instance + /// of this data. + const THREAD_LOCAL = elf::abi::SHF_TLS as u64; + // plus environment-specific use at 0x0F000000 // plus processor-specific use at 0xF0000000 } diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index fda9efde..12787197 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -98,7 +98,7 @@ pub use efi::{ EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFISdt32Tag, EFISdt64Tag, }; pub use elf_sections::{ - ElfSection, ElfSectionFlags, ElfSectionIter, ElfSectionType, ElfSectionsTag, + ElfSectionExt, ElfSectionFlags, ElfSectionIter, ElfSectionType, ElfSectionsTag, }; pub use end::EndTag; pub use framebuffer::{FramebufferColor, FramebufferField, FramebufferTag, FramebufferType}; @@ -833,76 +833,127 @@ mod tests { assert_eq!(addr, bi.start_address()); assert_eq!(addr + bytes.len(), bi.end_address()); assert_eq!(bytes.len(), bi.total_size()); + let strtab = bi.elf_sections_tag().unwrap().string_table().unwrap(); let mut es = bi.elf_sections_tag().unwrap().sections(); + + let _s0 = es.next().expect("Should have one more section"); let s1 = es.next().expect("Should have one more section"); - assert_eq!(".rodata", s1.name().expect("Should be valid utf-8")); - assert_eq!(0xFFFF_8000_0010_0000, s1.start_address()); - assert_eq!(0xFFFF_8000_0010_3000, s1.end_address()); - assert_eq!(0x0000_0000_0000_3000, s1.size()); + assert_eq!( + ".rodata", + s1.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(0xFFFF_8000_0010_0000, s1.sh_addr); + assert_eq!(0x0000_0000_0000_3000, s1.sh_size); assert_eq!(ElfSectionFlags::ALLOCATED, s1.flags()); assert_eq!(ElfSectionType::ProgramSection, s1.section_type()); + let s2 = es.next().expect("Should have one more section"); - assert_eq!(".text", s2.name().expect("Should be valid utf-8")); - assert_eq!(0xFFFF_8000_0010_3000, s2.start_address()); - assert_eq!(0xFFFF_8000_0010_C000, s2.end_address()); - assert_eq!(0x0000_0000_0000_9000, s2.size()); + assert_eq!( + ".text", + s2.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(0xFFFF_8000_0010_3000, s2.sh_addr); + assert_eq!(0x0000_0000_0000_9000, s2.sh_size); assert_eq!( ElfSectionFlags::EXECUTABLE | ElfSectionFlags::ALLOCATED, s2.flags() ); assert_eq!(ElfSectionType::ProgramSection, s2.section_type()); + let s3 = es.next().expect("Should have one more section"); - assert_eq!(".data", s3.name().expect("Should be valid utf-8")); - assert_eq!(0xFFFF_8000_0010_C000, s3.start_address()); - assert_eq!(0xFFFF_8000_0010_E000, s3.end_address()); - assert_eq!(0x0000_0000_0000_2000, s3.size()); + assert_eq!( + ".data", + s3.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(0xFFFF_8000_0010_C000, s3.sh_addr); + assert_eq!(0x0000_0000_0000_2000, s3.sh_size); assert_eq!( ElfSectionFlags::ALLOCATED | ElfSectionFlags::WRITABLE, s3.flags() ); assert_eq!(ElfSectionType::ProgramSection, s3.section_type()); + let s4 = es.next().expect("Should have one more section"); - assert_eq!(".bss", s4.name().expect("Should be valid utf-8")); - assert_eq!(0xFFFF_8000_0010_E000, s4.start_address()); - assert_eq!(0xFFFF_8000_0011_3000, s4.end_address()); - assert_eq!(0x0000_0000_0000_5000, s4.size()); + assert_eq!( + ".bss", + s4.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(0xFFFF_8000_0010_E000, s4.sh_addr); + assert_eq!(0x0000_0000_0000_5000, s4.sh_size); assert_eq!( ElfSectionFlags::ALLOCATED | ElfSectionFlags::WRITABLE, s4.flags() ); assert_eq!(ElfSectionType::Uninitialized, s4.section_type()); + let s5 = es.next().expect("Should have one more section"); - assert_eq!(".data.rel.ro", s5.name().expect("Should be valid utf-8")); - assert_eq!(0xFFFF_8000_0011_3000, s5.start_address()); - assert_eq!(0xFFFF_8000_0011_3000, s5.end_address()); - assert_eq!(0x0000_0000_0000_0000, s5.size()); + assert_eq!( + ".data.rel.ro", + s5.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(0xFFFF_8000_0011_3000, s5.sh_addr); + assert_eq!(0x0000_0000_0000_0000, s5.sh_size); assert_eq!( ElfSectionFlags::ALLOCATED | ElfSectionFlags::WRITABLE, s5.flags() ); assert_eq!(ElfSectionType::ProgramSection, s5.section_type()); + let s6 = es.next().expect("Should have one more section"); - assert_eq!(".symtab", s6.name().expect("Should be valid utf-8")); - assert_eq!(0x0000_0000_0011_3000, s6.start_address()); - assert_eq!(0x0000_0000_0011_5BE0, s6.end_address()); - assert_eq!(0x0000_0000_0000_2BE0, s6.size()); + assert_eq!( + ".symtab", + s6.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(0x0000_0000_0011_3000, s6.sh_addr); + assert_eq!(0x0000_0000_0000_2BE0, s6.sh_size); assert_eq!(ElfSectionFlags::empty(), s6.flags()); assert_eq!(ElfSectionType::LinkerSymbolTable, s6.section_type()); + let s7 = es.next().expect("Should have one more section"); - assert_eq!(".strtab", s7.name().expect("Should be valid utf-8")); - assert_eq!(0x0000_0000_0011_5BE0, s7.start_address()); - assert_eq!(0x0000_0000_0011_9371, s7.end_address()); - assert_eq!(0x0000_0000_0000_3791, s7.size()); + assert_eq!( + ".strtab", + s7.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(0x0000_0000_0011_5BE0, s7.sh_addr); + assert_eq!(0x0000_0000_0000_3791, s7.sh_size); assert_eq!(ElfSectionFlags::empty(), s7.flags()); assert_eq!(ElfSectionType::StringTable, s7.section_type()); + let s8 = es.next().expect("Should have one more section"); - assert_eq!(".shstrtab", s8.name().expect("Should be valid utf-8")); - assert_eq!(string_addr, s8.start_address()); - assert_eq!(string_addr + string_bytes.len() as u64, s8.end_address()); - assert_eq!(string_bytes.len() as u64, s8.size()); + assert_eq!( + ".shstrtab", + s8.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(string_addr, s8.sh_addr); + assert_eq!(string_bytes.len() as u64, s8.sh_size); assert_eq!(ElfSectionFlags::empty(), s8.flags()); assert_eq!(ElfSectionType::StringTable, s8.section_type()); assert!(es.next().is_none()); + let mut mm = bi .memory_map_tag() .unwrap() @@ -1023,12 +1074,36 @@ mod tests { assert_eq!(addr, bi.start_address()); assert_eq!(addr + bytes.0.len(), bi.end_address()); assert_eq!(bytes.0.len(), bi.total_size()); + let strtab = bi.elf_sections_tag().unwrap().string_table().unwrap(); let mut es = bi.elf_sections_tag().unwrap().sections(); + + let s0 = es.next().expect("Should have one more sections"); + assert_eq!( + "", + s0.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(0, s0.sh_addr); + assert_eq!(0, s0.sh_size); + assert_eq!(s0.section_type(), ElfSectionType::Unused); + assert_eq!(s0.flags(), ElfSectionFlags::empty()); + let s1 = es.next().expect("Should have one more section"); - assert_eq!(".shstrtab", s1.name().expect("Should be valid utf-8")); - assert_eq!(string_addr, s1.start_address()); - assert_eq!(string_addr + string_bytes.0.len() as u64, s1.end_address()); - assert_eq!(string_bytes.0.len() as u64, s1.size()); + assert_eq!( + ".shstrtab", + s1.name_from_string_table(strtab) + .expect("No NULL byte found") + .to_str() + .expect("Should be valid utf-8") + ); + assert_eq!(string_addr, s1.sh_addr); + assert_eq!( + string_addr + string_bytes.0.len() as u64, + s1.sh_addr + s1.sh_size + ); + assert_eq!(string_bytes.0.len() as u64, s1.sh_size); assert_eq!(ElfSectionFlags::empty(), s1.flags()); assert_eq!(ElfSectionType::StringTable, s1.section_type()); assert!(es.next().is_none());