use super::addrsig::{ADDRSIG_SECTION, AddrsigIter};
use super::consts::{
FEAT00_GUARD_CF, FEAT00_GUARD_EHCONT, FEAT00_GUARD_STACK, FEAT00_KERNEL, FEAT00_SAFESEH,
FEAT00_SDL, machine_architecture,
};
use super::directives::{DRECTVE_SECTION, Directives, parse_directives};
use super::header::{FileHeader, is_bigobj, is_import_object};
use super::reloc::{RELOCATION_SIZE, Relocation, Relocations};
use super::section::{SECTION_HEADER_SIZE, SectionHeader, SectionTable};
use super::source::{Source, subslice, to_u64};
use super::symbol::{Symbol, SymbolTable};
use crate::error::Result;
use crate::target::Architecture;
pub const FEAT00_SYMBOL: &[u8] = b"@feat.00";
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Section<'a> {
pub number: u32,
pub header: SectionHeader,
pub name: &'a [u8],
}
impl Section<'_> {
#[must_use]
pub fn is_resource(&self) -> bool {
is_resource_section_name(self.name)
}
#[must_use]
pub fn is_tls(&self) -> bool {
self.name == b".tls" || self.name.starts_with(b".tls$")
}
}
#[must_use]
pub fn is_resource_section_name(name: &[u8]) -> bool {
name == b".rsrc" || name.starts_with(b".rsrc$")
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Feat00(pub u32);
impl Feat00 {
#[must_use]
pub fn safe_seh(self) -> bool {
self.0 & FEAT00_SAFESEH != 0
}
#[must_use]
pub fn guard_stack(self) -> bool {
self.0 & FEAT00_GUARD_STACK != 0
}
#[must_use]
pub fn sdl(self) -> bool {
self.0 & FEAT00_SDL != 0
}
#[must_use]
pub fn guard_cf(self) -> bool {
self.0 & FEAT00_GUARD_CF != 0
}
#[must_use]
pub fn guard_ehcont(self) -> bool {
self.0 & FEAT00_GUARD_EHCONT != 0
}
#[must_use]
pub fn kernel(self) -> bool {
self.0 & FEAT00_KERNEL != 0
}
}
#[derive(Clone, Copy, Debug)]
pub struct CoffObject<'a> {
data: &'a [u8],
source: Source<'a>,
header: FileHeader,
sections: SectionTable<'a>,
symbols: SymbolTable<'a>,
}
impl<'a> CoffObject<'a> {
pub fn parse(data: &'a [u8], source: Source<'a>) -> Result<Self> {
let header = if is_bigobj(data) {
FileHeader::parse_bigobj(data)
} else if is_import_object(data) {
return Err(source.malformed(0, "COFF object (a short import object)"));
} else if data.starts_with(&[0, 0, 0xff, 0xff]) {
return Err(
source.malformed(0, "COFF object (an anonymous object, such as MSVC /GL IR)")
);
} else if data.starts_with(b"MZ") {
return Err(source.malformed(0, "COFF object (a PE image)"));
} else {
FileHeader::parse_regular(data)
}
.ok_or_else(|| source.malformed(0, "COFF file header (truncated)"))?;
let table_offset = header
.header_size()
.checked_add(usize::from(header.size_of_optional_header))
.ok_or_else(|| source.malformed(16, "optional header size"))?;
let table_size =
u64::from(header.number_of_sections).saturating_mul(to_u64(SECTION_HEADER_SIZE));
let table = subslice(data, to_u64(table_offset), table_size).ok_or_else(|| {
source.malformed(to_u64(table_offset), "section table (out of bounds)")
})?;
let sections = SectionTable::from_bytes(table, to_u64(table_offset));
let symbols = SymbolTable::parse(
data,
header.pointer_to_symbol_table,
header.number_of_symbols,
header.bigobj,
source,
)?;
Ok(Self {
data,
source,
header,
sections,
symbols,
})
}
#[must_use]
pub fn data(&self) -> &'a [u8] {
self.data
}
#[must_use]
pub fn source(&self) -> Source<'a> {
self.source
}
#[must_use]
pub fn header(&self) -> &FileHeader {
&self.header
}
#[must_use]
pub fn machine(&self) -> u16 {
self.header.machine
}
#[must_use]
pub fn architecture(&self) -> Option<Architecture> {
machine_architecture(self.header.machine)
}
#[must_use]
pub fn is_bigobj(&self) -> bool {
self.header.bigobj
}
#[must_use]
pub fn section_count(&self) -> u32 {
self.header.number_of_sections
}
#[must_use]
pub fn section_table(&self) -> SectionTable<'a> {
self.sections
}
#[must_use]
pub fn symbols(&self) -> SymbolTable<'a> {
self.symbols
}
pub fn symbol(&self, index: u32) -> Result<Symbol<'a>> {
self.symbols.get(index)
}
pub fn section(&self, number: u32) -> Result<Section<'a>> {
let header = self.sections.get(number).ok_or_else(|| {
self.source.malformed(
self.sections.file_offset(),
format!("section number {number} (out of range)"),
)
})?;
let name = self
.sections
.name(number, &self.symbols.strings())
.ok_or_else(|| {
self.source.malformed(
self.sections.header_offset(number),
format!("section name (section {number})"),
)
})?;
Ok(Section {
number,
header,
name,
})
}
pub fn sections(&self) -> impl Iterator<Item = Result<Section<'a>>> + '_ {
(1..=self.header.number_of_sections).map(|number| self.section(number))
}
pub fn sections_named<'n>(
&self,
name: &'n [u8],
) -> impl Iterator<Item = Result<Section<'a>>> + use<'_, 'a, 'n> {
self.sections()
.filter(move |section| section.as_ref().map_or(true, |s| s.name == name))
}
pub fn section_data(&self, header: &SectionHeader) -> Result<&'a [u8]> {
if header.pointer_to_raw_data == 0 {
return Ok(&[]);
}
let offset = u64::from(header.pointer_to_raw_data);
subslice(self.data, offset, u64::from(header.size_of_raw_data)).ok_or_else(|| {
self.source
.malformed(offset, "section contents (out of bounds)")
})
}
pub fn relocations(&self, header: &SectionHeader) -> Result<Relocations<'a>> {
let mut offset = u64::from(header.pointer_to_relocations);
let mut count = u64::from(header.number_of_relocations);
if count == 0 {
return Ok(Relocations::default());
}
if header.has_extended_relocations() {
let first = subslice(self.data, offset, to_u64(RELOCATION_SIZE))
.and_then(|b| b.first_chunk::<RELOCATION_SIZE>())
.ok_or_else(|| self.source.malformed(offset, "relocations (out of bounds)"))?;
let total = Relocation::decode(first).virtual_address;
count = u64::from(total).checked_sub(1).ok_or_else(|| {
self.source
.malformed(offset, "extended relocation count (zero)")
})?;
offset = offset.saturating_add(to_u64(RELOCATION_SIZE));
}
let bytes = subslice(
self.data,
offset,
count.saturating_mul(to_u64(RELOCATION_SIZE)),
)
.ok_or_else(|| self.source.malformed(offset, "relocations (out of bounds)"))?;
let (records, _) = bytes.as_chunks::<RELOCATION_SIZE>();
Ok(Relocations::new(records, offset))
}
pub fn directives(&self) -> impl Iterator<Item = Result<Directives<'a>>> + '_ {
self.sections_named(DRECTVE_SECTION).map(|section| {
let section = section?;
let data = self.section_data(§ion.header)?;
Ok(parse_directives(
data,
u64::from(section.header.pointer_to_raw_data),
self.source,
))
})
}
pub fn addrsig(&self) -> Result<Option<AddrsigIter<'a>>> {
match self.sections_named(ADDRSIG_SECTION).next() {
Some(section) => Ok(Some(AddrsigIter::new(self.section_data(§ion?.header)?))),
None => Ok(None),
}
}
pub fn feat00(&self) -> Result<Option<Feat00>> {
for symbol in self.symbols.iter() {
let symbol = symbol?;
if symbol.name == FEAT00_SYMBOL && symbol.is_absolute() {
return Ok(Some(Feat00(symbol.value)));
}
}
Ok(None)
}
pub fn resource_sections(&self) -> impl Iterator<Item = Result<Section<'a>>> + '_ {
self.sections()
.filter(|section| section.as_ref().map_or(true, Section::is_resource))
}
}