use crate::{Tag, TagTrait, TagTypeId};
use core::fmt::{Debug, Formatter};
use core::mem::size_of;
use core::str::Utf8Error;
#[cfg(feature = "builder")]
use {crate::builder::traits::StructAsBytes, crate::builder::BoxedDst, crate::TagType};
const METADATA_SIZE: usize = size_of::<TagTypeId>() + 4 * size_of::<u32>();
#[derive(ptr_meta::Pointee, PartialEq, Eq)]
#[repr(C)]
pub struct ElfSectionsTag {
typ: TagTypeId,
pub(crate) size: u32,
number_of_sections: u32,
pub(crate) entry_size: u32,
pub(crate) shndx: u32, sections: [u8],
}
impl ElfSectionsTag {
#[cfg(feature = "builder")]
pub fn new(
number_of_sections: u32,
entry_size: u32,
shndx: u32,
sections: &[u8],
) -> BoxedDst<Self> {
let mut bytes = [
number_of_sections.to_le_bytes(),
entry_size.to_le_bytes(),
shndx.to_le_bytes(),
]
.concat();
bytes.extend_from_slice(sections);
BoxedDst::new(TagType::ElfSections, &bytes)
}
pub(crate) fn sections(&self) -> ElfSectionIter {
let string_section_offset = (self.shndx * self.entry_size) as isize;
let string_section_ptr =
unsafe { self.first_section().offset(string_section_offset) as *const _ };
ElfSectionIter {
current_section: self.first_section(),
remaining_sections: self.number_of_sections,
entry_size: self.entry_size,
string_section: string_section_ptr,
}
}
fn first_section(&self) -> *const u8 {
&(self.sections[0]) as *const _
}
}
impl TagTrait for ElfSectionsTag {
fn dst_size(base_tag: &Tag) -> usize {
assert!(base_tag.size as usize >= METADATA_SIZE);
base_tag.size as usize - METADATA_SIZE
}
}
#[cfg(feature = "builder")]
impl StructAsBytes for ElfSectionsTag {
fn byte_size(&self) -> usize {
self.size.try_into().unwrap()
}
}
impl Debug for ElfSectionsTag {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ElfSectionsTag")
.field("typ", &{ self.typ })
.field("size", &{ self.size })
.field("number_of_sections", &{ self.number_of_sections })
.field("entry_size", &{ self.entry_size })
.field("shndx", &{ self.shndx })
.field("sections", &self.sections())
.finish()
}
}
#[derive(Clone)]
pub struct ElfSectionIter {
current_section: *const u8,
remaining_sections: u32,
entry_size: u32,
string_section: *const u8,
}
impl Iterator for ElfSectionIter {
type Item = ElfSection;
fn next(&mut self) -> Option<ElfSection> {
while self.remaining_sections != 0 {
let section = ElfSection {
inner: self.current_section,
string_section: self.string_section,
entry_size: self.entry_size,
};
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
}
}
impl Debug for ElfSectionIter {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let mut debug = f.debug_list();
self.clone().for_each(|ref e| {
debug.entry(e);
});
debug.finish()
}
}
impl Default for ElfSectionIter {
fn default() -> Self {
Self {
current_section: core::ptr::null(),
remaining_sections: 0,
entry_size: 0,
string_section: core::ptr::null(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ElfSection {
inner: *const u8,
string_section: *const u8,
entry_size: u32,
}
#[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 {
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 {:x}. Treating as ElfSectionType::Unused",
e
);
ElfSectionType::Unused
}
}
}
pub fn section_type_raw(&self) -> u32 {
self.get().typ()
}
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) };
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) })
}
pub fn start_address(&self) -> u64 {
self.get().addr()
}
pub fn end_address(&self) -> u64 {
self.get().addr() + self.get().size()
}
pub fn size(&self) -> u64 {
self.get().size()
}
pub fn addralign(&self) -> u64 {
self.get().addralign()
}
pub fn flags(&self) -> ElfSectionFlags {
ElfSectionFlags::from_bits_truncate(self.get().flags())
}
pub fn is_allocated(&self) -> bool {
self.flags().contains(ElfSectionFlags::ALLOCATED)
}
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),
}
}
unsafe fn string_table(&self) -> *const u8 {
let addr = match self.entry_size {
40 => (*(self.string_section as *const ElfSectionInner32)).addr as usize,
64 => (*(self.string_section as *const ElfSectionInner64)).addr as usize,
s => panic!("Unexpected entry size: {}", s),
};
addr as *const _
}
}
trait ElfSectionInner {
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) -> 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
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u32)]
pub enum ElfSectionType {
Unused = 0,
ProgramSection = 1,
LinkerSymbolTable = 2,
StringTable = 3,
RelaRelocation = 4,
SymbolHashTable = 5,
DynamicLinkingTable = 6,
Note = 7,
Uninitialized = 8,
RelRelocation = 9,
Reserved = 10,
DynamicLoaderSymbolTable = 11,
EnvironmentSpecific = 0x6000_0000,
ProcessorSpecific = 0x7000_0000,
}
bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct ElfSectionFlags: u64 {
const WRITABLE = 0x1;
const ALLOCATED = 0x2;
const EXECUTABLE = 0x4;
}
}