use std::io::{Error, Seek, Write};
use std::mem::size_of;
use memoffset::offset_of;
use object::endian::{LittleEndian as LE, U16, U32};
use object::pe::*;
use object::pod::bytes_of;
use crate::def::{ModuleDef, ShortExport};
use crate::{ar, ArchiveMember, MachineType};
const NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME: &str = "__NULL_IMPORT_DESCRIPTOR";
#[derive(Debug, Clone, Copy)]
#[repr(u16)]
enum ImportType {
Code,
Data,
Const,
}
impl ShortExport {
fn import_type(&self) -> ImportType {
if self.data {
ImportType::Data
} else if self.constant {
ImportType::Const
} else {
ImportType::Code
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u16)]
enum ImportNameType {
Ordinal = IMPORT_OBJECT_ORDINAL,
Name = IMPORT_OBJECT_NAME,
NameNoPrefix = IMPORT_OBJECT_NAME_NO_PREFIX,
NameUndecorate = IMPORT_OBJECT_NAME_UNDECORATE,
ExportAs = IMPORT_OBJECT_NAME_EXPORTAS,
}
fn arm64ec_mangle_name(name: &str) -> Option<String> {
if name.starts_with('#') {
return None;
}
if !name.starts_with('?') {
return Some(format!("#{}", name));
}
if name.contains("$$h") {
return None;
}
if name.starts_with("??@") && name.ends_with('@') {
return Some(format!("{}$$h@", name));
}
let insert_idx = find_arm64ec_insertion_point(name)?;
let mut result = String::with_capacity(name.len() + 3);
result.push_str(&name[..insert_idx]);
result.push_str("$$h");
result.push_str(&name[insert_idx..]);
Some(result)
}
fn find_arm64ec_insertion_point(name: &str) -> Option<usize> {
let b = name.as_bytes();
if b.first() != Some(&b'?') {
return None;
}
let mut pos = 1;
pos = skip_unqualified_name(b, pos)?;
pos = skip_name_scope_chain(b, pos)?;
Some(pos)
}
fn skip_unqualified_name(b: &[u8], pos: usize) -> Option<usize> {
if pos >= b.len() {
return None;
}
match b[pos] {
b'0'..=b'9' => Some(pos + 1), b'?' if b.get(pos + 1) == Some(&b'$') => skip_template_instantiation(b, pos),
b'?' => skip_special_identifier(b, pos + 1),
_ => skip_simple_name(b, pos), }
}
fn skip_simple_name(b: &[u8], pos: usize) -> Option<usize> {
let end = memchr::memchr(b'@', &b[pos..])?;
Some(pos + end + 1)
}
fn skip_template_instantiation(b: &[u8], pos: usize) -> Option<usize> {
let mut p = pos + 2;
p = skip_simple_name(b, p)?;
p = skip_template_args(b, p)?;
Some(p)
}
fn skip_template_args(b: &[u8], mut pos: usize) -> Option<usize> {
let mut depth = 1u32;
while pos < b.len() {
match b[pos] {
b'@' => {
pos += 1;
depth -= 1;
if depth == 0 {
return Some(pos);
}
}
b'?' if b.get(pos + 1) == Some(&b'$') => {
pos += 2;
pos = skip_simple_name(b, pos)?;
depth += 1;
}
_ => {
pos += 1;
}
}
}
None
}
fn skip_special_identifier(b: &[u8], mut pos: usize) -> Option<usize> {
if pos >= b.len() {
return None;
}
match b[pos] {
b'?' => {
pos += 1;
if pos < b.len() && b[pos] == b'_' {
pos += 1; }
if pos < b.len() {
pos += 1; }
Some(pos)
}
b'0'..=b'9' => Some(pos + 1), b'A'..=b'Z' => Some(pos + 1), b'_' => {
pos += 1;
if pos < b.len() {
pos += 1; }
Some(pos)
}
_ => Some(pos),
}
}
fn skip_name_scope_chain(b: &[u8], mut pos: usize) -> Option<usize> {
while pos < b.len() {
if b[pos] == b'@' {
return Some(pos + 1);
}
pos = skip_name_scope_piece(b, pos)?;
}
None
}
fn skip_name_scope_piece(b: &[u8], pos: usize) -> Option<usize> {
if pos >= b.len() {
return None;
}
match b[pos] {
b'0'..=b'9' => Some(pos + 1), b'?' if b.get(pos + 1) == Some(&b'$') => skip_template_instantiation(b, pos),
b'?' if b.get(pos + 1) == Some(&b'A') => {
skip_simple_name(b, pos + 1)
}
b'?' => {
let mut p = pos + 1;
while p < b.len() && b[p].is_ascii_digit() {
p += 1;
}
if p < b.len() && b[p] == b'?' {
p += 1;
}
p = skip_unqualified_name(b, p)?;
p = skip_name_scope_chain(b, p)?;
Some(p)
}
_ => skip_simple_name(b, pos),
}
}
impl MachineType {
fn is_32bit(&self) -> bool {
matches!(self, Self::ARMNT | Self::I386)
}
}
#[derive(Debug, Clone)]
pub struct MsvcImportLibrary {
def: ModuleDef,
native_def: Option<ModuleDef>,
machine: MachineType,
}
impl MsvcImportLibrary {
pub fn new(mut def: ModuleDef, native_def: Option<ModuleDef>, machine: MachineType) -> Self {
for export in &mut def.exports {
if let Some(ext_name) = export.ext_name.take() {
export.name = ext_name;
}
}
let native_def = native_def.map(|mut nd| {
for export in &mut nd.exports {
if let Some(ext_name) = export.ext_name.take() {
export.name = ext_name;
}
}
nd
});
MsvcImportLibrary {
def,
native_def,
machine,
}
}
fn get_name_type(&self, sym: &str, ext_name: &str) -> ImportNameType {
if ext_name.starts_with('_') && ext_name.contains('@') {
ImportNameType::Name
} else if sym != ext_name {
ImportNameType::NameUndecorate
} else if self.machine == MachineType::I386 && sym.starts_with('_') {
ImportNameType::NameNoPrefix
} else {
ImportNameType::Name
}
}
pub fn write_to<W: Write + Seek>(&self, writer: &mut W) -> Result<(), Error> {
let mut members: Vec<((ar::Header, ArchiveMember), MemberKind)> = Vec::new();
let factory = ObjectFactory::new(&self.def.import_name, self.machine);
let import_descriptor = factory.create_import_descriptor();
members.push((
import_descriptor.create_archive_entry(),
MemberKind::Descriptor,
));
let null_import_descriptor = factory.create_null_import_descriptor();
members.push((
null_import_descriptor.create_archive_entry(),
MemberKind::Descriptor,
));
let null_thunk = factory.create_null_thunk();
members.push((null_thunk.create_archive_entry(), MemberKind::Descriptor));
let ec_machine = if self.machine == MachineType::ARM64X {
MachineType::ARM64EC
} else {
self.machine
};
self.add_exports(
&factory,
&self.def.exports,
ec_machine,
MemberKind::Ec,
&mut members,
)?;
if self.machine == MachineType::ARM64X {
if let Some(native_def) = &self.native_def {
self.add_exports(
&factory,
&native_def.exports,
MachineType::ARM64,
MemberKind::Native,
&mut members,
)?;
}
}
let identifiers = members
.iter()
.map(|((header, _), _)| header.identifier().to_vec())
.collect();
if self.machine.is_ec() {
let mut regular_symbol_table: Vec<Vec<Vec<u8>>> = Vec::with_capacity(members.len());
let mut ec_symbol_table: Vec<Vec<Vec<u8>>> = Vec::with_capacity(members.len());
for ((_, member), kind) in &members {
let syms: Vec<Vec<u8>> = member
.symbols
.iter()
.map(|s| s.to_string().into_bytes())
.collect();
match kind {
MemberKind::Descriptor => {
regular_symbol_table.push(syms.clone());
ec_symbol_table.push(syms);
}
MemberKind::Ec => {
regular_symbol_table.push(Vec::new());
ec_symbol_table.push(syms);
}
MemberKind::Native => {
regular_symbol_table.push(syms);
ec_symbol_table.push(Vec::new());
}
}
}
let mut archive = ar::GnuBuilder::new_with_symbol_tables(
writer,
true,
identifiers,
regular_symbol_table,
Some(ec_symbol_table),
)?;
for ((header, member), _) in members {
archive.append(&header, &member.data[..])?;
}
} else {
let symbol_table: Vec<Vec<Vec<u8>>> = members
.iter()
.map(|((_, member), _)| {
member
.symbols
.iter()
.map(|s| s.to_string().into_bytes())
.collect::<Vec<Vec<u8>>>()
})
.collect();
let mut archive =
ar::GnuBuilder::new_with_symbol_table(writer, true, identifiers, symbol_table)?;
for ((header, member), _) in members {
archive.append(&header, &member.data[..])?;
}
}
Ok(())
}
fn add_exports(
&self,
factory: &ObjectFactory<'_>,
exports: &[ShortExport],
machine: MachineType,
kind: MemberKind,
members: &mut Vec<((ar::Header, ArchiveMember), MemberKind)>,
) -> Result<(), Error> {
for export in exports {
if export.private {
continue;
}
let sym = if export.symbol_name.is_empty() {
&export.name
} else {
&export.symbol_name
};
let name_type = if export.no_name {
ImportNameType::Ordinal
} else {
self.get_name_type(sym, &export.name)
};
let name = if let Some(ext_name) = &export.ext_name {
replace(sym, &export.name, ext_name)?
} else {
sym.to_string()
};
let (name, name_type, export_name) = if machine.is_ec()
&& matches!(export.import_type(), ImportType::Code)
&& !export.no_name
{
if let Some(mangled) = arm64ec_mangle_name(&name) {
(mangled, ImportNameType::ExportAs, Some(name))
} else {
(name, name_type, None)
}
} else {
(name, name_type, None)
};
if !export.alias_target.is_empty() && name != export.alias_target {
let weak_non_imp =
factory.create_weak_external(&export.alias_target, &name, false, machine);
members.push((weak_non_imp.create_archive_entry(), kind));
let weak_imp =
factory.create_weak_external(&export.alias_target, &name, true, machine);
members.push((weak_imp.create_archive_entry(), kind));
}
let short_import = factory.create_short_import(
&name,
export.ordinal,
export.import_type(),
name_type,
export_name.as_deref(),
machine,
);
members.push((short_import.create_archive_entry(), kind));
}
Ok(())
}
}
#[derive(Clone, Copy, PartialEq)]
enum MemberKind {
Descriptor,
Ec,
Native,
}
fn replace(sym: &str, from: &str, to: &str) -> Result<String, Error> {
use std::io::ErrorKind;
match sym.find(from) {
Some(pos) => return Ok(format!("{}{}{}", &sym[..pos], to, &sym[pos + from.len()..])),
None => {
if from.starts_with('_') && to.starts_with('_') {
if let Some(pos) = sym.find(&from[1..]) {
return Ok(format!(
"{}{}{}",
&sym[..pos],
&to[1..],
&sym[pos + from.len() - 1..]
));
}
}
}
}
Err(Error::new(
ErrorKind::InvalidInput,
format!("{}: replacing '{}' with '{}' failed", sym, from, to),
))
}
#[derive(Debug)]
struct ObjectFactory<'a> {
native_machine: MachineType,
import_name: &'a str,
import_descriptor_symbol_name: String,
null_thunk_symbol_name: String,
}
impl<'a> ObjectFactory<'a> {
fn new(import_name: &'a str, machine: MachineType) -> Self {
let library = if import_name.ends_with(".dll") || import_name.ends_with(".exe") {
&import_name[..import_name.len() - 4]
} else {
import_name
};
Self {
native_machine: machine.native_machine(),
import_name,
import_descriptor_symbol_name: format!("__IMPORT_DESCRIPTOR_{}", library),
null_thunk_symbol_name: format!("\x7f{}_NULL_THUNK_DATA", library),
}
}
fn write_string_table(buffer: &mut Vec<u8>, strings: &[&str]) {
let offset = buffer.len();
buffer.extend_from_slice(&[0, 0, 0, 0]);
for s in strings {
buffer.extend(s.as_bytes());
buffer.push(b'\0');
}
let size = (buffer.len() - offset) as u32;
buffer[offset..offset + 4].copy_from_slice(&size.to_le_bytes());
}
fn create_import_descriptor(&self) -> ArchiveMember {
const NUM_SECTIONS: usize = 2;
const NUM_SYMBOLS: usize = 7;
const NUM_RELOCATIONS: usize = 3;
let mut buffer = Vec::new();
let pointer_to_symbol_table = size_of::<ImageFileHeader>()
+ NUM_SECTIONS * size_of::<ImageSectionHeader>()
+ size_of::<ImageImportDescriptor>() + NUM_RELOCATIONS * size_of::<ImageRelocation>()
+ self.import_name.len() + 1;
let characteristics = if self.native_machine.is_32bit() {
IMAGE_FILE_32BIT_MACHINE
} else {
0
};
let header = ImageFileHeader {
machine: U16::new(LE, self.native_machine as u16),
number_of_sections: U16::new(LE, NUM_SECTIONS as u16),
time_date_stamp: U32::new(LE, 0),
pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32),
number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32),
size_of_optional_header: U16::new(LE, 0),
characteristics: U16::new(LE, characteristics),
};
buffer.extend_from_slice(bytes_of(&header));
let section_header_table = [
ImageSectionHeader {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'2'],
virtual_size: U32::new(LE, 0),
virtual_address: U32::new(LE, 0),
size_of_raw_data: U32::new(LE, size_of::<ImageImportDescriptor>() as _),
pointer_to_raw_data: U32::new(
LE,
(size_of::<ImageFileHeader>() + NUM_SECTIONS * size_of::<ImageSectionHeader>())
as _,
),
pointer_to_relocations: U32::new(
LE,
(size_of::<ImageFileHeader>()
+ NUM_SECTIONS * size_of::<ImageSectionHeader>()
+ size_of::<ImageImportDescriptor>()) as _,
),
pointer_to_linenumbers: U32::new(LE, 0),
number_of_relocations: U16::new(LE, NUM_RELOCATIONS as _),
number_of_linenumbers: U16::new(LE, 0),
characteristics: U32::new(
LE,
IMAGE_SCN_ALIGN_4BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE,
),
},
ImageSectionHeader {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'6'],
virtual_size: U32::new(LE, 0),
virtual_address: U32::new(LE, 0),
size_of_raw_data: U32::new(LE, (self.import_name.len() + 1) as _),
pointer_to_raw_data: U32::new(
LE,
(size_of::<ImageFileHeader>()
+ NUM_SECTIONS * size_of::<ImageSectionHeader>()
+ size_of::<ImageImportDescriptor>()
+ NUM_RELOCATIONS * size_of::<ImageRelocation>()) as _,
),
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,
IMAGE_SCN_ALIGN_2BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE,
),
},
];
for section in section_header_table {
buffer.extend_from_slice(bytes_of(§ion));
}
let import_descriptor = ImageImportDescriptor {
original_first_thunk: U32::new(LE, 0),
time_date_stamp: U32::new(LE, 0),
forwarder_chain: U32::new(LE, 0),
name: U32::new(LE, 0),
first_thunk: U32::new(LE, 0),
};
buffer.extend_from_slice(bytes_of(&import_descriptor));
let relocation_table = [
ImageRelocation {
virtual_address: U32::new(LE, offset_of!(ImageImportDescriptor, name) as _),
symbol_table_index: U32::new(LE, 2),
typ: U16::new(LE, self.native_machine.img_rel_relocation()),
},
ImageRelocation {
virtual_address: U32::new(
LE,
offset_of!(ImageImportDescriptor, original_first_thunk) as _,
),
symbol_table_index: U32::new(LE, 3),
typ: U16::new(LE, self.native_machine.img_rel_relocation()),
},
ImageRelocation {
virtual_address: U32::new(LE, offset_of!(ImageImportDescriptor, first_thunk) as _),
symbol_table_index: U32::new(LE, 4),
typ: U16::new(LE, self.native_machine.img_rel_relocation()),
},
];
for relocation in &relocation_table {
buffer.extend_from_slice(bytes_of(relocation));
}
buffer.extend_from_slice(self.import_name.as_bytes());
buffer.push(b'\0');
let sym5_offset =
(size_of::<u32>() + self.import_descriptor_symbol_name.len() + 1).to_le_bytes();
let sym6_offset = (size_of::<u32>()
+ self.import_descriptor_symbol_name.len()
+ 1
+ NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME.len()
+ 1)
.to_le_bytes();
let symbol_table = [
ImageSymbol {
name: [0, 0, 0, 0, size_of::<u32>() as _, 0, 0, 0],
value: U32::new(LE, 0),
section_number: U16::new(LE, 1),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_EXTERNAL,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'2'],
value: U32::new(LE, 0),
section_number: U16::new(LE, 1),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_SECTION,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'6'],
value: U32::new(LE, 0),
section_number: U16::new(LE, 2),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_STATIC,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'4'],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_SECTION,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'5'],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_SECTION,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [
0,
0,
0,
0,
sym5_offset[0],
sym5_offset[1],
sym5_offset[2],
sym5_offset[3],
],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_EXTERNAL,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [
0,
0,
0,
0,
sym6_offset[0],
sym6_offset[1],
sym6_offset[2],
sym6_offset[3],
],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_EXTERNAL,
number_of_aux_symbols: 0,
},
];
for table in &symbol_table {
buffer.extend_from_slice(bytes_of(table));
}
Self::write_string_table(
&mut buffer,
&[
&self.import_descriptor_symbol_name,
NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME,
&self.null_thunk_symbol_name,
],
);
ArchiveMember {
name: self.import_name.to_string(),
data: buffer,
symbols: vec![self.import_descriptor_symbol_name.to_string()],
}
}
fn create_null_import_descriptor(&self) -> ArchiveMember {
const NUM_SECTIONS: usize = 1;
const NUM_SYMBOLS: usize = 1;
let mut buffer = Vec::new();
let pointer_to_symbol_table = size_of::<ImageFileHeader>()
+ NUM_SECTIONS * size_of::<ImageSectionHeader>()
+ size_of::<ImageImportDescriptor>();
let characteristics = if self.native_machine.is_32bit() {
IMAGE_FILE_32BIT_MACHINE
} else {
0
};
let header = ImageFileHeader {
machine: U16::new(LE, self.native_machine as u16),
number_of_sections: U16::new(LE, NUM_SECTIONS as u16),
time_date_stamp: U32::new(LE, 0),
pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32),
number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32),
size_of_optional_header: U16::new(LE, 0),
characteristics: U16::new(LE, characteristics),
};
buffer.extend_from_slice(bytes_of(&header));
let section_header_table = ImageSectionHeader {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'3'],
virtual_size: U32::new(LE, 0),
virtual_address: U32::new(LE, 0),
size_of_raw_data: U32::new(LE, size_of::<ImageImportDescriptor>() as _),
pointer_to_raw_data: U32::new(
LE,
(size_of::<ImageFileHeader>() + NUM_SECTIONS * size_of::<ImageSectionHeader>())
as _,
),
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,
IMAGE_SCN_ALIGN_4BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE,
),
};
buffer.extend_from_slice(bytes_of(§ion_header_table));
let import_descriptor = ImageImportDescriptor {
original_first_thunk: U32::new(LE, 0),
time_date_stamp: U32::new(LE, 0),
forwarder_chain: U32::new(LE, 0),
name: U32::new(LE, 0),
first_thunk: U32::new(LE, 0),
};
buffer.extend_from_slice(bytes_of(&import_descriptor));
let symbol_table = ImageSymbol {
name: [0, 0, 0, 0, size_of::<u32>() as _, 0, 0, 0],
value: U32::new(LE, 0),
section_number: U16::new(LE, 1),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_EXTERNAL,
number_of_aux_symbols: 0,
};
buffer.extend_from_slice(bytes_of(&symbol_table));
Self::write_string_table(&mut buffer, &[NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME]);
ArchiveMember {
name: self.import_name.to_string(),
data: buffer,
symbols: vec![NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME.to_string()],
}
}
fn create_null_thunk(&self) -> ArchiveMember {
const NUM_SECTIONS: usize = 2;
const NUM_SYMBOLS: usize = 1;
let mut buffer = Vec::new();
let va_size = if self.native_machine.is_32bit() { 4 } else { 8 };
let pointer_to_symbol_table = size_of::<ImageFileHeader>()
+ NUM_SECTIONS * size_of::<ImageSectionHeader>()
+ va_size
+ va_size;
let characteristics = if self.native_machine.is_32bit() {
IMAGE_FILE_32BIT_MACHINE
} else {
0
};
let header = ImageFileHeader {
machine: U16::new(LE, self.native_machine as u16),
number_of_sections: U16::new(LE, NUM_SECTIONS as u16),
time_date_stamp: U32::new(LE, 0),
pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32),
number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32),
size_of_optional_header: U16::new(LE, 0),
characteristics: U16::new(LE, characteristics),
};
buffer.extend_from_slice(bytes_of(&header));
let align = if self.native_machine.is_32bit() {
IMAGE_SCN_ALIGN_4BYTES
} else {
IMAGE_SCN_ALIGN_8BYTES
};
let section_header_table = [
ImageSectionHeader {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'5'],
virtual_size: U32::new(LE, 0),
virtual_address: U32::new(LE, 0),
size_of_raw_data: U32::new(LE, va_size as _),
pointer_to_raw_data: U32::new(
LE,
(size_of::<ImageFileHeader>() + NUM_SECTIONS * size_of::<ImageSectionHeader>())
as _,
),
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,
align
| IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE,
),
},
ImageSectionHeader {
name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'4'],
virtual_size: U32::new(LE, 0),
virtual_address: U32::new(LE, 0),
size_of_raw_data: U32::new(LE, va_size as _),
pointer_to_raw_data: U32::new(
LE,
(size_of::<ImageFileHeader>()
+ NUM_SECTIONS * size_of::<ImageSectionHeader>()
+ va_size) as _,
),
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,
align
| IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE,
),
},
];
for section in section_header_table {
buffer.extend_from_slice(bytes_of(§ion));
}
buffer.extend(0u32.to_le_bytes());
if !self.native_machine.is_32bit() {
buffer.extend(0u32.to_le_bytes());
}
buffer.extend(0u32.to_le_bytes());
if !self.native_machine.is_32bit() {
buffer.extend(0u32.to_le_bytes());
}
let symbol_table = ImageSymbol {
name: [0, 0, 0, 0, size_of::<u32>() as _, 0, 0, 0],
value: U32::new(LE, 0),
section_number: U16::new(LE, 1),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_EXTERNAL,
number_of_aux_symbols: 0,
};
buffer.extend_from_slice(bytes_of(&symbol_table));
Self::write_string_table(&mut buffer, &[&self.null_thunk_symbol_name]);
ArchiveMember {
name: self.import_name.to_string(),
data: buffer,
symbols: vec![self.null_thunk_symbol_name.to_string()],
}
}
fn create_short_import(
&self,
sym: &str,
ordinal: u16,
import_type: ImportType,
name_type: ImportNameType,
export_name: Option<&str>,
machine: MachineType,
) -> ArchiveMember {
let export_name_size = export_name.map(|n| n.len() + 1).unwrap_or(0);
let import_name_size = self.import_name.len() + sym.len() + 2 + export_name_size;
let size = size_of::<ImportObjectHeader>() + import_name_size;
let mut buffer = Vec::with_capacity(size);
let import_header = ImportObjectHeader {
sig1: U16::new(LE, 0),
sig2: U16::new(LE, 0xFFFF),
version: U16::new(LE, 0),
machine: U16::new(LE, machine as _),
time_date_stamp: U32::new(LE, 0),
size_of_data: U32::new(LE, import_name_size as _),
ordinal_or_hint: if ordinal > 0 {
U16::new(LE, ordinal)
} else {
U16::new(LE, 0)
},
name_type: U16::new(LE, ((name_type as u16) << 2) | import_type as u16),
};
buffer.extend_from_slice(bytes_of(&import_header));
let is_ec = machine.is_ec();
let symbols = if is_ec && matches!(import_type, ImportType::Code) {
let demangled = export_name.unwrap_or_else(|| sym.strip_prefix('#').unwrap_or(sym));
let mut syms = vec![
format!("__imp_{}", demangled),
demangled.to_string(),
format!("__imp_aux_{}", demangled),
sym.to_string(),
];
let mut seen = std::collections::HashSet::new();
syms.retain(|s| seen.insert(s.clone()));
syms
} else if matches!(import_type, ImportType::Data) {
vec![format!("__imp_{}", sym)]
} else {
vec![format!("__imp_{}", sym), sym.to_string()]
};
buffer.extend(sym.as_bytes());
buffer.push(b'\0');
buffer.extend(self.import_name.as_bytes());
buffer.push(b'\0');
if let Some(export_name) = export_name {
buffer.extend(export_name.as_bytes());
buffer.push(b'\0');
}
ArchiveMember {
name: self.import_name.to_string(),
data: buffer,
symbols,
}
}
fn create_weak_external(
&self,
sym: &str,
weak: &str,
imp: bool,
machine: MachineType,
) -> ArchiveMember {
const NUM_SECTIONS: usize = 1;
const NUM_SYMBOLS: usize = 5;
let mut buffer = Vec::new();
let pointer_to_symbol_table =
size_of::<ImageFileHeader>() + NUM_SECTIONS * size_of::<ImageSectionHeader>();
let header = ImageFileHeader {
machine: U16::new(LE, machine as u16),
number_of_sections: U16::new(LE, NUM_SECTIONS as u16),
time_date_stamp: U32::new(LE, 0),
pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32),
number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32),
size_of_optional_header: U16::new(LE, 0),
characteristics: U16::new(LE, 0),
};
buffer.extend_from_slice(bytes_of(&header));
let section_header_table = ImageSectionHeader {
name: [b'.', b'd', b'r', b'e', b'c', b't', b'v', b'e'],
virtual_size: U32::new(LE, 0),
virtual_address: U32::new(LE, 0),
size_of_raw_data: U32::new(LE, 0),
pointer_to_raw_data: U32::new(LE, 0),
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, IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE),
};
buffer.extend_from_slice(bytes_of(§ion_header_table));
let prefix = if imp { "__imp_" } else { "" };
let sym3_offset = (size_of::<u32>() + sym.len() + prefix.len() + 1).to_le_bytes();
let symbol_table = [
ImageSymbol {
name: [b'@', b'c', b'o', b'm', b'p', b'.', b'i', b'd'],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0xFFFF),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_STATIC,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [b'@', b'f', b'e', b'a', b't', b'.', b'0', b'0'],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0xFFFF),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_STATIC,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [0, 0, 0, 0, size_of::<u32>() as _, 0, 0, 0],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_EXTERNAL,
number_of_aux_symbols: 0,
},
ImageSymbol {
name: [
0,
0,
0,
0,
sym3_offset[0],
sym3_offset[1],
sym3_offset[2],
sym3_offset[3],
],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_WEAK_EXTERNAL,
number_of_aux_symbols: 1,
},
ImageSymbol {
name: [2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS as u8, 0, 0, 0],
value: U32::new(LE, 0),
section_number: U16::new(LE, 0),
typ: U16::new(LE, 0),
storage_class: IMAGE_SYM_CLASS_NULL,
number_of_aux_symbols: 0,
},
];
for table in &symbol_table {
buffer.extend_from_slice(bytes_of(table));
}
Self::write_string_table(
&mut buffer,
&[
&format!("{}{}", prefix, sym),
&format!("{}{}", prefix, weak),
],
);
ArchiveMember {
name: self.import_name.to_string(),
data: buffer,
symbols: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arm64ec_mangle_name() {
assert_eq!(arm64ec_mangle_name("foo"), Some("#foo".into()));
assert_eq!(
arm64ec_mangle_name("PyInit_mod"),
Some("#PyInit_mod".into())
);
assert_eq!(arm64ec_mangle_name("#foo"), None);
assert_eq!(arm64ec_mangle_name("?func@@$$hYAHXZ"), None);
assert_eq!(
arm64ec_mangle_name("??@abc123@"),
Some("??@abc123@$$h@".into())
);
let cases = [
("?func@@YAHXZ", "?func@@$$hYAHXZ"),
("?Method@Class@@QEAAHXZ", "?Method@Class@@$$hQEAAHXZ"),
("?func@NS1@NS2@@YAHXZ", "?func@NS1@NS2@@$$hYAHXZ"),
("??0Class@@QEAA@XZ", "??0Class@@$$hQEAA@XZ"),
("??1Class@@UEAA@XZ", "??1Class@@$$hUEAA@XZ"),
("??HClass@@QEAAHH@Z", "??HClass@@$$hQEAAHH@Z"),
];
for (input, expected) in cases {
assert_eq!(
arm64ec_mangle_name(input),
Some(expected.into()),
"input: {input}"
);
}
}
}