use alloc::string::String;
use alloc::vec::Vec;
use core::ops::{BitOr, BitOrAssign};
use crate::error::ModelError;
use crate::reloc::Relocation;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum SectionKind {
Text,
Data,
ReadOnlyData,
Bss,
Note,
InitArray,
FiniArray,
PreinitArray,
Other,
}
impl SectionKind {
#[must_use]
pub const fn default_flags(self) -> SectionFlags {
match self {
SectionKind::Text => SectionFlags(SectionFlags::ALLOC.0 | SectionFlags::EXEC.0),
SectionKind::Data
| SectionKind::Bss
| SectionKind::InitArray
| SectionKind::FiniArray
| SectionKind::PreinitArray => {
SectionFlags(SectionFlags::ALLOC.0 | SectionFlags::WRITE.0)
}
SectionKind::ReadOnlyData => SectionFlags::ALLOC,
SectionKind::Note | SectionKind::Other => SectionFlags::empty(),
}
}
#[must_use]
pub const fn is_uninitialized(self) -> bool {
matches!(self, SectionKind::Bss)
}
#[must_use]
pub const fn allows(self, flags: SectionFlags) -> bool {
let (required, forbidden) = match self {
SectionKind::Text => (
SectionFlags::ALLOC.0 | SectionFlags::EXEC.0,
SectionFlags::TLS.0,
),
SectionKind::Data => (
SectionFlags::ALLOC.0 | SectionFlags::WRITE.0,
SectionFlags::EXEC.0,
),
SectionKind::ReadOnlyData => (
SectionFlags::ALLOC.0,
SectionFlags::WRITE.0 | SectionFlags::EXEC.0 | SectionFlags::TLS.0,
),
SectionKind::Bss => (
SectionFlags::ALLOC.0,
SectionFlags::EXEC.0 | SectionFlags::MERGE.0 | SectionFlags::STRINGS.0,
),
SectionKind::Note => (
0,
SectionFlags::WRITE.0
| SectionFlags::EXEC.0
| SectionFlags::TLS.0
| SectionFlags::MERGE.0
| SectionFlags::STRINGS.0,
),
SectionKind::InitArray | SectionKind::FiniArray | SectionKind::PreinitArray => (
SectionFlags::ALLOC.0 | SectionFlags::WRITE.0,
SectionFlags::EXEC.0
| SectionFlags::TLS.0
| SectionFlags::MERGE.0
| SectionFlags::STRINGS.0,
),
SectionKind::Other => (
0,
SectionFlags::ALLOC.0
| SectionFlags::WRITE.0
| SectionFlags::EXEC.0
| SectionFlags::TLS.0,
),
};
flags.0 & required == required && flags.0 & forbidden == 0
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct SectionFlags(u8);
impl SectionFlags {
pub const ALLOC: SectionFlags = SectionFlags(1);
pub const WRITE: SectionFlags = SectionFlags(1 << 1);
pub const EXEC: SectionFlags = SectionFlags(1 << 2);
pub const TLS: SectionFlags = SectionFlags(1 << 3);
pub const MERGE: SectionFlags = SectionFlags(1 << 4);
pub const STRINGS: SectionFlags = SectionFlags(1 << 5);
const ALL: u8 = (1 << 6) - 1;
#[must_use]
pub const fn empty() -> Self {
SectionFlags(0)
}
#[must_use]
pub const fn contains(self, other: SectionFlags) -> bool {
self.0 & other.0 == other.0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
#[must_use]
pub const fn union(self, other: SectionFlags) -> Self {
SectionFlags(self.0 | other.0)
}
#[must_use]
pub const fn difference(self, other: SectionFlags) -> Self {
SectionFlags(self.0 & !other.0)
}
#[must_use]
pub const fn bits(self) -> u8 {
self.0
}
#[must_use]
pub const fn from_bits(bits: u8) -> Option<Self> {
if bits & !SectionFlags::ALL == 0 {
Some(SectionFlags(bits))
} else {
None
}
}
}
impl BitOr for SectionFlags {
type Output = SectionFlags;
fn bitor(self, rhs: SectionFlags) -> SectionFlags {
self.union(rhs)
}
}
impl BitOrAssign for SectionFlags {
fn bitor_assign(&mut self, rhs: SectionFlags) {
*self = self.union(rhs);
}
}
impl core::fmt::Debug for SectionFlags {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
const NAMES: [&str; 6] = ["ALLOC", "WRITE", "EXEC", "TLS", "MERGE", "STRINGS"];
if self.0 == 0 {
return f.write_str("SectionFlags(empty)");
}
f.write_str("SectionFlags(")?;
let mut first = true;
for (bit, name) in NAMES.iter().enumerate() {
if self.0 & (1 << bit) != 0 {
if !first {
f.write_str(" | ")?;
}
f.write_str(name)?;
first = false;
}
}
f.write_str(")")
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Section {
name: String,
kind: SectionKind,
flags: SectionFlags,
align: u64,
entry_size: u64,
address: u64,
data: Vec<u8>,
bss_size: u64,
relocations: Vec<Relocation>,
}
impl Section {
#[must_use]
pub fn new(name: impl Into<String>, kind: SectionKind) -> Self {
Section {
name: name.into(),
kind,
flags: kind.default_flags(),
align: 1,
entry_size: 0,
address: 0,
data: Vec::new(),
bss_size: 0,
relocations: Vec::new(),
}
}
#[must_use]
pub fn with_flags(mut self, flags: SectionFlags) -> Self {
self.flags = flags;
self
}
#[must_use]
pub fn with_align(mut self, align: u64) -> Self {
self.align = align;
self
}
#[must_use]
pub fn with_entry_size(mut self, entry_size: u64) -> Self {
self.entry_size = entry_size;
self
}
#[must_use]
pub fn with_address(mut self, address: u64) -> Self {
self.address = address;
self
}
#[must_use]
pub fn with_data(mut self, data: Vec<u8>) -> Self {
self.data = data;
self
}
#[must_use]
pub fn with_bss_size(mut self, size: u64) -> Self {
self.bss_size = size;
self
}
pub fn append(&mut self, bytes: &[u8], align: u64) -> Result<u64, ModelError> {
if self.kind.is_uninitialized() {
return Err(ModelError::AppendToBss);
}
let offset = self.next_offset(align)?;
let len = u64::try_from(bytes.len()).map_err(|_| ModelError::SizeOverflow)?;
let end = offset.checked_add(len).ok_or(ModelError::SizeOverflow)?;
self.grow_to(offset, end)?;
self.data.extend_from_slice(bytes);
self.align = self.align.max(align);
Ok(offset)
}
pub fn reserve(&mut self, size: u64, align: u64) -> Result<u64, ModelError> {
if self.kind.is_uninitialized() {
if !align.is_power_of_two() {
return Err(ModelError::InvalidAlignment { align });
}
let offset = align_up(self.bss_size, align).ok_or(ModelError::SizeOverflow)?;
self.bss_size = offset.checked_add(size).ok_or(ModelError::SizeOverflow)?;
self.align = self.align.max(align);
return Ok(offset);
}
let offset = self.next_offset(align)?;
let end = offset.checked_add(size).ok_or(ModelError::SizeOverflow)?;
self.grow_to(end, end)?;
self.align = self.align.max(align);
Ok(offset)
}
fn next_offset(&self, align: u64) -> Result<u64, ModelError> {
if !align.is_power_of_two() {
return Err(ModelError::InvalidAlignment { align });
}
let len = u64::try_from(self.data.len()).map_err(|_| ModelError::SizeOverflow)?;
align_up(len, align).ok_or(ModelError::SizeOverflow)
}
fn grow_to(&mut self, fill: u64, end: u64) -> Result<(), ModelError> {
let fill = usize::try_from(fill).map_err(|_| ModelError::SizeOverflow)?;
let end = usize::try_from(end).map_err(|_| ModelError::SizeOverflow)?;
let additional = end.saturating_sub(self.data.len());
self.data
.try_reserve(additional)
.map_err(|_| ModelError::SizeOverflow)?;
self.data.resize(fill, 0);
Ok(())
}
pub fn add_relocation(&mut self, relocation: Relocation) {
self.relocations.push(relocation);
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub const fn kind(&self) -> SectionKind {
self.kind
}
#[must_use]
pub const fn flags(&self) -> SectionFlags {
self.flags
}
#[must_use]
pub const fn align(&self) -> u64 {
self.align
}
#[must_use]
pub const fn entry_size(&self) -> u64 {
self.entry_size
}
#[must_use]
pub const fn address(&self) -> u64 {
self.address
}
#[must_use]
pub fn data(&self) -> &[u8] {
&self.data
}
#[must_use]
pub fn size(&self) -> u64 {
if self.kind.is_uninitialized() {
self.bss_size
} else {
u64::try_from(self.data.len()).unwrap_or(u64::MAX)
}
}
#[must_use]
pub fn relocations(&self) -> &[Relocation] {
&self.relocations
}
pub(crate) const fn bss_size(&self) -> u64 {
self.bss_size
}
pub(crate) fn set_relocations(&mut self, relocations: Vec<Relocation>) {
self.relocations = relocations;
}
}
pub(crate) const fn align_up(value: u64, align: u64) -> Option<u64> {
let mask = align.wrapping_sub(1);
match value.checked_add(mask) {
Some(v) => Some(v & !mask),
None => None,
}
}