584 lines
26 KiB
Rust
584 lines
26 KiB
Rust
|
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();
|
||
|
name.push(b'$');
|
||
|
name.extend_from_slice(value);
|
||
|
name
|
||
|
}
|
||
|
|
||
|
pub(crate) fn coff_fixup_relocation(&mut self, relocation: &mut Relocation) -> i64 {
|
||
|
if relocation.kind == RelocationKind::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?
|
||
|
relocation.kind = RelocationKind::Relative;
|
||
|
relocation.symbol = self.coff_add_stub_symbol(relocation.symbol);
|
||
|
} else if relocation.kind == RelocationKind::PltRelative {
|
||
|
// Windows doesn't need a separate relocation type for
|
||
|
// references to functions in import libraries.
|
||
|
// For convenience, treat this the same as Relative.
|
||
|
relocation.kind = RelocationKind::Relative;
|
||
|
}
|
||
|
|
||
|
let constant = match self.architecture {
|
||
|
Architecture::I386 | Architecture::Arm | Architecture::Aarch64 => match relocation.kind
|
||
|
{
|
||
|
RelocationKind::Relative => {
|
||
|
// IMAGE_REL_I386_REL32, IMAGE_REL_ARM_REL32, IMAGE_REL_ARM64_REL32
|
||
|
relocation.addend + 4
|
||
|
}
|
||
|
_ => relocation.addend,
|
||
|
},
|
||
|
Architecture::X86_64 => match relocation.kind {
|
||
|
RelocationKind::Relative => {
|
||
|
// IMAGE_REL_AMD64_REL32 through to IMAGE_REL_AMD64_REL32_5
|
||
|
if relocation.addend <= -4 && relocation.addend >= -9 {
|
||
|
0
|
||
|
} else {
|
||
|
relocation.addend + 4
|
||
|
}
|
||
|
}
|
||
|
_ => relocation.addend,
|
||
|
},
|
||
|
_ => unimplemented!(),
|
||
|
};
|
||
|
relocation.addend -= constant;
|
||
|
constant
|
||
|
}
|
||
|
|
||
|
fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> SymbolId {
|
||
|
if let Some(stub_id) = self.stub_symbols.get(&symbol_id) {
|
||
|
return *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));
|
||
|
section.relocations = vec![Relocation {
|
||
|
offset: 0,
|
||
|
size: stub_size * 8,
|
||
|
kind: RelocationKind::Absolute,
|
||
|
encoding: RelocationEncoding::Generic,
|
||
|
symbol: symbol_id,
|
||
|
addend: 0,
|
||
|
}];
|
||
|
|
||
|
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);
|
||
|
|
||
|
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::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 {
|
||
|
//assert!(reloc.implicit_addend);
|
||
|
let typ = match self.architecture {
|
||
|
Architecture::I386 => match (reloc.kind, reloc.size, reloc.addend) {
|
||
|
(RelocationKind::Absolute, 16, 0) => coff::IMAGE_REL_I386_DIR16,
|
||
|
(RelocationKind::Relative, 16, 0) => coff::IMAGE_REL_I386_REL16,
|
||
|
(RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_I386_DIR32,
|
||
|
(RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_I386_DIR32NB,
|
||
|
(RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_I386_SECTION,
|
||
|
(RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_I386_SECREL,
|
||
|
(RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_I386_SECREL7,
|
||
|
(RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_I386_REL32,
|
||
|
(RelocationKind::Coff(x), _, _) => x,
|
||
|
_ => {
|
||
|
return Err(Error(format!("unimplemented relocation {:?}", reloc)));
|
||
|
}
|
||
|
},
|
||
|
Architecture::X86_64 => match (reloc.kind, reloc.size, reloc.addend) {
|
||
|
(RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_AMD64_ADDR64,
|
||
|
(RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32,
|
||
|
(RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32NB,
|
||
|
(RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_AMD64_REL32,
|
||
|
(RelocationKind::Relative, 32, -5) => coff::IMAGE_REL_AMD64_REL32_1,
|
||
|
(RelocationKind::Relative, 32, -6) => coff::IMAGE_REL_AMD64_REL32_2,
|
||
|
(RelocationKind::Relative, 32, -7) => coff::IMAGE_REL_AMD64_REL32_3,
|
||
|
(RelocationKind::Relative, 32, -8) => coff::IMAGE_REL_AMD64_REL32_4,
|
||
|
(RelocationKind::Relative, 32, -9) => coff::IMAGE_REL_AMD64_REL32_5,
|
||
|
(RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_AMD64_SECTION,
|
||
|
(RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_AMD64_SECREL,
|
||
|
(RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_AMD64_SECREL7,
|
||
|
(RelocationKind::Coff(x), _, _) => x,
|
||
|
_ => {
|
||
|
return Err(Error(format!("unimplemented relocation {:?}", reloc)));
|
||
|
}
|
||
|
},
|
||
|
Architecture::Arm => match (reloc.kind, reloc.size, reloc.addend) {
|
||
|
(RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_ARM_ADDR32,
|
||
|
(RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_ARM_ADDR32NB,
|
||
|
(RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_ARM_REL32,
|
||
|
(RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_ARM_SECTION,
|
||
|
(RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_ARM_SECREL,
|
||
|
(RelocationKind::Coff(x), _, _) => x,
|
||
|
_ => {
|
||
|
return Err(Error(format!("unimplemented relocation {:?}", reloc)));
|
||
|
}
|
||
|
},
|
||
|
Architecture::Aarch64 => match (reloc.kind, reloc.size, reloc.addend) {
|
||
|
(RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_ARM64_ADDR32,
|
||
|
(RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_ARM64_ADDR32NB,
|
||
|
(RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_ARM64_SECTION,
|
||
|
(RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_ARM64_SECREL,
|
||
|
(RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_ARM64_ADDR64,
|
||
|
(RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_ARM64_REL32,
|
||
|
(RelocationKind::Coff(x), _, _) => x,
|
||
|
_ => {
|
||
|
return Err(Error(format!("unimplemented relocation {:?}", reloc)));
|
||
|
}
|
||
|
},
|
||
|
_ => {
|
||
|
return Err(Error(format!(
|
||
|
"unimplemented architecture {:?}",
|
||
|
self.architecture
|
||
|
)));
|
||
|
}
|
||
|
};
|
||
|
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 | SymbolKind::Null => {
|
||
|
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: 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()
|
||
|
}
|