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:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+10
@@ -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
@@ -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(§ion.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(§ion.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 §ion.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
@@ -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,
|
||||
}
|
||||
Vendored
+9
@@ -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
@@ -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(§ion.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(§ion.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(§ion.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 §ion.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(())
|
||||
}
|
||||
}
|
||||
+2394
File diff suppressed because it is too large.
Load diff
Vendored
+1107
File diff suppressed because it is too large.
Load diff
Vendored
+1025
File diff suppressed because it is too large.
Load diff
Vendored
+849
@@ -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(§ion_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
|
||||
}
|
||||
}
|
||||
Vendored
+186
@@ -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);
|
||||
}
|
||||
}
|
||||
Vendored
+261
@@ -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(()));
|
||||
}
|
||||
}
|
||||
Vendored
+588
@@ -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(§ion.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(§ion_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(§ion_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(§ion.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 §ion.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(())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user