feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
//! Support for writing COFF files.
//!
//! Provides [`Writer`] for low level writing of COFF files.
//! This is also used to provide COFF support for [`write::Object`](crate::write::Object).
mod object;
pub use self::object::*;
mod writer;
pub use writer::*;
+684
View File
@@ -0,0 +1,684 @@
use alloc::vec::Vec;
use crate::pe as coff;
use crate::write::coff::writer;
use crate::write::util::*;
use crate::write::*;
#[derive(Default, Clone, Copy)]
struct SectionOffsets {
name: writer::Name,
offset: u32,
reloc_offset: u32,
selection: u8,
associative_section: u32,
}
#[derive(Default, Clone, Copy)]
struct SymbolOffsets {
name: writer::Name,
index: u32,
aux_count: u8,
}
/// Internal format to use for the `.drectve` section containing linker
/// directives for symbol exports.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CoffExportStyle {
/// MSVC format supported by link.exe and LLD.
Msvc,
/// Gnu format supported by GNU LD and LLD.
Gnu,
}
impl<'a> Object<'a> {
pub(crate) fn coff_section_info(
&self,
section: StandardSection,
) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) {
match section {
StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None),
StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None),
StandardSection::ReadOnlyData
| StandardSection::ReadOnlyDataWithRel
| StandardSection::ReadOnlyString => (
&[],
&b".rdata"[..],
SectionKind::ReadOnlyData,
SectionFlags::None,
),
StandardSection::UninitializedData => (
&[],
&b".bss"[..],
SectionKind::UninitializedData,
SectionFlags::None,
),
// TLS sections are data sections with a special name.
StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Data, SectionFlags::None),
StandardSection::UninitializedTls => {
// Unsupported section.
(&[], &[], SectionKind::UninitializedTls, SectionFlags::None)
}
StandardSection::TlsVariables => {
// Unsupported section.
(&[], &[], SectionKind::TlsVariables, SectionFlags::None)
}
StandardSection::Common => {
// Unsupported section.
(&[], &[], SectionKind::Common, SectionFlags::None)
}
StandardSection::GnuProperty => {
// Unsupported section.
(&[], &[], SectionKind::Note, SectionFlags::None)
}
}
}
pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> {
let mut name = section.to_vec();
if !value.is_empty() {
name.push(b'$');
name.extend_from_slice(value);
}
name
}
pub(crate) fn coff_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> {
use RelocationEncoding as E;
use RelocationKind as K;
let (mut kind, encoding, size) = if let RelocationFlags::Generic {
kind,
encoding,
size,
} = reloc.flags
{
(kind, encoding, size)
} else {
return Ok(());
};
if kind == K::GotRelative {
// Use a stub symbol for the relocation instead.
// This isn't really a GOT, but it's a similar purpose.
// TODO: need to handle DLL imports differently?
kind = K::Relative;
reloc.symbol = self.coff_add_stub_symbol(reloc.symbol)?;
} else if kind == K::PltRelative {
// Windows doesn't need a separate relocation type for
// references to functions in import libraries.
// For convenience, treat this the same as Relative.
kind = K::Relative;
}
let unsupported_reloc = || Err(Error(format!("unimplemented relocation {:?}", reloc)));
let typ = match self.architecture {
Architecture::I386 => match (kind, size) {
(K::Absolute, 16) => coff::IMAGE_REL_I386_DIR16,
(K::Relative, 16) => coff::IMAGE_REL_I386_REL16,
(K::Absolute, 32) => coff::IMAGE_REL_I386_DIR32,
(K::ImageOffset, 32) => coff::IMAGE_REL_I386_DIR32NB,
(K::SectionIndex, 16) => coff::IMAGE_REL_I386_SECTION,
(K::SectionOffset, 32) => coff::IMAGE_REL_I386_SECREL,
(K::SectionOffset, 7) => coff::IMAGE_REL_I386_SECREL7,
(K::Relative, 32) => coff::IMAGE_REL_I386_REL32,
_ => return unsupported_reloc(),
},
Architecture::X86_64 => match (kind, size) {
(K::Absolute, 64) => coff::IMAGE_REL_AMD64_ADDR64,
(K::Absolute, 32) => coff::IMAGE_REL_AMD64_ADDR32,
(K::ImageOffset, 32) => coff::IMAGE_REL_AMD64_ADDR32NB,
(K::Relative, 32) => match reloc.addend {
-5 => coff::IMAGE_REL_AMD64_REL32_1,
-6 => coff::IMAGE_REL_AMD64_REL32_2,
-7 => coff::IMAGE_REL_AMD64_REL32_3,
-8 => coff::IMAGE_REL_AMD64_REL32_4,
-9 => coff::IMAGE_REL_AMD64_REL32_5,
_ => coff::IMAGE_REL_AMD64_REL32,
},
(K::SectionIndex, 16) => coff::IMAGE_REL_AMD64_SECTION,
(K::SectionOffset, 32) => coff::IMAGE_REL_AMD64_SECREL,
(K::SectionOffset, 7) => coff::IMAGE_REL_AMD64_SECREL7,
_ => return unsupported_reloc(),
},
Architecture::Arm => match (kind, size) {
(K::Absolute, 32) => coff::IMAGE_REL_ARM_ADDR32,
(K::ImageOffset, 32) => coff::IMAGE_REL_ARM_ADDR32NB,
(K::Relative, 32) => coff::IMAGE_REL_ARM_REL32,
(K::SectionIndex, 16) => coff::IMAGE_REL_ARM_SECTION,
(K::SectionOffset, 32) => coff::IMAGE_REL_ARM_SECREL,
_ => return unsupported_reloc(),
},
Architecture::Aarch64 => match (kind, encoding, size) {
(K::Absolute, _, 32) => coff::IMAGE_REL_ARM64_ADDR32,
(K::ImageOffset, _, 32) => coff::IMAGE_REL_ARM64_ADDR32NB,
(K::SectionIndex, _, 16) => coff::IMAGE_REL_ARM64_SECTION,
(K::SectionOffset, _, 32) => coff::IMAGE_REL_ARM64_SECREL,
(K::Absolute, _, 64) => coff::IMAGE_REL_ARM64_ADDR64,
(K::Relative, _, 32) => coff::IMAGE_REL_ARM64_REL32,
(K::Relative, E::AArch64Call, 26) => coff::IMAGE_REL_ARM64_BRANCH26,
_ => return unsupported_reloc(),
},
_ => {
return Err(Error(format!(
"unimplemented architecture {:?}",
self.architecture
)));
}
};
reloc.flags = RelocationFlags::Coff { typ };
Ok(())
}
pub(crate) fn coff_adjust_addend(&self, relocation: &mut Relocation) -> Result<bool> {
let typ = if let RelocationFlags::Coff { typ } = relocation.flags {
typ
} else {
return Err(Error(format!("invalid relocation flags {:?}", relocation)));
};
let offset = match self.architecture {
Architecture::Arm => {
if typ == coff::IMAGE_REL_ARM_REL32 {
4
} else {
0
}
}
Architecture::Aarch64 => {
if typ == coff::IMAGE_REL_ARM64_REL32 {
4
} else {
0
}
}
Architecture::I386 => {
if typ == coff::IMAGE_REL_I386_REL32 {
4
} else {
0
}
}
Architecture::X86_64 => match typ {
coff::IMAGE_REL_AMD64_REL32 => 4,
coff::IMAGE_REL_AMD64_REL32_1 => 5,
coff::IMAGE_REL_AMD64_REL32_2 => 6,
coff::IMAGE_REL_AMD64_REL32_3 => 7,
coff::IMAGE_REL_AMD64_REL32_4 => 8,
coff::IMAGE_REL_AMD64_REL32_5 => 9,
_ => 0,
},
_ => return Err(Error(format!("unimplemented relocation {:?}", relocation))),
};
relocation.addend += offset;
Ok(true)
}
pub(crate) fn coff_relocation_size(&self, reloc: &Relocation) -> Result<u8> {
let typ = if let RelocationFlags::Coff { typ } = reloc.flags {
typ
} else {
return Err(Error(format!("unexpected relocation for size {:?}", reloc)));
};
let size = match self.architecture {
Architecture::I386 => match typ {
coff::IMAGE_REL_I386_DIR16
| coff::IMAGE_REL_I386_REL16
| coff::IMAGE_REL_I386_SECTION => Some(16),
coff::IMAGE_REL_I386_DIR32
| coff::IMAGE_REL_I386_DIR32NB
| coff::IMAGE_REL_I386_SECREL
| coff::IMAGE_REL_I386_TOKEN
| coff::IMAGE_REL_I386_REL32 => Some(32),
_ => None,
},
Architecture::X86_64 => match typ {
coff::IMAGE_REL_AMD64_SECTION => Some(16),
coff::IMAGE_REL_AMD64_ADDR32
| coff::IMAGE_REL_AMD64_ADDR32NB
| coff::IMAGE_REL_AMD64_REL32
| coff::IMAGE_REL_AMD64_REL32_1
| coff::IMAGE_REL_AMD64_REL32_2
| coff::IMAGE_REL_AMD64_REL32_3
| coff::IMAGE_REL_AMD64_REL32_4
| coff::IMAGE_REL_AMD64_REL32_5
| coff::IMAGE_REL_AMD64_SECREL
| coff::IMAGE_REL_AMD64_TOKEN => Some(32),
coff::IMAGE_REL_AMD64_ADDR64 => Some(64),
_ => None,
},
Architecture::Arm => match typ {
coff::IMAGE_REL_ARM_SECTION => Some(16),
coff::IMAGE_REL_ARM_ADDR32
| coff::IMAGE_REL_ARM_ADDR32NB
| coff::IMAGE_REL_ARM_TOKEN
| coff::IMAGE_REL_ARM_REL32
| coff::IMAGE_REL_ARM_SECREL => Some(32),
_ => None,
},
Architecture::Aarch64 => match typ {
coff::IMAGE_REL_ARM64_SECTION => Some(16),
coff::IMAGE_REL_ARM64_ADDR32
| coff::IMAGE_REL_ARM64_ADDR32NB
| coff::IMAGE_REL_ARM64_SECREL
| coff::IMAGE_REL_ARM64_TOKEN
| coff::IMAGE_REL_ARM64_REL32 => Some(32),
coff::IMAGE_REL_ARM64_ADDR64 => Some(64),
_ => None,
},
_ => None,
};
size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc)))
}
fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> Result<SymbolId> {
if let Some(stub_id) = self.stub_symbols.get(&symbol_id) {
return Ok(*stub_id);
}
let stub_size = self.architecture.address_size().unwrap().bytes();
let name = b".rdata$.refptr".to_vec();
let section_id = self.add_section(Vec::new(), name, SectionKind::ReadOnlyData);
let section = self.section_mut(section_id);
section.set_data(vec![0; stub_size as usize], u64::from(stub_size));
self.add_relocation(
section_id,
Relocation {
offset: 0,
symbol: symbol_id,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: stub_size * 8,
},
},
)?;
let mut name = b".refptr.".to_vec();
name.extend_from_slice(&self.symbol(symbol_id).name);
let stub_id = self.add_raw_symbol(Symbol {
name,
value: 0,
size: u64::from(stub_size),
kind: SymbolKind::Data,
scope: SymbolScope::Compilation,
weak: false,
section: SymbolSection::Section(section_id),
flags: SymbolFlags::None,
});
self.stub_symbols.insert(symbol_id, stub_id);
Ok(stub_id)
}
/// Appends linker directives to the `.drectve` section to tell the linker
/// to export all symbols with `SymbolScope::Dynamic`.
///
/// This must be called after all symbols have been defined.
pub fn add_coff_exports(&mut self, style: CoffExportStyle) {
assert_eq!(self.format, BinaryFormat::Coff);
let mut directives = vec![];
for symbol in &self.symbols {
if symbol.scope == SymbolScope::Dynamic {
match style {
CoffExportStyle::Msvc => directives.extend(b" /EXPORT:\""),
CoffExportStyle::Gnu => directives.extend(b" -export:\""),
}
directives.extend(&symbol.name);
directives.extend(b"\"");
if symbol.kind != SymbolKind::Text {
match style {
CoffExportStyle::Msvc => directives.extend(b",DATA"),
CoffExportStyle::Gnu => directives.extend(b",data"),
}
}
}
}
let drectve = self.add_section(vec![], b".drectve".to_vec(), SectionKind::Linker);
self.append_section_data(drectve, &directives, 1);
}
pub(crate) fn coff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
let mut writer = writer::Writer::new(buffer);
// Add section strings to strtab.
let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()];
for (index, section) in self.sections.iter().enumerate() {
section_offsets[index].name = writer.add_name(&section.name);
}
// Set COMDAT flags.
for comdat in &self.comdats {
let symbol = &self.symbols[comdat.symbol.0];
let comdat_section = match symbol.section {
SymbolSection::Section(id) => id.0,
_ => {
return Err(Error(format!(
"unsupported COMDAT symbol `{}` section {:?}",
symbol.name().unwrap_or(""),
symbol.section
)));
}
};
section_offsets[comdat_section].selection = match comdat.kind {
ComdatKind::NoDuplicates => coff::IMAGE_COMDAT_SELECT_NODUPLICATES,
ComdatKind::Any => coff::IMAGE_COMDAT_SELECT_ANY,
ComdatKind::SameSize => coff::IMAGE_COMDAT_SELECT_SAME_SIZE,
ComdatKind::ExactMatch => coff::IMAGE_COMDAT_SELECT_EXACT_MATCH,
ComdatKind::Largest => coff::IMAGE_COMDAT_SELECT_LARGEST,
ComdatKind::Newest => coff::IMAGE_COMDAT_SELECT_NEWEST,
ComdatKind::Unknown => {
return Err(Error(format!(
"unsupported COMDAT symbol `{}` kind {:?}",
symbol.name().unwrap_or(""),
comdat.kind
)));
}
};
for id in &comdat.sections {
let section = &self.sections[id.0];
if section.symbol.is_none() {
return Err(Error(format!(
"missing symbol for COMDAT section `{}`",
section.name().unwrap_or(""),
)));
}
if id.0 != comdat_section {
section_offsets[id.0].selection = coff::IMAGE_COMDAT_SELECT_ASSOCIATIVE;
section_offsets[id.0].associative_section = comdat_section as u32 + 1;
}
}
}
// Reserve symbol indices and add symbol strings to strtab.
let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
for (index, symbol) in self.symbols.iter().enumerate() {
symbol_offsets[index].index = writer.reserve_symbol_index();
let mut name = &*symbol.name;
match symbol.kind {
SymbolKind::File => {
// Name goes in auxiliary symbol records.
symbol_offsets[index].aux_count = writer.reserve_aux_file_name(&symbol.name);
name = b".file";
}
SymbolKind::Section if symbol.section.id().is_some() => {
symbol_offsets[index].aux_count = writer.reserve_aux_section();
}
_ => {}
};
symbol_offsets[index].name = writer.add_name(name);
}
// Reserve file ranges.
writer.reserve_file_header();
writer.reserve_section_headers(self.sections.len() as u16);
for (index, section) in self.sections.iter().enumerate() {
section_offsets[index].offset = writer.reserve_section(section.data.len());
section_offsets[index].reloc_offset =
writer.reserve_relocations(section.relocations.len());
}
writer.reserve_symtab_strtab();
// Start writing.
writer.write_file_header(writer::FileHeader {
machine: match (self.architecture, self.sub_architecture) {
(Architecture::Arm, None) => coff::IMAGE_FILE_MACHINE_ARMNT,
(Architecture::Aarch64, None) => coff::IMAGE_FILE_MACHINE_ARM64,
(Architecture::Aarch64, Some(SubArchitecture::Arm64EC)) => {
coff::IMAGE_FILE_MACHINE_ARM64EC
}
(Architecture::I386, None) => coff::IMAGE_FILE_MACHINE_I386,
(Architecture::X86_64, None) => coff::IMAGE_FILE_MACHINE_AMD64,
_ => {
return Err(Error(format!(
"unimplemented architecture {:?} with sub-architecture {:?}",
self.architecture, self.sub_architecture
)));
}
},
time_date_stamp: 0,
characteristics: match self.flags {
FileFlags::Coff { characteristics } => characteristics,
_ => 0,
},
})?;
// Write section headers.
for (index, section) in self.sections.iter().enumerate() {
let mut characteristics = if let SectionFlags::Coff {
characteristics, ..
} = section.flags
{
characteristics
} else {
match section.kind {
SectionKind::Text => {
coff::IMAGE_SCN_CNT_CODE
| coff::IMAGE_SCN_MEM_EXECUTE
| coff::IMAGE_SCN_MEM_READ
}
SectionKind::Data => {
coff::IMAGE_SCN_CNT_INITIALIZED_DATA
| coff::IMAGE_SCN_MEM_READ
| coff::IMAGE_SCN_MEM_WRITE
}
SectionKind::UninitializedData => {
coff::IMAGE_SCN_CNT_UNINITIALIZED_DATA
| coff::IMAGE_SCN_MEM_READ
| coff::IMAGE_SCN_MEM_WRITE
}
SectionKind::ReadOnlyData
| SectionKind::ReadOnlyDataWithRel
| SectionKind::ReadOnlyString => {
coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ
}
SectionKind::Debug
| SectionKind::DebugString
| SectionKind::Other
| SectionKind::OtherString => {
coff::IMAGE_SCN_CNT_INITIALIZED_DATA
| coff::IMAGE_SCN_MEM_READ
| coff::IMAGE_SCN_MEM_DISCARDABLE
}
SectionKind::Linker => coff::IMAGE_SCN_LNK_INFO | coff::IMAGE_SCN_LNK_REMOVE,
SectionKind::Common
| SectionKind::Tls
| SectionKind::UninitializedTls
| SectionKind::TlsVariables
| SectionKind::Note
| SectionKind::Unknown
| SectionKind::Metadata
| SectionKind::Elf(_) => {
return Err(Error(format!(
"unimplemented section `{}` kind {:?}",
section.name().unwrap_or(""),
section.kind
)));
}
}
};
if section_offsets[index].selection != 0 {
characteristics |= coff::IMAGE_SCN_LNK_COMDAT;
};
if section.relocations.len() > 0xffff {
characteristics |= coff::IMAGE_SCN_LNK_NRELOC_OVFL;
}
characteristics |= match section.align {
1 => coff::IMAGE_SCN_ALIGN_1BYTES,
2 => coff::IMAGE_SCN_ALIGN_2BYTES,
4 => coff::IMAGE_SCN_ALIGN_4BYTES,
8 => coff::IMAGE_SCN_ALIGN_8BYTES,
16 => coff::IMAGE_SCN_ALIGN_16BYTES,
32 => coff::IMAGE_SCN_ALIGN_32BYTES,
64 => coff::IMAGE_SCN_ALIGN_64BYTES,
128 => coff::IMAGE_SCN_ALIGN_128BYTES,
256 => coff::IMAGE_SCN_ALIGN_256BYTES,
512 => coff::IMAGE_SCN_ALIGN_512BYTES,
1024 => coff::IMAGE_SCN_ALIGN_1024BYTES,
2048 => coff::IMAGE_SCN_ALIGN_2048BYTES,
4096 => coff::IMAGE_SCN_ALIGN_4096BYTES,
8192 => coff::IMAGE_SCN_ALIGN_8192BYTES,
_ => {
return Err(Error(format!(
"unimplemented section `{}` align {}",
section.name().unwrap_or(""),
section.align
)));
}
};
writer.write_section_header(writer::SectionHeader {
name: section_offsets[index].name,
size_of_raw_data: section.size as u32,
pointer_to_raw_data: section_offsets[index].offset,
pointer_to_relocations: section_offsets[index].reloc_offset,
pointer_to_linenumbers: 0,
number_of_relocations: section.relocations.len() as u32,
number_of_linenumbers: 0,
characteristics,
});
}
// Write section data and relocations.
for section in &self.sections {
writer.write_section(&section.data);
if !section.relocations.is_empty() {
//debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
writer.write_relocations_count(section.relocations.len());
for reloc in &section.relocations {
let typ = if let RelocationFlags::Coff { typ } = reloc.flags {
typ
} else {
return Err(Error("invalid relocation flags".into()));
};
writer.write_relocation(writer::Relocation {
virtual_address: reloc.offset as u32,
symbol: symbol_offsets[reloc.symbol.0].index,
typ,
});
}
}
}
// Write symbols.
for (index, symbol) in self.symbols.iter().enumerate() {
let section_number = match symbol.section {
SymbolSection::None => {
debug_assert_eq!(symbol.kind, SymbolKind::File);
coff::IMAGE_SYM_DEBUG as u16
}
SymbolSection::Undefined => coff::IMAGE_SYM_UNDEFINED as u16,
SymbolSection::Absolute => coff::IMAGE_SYM_ABSOLUTE as u16,
SymbolSection::Common => coff::IMAGE_SYM_UNDEFINED as u16,
SymbolSection::Section(id) => id.0 as u16 + 1,
};
let typ = if symbol.kind == SymbolKind::Text {
coff::IMAGE_SYM_DTYPE_FUNCTION << coff::IMAGE_SYM_DTYPE_SHIFT
} else {
coff::IMAGE_SYM_TYPE_NULL
};
let storage_class = match symbol.kind {
SymbolKind::File => coff::IMAGE_SYM_CLASS_FILE,
SymbolKind::Section => {
if symbol.section.id().is_some() {
coff::IMAGE_SYM_CLASS_STATIC
} else {
coff::IMAGE_SYM_CLASS_SECTION
}
}
SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL,
SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => {
match symbol.section {
SymbolSection::None => {
return Err(Error(format!(
"missing section for symbol `{}`",
symbol.name().unwrap_or("")
)));
}
SymbolSection::Undefined | SymbolSection::Common => {
coff::IMAGE_SYM_CLASS_EXTERNAL
}
SymbolSection::Absolute | SymbolSection::Section(_) => {
match symbol.scope {
// TODO: does this need aux symbol records too?
_ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL,
SymbolScope::Unknown => {
return Err(Error(format!(
"unimplemented symbol `{}` scope {:?}",
symbol.name().unwrap_or(""),
symbol.scope
)));
}
SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC,
SymbolScope::Linkage | SymbolScope::Dynamic => {
coff::IMAGE_SYM_CLASS_EXTERNAL
}
}
}
}
}
SymbolKind::Unknown => {
return Err(Error(format!(
"unimplemented symbol `{}` kind {:?}",
symbol.name().unwrap_or(""),
symbol.kind
)));
}
};
let number_of_aux_symbols = symbol_offsets[index].aux_count;
let value = if symbol.section == SymbolSection::Common {
symbol.size as u32
} else {
symbol.value as u32
};
writer.write_symbol(writer::Symbol {
name: symbol_offsets[index].name,
value,
section_number,
typ,
storage_class,
number_of_aux_symbols,
});
// Write auxiliary symbols.
match symbol.kind {
SymbolKind::File => {
writer.write_aux_file_name(&symbol.name, number_of_aux_symbols);
}
SymbolKind::Section if symbol.section.id().is_some() => {
debug_assert_eq!(number_of_aux_symbols, 1);
let section_index = symbol.section.id().unwrap().0;
let section = &self.sections[section_index];
writer.write_aux_section(writer::AuxSymbolSection {
length: section.size as u32,
number_of_relocations: section.relocations.len() as u32,
number_of_linenumbers: 0,
check_sum: if section.is_bss() {
0
} else {
checksum(section.data())
},
number: section_offsets[section_index].associative_section,
selection: section_offsets[section_index].selection,
});
}
_ => {
debug_assert_eq!(number_of_aux_symbols, 0);
}
}
}
writer.write_strtab();
debug_assert_eq!(writer.reserved_len(), writer.len());
Ok(())
}
}
// JamCRC
fn checksum(data: &[u8]) -> u32 {
let mut hasher = crc32fast::Hasher::new_with_initial(0xffff_ffff);
hasher.update(data);
!hasher.finalize()
}
+520
View File
@@ -0,0 +1,520 @@
//! Helper for writing COFF files.
use alloc::string::String;
use alloc::vec::Vec;
use core::mem;
use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes, U16, U32};
use crate::pe;
use crate::write::string::{StringId, StringTable};
use crate::write::util;
use crate::write::{Error, Result, WritableBuffer};
/// A helper for writing COFF files.
///
/// Writing uses a two phase approach. The first phase builds up all of the information
/// that may need to be known ahead of time:
/// - build string table
/// - reserve section indices
/// - reserve symbol indices
/// - reserve file ranges for headers and sections
///
/// Some of the information has ordering requirements. For example, strings must be added
/// to the string table before reserving the file range for the string table. There are debug
/// asserts to check some of these requirements.
///
/// The second phase writes everything out in order. Thus the caller must ensure writing
/// is in the same order that file ranges were reserved. There are debug asserts to assist
/// with checking this.
#[allow(missing_debug_implementations)]
pub struct Writer<'a> {
buffer: &'a mut dyn WritableBuffer,
len: usize,
section_num: u16,
symtab_offset: u32,
symtab_num: u32,
strtab: StringTable<'a>,
strtab_len: usize,
strtab_offset: u32,
strtab_data: Vec<u8>,
}
impl<'a> Writer<'a> {
/// Create a new `Writer`.
pub fn new(buffer: &'a mut dyn WritableBuffer) -> Self {
Writer {
buffer,
len: 0,
section_num: 0,
symtab_offset: 0,
symtab_num: 0,
strtab: StringTable::default(),
strtab_len: 0,
strtab_offset: 0,
strtab_data: Vec::new(),
}
}
/// Return the current file length that has been reserved.
pub fn reserved_len(&self) -> usize {
self.len
}
/// Return the current file length that has been written.
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.buffer.len()
}
/// Reserve a file range with the given size and starting alignment.
///
/// Returns the aligned offset of the start of the range.
///
/// `align_start` must be a power of two.
pub fn reserve(&mut self, len: usize, align_start: usize) -> u32 {
if align_start > 1 {
self.len = util::align(self.len, align_start);
}
let offset = self.len;
self.len += len;
offset as u32
}
/// Write alignment padding bytes.
pub fn write_align(&mut self, align_start: usize) {
if align_start > 1 {
util::write_align(self.buffer, align_start);
}
}
/// Write data.
pub fn write(&mut self, data: &[u8]) {
self.buffer.write_bytes(data);
}
/// Reserve the file range up to the given file offset.
pub fn reserve_until(&mut self, offset: usize) {
debug_assert!(self.len <= offset);
self.len = offset;
}
/// Write padding up to the given file offset.
pub fn pad_until(&mut self, offset: usize) {
debug_assert!(self.buffer.len() <= offset);
self.buffer.resize(offset);
}
/// Reserve the range for the file header.
///
/// This must be at the start of the file.
pub fn reserve_file_header(&mut self) {
debug_assert_eq!(self.len, 0);
self.reserve(mem::size_of::<pe::ImageFileHeader>(), 1);
}
/// Write the file header.
///
/// This must be at the start of the file.
///
/// Fields that can be derived from known information are automatically set by this function.
pub fn write_file_header(&mut self, header: FileHeader) -> Result<()> {
debug_assert_eq!(self.buffer.len(), 0);
// Start writing.
self.buffer
.reserve(self.len)
.map_err(|_| Error(String::from("Cannot allocate buffer")))?;
// Write file header.
let header = pe::ImageFileHeader {
machine: U16::new(LE, header.machine),
number_of_sections: U16::new(LE, self.section_num),
time_date_stamp: U32::new(LE, header.time_date_stamp),
pointer_to_symbol_table: U32::new(LE, self.symtab_offset),
number_of_symbols: U32::new(LE, self.symtab_num),
size_of_optional_header: U16::default(),
characteristics: U16::new(LE, header.characteristics),
};
self.buffer.write(&header);
Ok(())
}
/// Reserve the range for the section headers.
pub fn reserve_section_headers(&mut self, section_num: u16) {
debug_assert_eq!(self.section_num, 0);
self.section_num = section_num;
self.reserve(
section_num as usize * mem::size_of::<pe::ImageSectionHeader>(),
1,
);
}
/// Write a section header.
pub fn write_section_header(&mut self, section: SectionHeader) {
let mut coff_section = pe::ImageSectionHeader {
name: [0; 8],
virtual_size: U32::default(),
virtual_address: U32::default(),
size_of_raw_data: U32::new(LE, section.size_of_raw_data),
pointer_to_raw_data: U32::new(LE, section.pointer_to_raw_data),
pointer_to_relocations: U32::new(LE, section.pointer_to_relocations),
pointer_to_linenumbers: U32::new(LE, section.pointer_to_linenumbers),
number_of_relocations: if section.number_of_relocations > 0xffff {
U16::new(LE, 0xffff)
} else {
U16::new(LE, section.number_of_relocations as u16)
},
number_of_linenumbers: U16::default(),
characteristics: U32::new(LE, section.characteristics),
};
match section.name {
Name::Short(name) => coff_section.name = name,
Name::Long(str_id) => {
let mut str_offset = self.strtab.get_offset(str_id);
if str_offset <= 9_999_999 {
let mut name = [0; 7];
let mut len = 0;
if str_offset == 0 {
name[6] = b'0';
len = 1;
} else {
while str_offset != 0 {
let rem = (str_offset % 10) as u8;
str_offset /= 10;
name[6 - len] = b'0' + rem;
len += 1;
}
}
coff_section.name = [0; 8];
coff_section.name[0] = b'/';
coff_section.name[1..][..len].copy_from_slice(&name[7 - len..]);
} else {
debug_assert!(str_offset as u64 <= 0xf_ffff_ffff);
coff_section.name[0] = b'/';
coff_section.name[1] = b'/';
for i in 0..6 {
let rem = (str_offset % 64) as u8;
str_offset /= 64;
let c = match rem {
0..=25 => b'A' + rem,
26..=51 => b'a' + rem - 26,
52..=61 => b'0' + rem - 52,
62 => b'+',
63 => b'/',
_ => unreachable!(),
};
coff_section.name[7 - i] = c;
}
}
}
}
self.buffer.write(&coff_section);
}
/// Reserve the range for the section data.
///
/// Returns the aligned offset of the start of the range.
/// Does nothing and returns 0 if the length is zero.
pub fn reserve_section(&mut self, len: usize) -> u32 {
if len == 0 {
return 0;
}
// TODO: not sure what alignment is required here, but this seems to match LLVM
self.reserve(len, 4)
}
/// Write the alignment bytes prior to section data.
///
/// This is unneeded if you are using `write_section` or `write_section_zeroes`
/// for the data.
pub fn write_section_align(&mut self) {
util::write_align(self.buffer, 4);
}
/// Write the section data.
///
/// Writes alignment bytes prior to the data.
/// Does nothing if the data is empty.
pub fn write_section(&mut self, data: &[u8]) {
if data.is_empty() {
return;
}
self.write_section_align();
self.buffer.write_bytes(data);
}
/// Write the section data using zero bytes.
///
/// Writes alignment bytes prior to the data.
/// Does nothing if the length is zero.
pub fn write_section_zeroes(&mut self, len: usize) {
if len == 0 {
return;
}
self.write_section_align();
self.buffer.resize(self.buffer.len() + len);
}
/// Reserve a file range for the given number of relocations.
///
/// This will automatically reserve an extra relocation if there are more than 0xffff.
///
/// Returns the offset of the range.
/// Does nothing and returns 0 if the count is zero.
pub fn reserve_relocations(&mut self, mut count: usize) -> u32 {
if count == 0 {
return 0;
}
if count > 0xffff {
count += 1;
}
self.reserve(count * mem::size_of::<pe::ImageRelocation>(), 1)
}
/// Write a relocation containing the count if required.
///
/// This should be called before writing the first relocation for a section.
pub fn write_relocations_count(&mut self, count: usize) {
if count > 0xffff {
let coff_relocation = pe::ImageRelocation {
virtual_address: U32Bytes::new(LE, count as u32 + 1),
symbol_table_index: U32Bytes::new(LE, 0),
typ: U16Bytes::new(LE, 0),
};
self.buffer.write(&coff_relocation);
}
}
/// Write a relocation.
pub fn write_relocation(&mut self, reloc: Relocation) {
let coff_relocation = pe::ImageRelocation {
virtual_address: U32Bytes::new(LE, reloc.virtual_address),
symbol_table_index: U32Bytes::new(LE, reloc.symbol),
typ: U16Bytes::new(LE, reloc.typ),
};
self.buffer.write(&coff_relocation);
}
/// Reserve a symbol table entry.
///
/// This must be called before [`Self::reserve_symtab_strtab`].
pub fn reserve_symbol_index(&mut self) -> u32 {
debug_assert_eq!(self.symtab_offset, 0);
let index = self.symtab_num;
self.symtab_num += 1;
index
}
/// Reserve a number of symbol table entries.
pub fn reserve_symbol_indices(&mut self, count: u32) {
debug_assert_eq!(self.symtab_offset, 0);
self.symtab_num += count;
}
/// Write a symbol table entry.
pub fn write_symbol(&mut self, symbol: Symbol) {
let mut coff_symbol = pe::ImageSymbol {
name: [0; 8],
value: U32Bytes::new(LE, symbol.value),
section_number: U16Bytes::new(LE, symbol.section_number),
typ: U16Bytes::new(LE, symbol.typ),
storage_class: symbol.storage_class,
number_of_aux_symbols: symbol.number_of_aux_symbols,
};
match symbol.name {
Name::Short(name) => coff_symbol.name = name,
Name::Long(str_id) => {
let str_offset = self.strtab.get_offset(str_id);
coff_symbol.name[4..8].copy_from_slice(&u32::to_le_bytes(str_offset as u32));
}
}
self.buffer.write(&coff_symbol);
}
/// Reserve auxiliary symbols for a file name.
///
/// Returns the number of auxiliary symbols required.
///
/// This must be called before [`Self::reserve_symtab_strtab`].
pub fn reserve_aux_file_name(&mut self, name: &[u8]) -> u8 {
debug_assert_eq!(self.symtab_offset, 0);
let aux_count = (name.len() + pe::IMAGE_SIZEOF_SYMBOL - 1) / pe::IMAGE_SIZEOF_SYMBOL;
self.symtab_num += aux_count as u32;
aux_count as u8
}
/// Write auxiliary symbols for a file name.
pub fn write_aux_file_name(&mut self, name: &[u8], aux_count: u8) {
let aux_len = aux_count as usize * pe::IMAGE_SIZEOF_SYMBOL;
debug_assert!(aux_len >= name.len());
let old_len = self.buffer.len();
self.buffer.write_bytes(name);
self.buffer.resize(old_len + aux_len);
}
/// Reserve an auxiliary symbol for a section.
///
/// Returns the number of auxiliary symbols required.
///
/// This must be called before [`Self::reserve_symtab_strtab`].
pub fn reserve_aux_section(&mut self) -> u8 {
debug_assert_eq!(self.symtab_offset, 0);
self.symtab_num += 1;
1
}
/// Write an auxiliary symbol for a section.
pub fn write_aux_section(&mut self, section: AuxSymbolSection) {
let aux = pe::ImageAuxSymbolSection {
length: U32Bytes::new(LE, section.length),
number_of_relocations: if section.number_of_relocations > 0xffff {
U16Bytes::new(LE, 0xffff)
} else {
U16Bytes::new(LE, section.number_of_relocations as u16)
},
number_of_linenumbers: U16Bytes::new(LE, section.number_of_linenumbers),
check_sum: U32Bytes::new(LE, section.check_sum),
number: U16Bytes::new(LE, section.number as u16),
selection: section.selection,
reserved: 0,
high_number: U16Bytes::new(LE, (section.number >> 16) as u16),
};
self.buffer.write(&aux);
}
/// Return the number of reserved symbol table entries.
pub fn symbol_count(&self) -> u32 {
self.symtab_num
}
/// Add a string to the string table.
///
/// This must be called before [`Self::reserve_symtab_strtab`].
pub fn add_string(&mut self, name: &'a [u8]) -> StringId {
debug_assert_eq!(self.strtab_offset, 0);
self.strtab.add(name)
}
/// Add a section or symbol name to the string table if required.
///
/// This must be called before [`Self::reserve_symtab_strtab`].
pub fn add_name(&mut self, name: &'a [u8]) -> Name {
if name.len() > 8 {
Name::Long(self.add_string(name))
} else {
let mut short_name = [0; 8];
short_name[..name.len()].copy_from_slice(name);
Name::Short(short_name)
}
}
/// Reserve the range for the symbol table and string table.
///
/// This must be called after functions that reserve symbol
/// indices or add strings.
pub fn reserve_symtab_strtab(&mut self) {
debug_assert_eq!(self.symtab_offset, 0);
self.symtab_offset = self.reserve(self.symtab_num as usize * pe::IMAGE_SIZEOF_SYMBOL, 1);
debug_assert_eq!(self.strtab_offset, 0);
// First 4 bytes of strtab are the length.
self.strtab.write(4, &mut self.strtab_data);
self.strtab_len = self.strtab_data.len() + 4;
self.strtab_offset = self.reserve(self.strtab_len, 1);
}
/// Write the string table.
pub fn write_strtab(&mut self) {
debug_assert_eq!(self.strtab_offset, self.buffer.len() as u32);
self.buffer
.write_bytes(&u32::to_le_bytes(self.strtab_len as u32));
self.buffer.write_bytes(&self.strtab_data);
}
}
/// Shortened and native endian version of [`pe::ImageFileHeader`].
#[allow(missing_docs)]
#[derive(Debug, Default, Clone)]
pub struct FileHeader {
pub machine: u16,
pub time_date_stamp: u32,
pub characteristics: u16,
}
/// A section or symbol name.
#[derive(Debug, Clone, Copy)]
pub enum Name {
/// An inline name.
Short([u8; 8]),
/// An id of a string table entry.
Long(StringId),
}
impl Default for Name {
fn default() -> Name {
Name::Short([0; 8])
}
}
// From isn't useful.
#[allow(clippy::from_over_into)]
impl<'a> Into<Name> for &'a [u8; 8] {
fn into(self) -> Name {
Name::Short(*self)
}
}
/// Native endian version of [`pe::ImageSectionHeader`].
#[allow(missing_docs)]
#[derive(Debug, Default, Clone)]
pub struct SectionHeader {
pub name: Name,
pub size_of_raw_data: u32,
pub pointer_to_raw_data: u32,
pub pointer_to_relocations: u32,
pub pointer_to_linenumbers: u32,
/// This will automatically be clamped if there are more than 0xffff.
pub number_of_relocations: u32,
pub number_of_linenumbers: u16,
pub characteristics: u32,
}
/// Native endian version of [`pe::ImageSymbol`].
#[allow(missing_docs)]
#[derive(Debug, Default, Clone)]
pub struct Symbol {
pub name: Name,
pub value: u32,
pub section_number: u16,
pub typ: u16,
pub storage_class: u8,
pub number_of_aux_symbols: u8,
}
/// Native endian version of [`pe::ImageAuxSymbolSection`].
#[allow(missing_docs)]
#[derive(Debug, Default, Clone)]
pub struct AuxSymbolSection {
pub length: u32,
/// This will automatically be clamped if there are more than 0xffff.
pub number_of_relocations: u32,
pub number_of_linenumbers: u16,
pub check_sum: u32,
pub number: u32,
pub selection: u8,
}
/// Native endian version of [`pe::ImageRelocation`].
#[allow(missing_docs)]
#[derive(Debug, Default, Clone)]
pub struct Relocation {
pub virtual_address: u32,
pub symbol: u32,
pub typ: u16,
}
+9
View File
@@ -0,0 +1,9 @@
//! Support for writing ELF files.
//!
//! Provides [`Writer`] for low level writing of ELF files.
//! This is also used to provide ELF support for [`write::Object`](crate::write::Object).
mod object;
mod writer;
pub use writer::*;
+841
View File
@@ -0,0 +1,841 @@
use alloc::vec::Vec;
use crate::write::elf::writer::*;
use crate::write::string::StringId;
use crate::write::*;
use crate::{elf, pod};
#[derive(Clone, Copy)]
struct ComdatOffsets {
offset: usize,
str_id: StringId,
}
#[derive(Clone, Copy)]
struct SectionOffsets {
index: SectionIndex,
offset: usize,
str_id: StringId,
reloc_offset: usize,
reloc_str_id: Option<StringId>,
}
#[derive(Default, Clone, Copy)]
struct SymbolOffsets {
index: SymbolIndex,
str_id: Option<StringId>,
}
// Public methods.
impl<'a> Object<'a> {
/// Add a property with a u32 value to the ELF ".note.gnu.property" section.
///
/// Requires `feature = "elf"`.
pub fn add_elf_gnu_property_u32(&mut self, property: u32, value: u32) {
if self.format != BinaryFormat::Elf {
return;
}
let align = if self.elf_is_64() { 8 } else { 4 };
let mut data = Vec::with_capacity(32);
let n_name = b"GNU\0";
data.extend_from_slice(pod::bytes_of(&elf::NoteHeader32 {
n_namesz: U32::new(self.endian, n_name.len() as u32),
n_descsz: U32::new(self.endian, util::align(3 * 4, align) as u32),
n_type: U32::new(self.endian, elf::NT_GNU_PROPERTY_TYPE_0),
}));
data.extend_from_slice(n_name);
// This happens to already be aligned correctly.
debug_assert_eq!(util::align(data.len(), align), data.len());
data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, property)));
// Value size
data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, 4)));
data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, value)));
util::write_align(&mut data, align);
let section = self.section_id(StandardSection::GnuProperty);
self.append_section_data(section, &data, align as u64);
}
}
// Private methods.
impl<'a> Object<'a> {
pub(crate) fn elf_section_info(
&self,
section: StandardSection,
) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) {
match section {
StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None),
StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None),
StandardSection::ReadOnlyData | StandardSection::ReadOnlyString => (
&[],
&b".rodata"[..],
SectionKind::ReadOnlyData,
SectionFlags::None,
),
StandardSection::ReadOnlyDataWithRel => (
&[],
b".data.rel.ro",
SectionKind::ReadOnlyDataWithRel,
SectionFlags::None,
),
StandardSection::UninitializedData => (
&[],
&b".bss"[..],
SectionKind::UninitializedData,
SectionFlags::None,
),
StandardSection::Tls => (&[], &b".tdata"[..], SectionKind::Tls, SectionFlags::None),
StandardSection::UninitializedTls => (
&[],
&b".tbss"[..],
SectionKind::UninitializedTls,
SectionFlags::None,
),
StandardSection::TlsVariables => {
// Unsupported section.
(&[], &[], SectionKind::TlsVariables, SectionFlags::None)
}
StandardSection::Common => {
// Unsupported section.
(&[], &[], SectionKind::Common, SectionFlags::None)
}
StandardSection::GnuProperty => (
&[],
&b".note.gnu.property"[..],
SectionKind::Note,
SectionFlags::Elf {
sh_flags: u64::from(elf::SHF_ALLOC),
},
),
}
}
pub(crate) fn elf_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> {
let mut name = section.to_vec();
if !value.is_empty() {
name.push(b'.');
name.extend_from_slice(value);
}
name
}
fn elf_has_relocation_addend(&self) -> Result<bool> {
Ok(match self.architecture {
Architecture::Aarch64 => true,
Architecture::Aarch64_Ilp32 => true,
Architecture::Arm => false,
Architecture::Avr => true,
Architecture::Bpf => false,
Architecture::Csky => true,
Architecture::E2K32 => true,
Architecture::E2K64 => true,
Architecture::I386 => false,
Architecture::X86_64 => true,
Architecture::X86_64_X32 => true,
Architecture::Hexagon => true,
Architecture::LoongArch64 => true,
Architecture::M68k => true,
Architecture::Mips => false,
Architecture::Mips64 => true,
Architecture::Mips64_N32 => true,
Architecture::Msp430 => true,
Architecture::PowerPc => true,
Architecture::PowerPc64 => true,
Architecture::Riscv64 => true,
Architecture::Riscv32 => true,
Architecture::S390x => true,
Architecture::Sbf => false,
Architecture::Sharc => true,
Architecture::Sparc => true,
Architecture::Sparc32Plus => true,
Architecture::Sparc64 => true,
Architecture::Xtensa => true,
_ => {
return Err(Error(format!(
"unimplemented architecture {:?}",
self.architecture
)));
}
})
}
pub(crate) fn elf_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> {
use RelocationEncoding as E;
use RelocationKind as K;
let (kind, encoding, size) = if let RelocationFlags::Generic {
kind,
encoding,
size,
} = reloc.flags
{
(kind, encoding, size)
} else {
return Ok(());
};
let unsupported_reloc = || Err(Error(format!("unimplemented ELF relocation {:?}", reloc)));
let r_type = match self.architecture {
Architecture::Aarch64 => match (kind, encoding, size) {
(K::Absolute, E::Generic, 64) => elf::R_AARCH64_ABS64,
(K::Absolute, E::Generic, 32) => elf::R_AARCH64_ABS32,
(K::Absolute, E::Generic, 16) => elf::R_AARCH64_ABS16,
(K::Relative, E::Generic, 64) => elf::R_AARCH64_PREL64,
(K::Relative, E::Generic, 32) => elf::R_AARCH64_PREL32,
(K::Relative, E::Generic, 16) => elf::R_AARCH64_PREL16,
(K::Relative, E::AArch64Call, 26) => elf::R_AARCH64_CALL26,
(K::PltRelative, E::AArch64Call, 26) => elf::R_AARCH64_CALL26,
_ => return unsupported_reloc(),
},
Architecture::Aarch64_Ilp32 => match (kind, encoding, size) {
(K::Absolute, E::Generic, 32) => elf::R_AARCH64_P32_ABS32,
_ => return unsupported_reloc(),
},
Architecture::Arm => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_ARM_ABS32,
_ => return unsupported_reloc(),
},
Architecture::Avr => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_AVR_32,
(K::Absolute, _, 16) => elf::R_AVR_16,
_ => return unsupported_reloc(),
},
Architecture::Bpf => match (kind, encoding, size) {
(K::Absolute, _, 64) => elf::R_BPF_64_64,
(K::Absolute, _, 32) => elf::R_BPF_64_32,
_ => return unsupported_reloc(),
},
Architecture::Csky => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_CKCORE_ADDR32,
(K::Relative, E::Generic, 32) => elf::R_CKCORE_PCREL32,
_ => return unsupported_reloc(),
},
Architecture::I386 => match (kind, size) {
(K::Absolute, 32) => elf::R_386_32,
(K::Relative, 32) => elf::R_386_PC32,
(K::Got, 32) => elf::R_386_GOT32,
(K::PltRelative, 32) => elf::R_386_PLT32,
(K::GotBaseOffset, 32) => elf::R_386_GOTOFF,
(K::GotBaseRelative, 32) => elf::R_386_GOTPC,
(K::Absolute, 16) => elf::R_386_16,
(K::Relative, 16) => elf::R_386_PC16,
(K::Absolute, 8) => elf::R_386_8,
(K::Relative, 8) => elf::R_386_PC8,
_ => return unsupported_reloc(),
},
Architecture::E2K32 | Architecture::E2K64 => match (kind, encoding, size) {
(K::Absolute, E::Generic, 32) => elf::R_E2K_32_ABS,
(K::Absolute, E::E2KLit, 64) => elf::R_E2K_64_ABS_LIT,
(K::Absolute, E::Generic, 64) => elf::R_E2K_64_ABS,
(K::Relative, E::E2KDisp, 28) => elf::R_E2K_DISP,
(K::Got, _, 32) => elf::R_E2K_GOT,
_ => return unsupported_reloc(),
},
Architecture::X86_64 | Architecture::X86_64_X32 => match (kind, encoding, size) {
(K::Absolute, E::Generic, 64) => elf::R_X86_64_64,
(K::Relative, E::X86Branch, 32) => elf::R_X86_64_PLT32,
(K::Relative, _, 32) => elf::R_X86_64_PC32,
(K::Got, _, 32) => elf::R_X86_64_GOT32,
(K::PltRelative, _, 32) => elf::R_X86_64_PLT32,
(K::GotRelative, _, 32) => elf::R_X86_64_GOTPCREL,
(K::Absolute, E::Generic, 32) => elf::R_X86_64_32,
(K::Absolute, E::X86Signed, 32) => elf::R_X86_64_32S,
(K::Absolute, _, 16) => elf::R_X86_64_16,
(K::Relative, _, 16) => elf::R_X86_64_PC16,
(K::Absolute, _, 8) => elf::R_X86_64_8,
(K::Relative, _, 8) => elf::R_X86_64_PC8,
_ => return unsupported_reloc(),
},
Architecture::Hexagon => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_HEX_32,
_ => return unsupported_reloc(),
},
Architecture::LoongArch64 => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_LARCH_32,
(K::Absolute, _, 64) => elf::R_LARCH_64,
(K::Relative, _, 32) => elf::R_LARCH_32_PCREL,
(K::Relative, _, 64) => elf::R_LARCH_64_PCREL,
(K::Relative, E::LoongArchBranch, 16) => elf::R_LARCH_B16,
(K::PltRelative, E::LoongArchBranch, 16) => elf::R_LARCH_B16,
(K::Relative, E::LoongArchBranch, 21) => elf::R_LARCH_B21,
(K::PltRelative, E::LoongArchBranch, 21) => elf::R_LARCH_B21,
(K::Relative, E::LoongArchBranch, 26) => elf::R_LARCH_B26,
(K::PltRelative, E::LoongArchBranch, 26) => elf::R_LARCH_B26,
_ => return unsupported_reloc(),
},
Architecture::M68k => match (kind, encoding, size) {
(K::Absolute, _, 8) => elf::R_68K_8,
(K::Absolute, _, 16) => elf::R_68K_16,
(K::Absolute, _, 32) => elf::R_68K_32,
(K::Relative, _, 8) => elf::R_68K_PC8,
(K::Relative, _, 16) => elf::R_68K_PC16,
(K::Relative, _, 32) => elf::R_68K_PC32,
(K::GotRelative, _, 8) => elf::R_68K_GOT8,
(K::GotRelative, _, 16) => elf::R_68K_GOT16,
(K::GotRelative, _, 32) => elf::R_68K_GOT32,
(K::Got, _, 8) => elf::R_68K_GOT8O,
(K::Got, _, 16) => elf::R_68K_GOT16O,
(K::Got, _, 32) => elf::R_68K_GOT32O,
(K::PltRelative, _, 8) => elf::R_68K_PLT8,
(K::PltRelative, _, 16) => elf::R_68K_PLT16,
(K::PltRelative, _, 32) => elf::R_68K_PLT32,
_ => return unsupported_reloc(),
},
Architecture::Mips | Architecture::Mips64 | Architecture::Mips64_N32 => {
match (kind, encoding, size) {
(K::Absolute, _, 16) => elf::R_MIPS_16,
(K::Absolute, _, 32) => elf::R_MIPS_32,
(K::Absolute, _, 64) => elf::R_MIPS_64,
_ => return unsupported_reloc(),
}
}
Architecture::Msp430 => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_MSP430_32,
(K::Absolute, _, 16) => elf::R_MSP430_16_BYTE,
_ => return unsupported_reloc(),
},
Architecture::PowerPc => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_PPC_ADDR32,
_ => return unsupported_reloc(),
},
Architecture::PowerPc64 => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_PPC64_ADDR32,
(K::Absolute, _, 64) => elf::R_PPC64_ADDR64,
_ => return unsupported_reloc(),
},
Architecture::Riscv32 | Architecture::Riscv64 => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_RISCV_32,
(K::Absolute, _, 64) => elf::R_RISCV_64,
(K::Relative, E::Generic, 32) => elf::R_RISCV_32_PCREL,
_ => return unsupported_reloc(),
},
Architecture::S390x => match (kind, encoding, size) {
(K::Absolute, E::Generic, 8) => elf::R_390_8,
(K::Absolute, E::Generic, 16) => elf::R_390_16,
(K::Absolute, E::Generic, 32) => elf::R_390_32,
(K::Absolute, E::Generic, 64) => elf::R_390_64,
(K::Relative, E::Generic, 16) => elf::R_390_PC16,
(K::Relative, E::Generic, 32) => elf::R_390_PC32,
(K::Relative, E::Generic, 64) => elf::R_390_PC64,
(K::Relative, E::S390xDbl, 16) => elf::R_390_PC16DBL,
(K::Relative, E::S390xDbl, 32) => elf::R_390_PC32DBL,
(K::PltRelative, E::S390xDbl, 16) => elf::R_390_PLT16DBL,
(K::PltRelative, E::S390xDbl, 32) => elf::R_390_PLT32DBL,
(K::Got, E::Generic, 16) => elf::R_390_GOT16,
(K::Got, E::Generic, 32) => elf::R_390_GOT32,
(K::Got, E::Generic, 64) => elf::R_390_GOT64,
(K::GotRelative, E::S390xDbl, 32) => elf::R_390_GOTENT,
(K::GotBaseOffset, E::Generic, 16) => elf::R_390_GOTOFF16,
(K::GotBaseOffset, E::Generic, 32) => elf::R_390_GOTOFF32,
(K::GotBaseOffset, E::Generic, 64) => elf::R_390_GOTOFF64,
(K::GotBaseRelative, E::Generic, 64) => elf::R_390_GOTPC,
(K::GotBaseRelative, E::S390xDbl, 32) => elf::R_390_GOTPCDBL,
_ => return unsupported_reloc(),
},
Architecture::Sbf => match (kind, encoding, size) {
(K::Absolute, _, 64) => elf::R_SBF_64_64,
(K::Absolute, _, 32) => elf::R_SBF_64_32,
_ => return unsupported_reloc(),
},
Architecture::Sharc => match (kind, encoding, size) {
(K::Absolute, E::SharcTypeA, 32) => elf::R_SHARC_ADDR32_V3,
(K::Absolute, E::Generic, 32) => elf::R_SHARC_ADDR_VAR_V3,
(K::Relative, E::SharcTypeA, 24) => elf::R_SHARC_PCRLONG_V3,
(K::Relative, E::SharcTypeA, 6) => elf::R_SHARC_PCRSHORT_V3,
(K::Relative, E::SharcTypeB, 6) => elf::R_SHARC_PCRSHORT_V3,
(K::Absolute, E::Generic, 16) => elf::R_SHARC_ADDR_VAR16_V3,
(K::Absolute, E::SharcTypeA, 16) => elf::R_SHARC_DATA16_V3,
(K::Absolute, E::SharcTypeB, 16) => elf::R_SHARC_DATA16_VISA_V3,
(K::Absolute, E::SharcTypeA, 24) => elf::R_SHARC_ADDR24_V3,
(K::Absolute, E::SharcTypeA, 6) => elf::R_SHARC_DATA6_V3,
(K::Absolute, E::SharcTypeB, 6) => elf::R_SHARC_DATA6_VISA_V3,
(K::Absolute, E::SharcTypeB, 7) => elf::R_SHARC_DATA7_VISA_V3,
_ => return unsupported_reloc(),
},
Architecture::Sparc | Architecture::Sparc32Plus => match (kind, encoding, size) {
// TODO: use R_SPARC_32 if aligned.
(K::Absolute, _, 32) => elf::R_SPARC_UA32,
_ => return unsupported_reloc(),
},
Architecture::Sparc64 => match (kind, encoding, size) {
// TODO: use R_SPARC_32/R_SPARC_64 if aligned.
(K::Absolute, _, 32) => elf::R_SPARC_UA32,
(K::Absolute, _, 64) => elf::R_SPARC_UA64,
_ => return unsupported_reloc(),
},
Architecture::Xtensa => match (kind, encoding, size) {
(K::Absolute, _, 32) => elf::R_XTENSA_32,
(K::Relative, E::Generic, 32) => elf::R_XTENSA_32_PCREL,
_ => return unsupported_reloc(),
},
_ => {
return Err(Error(format!(
"unimplemented architecture {:?}",
self.architecture
)));
}
};
reloc.flags = RelocationFlags::Elf { r_type };
Ok(())
}
pub(crate) fn elf_adjust_addend(&mut self, _relocation: &mut Relocation) -> Result<bool> {
// Determine whether the addend is stored in the relocation or the data.
let implicit = !self.elf_has_relocation_addend()?;
Ok(implicit)
}
pub(crate) fn elf_relocation_size(&self, reloc: &Relocation) -> Result<u8> {
let r_type = if let RelocationFlags::Elf { r_type } = reloc.flags {
r_type
} else {
return Err(Error("invalid relocation flags".into()));
};
// This only needs to support architectures that use implicit addends.
let size = match self.architecture {
Architecture::Arm => match r_type {
elf::R_ARM_ABS16 => Some(16),
elf::R_ARM_ABS32 | elf::R_ARM_REL32 => Some(32),
_ => None,
},
Architecture::Bpf => match r_type {
elf::R_BPF_64_32 => Some(32),
elf::R_BPF_64_64 => Some(64),
_ => None,
},
Architecture::I386 => match r_type {
elf::R_386_8 | elf::R_386_PC8 => Some(8),
elf::R_386_16 | elf::R_386_PC16 => Some(16),
elf::R_386_32
| elf::R_386_PC32
| elf::R_386_GOT32
| elf::R_386_PLT32
| elf::R_386_GOTOFF
| elf::R_386_GOTPC => Some(32),
_ => None,
},
Architecture::Mips => match r_type {
elf::R_MIPS_16 => Some(16),
elf::R_MIPS_32 => Some(32),
elf::R_MIPS_64 => Some(64),
_ => None,
},
Architecture::Sbf => match r_type {
elf::R_SBF_64_32 => Some(32),
elf::R_SBF_64_64 => Some(64),
_ => None,
},
_ => {
return Err(Error(format!(
"unimplemented architecture {:?}",
self.architecture
)));
}
};
size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc)))
}
pub(crate) fn elf_is_64(&self) -> bool {
match self.architecture.address_size().unwrap() {
AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false,
AddressSize::U64 => true,
}
}
pub(crate) fn elf_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
// Create reloc section header names so we can reference them.
let is_rela = self.elf_has_relocation_addend()?;
let reloc_names: Vec<_> = self
.sections
.iter()
.map(|section| {
let mut reloc_name = Vec::with_capacity(
if is_rela { ".rela".len() } else { ".rel".len() } + section.name.len(),
);
if !section.relocations.is_empty() {
reloc_name.extend_from_slice(if is_rela {
&b".rela"[..]
} else {
&b".rel"[..]
});
reloc_name.extend_from_slice(&section.name);
}
reloc_name
})
.collect();
// Start calculating offsets of everything.
let mut writer = Writer::new(self.endian, self.elf_is_64(), buffer);
writer.reserve_file_header();
// Calculate size of section data.
let mut comdat_offsets = Vec::with_capacity(self.comdats.len());
for comdat in &self.comdats {
if comdat.kind != ComdatKind::Any {
return Err(Error(format!(
"unsupported COMDAT symbol `{}` kind {:?}",
self.symbols[comdat.symbol.0].name().unwrap_or(""),
comdat.kind
)));
}
writer.reserve_section_index();
let offset = writer.reserve_comdat(comdat.sections.len());
let str_id = writer.add_section_name(b".group");
comdat_offsets.push(ComdatOffsets { offset, str_id });
}
let mut section_offsets = Vec::with_capacity(self.sections.len());
for (section, reloc_name) in self.sections.iter().zip(reloc_names.iter()) {
let index = writer.reserve_section_index();
let offset = writer.reserve(section.data.len(), section.align as usize);
let str_id = writer.add_section_name(&section.name);
let mut reloc_str_id = None;
if !section.relocations.is_empty() {
writer.reserve_section_index();
reloc_str_id = Some(writer.add_section_name(reloc_name));
}
section_offsets.push(SectionOffsets {
index,
offset,
str_id,
// Relocation data is reserved later.
reloc_offset: 0,
reloc_str_id,
});
}
// Calculate index of symbols and add symbol strings to strtab.
let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
writer.reserve_null_symbol_index();
// Local symbols must come before global.
for (index, symbol) in self.symbols.iter().enumerate() {
if symbol.is_local() {
let section_index = symbol.section.id().map(|s| section_offsets[s.0].index);
symbol_offsets[index].index = writer.reserve_symbol_index(section_index);
}
}
let symtab_num_local = writer.symbol_count();
for (index, symbol) in self.symbols.iter().enumerate() {
if !symbol.is_local() {
let section_index = symbol.section.id().map(|s| section_offsets[s.0].index);
symbol_offsets[index].index = writer.reserve_symbol_index(section_index);
}
}
for (index, symbol) in self.symbols.iter().enumerate() {
if symbol.kind != SymbolKind::Section && !symbol.name.is_empty() {
symbol_offsets[index].str_id = Some(writer.add_string(&symbol.name));
}
}
// Calculate size of symbols.
writer.reserve_symtab_section_index();
writer.reserve_symtab();
if writer.symtab_shndx_needed() {
writer.reserve_symtab_shndx_section_index();
}
writer.reserve_symtab_shndx();
writer.reserve_strtab_section_index();
writer.reserve_strtab();
// Calculate size of relocations.
for (index, section) in self.sections.iter().enumerate() {
let count = section.relocations.len();
if count != 0 {
section_offsets[index].reloc_offset = writer.reserve_relocations(count, is_rela);
}
}
// Calculate size of section headers.
writer.reserve_shstrtab_section_index();
writer.reserve_shstrtab();
writer.reserve_section_headers();
// Start writing.
let e_type = elf::ET_REL;
let e_machine = match (self.architecture, self.sub_architecture) {
(Architecture::Aarch64, None) => elf::EM_AARCH64,
(Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
(Architecture::Arm, None) => elf::EM_ARM,
(Architecture::Avr, None) => elf::EM_AVR,
(Architecture::Bpf, None) => elf::EM_BPF,
(Architecture::Csky, None) => elf::EM_CSKY,
(Architecture::E2K32, None) => elf::EM_MCST_ELBRUS,
(Architecture::E2K64, None) => elf::EM_MCST_ELBRUS,
(Architecture::I386, None) => elf::EM_386,
(Architecture::X86_64, None) => elf::EM_X86_64,
(Architecture::X86_64_X32, None) => elf::EM_X86_64,
(Architecture::Hexagon, None) => elf::EM_HEXAGON,
(Architecture::LoongArch64, None) => elf::EM_LOONGARCH,
(Architecture::M68k, None) => elf::EM_68K,
(Architecture::Mips, None) => elf::EM_MIPS,
(Architecture::Mips64, None) => elf::EM_MIPS,
(Architecture::Mips64_N32, None) => elf::EM_MIPS,
(Architecture::Msp430, None) => elf::EM_MSP430,
(Architecture::PowerPc, None) => elf::EM_PPC,
(Architecture::PowerPc64, None) => elf::EM_PPC64,
(Architecture::Riscv32, None) => elf::EM_RISCV,
(Architecture::Riscv64, None) => elf::EM_RISCV,
(Architecture::S390x, None) => elf::EM_S390,
(Architecture::Sbf, None) => elf::EM_SBF,
(Architecture::Sharc, None) => elf::EM_SHARC,
(Architecture::Sparc, None) => elf::EM_SPARC,
(Architecture::Sparc32Plus, None) => elf::EM_SPARC32PLUS,
(Architecture::Sparc64, None) => elf::EM_SPARCV9,
(Architecture::Xtensa, None) => elf::EM_XTENSA,
_ => {
return Err(Error(format!(
"unimplemented architecture {:?} with sub-architecture {:?}",
self.architecture, self.sub_architecture
)));
}
};
let (os_abi, abi_version, mut e_flags) = if let FileFlags::Elf {
os_abi,
abi_version,
e_flags,
} = self.flags
{
(os_abi, abi_version, e_flags)
} else {
(elf::ELFOSABI_NONE, 0, 0)
};
if self.architecture == Architecture::Mips64_N32 {
e_flags |= elf::EF_MIPS_ABI2;
}
writer.write_file_header(&FileHeader {
os_abi,
abi_version,
e_type,
e_machine,
e_entry: 0,
e_flags,
})?;
// Write section data.
for comdat in &self.comdats {
writer.write_comdat_header();
for section in &comdat.sections {
writer.write_comdat_entry(section_offsets[section.0].index);
}
}
for (index, section) in self.sections.iter().enumerate() {
writer.write_align(section.align as usize);
debug_assert_eq!(section_offsets[index].offset, writer.len());
writer.write(&section.data);
}
// Write symbols.
writer.write_null_symbol();
let mut write_symbol = |index: usize, symbol: &Symbol| -> Result<()> {
let st_info = if let SymbolFlags::Elf { st_info, .. } = symbol.flags {
st_info
} else {
let st_type = match symbol.kind {
SymbolKind::Text => {
if symbol.is_undefined() {
elf::STT_NOTYPE
} else {
elf::STT_FUNC
}
}
SymbolKind::Data => {
if symbol.is_undefined() {
elf::STT_NOTYPE
} else if symbol.is_common() {
elf::STT_COMMON
} else {
elf::STT_OBJECT
}
}
SymbolKind::Section => elf::STT_SECTION,
SymbolKind::File => elf::STT_FILE,
SymbolKind::Tls => elf::STT_TLS,
SymbolKind::Label => elf::STT_NOTYPE,
SymbolKind::Unknown => {
if symbol.is_undefined() {
elf::STT_NOTYPE
} else {
return Err(Error(format!(
"unimplemented symbol `{}` kind {:?}",
symbol.name().unwrap_or(""),
symbol.kind
)));
}
}
};
let st_bind = if symbol.weak {
elf::STB_WEAK
} else if symbol.is_undefined() {
elf::STB_GLOBAL
} else if symbol.is_local() {
elf::STB_LOCAL
} else {
elf::STB_GLOBAL
};
(st_bind << 4) + st_type
};
let st_other = if let SymbolFlags::Elf { st_other, .. } = symbol.flags {
st_other
} else if symbol.scope == SymbolScope::Linkage {
elf::STV_HIDDEN
} else {
elf::STV_DEFAULT
};
let (st_shndx, section) = match symbol.section {
SymbolSection::None => {
debug_assert_eq!(symbol.kind, SymbolKind::File);
(elf::SHN_ABS, None)
}
SymbolSection::Undefined => (elf::SHN_UNDEF, None),
SymbolSection::Absolute => (elf::SHN_ABS, None),
SymbolSection::Common => (elf::SHN_COMMON, None),
SymbolSection::Section(id) => (0, Some(section_offsets[id.0].index)),
};
writer.write_symbol(&Sym {
name: symbol_offsets[index].str_id,
section,
st_info,
st_other,
st_shndx,
st_value: symbol.value,
st_size: symbol.size,
});
Ok(())
};
for (index, symbol) in self.symbols.iter().enumerate() {
if symbol.is_local() {
write_symbol(index, symbol)?;
}
}
for (index, symbol) in self.symbols.iter().enumerate() {
if !symbol.is_local() {
write_symbol(index, symbol)?;
}
}
writer.write_symtab_shndx();
writer.write_strtab();
// Write relocations.
for (index, section) in self.sections.iter().enumerate() {
if !section.relocations.is_empty() {
writer.write_align_relocation();
debug_assert_eq!(section_offsets[index].reloc_offset, writer.len());
for reloc in &section.relocations {
let r_type = if let RelocationFlags::Elf { r_type } = reloc.flags {
r_type
} else {
return Err(Error("invalid relocation flags".into()));
};
let r_sym = symbol_offsets[reloc.symbol.0].index.0;
writer.write_relocation(
is_rela,
&Rel {
r_offset: reloc.offset,
r_sym,
r_type,
r_addend: reloc.addend,
},
);
}
}
}
writer.write_shstrtab();
// Write section headers.
writer.write_null_section_header();
let symtab_index = writer.symtab_index();
for (comdat, comdat_offset) in self.comdats.iter().zip(comdat_offsets.iter()) {
writer.write_comdat_section_header(
comdat_offset.str_id,
symtab_index,
symbol_offsets[comdat.symbol.0].index,
comdat_offset.offset,
comdat.sections.len(),
);
}
for (index, section) in self.sections.iter().enumerate() {
let sh_type = match section.kind {
SectionKind::UninitializedData | SectionKind::UninitializedTls => elf::SHT_NOBITS,
SectionKind::Note => elf::SHT_NOTE,
SectionKind::Elf(sh_type) => sh_type,
_ => elf::SHT_PROGBITS,
};
let sh_flags = if let SectionFlags::Elf { sh_flags } = section.flags {
sh_flags
} else {
match section.kind {
SectionKind::Text => elf::SHF_ALLOC | elf::SHF_EXECINSTR,
SectionKind::Data | SectionKind::ReadOnlyDataWithRel => {
elf::SHF_ALLOC | elf::SHF_WRITE
}
SectionKind::Tls => elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_TLS,
SectionKind::UninitializedData => elf::SHF_ALLOC | elf::SHF_WRITE,
SectionKind::UninitializedTls => elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_TLS,
SectionKind::ReadOnlyData => elf::SHF_ALLOC,
SectionKind::ReadOnlyString => {
elf::SHF_ALLOC | elf::SHF_STRINGS | elf::SHF_MERGE
}
SectionKind::OtherString | SectionKind::DebugString => {
elf::SHF_STRINGS | elf::SHF_MERGE
}
SectionKind::Other
| SectionKind::Debug
| SectionKind::Metadata
| SectionKind::Linker
| SectionKind::Note
| SectionKind::Elf(_) => 0,
SectionKind::Unknown | SectionKind::Common | SectionKind::TlsVariables => {
return Err(Error(format!(
"unimplemented section `{}` kind {:?}",
section.name().unwrap_or(""),
section.kind
)));
}
}
.into()
};
// TODO: not sure if this is correct, maybe user should determine this
let sh_entsize = match section.kind {
SectionKind::ReadOnlyString | SectionKind::OtherString => 1,
_ => 0,
};
writer.write_section_header(&SectionHeader {
name: Some(section_offsets[index].str_id),
sh_type,
sh_flags,
sh_addr: 0,
sh_offset: section_offsets[index].offset as u64,
sh_size: section.size,
sh_link: 0,
sh_info: 0,
sh_addralign: section.align,
sh_entsize,
});
if !section.relocations.is_empty() {
writer.write_relocation_section_header(
section_offsets[index].reloc_str_id.unwrap(),
section_offsets[index].index,
symtab_index,
section_offsets[index].reloc_offset,
section.relocations.len(),
is_rela,
);
}
}
writer.write_symtab_section_header(symtab_num_local);
writer.write_symtab_shndx_section_header();
writer.write_strtab_section_header();
writer.write_shstrtab_section_header();
debug_assert_eq!(writer.reserved_len(), writer.len());
Ok(())
}
}
File diff suppressed because it is too large. Load diff
+1107
View File
File diff suppressed because it is too large. Load diff
+1025
View File
File diff suppressed because it is too large. Load diff
+849
View File
@@ -0,0 +1,849 @@
//! Helper for writing PE files.
use alloc::string::String;
use alloc::vec::Vec;
use core::mem;
use crate::endian::{LittleEndian as LE, *};
use crate::pe;
use crate::write::util;
use crate::write::{Error, Result, WritableBuffer};
/// A helper for writing PE files.
///
/// Writing uses a two phase approach. The first phase reserves file ranges and virtual
/// address ranges for everything in the order that they will be written.
///
/// The second phase writes everything out in order. Thus the caller must ensure writing
/// is in the same order that file ranges were reserved.
#[allow(missing_debug_implementations)]
pub struct Writer<'a> {
is_64: bool,
section_alignment: u32,
file_alignment: u32,
buffer: &'a mut dyn WritableBuffer,
len: u32,
virtual_len: u32,
headers_len: u32,
code_address: u32,
data_address: u32,
code_len: u32,
data_len: u32,
bss_len: u32,
nt_headers_offset: u32,
data_directories: Vec<DataDirectory>,
section_header_num: u16,
sections: Vec<Section>,
symbol_offset: u32,
symbol_num: u32,
reloc_blocks: Vec<RelocBlock>,
relocs: Vec<U16<LE>>,
reloc_offset: u32,
}
impl<'a> Writer<'a> {
/// Create a new `Writer`.
///
/// The alignment values must be powers of two.
pub fn new(
is_64: bool,
section_alignment: u32,
file_alignment: u32,
buffer: &'a mut dyn WritableBuffer,
) -> Self {
Writer {
is_64,
section_alignment,
file_alignment,
buffer,
len: 0,
virtual_len: 0,
headers_len: 0,
code_address: 0,
data_address: 0,
code_len: 0,
data_len: 0,
bss_len: 0,
nt_headers_offset: 0,
data_directories: Vec::new(),
section_header_num: 0,
sections: Vec::new(),
symbol_offset: 0,
symbol_num: 0,
reloc_blocks: Vec::new(),
relocs: Vec::new(),
reloc_offset: 0,
}
}
/// Return the current virtual address size that has been reserved.
///
/// This is only valid after section headers have been reserved.
pub fn virtual_len(&self) -> u32 {
self.virtual_len
}
/// Reserve a virtual address range with the given size.
///
/// The reserved length will be increased to match the section alignment.
///
/// Returns the aligned offset of the start of the range.
pub fn reserve_virtual(&mut self, len: u32) -> u32 {
let offset = self.virtual_len;
self.virtual_len += len;
self.virtual_len = util::align_u32(self.virtual_len, self.section_alignment);
offset
}
/// Reserve up to the given virtual address.
///
/// The reserved length will be increased to match the section alignment.
pub fn reserve_virtual_until(&mut self, address: u32) {
debug_assert!(self.virtual_len <= address);
self.virtual_len = util::align_u32(address, self.section_alignment);
}
/// Return the current file length that has been reserved.
pub fn reserved_len(&self) -> u32 {
self.len
}
/// Return the current file length that has been written.
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.buffer.len()
}
/// Reserve a file range with the given size and starting alignment.
///
/// Returns the aligned offset of the start of the range.
pub fn reserve(&mut self, len: u32, align_start: u32) -> u32 {
if len == 0 {
return self.len;
}
self.reserve_align(align_start);
let offset = self.len;
self.len += len;
offset
}
/// Reserve a file range with the given size and using the file alignment.
///
/// Returns the aligned offset of the start of the range.
pub fn reserve_file(&mut self, len: u32) -> u32 {
self.reserve(len, self.file_alignment)
}
/// Write data.
pub fn write(&mut self, data: &[u8]) {
self.buffer.write_bytes(data);
}
/// Reserve alignment padding bytes.
pub fn reserve_align(&mut self, align_start: u32) {
self.len = util::align_u32(self.len, align_start);
}
/// Write alignment padding bytes.
pub fn write_align(&mut self, align_start: u32) {
util::write_align(self.buffer, align_start as usize);
}
/// Write padding up to the next multiple of file alignment.
pub fn write_file_align(&mut self) {
self.write_align(self.file_alignment);
}
/// Reserve the file range up to the given file offset.
pub fn reserve_until(&mut self, offset: u32) {
debug_assert!(self.len <= offset);
self.len = offset;
}
/// Write padding up to the given file offset.
pub fn pad_until(&mut self, offset: u32) {
debug_assert!(self.buffer.len() <= offset as usize);
self.buffer.resize(offset as usize);
}
/// Reserve the range for the DOS header.
///
/// This must be at the start of the file.
///
/// When writing, you may use `write_custom_dos_header` or `write_empty_dos_header`.
pub fn reserve_dos_header(&mut self) {
debug_assert_eq!(self.len, 0);
self.reserve(mem::size_of::<pe::ImageDosHeader>() as u32, 1);
}
/// Write a custom DOS header.
///
/// This must be at the start of the file.
pub fn write_custom_dos_header(&mut self, dos_header: &pe::ImageDosHeader) -> Result<()> {
debug_assert_eq!(self.buffer.len(), 0);
// Start writing.
self.buffer
.reserve(self.len as usize)
.map_err(|_| Error(String::from("Cannot allocate buffer")))?;
self.buffer.write(dos_header);
Ok(())
}
/// Write the DOS header for a file without a stub.
///
/// This must be at the start of the file.
///
/// Uses default values for all fields.
pub fn write_empty_dos_header(&mut self) -> Result<()> {
self.write_custom_dos_header(&pe::ImageDosHeader {
e_magic: U16::new(LE, pe::IMAGE_DOS_SIGNATURE),
e_cblp: U16::new(LE, 0),
e_cp: U16::new(LE, 0),
e_crlc: U16::new(LE, 0),
e_cparhdr: U16::new(LE, 0),
e_minalloc: U16::new(LE, 0),
e_maxalloc: U16::new(LE, 0),
e_ss: U16::new(LE, 0),
e_sp: U16::new(LE, 0),
e_csum: U16::new(LE, 0),
e_ip: U16::new(LE, 0),
e_cs: U16::new(LE, 0),
e_lfarlc: U16::new(LE, 0),
e_ovno: U16::new(LE, 0),
e_res: [U16::new(LE, 0); 4],
e_oemid: U16::new(LE, 0),
e_oeminfo: U16::new(LE, 0),
e_res2: [U16::new(LE, 0); 10],
e_lfanew: U32::new(LE, self.nt_headers_offset),
})
}
/// Reserve a fixed DOS header and stub.
///
/// Use `reserve_dos_header` and `reserve` if you need a custom stub.
pub fn reserve_dos_header_and_stub(&mut self) {
self.reserve_dos_header();
self.reserve(64, 1);
}
/// Write a fixed DOS header and stub.
///
/// Use `write_custom_dos_header` and `write` if you need a custom stub.
pub fn write_dos_header_and_stub(&mut self) -> Result<()> {
self.write_custom_dos_header(&pe::ImageDosHeader {
e_magic: U16::new(LE, pe::IMAGE_DOS_SIGNATURE),
e_cblp: U16::new(LE, 0x90),
e_cp: U16::new(LE, 3),
e_crlc: U16::new(LE, 0),
e_cparhdr: U16::new(LE, 4),
e_minalloc: U16::new(LE, 0),
e_maxalloc: U16::new(LE, 0xffff),
e_ss: U16::new(LE, 0),
e_sp: U16::new(LE, 0xb8),
e_csum: U16::new(LE, 0),
e_ip: U16::new(LE, 0),
e_cs: U16::new(LE, 0),
e_lfarlc: U16::new(LE, 0x40),
e_ovno: U16::new(LE, 0),
e_res: [U16::new(LE, 0); 4],
e_oemid: U16::new(LE, 0),
e_oeminfo: U16::new(LE, 0),
e_res2: [U16::new(LE, 0); 10],
e_lfanew: U32::new(LE, self.nt_headers_offset),
})?;
#[rustfmt::skip]
self.buffer.write_bytes(&[
0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd,
0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68,
0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f,
0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e,
0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20,
0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
Ok(())
}
fn nt_headers_size(&self) -> u32 {
if self.is_64 {
mem::size_of::<pe::ImageNtHeaders64>() as u32
} else {
mem::size_of::<pe::ImageNtHeaders32>() as u32
}
}
fn optional_header_size(&self) -> u32 {
let size = if self.is_64 {
mem::size_of::<pe::ImageOptionalHeader64>() as u32
} else {
mem::size_of::<pe::ImageOptionalHeader32>() as u32
};
size + self.data_directories.len() as u32 * mem::size_of::<pe::ImageDataDirectory>() as u32
}
/// Return the offset of the NT headers, if reserved.
pub fn nt_headers_offset(&self) -> u32 {
self.nt_headers_offset
}
/// Reserve the range for the NT headers.
pub fn reserve_nt_headers(&mut self, data_directory_num: usize) {
debug_assert_eq!(self.nt_headers_offset, 0);
self.nt_headers_offset = self.reserve(self.nt_headers_size(), 8);
self.data_directories = vec![DataDirectory::default(); data_directory_num];
self.reserve(
data_directory_num as u32 * mem::size_of::<pe::ImageDataDirectory>() as u32,
1,
);
}
/// Set the virtual address and size of a data directory.
pub fn set_data_directory(&mut self, index: usize, virtual_address: u32, size: u32) {
self.data_directories[index] = DataDirectory {
virtual_address,
size,
}
}
/// Write the NT headers.
pub fn write_nt_headers(&mut self, nt_headers: NtHeaders) {
self.pad_until(self.nt_headers_offset);
self.buffer.write(&U32::new(LE, pe::IMAGE_NT_SIGNATURE));
let file_header = pe::ImageFileHeader {
machine: U16::new(LE, nt_headers.machine),
number_of_sections: U16::new(LE, self.section_header_num),
time_date_stamp: U32::new(LE, nt_headers.time_date_stamp),
pointer_to_symbol_table: U32::new(LE, self.symbol_offset),
number_of_symbols: U32::new(LE, self.symbol_num),
size_of_optional_header: U16::new(LE, self.optional_header_size() as u16),
characteristics: U16::new(LE, nt_headers.characteristics),
};
self.buffer.write(&file_header);
if self.is_64 {
let optional_header = pe::ImageOptionalHeader64 {
magic: U16::new(LE, pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC),
major_linker_version: nt_headers.major_linker_version,
minor_linker_version: nt_headers.minor_linker_version,
size_of_code: U32::new(LE, self.code_len),
size_of_initialized_data: U32::new(LE, self.data_len),
size_of_uninitialized_data: U32::new(LE, self.bss_len),
address_of_entry_point: U32::new(LE, nt_headers.address_of_entry_point),
base_of_code: U32::new(LE, self.code_address),
image_base: U64::new(LE, nt_headers.image_base),
section_alignment: U32::new(LE, self.section_alignment),
file_alignment: U32::new(LE, self.file_alignment),
major_operating_system_version: U16::new(
LE,
nt_headers.major_operating_system_version,
),
minor_operating_system_version: U16::new(
LE,
nt_headers.minor_operating_system_version,
),
major_image_version: U16::new(LE, nt_headers.major_image_version),
minor_image_version: U16::new(LE, nt_headers.minor_image_version),
major_subsystem_version: U16::new(LE, nt_headers.major_subsystem_version),
minor_subsystem_version: U16::new(LE, nt_headers.minor_subsystem_version),
win32_version_value: U32::new(LE, 0),
size_of_image: U32::new(LE, self.virtual_len),
size_of_headers: U32::new(LE, self.headers_len),
check_sum: U32::new(LE, 0),
subsystem: U16::new(LE, nt_headers.subsystem),
dll_characteristics: U16::new(LE, nt_headers.dll_characteristics),
size_of_stack_reserve: U64::new(LE, nt_headers.size_of_stack_reserve),
size_of_stack_commit: U64::new(LE, nt_headers.size_of_stack_commit),
size_of_heap_reserve: U64::new(LE, nt_headers.size_of_heap_reserve),
size_of_heap_commit: U64::new(LE, nt_headers.size_of_heap_commit),
loader_flags: U32::new(LE, 0),
number_of_rva_and_sizes: U32::new(LE, self.data_directories.len() as u32),
};
self.buffer.write(&optional_header);
} else {
let optional_header = pe::ImageOptionalHeader32 {
magic: U16::new(LE, pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC),
major_linker_version: nt_headers.major_linker_version,
minor_linker_version: nt_headers.minor_linker_version,
size_of_code: U32::new(LE, self.code_len),
size_of_initialized_data: U32::new(LE, self.data_len),
size_of_uninitialized_data: U32::new(LE, self.bss_len),
address_of_entry_point: U32::new(LE, nt_headers.address_of_entry_point),
base_of_code: U32::new(LE, self.code_address),
base_of_data: U32::new(LE, self.data_address),
image_base: U32::new(LE, nt_headers.image_base as u32),
section_alignment: U32::new(LE, self.section_alignment),
file_alignment: U32::new(LE, self.file_alignment),
major_operating_system_version: U16::new(
LE,
nt_headers.major_operating_system_version,
),
minor_operating_system_version: U16::new(
LE,
nt_headers.minor_operating_system_version,
),
major_image_version: U16::new(LE, nt_headers.major_image_version),
minor_image_version: U16::new(LE, nt_headers.minor_image_version),
major_subsystem_version: U16::new(LE, nt_headers.major_subsystem_version),
minor_subsystem_version: U16::new(LE, nt_headers.minor_subsystem_version),
win32_version_value: U32::new(LE, 0),
size_of_image: U32::new(LE, self.virtual_len),
size_of_headers: U32::new(LE, self.headers_len),
check_sum: U32::new(LE, 0),
subsystem: U16::new(LE, nt_headers.subsystem),
dll_characteristics: U16::new(LE, nt_headers.dll_characteristics),
size_of_stack_reserve: U32::new(LE, nt_headers.size_of_stack_reserve as u32),
size_of_stack_commit: U32::new(LE, nt_headers.size_of_stack_commit as u32),
size_of_heap_reserve: U32::new(LE, nt_headers.size_of_heap_reserve as u32),
size_of_heap_commit: U32::new(LE, nt_headers.size_of_heap_commit as u32),
loader_flags: U32::new(LE, 0),
number_of_rva_and_sizes: U32::new(LE, self.data_directories.len() as u32),
};
self.buffer.write(&optional_header);
}
for dir in &self.data_directories {
self.buffer.write(&pe::ImageDataDirectory {
virtual_address: U32::new(LE, dir.virtual_address),
size: U32::new(LE, dir.size),
})
}
}
/// Reserve the section headers.
///
/// The number of reserved section headers must be the same as the number of sections that
/// are later reserved.
// TODO: change this to a maximum number of sections?
pub fn reserve_section_headers(&mut self, section_header_num: u16) {
debug_assert_eq!(self.section_header_num, 0);
self.section_header_num = section_header_num;
self.reserve(
u32::from(section_header_num) * mem::size_of::<pe::ImageSectionHeader>() as u32,
1,
);
// Padding before sections must be included in headers_len.
self.reserve_align(self.file_alignment);
self.headers_len = self.len;
self.reserve_virtual(self.len);
}
/// Write the section headers.
///
/// This uses information that was recorded when the sections were reserved.
pub fn write_section_headers(&mut self) {
debug_assert_eq!(self.section_header_num as usize, self.sections.len());
for section in &self.sections {
let section_header = pe::ImageSectionHeader {
name: section.name,
virtual_size: U32::new(LE, section.range.virtual_size),
virtual_address: U32::new(LE, section.range.virtual_address),
size_of_raw_data: U32::new(LE, section.range.file_size),
pointer_to_raw_data: U32::new(LE, section.range.file_offset),
pointer_to_relocations: U32::new(LE, 0),
pointer_to_linenumbers: U32::new(LE, 0),
number_of_relocations: U16::new(LE, 0),
number_of_linenumbers: U16::new(LE, 0),
characteristics: U32::new(LE, section.characteristics),
};
self.buffer.write(&section_header);
}
}
/// Reserve a section.
///
/// Returns the file range and virtual address range that are reserved
/// for the section.
pub fn reserve_section(
&mut self,
name: [u8; 8],
characteristics: u32,
virtual_size: u32,
data_size: u32,
) -> SectionRange {
let virtual_address = self.reserve_virtual(virtual_size);
// Padding after section must be included in section file size.
let file_size = util::align_u32(data_size, self.file_alignment);
let file_offset = if file_size != 0 {
self.reserve(file_size, self.file_alignment)
} else {
0
};
// Sizes in optional header use the virtual size with the file alignment.
let aligned_virtual_size = util::align_u32(virtual_size, self.file_alignment);
if characteristics & pe::IMAGE_SCN_CNT_CODE != 0 {
if self.code_address == 0 {
self.code_address = virtual_address;
}
self.code_len += aligned_virtual_size;
} else if characteristics & pe::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 {
if self.data_address == 0 {
self.data_address = virtual_address;
}
self.data_len += aligned_virtual_size;
} else if characteristics & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 {
if self.data_address == 0 {
self.data_address = virtual_address;
}
self.bss_len += aligned_virtual_size;
}
let range = SectionRange {
virtual_address,
virtual_size,
file_offset,
file_size,
};
self.sections.push(Section {
name,
characteristics,
range,
});
range
}
/// Write the data for a section.
pub fn write_section(&mut self, offset: u32, data: &[u8]) {
if data.is_empty() {
return;
}
self.pad_until(offset);
self.write(data);
self.write_align(self.file_alignment);
}
/// Reserve a `.text` section.
///
/// Contains executable code.
pub fn reserve_text_section(&mut self, size: u32) -> SectionRange {
self.reserve_section(
*b".text\0\0\0",
pe::IMAGE_SCN_CNT_CODE | pe::IMAGE_SCN_MEM_EXECUTE | pe::IMAGE_SCN_MEM_READ,
size,
size,
)
}
/// Reserve a `.data` section.
///
/// Contains initialized data.
///
/// May also contain uninitialized data if `virtual_size` is greater than `data_size`.
pub fn reserve_data_section(&mut self, virtual_size: u32, data_size: u32) -> SectionRange {
self.reserve_section(
*b".data\0\0\0",
pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE,
virtual_size,
data_size,
)
}
/// Reserve a `.rdata` section.
///
/// Contains read-only initialized data.
pub fn reserve_rdata_section(&mut self, size: u32) -> SectionRange {
self.reserve_section(
*b".rdata\0\0",
pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ,
size,
size,
)
}
/// Reserve a `.bss` section.
///
/// Contains uninitialized data.
pub fn reserve_bss_section(&mut self, size: u32) -> SectionRange {
self.reserve_section(
*b".bss\0\0\0\0",
pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE,
size,
0,
)
}
/// Reserve an `.idata` section.
///
/// Contains import tables. Note that it is permissible to store import tables in a different
/// section.
///
/// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_IMPORT` data directory.
pub fn reserve_idata_section(&mut self, size: u32) -> SectionRange {
let range = self.reserve_section(
*b".idata\0\0",
pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE,
size,
size,
);
let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_IMPORT];
debug_assert_eq!(dir.virtual_address, 0);
*dir = DataDirectory {
virtual_address: range.virtual_address,
size,
};
range
}
/// Reserve an `.edata` section.
///
/// Contains export tables.
///
/// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_EXPORT` data directory.
pub fn reserve_edata_section(&mut self, size: u32) -> SectionRange {
let range = self.reserve_section(
*b".edata\0\0",
pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ,
size,
size,
);
let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_EXPORT];
debug_assert_eq!(dir.virtual_address, 0);
*dir = DataDirectory {
virtual_address: range.virtual_address,
size,
};
range
}
/// Reserve a `.pdata` section.
///
/// Contains exception information.
///
/// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION` data directory.
pub fn reserve_pdata_section(&mut self, size: u32) -> SectionRange {
let range = self.reserve_section(
*b".pdata\0\0",
pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ,
size,
size,
);
let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION];
debug_assert_eq!(dir.virtual_address, 0);
*dir = DataDirectory {
virtual_address: range.virtual_address,
size,
};
range
}
/// Reserve a `.xdata` section.
///
/// Contains exception information.
pub fn reserve_xdata_section(&mut self, size: u32) -> SectionRange {
self.reserve_section(
*b".xdata\0\0",
pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ,
size,
size,
)
}
/// Reserve a `.rsrc` section.
///
/// Contains the resource directory.
///
/// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_RESOURCE` data directory.
pub fn reserve_rsrc_section(&mut self, size: u32) -> SectionRange {
let range = self.reserve_section(
*b".rsrc\0\0\0",
pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ,
size,
size,
);
let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_RESOURCE];
debug_assert_eq!(dir.virtual_address, 0);
*dir = DataDirectory {
virtual_address: range.virtual_address,
size,
};
range
}
/// Add a base relocation.
///
/// `typ` must be one of the `IMAGE_REL_BASED_*` constants.
pub fn add_reloc(&mut self, mut virtual_address: u32, typ: u16) {
let reloc = U16::new(LE, typ << 12 | (virtual_address & 0xfff) as u16);
virtual_address &= !0xfff;
if let Some(block) = self.reloc_blocks.last_mut() {
if block.virtual_address == virtual_address {
self.relocs.push(reloc);
block.count += 1;
return;
}
// Blocks must have an even number of relocations.
if block.count & 1 != 0 {
self.relocs.push(U16::new(LE, 0));
block.count += 1;
}
debug_assert!(block.virtual_address < virtual_address);
}
self.relocs.push(reloc);
self.reloc_blocks.push(RelocBlock {
virtual_address,
count: 1,
});
}
/// Return true if a base relocation has been added.
pub fn has_relocs(&mut self) -> bool {
!self.relocs.is_empty()
}
/// Reserve a `.reloc` section.
///
/// This contains the base relocations that were added with `add_reloc`.
///
/// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_BASERELOC` data directory.
pub fn reserve_reloc_section(&mut self) -> SectionRange {
if let Some(block) = self.reloc_blocks.last_mut() {
// Blocks must have an even number of relocations.
if block.count & 1 != 0 {
self.relocs.push(U16::new(LE, 0));
block.count += 1;
}
}
let size = self.reloc_blocks.iter().map(RelocBlock::size).sum();
let range = self.reserve_section(
*b".reloc\0\0",
pe::IMAGE_SCN_CNT_INITIALIZED_DATA
| pe::IMAGE_SCN_MEM_READ
| pe::IMAGE_SCN_MEM_DISCARDABLE,
size,
size,
);
let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_BASERELOC];
debug_assert_eq!(dir.virtual_address, 0);
*dir = DataDirectory {
virtual_address: range.virtual_address,
size,
};
self.reloc_offset = range.file_offset;
range
}
/// Write a `.reloc` section.
///
/// This contains the base relocations that were added with `add_reloc`.
pub fn write_reloc_section(&mut self) {
if self.reloc_offset == 0 {
return;
}
self.pad_until(self.reloc_offset);
let mut total = 0;
for block in &self.reloc_blocks {
self.buffer.write(&pe::ImageBaseRelocation {
virtual_address: U32::new(LE, block.virtual_address),
size_of_block: U32::new(LE, block.size()),
});
self.buffer
.write_slice(&self.relocs[total..][..block.count as usize]);
total += block.count as usize;
}
debug_assert_eq!(total, self.relocs.len());
self.write_align(self.file_alignment);
}
/// Reserve the certificate table.
///
/// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_SECURITY` data directory.
// TODO: reserve individual certificates
pub fn reserve_certificate_table(&mut self, size: u32) {
let size = util::align_u32(size, 8);
let offset = self.reserve(size, 8);
let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_SECURITY];
debug_assert_eq!(dir.virtual_address, 0);
*dir = DataDirectory {
virtual_address: offset,
size,
};
}
/// Write the certificate table.
// TODO: write individual certificates
pub fn write_certificate_table(&mut self, data: &[u8]) {
let dir = self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_SECURITY];
self.pad_until(dir.virtual_address);
self.write(data);
self.pad_until(dir.virtual_address + dir.size);
}
}
/// Information required for writing [`pe::ImageNtHeaders32`] or [`pe::ImageNtHeaders64`].
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct NtHeaders {
// ImageFileHeader
pub machine: u16,
pub time_date_stamp: u32,
pub characteristics: u16,
// ImageOptionalHeader
pub major_linker_version: u8,
pub minor_linker_version: u8,
pub address_of_entry_point: u32,
pub image_base: u64,
pub major_operating_system_version: u16,
pub minor_operating_system_version: u16,
pub major_image_version: u16,
pub minor_image_version: u16,
pub major_subsystem_version: u16,
pub minor_subsystem_version: u16,
pub subsystem: u16,
pub dll_characteristics: u16,
pub size_of_stack_reserve: u64,
pub size_of_stack_commit: u64,
pub size_of_heap_reserve: u64,
pub size_of_heap_commit: u64,
}
#[derive(Default, Clone, Copy)]
struct DataDirectory {
virtual_address: u32,
size: u32,
}
/// Information required for writing [`pe::ImageSectionHeader`].
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct Section {
pub name: [u8; pe::IMAGE_SIZEOF_SHORT_NAME],
pub characteristics: u32,
pub range: SectionRange,
}
/// The file range and virtual address range for a section.
#[allow(missing_docs)]
#[derive(Debug, Default, Clone, Copy)]
pub struct SectionRange {
pub virtual_address: u32,
pub virtual_size: u32,
pub file_offset: u32,
pub file_size: u32,
}
struct RelocBlock {
virtual_address: u32,
count: u32,
}
impl RelocBlock {
fn size(&self) -> u32 {
mem::size_of::<pe::ImageBaseRelocation>() as u32 + self.count * mem::size_of::<u16>() as u32
}
}
+186
View File
@@ -0,0 +1,186 @@
use alloc::vec::Vec;
#[cfg(feature = "write_std")]
type IndexSet<K> = indexmap::IndexSet<K>;
#[cfg(not(feature = "write_std"))]
type IndexSet<K> = indexmap::IndexSet<K, hashbrown::DefaultHashBuilder>;
/// An identifier for an entry in a string table.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StringId(usize);
#[derive(Debug, Default)]
pub(crate) struct StringTable<'a> {
strings: IndexSet<&'a [u8]>,
offsets: Vec<usize>,
}
impl<'a> StringTable<'a> {
/// Add a string to the string table.
///
/// Panics if the string table has already been written, or
/// if the string contains a null byte.
pub fn add(&mut self, string: &'a [u8]) -> StringId {
assert!(self.offsets.is_empty());
assert!(!string.contains(&0));
let id = self.strings.insert_full(string).0;
StringId(id)
}
/// Return the id of the given string.
///
/// Panics if the string is not in the string table.
#[allow(dead_code)]
pub fn get_id(&self, string: &[u8]) -> StringId {
let id = self.strings.get_index_of(string).unwrap();
StringId(id)
}
/// Return the string for the given id.
///
/// Panics if the string is not in the string table.
#[allow(dead_code)]
pub fn get_string(&self, id: StringId) -> &'a [u8] {
self.strings.get_index(id.0).unwrap()
}
/// Return the offset of the given string.
///
/// Panics if the string table has not been written, or
/// if the string is not in the string table.
pub fn get_offset(&self, id: StringId) -> usize {
self.offsets[id.0]
}
/// Append the string table to the given `Vec`, and
/// calculate the list of string offsets.
///
/// `base` is the initial string table offset. For example,
/// this should be 1 for ELF, to account for the initial
/// null byte (which must have been written by the caller).
///
/// Panics if the string table has already been written.
pub fn write(&mut self, base: usize, w: &mut Vec<u8>) {
assert!(self.offsets.is_empty());
let mut ids: Vec<_> = (0..self.strings.len()).collect();
sort(&mut ids, 1, &self.strings);
self.offsets = vec![0; ids.len()];
let mut offset = base;
let mut previous = &[][..];
for id in ids {
let string = self.strings.get_index(id).unwrap();
if previous.ends_with(string) {
self.offsets[id] = offset - string.len() - 1;
} else {
self.offsets[id] = offset;
w.extend_from_slice(string);
w.push(0);
offset += string.len() + 1;
previous = string;
}
}
}
/// Calculate the size in bytes of the string table.
///
/// `base` is the initial string table offset. For example,
/// this should be 1 for ELF, to account for the initial
/// null byte.
#[allow(dead_code)]
pub fn size(&self, base: usize) -> usize {
// TODO: cache this result?
let mut ids: Vec<_> = (0..self.strings.len()).collect();
sort(&mut ids, 1, &self.strings);
let mut size = base;
let mut previous = &[][..];
for id in ids {
let string = self.strings.get_index(id).unwrap();
if !previous.ends_with(string) {
size += string.len() + 1;
previous = string;
}
}
size
}
}
// Multi-key quicksort.
//
// Ordering is such that if a string is a suffix of at least one other string,
// then it is placed immediately after one of those strings. That is:
// - comparison starts at the end of the string
// - shorter strings come later
//
// Based on the implementation in LLVM.
fn sort(mut ids: &mut [usize], mut pos: usize, strings: &IndexSet<&[u8]>) {
loop {
if ids.len() <= 1 {
return;
}
let pivot = byte(ids[0], pos, strings);
let mut lower = 0;
let mut upper = ids.len();
let mut i = 1;
while i < upper {
let b = byte(ids[i], pos, strings);
if b > pivot {
ids.swap(lower, i);
lower += 1;
i += 1;
} else if b < pivot {
upper -= 1;
ids.swap(upper, i);
} else {
i += 1;
}
}
sort(&mut ids[..lower], pos, strings);
sort(&mut ids[upper..], pos, strings);
if pivot == 0 {
return;
}
ids = &mut ids[lower..upper];
pos += 1;
}
}
fn byte(id: usize, pos: usize, strings: &IndexSet<&[u8]>) -> u8 {
let string = strings.get_index(id).unwrap();
let len = string.len();
if len >= pos {
string[len - pos]
} else {
// We know the strings don't contain null bytes.
0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn string_table() {
let mut table = StringTable::default();
let id0 = table.add(b"");
let id1 = table.add(b"foo");
let id2 = table.add(b"bar");
let id3 = table.add(b"foobar");
let mut data = Vec::new();
data.push(0);
table.write(1, &mut data);
assert_eq!(data, b"\0foobar\0foo\0");
assert_eq!(table.get_offset(id0), 11);
assert_eq!(table.get_offset(id1), 8);
assert_eq!(table.get_offset(id2), 4);
assert_eq!(table.get_offset(id3), 1);
}
}
+261
View File
@@ -0,0 +1,261 @@
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::{io, mem};
use crate::pod::{bytes_of, bytes_of_slice, Pod};
/// Trait for writable buffer.
#[allow(clippy::len_without_is_empty)]
pub trait WritableBuffer {
/// Returns position/offset for data to be written at.
///
/// Should only be used in debug assertions
fn len(&self) -> usize;
/// Reserves specified number of bytes in the buffer.
///
/// This will be called exactly once before writing anything to the buffer,
/// and the given size is the exact total number of bytes that will be written.
fn reserve(&mut self, size: usize) -> Result<(), ()>;
/// Writes zero bytes at the end of the buffer until the buffer
/// has the specified length.
fn resize(&mut self, new_len: usize);
/// Writes the specified slice of bytes at the end of the buffer.
fn write_bytes(&mut self, val: &[u8]);
/// Writes the specified `Pod` type at the end of the buffer.
fn write_pod<T: Pod>(&mut self, val: &T)
where
Self: Sized,
{
self.write_bytes(bytes_of(val))
}
/// Writes the specified `Pod` slice at the end of the buffer.
fn write_pod_slice<T: Pod>(&mut self, val: &[T])
where
Self: Sized,
{
self.write_bytes(bytes_of_slice(val))
}
}
impl<'a> dyn WritableBuffer + 'a {
/// Writes the specified `Pod` type at the end of the buffer.
pub fn write<T: Pod>(&mut self, val: &T) {
self.write_bytes(bytes_of(val))
}
/// Writes the specified `Pod` slice at the end of the buffer.
pub fn write_slice<T: Pod>(&mut self, val: &[T]) {
self.write_bytes(bytes_of_slice(val))
}
}
impl WritableBuffer for Vec<u8> {
#[inline]
fn len(&self) -> usize {
self.len()
}
#[inline]
fn reserve(&mut self, size: usize) -> Result<(), ()> {
debug_assert!(self.is_empty());
self.reserve(size);
Ok(())
}
#[inline]
fn resize(&mut self, new_len: usize) {
debug_assert!(new_len >= self.len());
self.resize(new_len, 0);
}
#[inline]
fn write_bytes(&mut self, val: &[u8]) {
debug_assert!(self.len() + val.len() <= self.capacity());
self.extend_from_slice(val)
}
}
/// A [`WritableBuffer`] that streams data to a [`Write`](std::io::Write) implementation.
///
/// [`Self::result`] must be called to determine if an I/O error occurred during writing.
///
/// It is advisable to use a buffered writer like [`BufWriter`](std::io::BufWriter)
/// instead of an unbuffered writer like [`File`](std::fs::File).
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct StreamingBuffer<W> {
writer: W,
len: usize,
result: Result<(), io::Error>,
}
#[cfg(feature = "std")]
impl<W> StreamingBuffer<W> {
/// Create a new `StreamingBuffer` backed by the given writer.
pub fn new(writer: W) -> Self {
StreamingBuffer {
writer,
len: 0,
result: Ok(()),
}
}
/// Unwraps this [`StreamingBuffer`] giving back the original writer.
pub fn into_inner(self) -> W {
self.writer
}
/// Returns any error that occurred during writing.
pub fn result(&mut self) -> Result<(), io::Error> {
mem::replace(&mut self.result, Ok(()))
}
}
#[cfg(feature = "std")]
impl<W: io::Write> WritableBuffer for StreamingBuffer<W> {
#[inline]
fn len(&self) -> usize {
self.len
}
#[inline]
fn reserve(&mut self, _size: usize) -> Result<(), ()> {
Ok(())
}
#[inline]
fn resize(&mut self, new_len: usize) {
debug_assert!(self.len <= new_len);
while self.len < new_len {
let write_amt = (new_len - self.len - 1) % 1024 + 1;
self.write_bytes(&[0; 1024][..write_amt]);
}
}
#[inline]
fn write_bytes(&mut self, val: &[u8]) {
if self.result.is_ok() {
self.result = self.writer.write_all(val);
}
self.len += val.len();
}
}
/// A trait for mutable byte slices.
///
/// It provides convenience methods for `Pod` types.
pub(crate) trait BytesMut {
fn write_at<T: Pod>(self, offset: usize, val: &T) -> Result<(), ()>;
}
impl<'a> BytesMut for &'a mut [u8] {
#[inline]
fn write_at<T: Pod>(self, offset: usize, val: &T) -> Result<(), ()> {
let src = bytes_of(val);
let dest = self.get_mut(offset..).ok_or(())?;
let dest = dest.get_mut(..src.len()).ok_or(())?;
dest.copy_from_slice(src);
Ok(())
}
}
/// Write an unsigned number using the LEB128 encoding to a buffer.
///
/// Returns the number of bytes written.
#[allow(dead_code)]
pub(crate) fn write_uleb128(buf: &mut Vec<u8>, mut val: u64) -> usize {
let mut len = 0;
loop {
let mut byte = (val & 0x7f) as u8;
val >>= 7;
let done = val == 0;
if !done {
byte |= 0x80;
}
buf.push(byte);
len += 1;
if done {
return len;
}
}
}
/// Write a signed number using the LEB128 encoding to a buffer.
///
/// Returns the number of bytes written.
#[allow(dead_code)]
pub(crate) fn write_sleb128(buf: &mut Vec<u8>, mut val: i64) -> usize {
let mut len = 0;
loop {
let mut byte = val as u8;
// Keep the sign bit for testing
val >>= 6;
let done = val == 0 || val == -1;
if done {
byte &= !0x80;
} else {
// Remove the sign bit
val >>= 1;
byte |= 0x80;
}
buf.push(byte);
len += 1;
if done {
return len;
}
}
}
pub(crate) fn align(offset: usize, size: usize) -> usize {
(offset + (size - 1)) & !(size - 1)
}
#[allow(dead_code)]
pub(crate) fn align_u32(offset: u32, size: u32) -> u32 {
(offset + (size - 1)) & !(size - 1)
}
#[allow(dead_code)]
pub(crate) fn align_u64(offset: u64, size: u64) -> u64 {
(offset + (size - 1)) & !(size - 1)
}
pub(crate) fn write_align(buffer: &mut dyn WritableBuffer, size: usize) {
let new_len = align(buffer.len(), size);
buffer.resize(new_len);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bytes_mut() {
let data = vec![0x01, 0x23, 0x45, 0x67];
let mut bytes = data.clone();
bytes.extend_from_slice(bytes_of(&u16::to_be(0x89ab)));
assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab]);
let mut bytes = data.clone();
assert_eq!(bytes.write_at(0, &u16::to_be(0x89ab)), Ok(()));
assert_eq!(bytes, [0x89, 0xab, 0x45, 0x67]);
let mut bytes = data.clone();
assert_eq!(bytes.write_at(2, &u16::to_be(0x89ab)), Ok(()));
assert_eq!(bytes, [0x01, 0x23, 0x89, 0xab]);
assert_eq!(bytes.write_at(3, &u16::to_be(0x89ab)), Err(()));
assert_eq!(bytes.write_at(4, &u16::to_be(0x89ab)), Err(()));
assert_eq!([].write_at(0, &u32::to_be(0x89ab)), Err(()));
}
}
+588
View File
@@ -0,0 +1,588 @@
use core::mem;
use crate::endian::{BigEndian as BE, I16, U16, U32};
use crate::write::string::*;
use crate::write::util::*;
use crate::write::*;
use crate::xcoff;
#[derive(Default, Clone, Copy)]
struct SectionOffsets {
address: u64,
data_offset: usize,
reloc_offset: usize,
}
#[derive(Default, Clone, Copy)]
struct SymbolOffsets {
index: usize,
str_id: Option<StringId>,
aux_count: u8,
storage_class: u8,
}
impl<'a> Object<'a> {
pub(crate) fn xcoff_section_info(
&self,
section: StandardSection,
) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) {
match section {
StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None),
StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None),
StandardSection::ReadOnlyData
| StandardSection::ReadOnlyDataWithRel
| StandardSection::ReadOnlyString => (
&[],
&b".rdata"[..],
SectionKind::ReadOnlyData,
SectionFlags::None,
),
StandardSection::UninitializedData => (
&[],
&b".bss"[..],
SectionKind::UninitializedData,
SectionFlags::None,
),
StandardSection::Tls => (&[], &b".tdata"[..], SectionKind::Tls, SectionFlags::None),
StandardSection::UninitializedTls => (
&[],
&b".tbss"[..],
SectionKind::UninitializedTls,
SectionFlags::None,
),
StandardSection::TlsVariables => {
// Unsupported section.
(&[], &[], SectionKind::TlsVariables, SectionFlags::None)
}
StandardSection::Common => {
// Unsupported section.
(&[], &[], SectionKind::Common, SectionFlags::None)
}
StandardSection::GnuProperty => {
// Unsupported section.
(&[], &[], SectionKind::Note, SectionFlags::None)
}
}
}
pub(crate) fn xcoff_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> {
let (kind, _encoding, size) = if let RelocationFlags::Generic {
kind,
encoding,
size,
} = reloc.flags
{
(kind, encoding, size)
} else {
return Ok(());
};
let r_rtype = match kind {
RelocationKind::Absolute => xcoff::R_POS,
RelocationKind::Relative => xcoff::R_REL,
RelocationKind::Got => xcoff::R_TOC,
_ => {
return Err(Error(format!("unimplemented relocation {:?}", reloc)));
}
};
let r_rsize = size - 1;
reloc.flags = RelocationFlags::Xcoff { r_rtype, r_rsize };
Ok(())
}
pub(crate) fn xcoff_adjust_addend(&mut self, relocation: &mut Relocation) -> Result<bool> {
let r_rtype = if let RelocationFlags::Xcoff { r_rtype, .. } = relocation.flags {
r_rtype
} else {
return Err(Error(format!("invalid relocation flags {:?}", relocation)));
};
if r_rtype == xcoff::R_REL {
relocation.addend += 4;
}
Ok(true)
}
pub(crate) fn xcoff_relocation_size(&self, reloc: &Relocation) -> Result<u8> {
let r_rsize = if let RelocationFlags::Xcoff { r_rsize, .. } = reloc.flags {
r_rsize
} else {
return Err(Error(format!("unexpected relocation {:?}", reloc)));
};
Ok(r_rsize + 1)
}
pub(crate) fn xcoff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
let is_64 = match self.architecture.address_size().unwrap() {
AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false,
AddressSize::U64 => true,
};
let (hdr_size, sechdr_size, rel_size, sym_size) = if is_64 {
(
mem::size_of::<xcoff::FileHeader64>(),
mem::size_of::<xcoff::SectionHeader64>(),
mem::size_of::<xcoff::Rel64>(),
mem::size_of::<xcoff::Symbol64>(),
)
} else {
(
mem::size_of::<xcoff::FileHeader32>(),
mem::size_of::<xcoff::SectionHeader32>(),
mem::size_of::<xcoff::Rel32>(),
mem::size_of::<xcoff::Symbol32>(),
)
};
// Calculate offsets and build strtab.
let mut offset = 0;
let mut strtab = StringTable::default();
// We place the shared address 0 immediately after the section header table.
let mut address = 0;
// XCOFF file header.
offset += hdr_size;
// Section headers.
offset += self.sections.len() * sechdr_size;
// Calculate size of section data.
let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()];
for (index, section) in self.sections.iter().enumerate() {
let len = section.data.len();
let sectype = section.kind;
// Section address should be 0 for all sections except the .text, .data, and .bss sections.
if sectype == SectionKind::Data
|| sectype == SectionKind::Text
|| sectype == SectionKind::UninitializedData
{
section_offsets[index].address = address as u64;
address += len;
address = align(address, 4);
} else {
section_offsets[index].address = 0;
}
if len != 0 {
// Set the default section alignment as 4.
offset = align(offset, 4);
section_offsets[index].data_offset = offset;
offset += len;
} else {
section_offsets[index].data_offset = 0;
}
}
// Calculate size of relocations.
for (index, section) in self.sections.iter().enumerate() {
let count = section.relocations.len();
if count != 0 {
section_offsets[index].reloc_offset = offset;
offset += count * rel_size;
} else {
section_offsets[index].reloc_offset = 0;
}
}
// Calculate size of symbols.
let mut file_str_id = None;
let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
let mut symtab_count = 0;
for (index, symbol) in self.symbols.iter().enumerate() {
symbol_offsets[index].index = symtab_count;
symtab_count += 1;
let storage_class = if let SymbolFlags::Xcoff { n_sclass, .. } = symbol.flags {
n_sclass
} else {
match symbol.kind {
SymbolKind::File => xcoff::C_FILE,
SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => {
if symbol.is_local() {
xcoff::C_STAT
} else if symbol.weak {
xcoff::C_WEAKEXT
} else {
xcoff::C_EXT
}
}
SymbolKind::Section | SymbolKind::Label | SymbolKind::Unknown => {
return Err(Error(format!(
"unimplemented symbol `{}` kind {:?}",
symbol.name().unwrap_or(""),
symbol.kind
)));
}
}
};
symbol_offsets[index].storage_class = storage_class;
if storage_class == xcoff::C_FILE {
if is_64 && file_str_id.is_none() {
file_str_id = Some(strtab.add(b".file"));
}
if symbol.name.len() > 8 {
symbol_offsets[index].str_id = Some(strtab.add(&symbol.name));
}
} else if is_64 || symbol.name.len() > 8 {
symbol_offsets[index].str_id = Some(strtab.add(&symbol.name));
}
symbol_offsets[index].aux_count = 0;
match storage_class {
xcoff::C_FILE => {
symbol_offsets[index].aux_count = 1;
symtab_count += 1;
}
xcoff::C_EXT | xcoff::C_WEAKEXT | xcoff::C_HIDEXT => {
symbol_offsets[index].aux_count = 1;
symtab_count += 1;
}
// TODO: support auxiliary entry for other types of symbol.
_ => {}
}
}
let symtab_offset = offset;
let symtab_len = symtab_count * sym_size;
offset += symtab_len;
// Calculate size of strtab.
let strtab_offset = offset;
let mut strtab_data = Vec::new();
// First 4 bytes of strtab are the length.
strtab.write(4, &mut strtab_data);
let strtab_len = strtab_data.len() + 4;
offset += strtab_len;
// Start writing.
buffer
.reserve(offset)
.map_err(|_| Error(String::from("Cannot allocate buffer")))?;
// Write file header.
if is_64 {
let header = xcoff::FileHeader64 {
f_magic: U16::new(BE, xcoff::MAGIC_64),
f_nscns: U16::new(BE, self.sections.len() as u16),
f_timdat: U32::new(BE, 0),
f_symptr: U64::new(BE, symtab_offset as u64),
f_nsyms: U32::new(BE, symtab_count as u32),
f_opthdr: U16::new(BE, 0),
f_flags: match self.flags {
FileFlags::Xcoff { f_flags } => U16::new(BE, f_flags),
_ => U16::default(),
},
};
buffer.write(&header);
} else {
let header = xcoff::FileHeader32 {
f_magic: U16::new(BE, xcoff::MAGIC_32),
f_nscns: U16::new(BE, self.sections.len() as u16),
f_timdat: U32::new(BE, 0),
f_symptr: U32::new(BE, symtab_offset as u32),
f_nsyms: U32::new(BE, symtab_count as u32),
f_opthdr: U16::new(BE, 0),
f_flags: match self.flags {
FileFlags::Xcoff { f_flags } => U16::new(BE, f_flags),
_ => U16::default(),
},
};
buffer.write(&header);
}
// Write section headers.
for (index, section) in self.sections.iter().enumerate() {
let mut sectname = [0; 8];
sectname
.get_mut(..section.name.len())
.ok_or_else(|| {
Error(format!(
"section name `{}` is too long",
section.name().unwrap_or(""),
))
})?
.copy_from_slice(&section.name);
let flags = if let SectionFlags::Xcoff { s_flags } = section.flags {
s_flags
} else {
match section.kind {
SectionKind::Text
| SectionKind::ReadOnlyData
| SectionKind::ReadOnlyString
| SectionKind::ReadOnlyDataWithRel => xcoff::STYP_TEXT,
SectionKind::Data => xcoff::STYP_DATA,
SectionKind::UninitializedData => xcoff::STYP_BSS,
SectionKind::Tls => xcoff::STYP_TDATA,
SectionKind::UninitializedTls => xcoff::STYP_TBSS,
SectionKind::OtherString => xcoff::STYP_INFO,
SectionKind::Debug | SectionKind::DebugString => xcoff::STYP_DEBUG,
SectionKind::Other | SectionKind::Metadata => 0,
SectionKind::Note
| SectionKind::Linker
| SectionKind::Common
| SectionKind::Unknown
| SectionKind::TlsVariables
| SectionKind::Elf(_) => {
return Err(Error(format!(
"unimplemented section `{}` kind {:?}",
section.name().unwrap_or(""),
section.kind
)));
}
}
.into()
};
if is_64 {
let section_header = xcoff::SectionHeader64 {
s_name: sectname,
s_paddr: U64::new(BE, section_offsets[index].address),
// This field has the same value as the s_paddr field.
s_vaddr: U64::new(BE, section_offsets[index].address),
s_size: U64::new(BE, section.data.len() as u64),
s_scnptr: U64::new(BE, section_offsets[index].data_offset as u64),
s_relptr: U64::new(BE, section_offsets[index].reloc_offset as u64),
s_lnnoptr: U64::new(BE, 0),
s_nreloc: U32::new(BE, section.relocations.len() as u32),
s_nlnno: U32::new(BE, 0),
s_flags: U32::new(BE, flags),
s_reserve: U32::new(BE, 0),
};
buffer.write(&section_header);
} else {
let section_header = xcoff::SectionHeader32 {
s_name: sectname,
s_paddr: U32::new(BE, section_offsets[index].address as u32),
// This field has the same value as the s_paddr field.
s_vaddr: U32::new(BE, section_offsets[index].address as u32),
s_size: U32::new(BE, section.data.len() as u32),
s_scnptr: U32::new(BE, section_offsets[index].data_offset as u32),
s_relptr: U32::new(BE, section_offsets[index].reloc_offset as u32),
s_lnnoptr: U32::new(BE, 0),
// TODO: If more than 65,534 relocation entries are required, the field
// value will be 65535, and an STYP_OVRFLO section header will contain
// the actual count of relocation entries in the s_paddr field.
s_nreloc: U16::new(BE, section.relocations.len() as u16),
s_nlnno: U16::new(BE, 0),
s_flags: U32::new(BE, flags),
};
buffer.write(&section_header);
}
}
// Write section data.
for (index, section) in self.sections.iter().enumerate() {
let len = section.data.len();
if len != 0 {
write_align(buffer, 4);
debug_assert_eq!(section_offsets[index].data_offset, buffer.len());
buffer.write_bytes(&section.data);
}
}
// Write relocations.
for (index, section) in self.sections.iter().enumerate() {
if !section.relocations.is_empty() {
debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
for reloc in &section.relocations {
let (r_rtype, r_rsize) =
if let RelocationFlags::Xcoff { r_rtype, r_rsize } = reloc.flags {
(r_rtype, r_rsize)
} else {
return Err(Error("invalid relocation flags".into()));
};
if is_64 {
let xcoff_rel = xcoff::Rel64 {
r_vaddr: U64::new(BE, reloc.offset),
r_symndx: U32::new(BE, symbol_offsets[reloc.symbol.0].index as u32),
r_rsize,
r_rtype,
};
buffer.write(&xcoff_rel);
} else {
let xcoff_rel = xcoff::Rel32 {
r_vaddr: U32::new(BE, reloc.offset as u32),
r_symndx: U32::new(BE, symbol_offsets[reloc.symbol.0].index as u32),
r_rsize,
r_rtype,
};
buffer.write(&xcoff_rel);
}
}
}
}
// Write symbols.
debug_assert_eq!(symtab_offset, buffer.len());
for (index, symbol) in self.symbols.iter().enumerate() {
let (n_value, section_kind) = if let SymbolSection::Section(id) = symbol.section {
(
section_offsets[id.0].address + symbol.value,
self.sections[id.0].kind,
)
} else {
(symbol.value, SectionKind::Unknown)
};
let n_scnum = match symbol.section {
SymbolSection::None => {
debug_assert_eq!(symbol.kind, SymbolKind::File);
xcoff::N_DEBUG
}
SymbolSection::Undefined | SymbolSection::Common => xcoff::N_UNDEF,
SymbolSection::Absolute => xcoff::N_ABS,
SymbolSection::Section(id) => id.0 as i16 + 1,
};
let n_sclass = symbol_offsets[index].storage_class;
let n_type = if (symbol.scope == SymbolScope::Linkage)
&& (n_sclass == xcoff::C_EXT
|| n_sclass == xcoff::C_WEAKEXT
|| n_sclass == xcoff::C_HIDEXT)
{
xcoff::SYM_V_HIDDEN
} else {
0
};
let n_numaux = symbol_offsets[index].aux_count;
if is_64 {
let str_id = if n_sclass == xcoff::C_FILE {
file_str_id.unwrap()
} else {
symbol_offsets[index].str_id.unwrap()
};
let xcoff_sym = xcoff::Symbol64 {
n_value: U64::new(BE, n_value),
n_offset: U32::new(BE, strtab.get_offset(str_id) as u32),
n_scnum: I16::new(BE, n_scnum),
n_type: U16::new(BE, n_type),
n_sclass,
n_numaux,
};
buffer.write(&xcoff_sym);
} else {
let mut sym_name = [0; 8];
if n_sclass == xcoff::C_FILE {
sym_name[..5].copy_from_slice(b".file");
} else if symbol.name.len() <= 8 {
sym_name[..symbol.name.len()].copy_from_slice(&symbol.name[..]);
} else {
let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap());
sym_name[4..8].copy_from_slice(&u32::to_be_bytes(str_offset as u32));
}
let xcoff_sym = xcoff::Symbol32 {
n_name: sym_name,
n_value: U32::new(BE, n_value as u32),
n_scnum: I16::new(BE, n_scnum),
n_type: U16::new(BE, n_type),
n_sclass,
n_numaux,
};
buffer.write(&xcoff_sym);
}
// Generate auxiliary entries.
if n_sclass == xcoff::C_FILE {
debug_assert_eq!(n_numaux, 1);
let mut x_fname = [0; 8];
if symbol.name.len() <= 8 {
x_fname[..symbol.name.len()].copy_from_slice(&symbol.name[..]);
} else {
let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap());
x_fname[4..8].copy_from_slice(&u32::to_be_bytes(str_offset as u32));
}
if is_64 {
let file_aux = xcoff::FileAux64 {
x_fname,
x_fpad: Default::default(),
x_ftype: xcoff::XFT_FN,
x_freserve: Default::default(),
x_auxtype: xcoff::AUX_FILE,
};
buffer.write(&file_aux);
} else {
let file_aux = xcoff::FileAux32 {
x_fname,
x_fpad: Default::default(),
x_ftype: xcoff::XFT_FN,
x_freserve: Default::default(),
};
buffer.write(&file_aux);
}
} else if n_sclass == xcoff::C_EXT
|| n_sclass == xcoff::C_WEAKEXT
|| n_sclass == xcoff::C_HIDEXT
{
debug_assert_eq!(n_numaux, 1);
let (x_smtyp, x_smclas) = if let SymbolFlags::Xcoff {
x_smtyp, x_smclas, ..
} = symbol.flags
{
(x_smtyp, x_smclas)
} else {
match symbol.kind {
SymbolKind::Text => (xcoff::XTY_SD, xcoff::XMC_PR),
SymbolKind::Data => {
if section_kind == SectionKind::UninitializedData {
(xcoff::XTY_CM, xcoff::XMC_BS)
} else if section_kind == SectionKind::ReadOnlyData {
(xcoff::XTY_SD, xcoff::XMC_RO)
} else {
(xcoff::XTY_SD, xcoff::XMC_RW)
}
}
SymbolKind::Tls => {
if section_kind == SectionKind::UninitializedTls {
(xcoff::XTY_CM, xcoff::XMC_UL)
} else {
(xcoff::XTY_SD, xcoff::XMC_TL)
}
}
_ => {
return Err(Error(format!(
"unimplemented symbol `{}` kind {:?}",
symbol.name().unwrap_or(""),
symbol.kind
)));
}
}
};
let scnlen = if let SymbolFlags::Xcoff {
containing_csect: Some(containing_csect),
..
} = symbol.flags
{
symbol_offsets[containing_csect.0].index as u64
} else {
symbol.size
};
if is_64 {
let csect_aux = xcoff::CsectAux64 {
x_scnlen_lo: U32::new(BE, (scnlen & 0xFFFFFFFF) as u32),
x_scnlen_hi: U32::new(BE, ((scnlen >> 32) & 0xFFFFFFFF) as u32),
x_parmhash: U32::new(BE, 0),
x_snhash: U16::new(BE, 0),
x_smtyp,
x_smclas,
pad: 0,
x_auxtype: xcoff::AUX_CSECT,
};
buffer.write(&csect_aux);
} else {
let csect_aux = xcoff::CsectAux32 {
x_scnlen: U32::new(BE, scnlen as u32),
x_parmhash: U32::new(BE, 0),
x_snhash: U16::new(BE, 0),
x_smtyp,
x_smclas,
x_stab: U32::new(BE, 0),
x_snstab: U16::new(BE, 0),
};
buffer.write(&csect_aux);
}
}
}
// Write string table.
debug_assert_eq!(strtab_offset, buffer.len());
buffer.write_bytes(&u32::to_be_bytes(strtab_len as u32));
buffer.write_bytes(&strtab_data);
debug_assert_eq!(offset, buffer.len());
Ok(())
}
}