use std::collections::HashMap;
use std::fs;
use std::io::Write;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum RelocationType {
R_X86_64_NONE,
R_X86_64_64, R_X86_64_PC32, R_X86_64_32, R_X86_64_32S, R_X86_64_PLT32, R_X86_64_GOTPCREL, R_AARCH64_ABS64,
R_AARCH64_ABS32,
R_AARCH64_CALL26,
R_AARCH64_ADR_PREL_PG_HI21,
R_AARCH64_LDST64_ABS_LO12_NC,
R_ARM_ABS32,
R_ARM_CALL,
R_ARM_MOVW_ABS_NC,
R_ARM_MOVT_ABS,
}
impl RelocationType {
pub fn elf_value(self) -> u32 {
match self {
RelocationType::R_X86_64_NONE => 0,
RelocationType::R_X86_64_64 => 1,
RelocationType::R_X86_64_PC32 => 2,
RelocationType::R_X86_64_32 => 10,
RelocationType::R_X86_64_32S => 11,
RelocationType::R_X86_64_PLT32 => 4,
RelocationType::R_X86_64_GOTPCREL => 9,
RelocationType::R_AARCH64_ABS64 => 257,
RelocationType::R_AARCH64_ABS32 => 258,
RelocationType::R_AARCH64_CALL26 => 283,
RelocationType::R_AARCH64_ADR_PREL_PG_HI21 => 275,
RelocationType::R_AARCH64_LDST64_ABS_LO12_NC => 286,
RelocationType::R_ARM_ABS32 => 2,
RelocationType::R_ARM_CALL => 28,
RelocationType::R_ARM_MOVW_ABS_NC => 43,
RelocationType::R_ARM_MOVT_ABS => 44,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObjectFormat {
ELF,
MachO,
COFF,
Wasm,
}
impl ObjectFormat {
pub fn from_target_triple(triple: &str) -> Self {
let lower = triple.to_lowercase();
if lower.contains("apple") || lower.contains("darwin") || lower.contains("macos") {
ObjectFormat::MachO
} else if lower.contains("windows") || lower.contains("msvc") || lower.contains("mingw") {
ObjectFormat::COFF
} else if lower.contains("wasm") {
ObjectFormat::Wasm
} else {
ObjectFormat::ELF
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SectionFlags {
pub alloc: bool, pub write: bool, pub exec: bool, pub merge: bool, pub strings: bool, }
impl SectionFlags {
pub fn text() -> Self {
Self {
alloc: true,
write: false,
exec: true,
merge: false,
strings: false,
}
}
pub fn data() -> Self {
Self {
alloc: true,
write: true,
exec: false,
merge: false,
strings: false,
}
}
pub fn rodata() -> Self {
Self {
alloc: true,
write: false,
exec: false,
merge: false,
strings: false,
}
}
pub fn bss() -> Self {
Self {
alloc: true,
write: true,
exec: false,
merge: false,
strings: false,
}
}
fn to_elf_flags(self) -> u64 {
let mut f = 0u64;
if self.alloc {
f |= 2; }
if self.write {
f |= 1; }
if self.exec {
f |= 4; }
if self.merge {
f |= 0x10; }
if self.strings {
f |= 0x20; }
f
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolBinding {
Local,
Global,
Weak,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolType {
NoType,
Object,
Func,
Section,
}
#[derive(Debug, Clone)]
pub struct ObjectSymbol {
pub name: String,
pub value: u64,
pub size: u64,
pub binding: SymbolBinding,
pub sym_type: SymbolType,
pub section_index: u16,
}
#[derive(Debug, Clone)]
pub struct RelocationEntry {
pub offset: u64,
pub symbol_index: u32,
pub rel_type: RelocationType,
pub addend: i64,
}
#[derive(Debug, Clone)]
pub struct ObjectSection {
pub name: String,
pub data: Vec<u8>,
pub flags: SectionFlags,
pub section_type: u32,
pub alignment: u64,
pub relocations: Vec<RelocationEntry>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MachOArch {
X86_64,
ARM64,
ARM,
}
impl MachOArch {
pub fn cpu_type(&self) -> u32 {
match self {
MachOArch::X86_64 => 0x01000007, MachOArch::ARM64 => 0x0100000C, MachOArch::ARM => 0x0000000C, }
}
pub fn cpu_subtype(&self) -> u32 {
match self {
MachOArch::X86_64 => 3, MachOArch::ARM64 => 0, MachOArch::ARM => 9, }
}
}
#[derive(Debug, Clone)]
pub struct MachOSegment {
pub name: String,
pub vmaddr: u64,
pub vmsize: u64,
pub fileoff: u64,
pub filesize: u64,
pub maxprot: u32,
pub initprot: u32,
pub nsects: u32,
pub flags: u32,
}
#[derive(Debug, Clone)]
pub struct MachoSymbol {
pub name: String,
pub n_type: u8,
pub n_sect: u8,
pub n_desc: u16,
pub n_value: u64,
}
#[derive(Debug, Clone)]
pub struct CoffSymbol {
pub name: String,
pub value: u32,
pub section_number: i16,
pub sym_type: u16,
pub storage_class: u8,
pub aux_count: u8,
}
#[derive(Debug, Clone)]
pub struct CoffRelocation {
pub virtual_address: u32,
pub symbol_index: u32,
pub rel_type: u16,
}
#[derive(Debug, Clone)]
pub struct WasmSection {
pub section_type: u8,
pub data: Vec<u8>,
}
pub mod macho_const {
pub const MH_MAGIC_64: u32 = 0xFEEDFACF;
pub const MH_MAGIC: u32 = 0xFEEDFACE;
pub const MH_OBJECT: u32 = 1;
pub const MH_EXECUTE: u32 = 2;
pub const MH_DYLIB: u32 = 6;
pub const LC_SEGMENT: u32 = 0x01;
pub const LC_SEGMENT_64: u32 = 0x19;
pub const LC_SYMTAB: u32 = 0x02;
pub const LC_DYSYMTAB: u32 = 0x0B;
pub const LC_UUID: u32 = 0x1B;
pub const LC_VERSION_MIN_MACOSX: u32 = 0x24;
pub const LC_VERSION_MIN_IPHONEOS: u32 = 0x25;
pub const LC_MAIN: u32 = 0x80000028;
pub const LC_LOAD_DYLIB: u32 = 0x0C;
pub const LC_RPATH: u32 = 0x8000001C;
pub const LC_FUNCTION_STARTS: u32 = 0x26;
pub const LC_DATA_IN_CODE: u32 = 0x29;
pub const VM_PROT_NONE: u32 = 0x0;
pub const VM_PROT_READ: u32 = 0x1;
pub const VM_PROT_WRITE: u32 = 0x2;
pub const VM_PROT_EXECUTE: u32 = 0x4;
pub const CPU_TYPE_X86_64: u32 = 0x01000007;
pub const CPU_TYPE_ARM64: u32 = 0x0100000C;
pub const CPU_TYPE_ARM: u32 = 0x0000000C;
pub const SG_HIGHVM: u32 = 0x1;
pub const SG_PROTECTED_VERSION_1: u32 = 0x8;
pub const S_REGULAR: u32 = 0x0;
pub const S_ATTR_PURE_INSTRUCTIONS: u32 = 0x80000000;
pub const S_ATTR_SOME_INSTRUCTIONS: u32 = 0x00000400;
pub const S_ATTR_DEBUG: u32 = 0x02000000;
pub const N_STAB: u8 = 0xE0;
pub const N_TYPE: u8 = 0x0E;
pub const N_EXT: u8 = 0x01;
pub const N_UNDF: u8 = 0x00;
pub const N_ABS: u8 = 0x02;
pub const N_SECT: u8 = 0x0E;
pub const N_PBUD: u8 = 0x0C;
pub const N_INDR: u8 = 0x0A;
}
pub mod coff_const {
pub const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664;
pub const IMAGE_FILE_MACHINE_ARM64: u16 = 0xAA64;
pub const IMAGE_FILE_MACHINE_ARM: u16 = 0x1C0;
pub const IMAGE_FILE_MACHINE_I386: u16 = 0x14C;
pub const IMAGE_SCN_CNT_CODE: u32 = 0x00000020;
pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x00000040;
pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x00000080;
pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x20000000;
pub const IMAGE_SCN_MEM_READ: u32 = 0x40000000;
pub const IMAGE_SCN_MEM_WRITE: u32 = 0x80000000;
pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x02000000;
pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x00000800;
pub const IMAGE_SYM_CLASS_EXTERNAL: u8 = 2;
pub const IMAGE_SYM_CLASS_STATIC: u8 = 3;
pub const IMAGE_SYM_CLASS_FUNCTION: u8 = 101;
pub const IMAGE_SYM_CLASS_FILE: u8 = 103;
}
pub struct ObjectWriter {
pub format: ObjectFormat,
pub target_triple: String,
pub sections: Vec<ObjectSection>,
pub symbols: Vec<ObjectSymbol>,
pub section_alignments: Vec<(String, u64)>,
pub debug_sections: Vec<(String, Vec<u8>)>,
}
impl ObjectWriter {
pub fn new(format: ObjectFormat, triple: &str) -> Self {
Self {
format,
target_triple: triple.to_string(),
sections: Vec::new(),
symbols: Vec::new(),
section_alignments: Vec::new(),
debug_sections: Vec::new(),
}
}
pub fn add_section(&mut self, name: &str, data: Vec<u8>, flags: SectionFlags) -> usize {
let idx = self.sections.len();
self.sections.push(ObjectSection {
name: name.to_string(),
data,
flags,
section_type: 1, alignment: 16,
relocations: Vec::new(),
});
idx
}
pub fn add_bss_section(&mut self, name: &str, size: u64) -> usize {
let idx = self.sections.len();
self.sections.push(ObjectSection {
name: name.to_string(),
data: vec![0u8; size as usize],
flags: SectionFlags::bss(),
section_type: 8, alignment: 16,
relocations: Vec::new(),
});
idx
}
pub fn add_symbol(
&mut self,
name: &str,
value: u64,
size: u64,
binding: SymbolBinding,
sym_type: SymbolType,
section_index: u16,
) {
self.symbols.push(ObjectSymbol {
name: name.to_string(),
value,
size,
binding,
sym_type,
section_index,
});
}
pub fn add_relocation(
&mut self,
section_index: usize,
offset: u64,
symbol_index: u32,
rel_type: RelocationType,
addend: i64,
) {
if section_index < self.sections.len() {
self.sections[section_index]
.relocations
.push(RelocationEntry {
offset,
symbol_index,
rel_type,
addend,
});
}
}
pub fn add_debug_section(&mut self, name: &str, data: Vec<u8>) {
self.debug_sections.push((name.to_string(), data));
}
pub fn set_section_alignment(&mut self, name: &str, align: u64) {
if let Some(sec) = self.sections.iter_mut().find(|s| s.name == name) {
sec.alignment = align;
}
self.section_alignments.retain(|(n, _)| n != name);
self.section_alignments.push((name.to_string(), align));
}
pub fn get_section_size(&self, name: &str) -> Option<u64> {
self.sections
.iter()
.find(|s| s.name == name)
.map(|s| s.data.len() as u64)
}
pub fn set_target_arch(&mut self, arch: &str) {
let triple = match arch {
"x86_64" | "amd64" => "x86_64-unknown-linux-gnu",
"aarch64" | "arm64" => "aarch64-unknown-linux-gnu",
"arm" | "arm32" => "arm-unknown-linux-gnueabihf",
"wasm32" | "wasm" => "wasm32-unknown-unknown",
_ => "x86_64-unknown-linux-gnu",
};
self.target_triple = triple.to_string();
self.format = ObjectFormat::from_target_triple(&self.target_triple);
}
pub fn write(&self) -> Vec<u8> {
match self.format {
ObjectFormat::ELF => self.write_elf64(),
ObjectFormat::MachO => self.write_macho64(),
ObjectFormat::COFF => self.write_coff64(),
ObjectFormat::Wasm => self.write_wasm_module(),
}
}
pub fn write_to_file(&self, path: &str) -> Result<(), String> {
let data = self.write();
let path = Path::new(path);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).map_err(|e| format!("Failed to create dir: {}", e))?;
}
let mut file =
fs::File::create(path).map_err(|e| format!("Failed to create file: {}", e))?;
file.write_all(&data)
.map_err(|e| format!("Failed to write: {}", e))?;
Ok(())
}
fn write_elf64(&self) -> Vec<u8> {
let shstrtab = self.build_shstrtab();
let strtab = self.build_strtab();
let symtab = self.build_symtab();
let num_sections = self.sections.len() + 4; let mut relocation_sections: Vec<(usize, Vec<u8>)> = Vec::new();
for (i, sec) in self.sections.iter().enumerate() {
if !sec.relocations.is_empty() {
let rela_data = self.build_rela_section(i);
relocation_sections.push((i, rela_data));
}
}
let num_rela = relocation_sections.len();
let total_sections = num_sections + num_rela;
let mut offset: u64 = 64;
let phoff: u64 = 0;
let phnum: u16 = 0;
let mut section_offsets: Vec<u64> = Vec::new();
let mut section_sizes: Vec<u64> = Vec::new();
section_offsets.push(0);
section_sizes.push(0);
for sec in &self.sections {
section_offsets.push(offset);
section_sizes.push(sec.data.len() as u64);
offset += sec.data.len() as u64;
}
let shstrtab_offset = offset;
section_offsets.push(shstrtab_offset);
section_sizes.push(shstrtab.len() as u64);
offset += shstrtab.len() as u64;
let strtab_offset = offset;
section_offsets.push(strtab_offset);
section_sizes.push(strtab.len() as u64);
offset += strtab.len() as u64;
let symtab_offset = offset;
section_offsets.push(symtab_offset);
section_sizes.push(symtab.len() as u64);
offset += symtab.len() as u64;
let mut rela_info: Vec<(usize, u64, u64)> = Vec::new(); for (sec_idx, rela_data) in &relocation_sections {
rela_info.push((*sec_idx, offset, rela_data.len() as u64));
section_offsets.push(offset);
section_sizes.push(rela_data.len() as u64);
offset += rela_data.len() as u64;
}
let shoff = offset;
let total_size = shoff + (total_sections as u64 * 64);
let mut out = vec![0u8; total_size as usize];
self.write_elf_header(&mut out, shoff, phoff, phnum, total_sections as u16);
let mut current_offset = 64u64;
for sec in &self.sections {
let len = sec.data.len();
out[current_offset as usize..current_offset as usize + len].copy_from_slice(&sec.data);
current_offset += len as u64;
}
let len = shstrtab.len();
out[shstrtab_offset as usize..shstrtab_offset as usize + len].copy_from_slice(&shstrtab);
let len = strtab.len();
out[strtab_offset as usize..strtab_offset as usize + len].copy_from_slice(&strtab);
let len = symtab.len();
out[symtab_offset as usize..symtab_offset as usize + len].copy_from_slice(&symtab);
for (_, rela_data) in &relocation_sections {
let off = rela_info
.iter()
.find(|(_, o, _)| {
*o == current_offset
|| out[*o as usize..*o as usize + 4] == rela_data[0..4.min(rela_data.len())]
})
.map(|(_, o, _)| *o)
.unwrap_or(current_offset);
let len = rela_data.len();
out[off as usize..off as usize + len].copy_from_slice(rela_data);
current_offset = off + len as u64;
}
self.write_section_headers(
&mut out,
shoff,
total_sections,
§ion_offsets,
§ion_sizes,
&shstrtab,
&rela_info,
);
out
}
fn write_elf_header(&self, out: &mut [u8], shoff: u64, _phoff: u64, phnum: u16, shnum: u16) {
out[0] = 0x7F;
out[1] = b'E';
out[2] = b'L';
out[3] = b'F';
out[4] = 2; out[5] = 1; out[6] = 1; out[7] = 0; out[8] = 0; out[9..16].fill(0);
out[16] = 1;
out[17] = 0;
let machine = self.elf_machine();
out[18] = machine as u8;
out[19] = (machine >> 8) as u8;
out[20] = 1;
out[21] = 0;
out[22] = 0;
out[23] = 0;
out[24..32].fill(0);
out[32..40].fill(0);
let bytes = shoff.to_le_bytes();
out[40..48].copy_from_slice(&bytes);
out[48..52].fill(0);
out[52] = 64;
out[53] = 0;
out[54] = 0;
out[55] = 0;
out[56] = phnum as u8;
out[57] = (phnum >> 8) as u8;
out[58] = 64;
out[59] = 0;
out[60] = shnum as u8;
out[61] = (shnum >> 8) as u8;
let shstrndx = self.sections.len() as u16 + 1; out[62] = shstrndx as u8;
out[63] = (shstrndx >> 8) as u8;
}
fn elf_machine(&self) -> u16 {
if self.target_triple.contains("x86_64") {
62 } else if self.target_triple.contains("aarch64") {
183 } else if self.target_triple.contains("arm") {
40 } else {
62 }
}
#[allow(clippy::too_many_arguments)]
fn write_section_headers(
&self,
out: &mut [u8],
shoff: u64,
total_sections: usize,
offsets: &[u64],
sizes: &[u64],
shstrtab: &[u8],
rela_info: &[(usize, u64, u64)],
) {
let user_section_count = self.sections.len();
let _shstrtab_idx = user_section_count + 1;
for i in 0..total_sections {
let hdr_off = (shoff as usize) + i * 64;
if hdr_off + 64 > out.len() {
break;
}
let hdr = &mut out[hdr_off..hdr_off + 64];
if i == 0 {
continue; }
let sec_idx = i - 1;
let name_offset = if i <= user_section_count {
if sec_idx < user_section_count {
self.shstrtab_offset(&self.sections[sec_idx].name, shstrtab)
} else {
0
}
} else {
let special_idx = i - user_section_count - 1;
let name = match special_idx {
0 => ".shstrtab",
1 => ".strtab",
2 => ".symtab",
_ => {
let rela_i = special_idx - 3;
if rela_i < rela_info.len() {
let (_user_sec, _, _) = rela_info[rela_i];
".rela"
} else {
".unknown"
}
}
};
self.shstrtab_offset(name, shstrtab)
};
let name_bytes = name_offset.to_le_bytes();
hdr[0..4].copy_from_slice(&name_bytes);
let (sh_type, sh_flags, sh_entsize, sh_link, sh_info) = if i <= user_section_count {
let sec = &self.sections[sec_idx];
(sec.section_type, sec.flags.to_elf_flags(), 0u64, 0u32, 0u32)
} else {
let special_idx = i - user_section_count - 1;
match special_idx {
0 => (3, 0, 0, 0, 0), 1 => (3, 0, 0, 0, 0), 2 => (2, 0, 24, (i as u32) - 2, 1), _ => {
let rela_i = special_idx - 3;
if rela_i < rela_info.len() {
let (user_sec, _, _) = rela_info[rela_i];
(4, 0, 24, (i as u32) - 2, (user_sec + 1) as u32) } else {
(1, 0, 0, 0, 0) }
}
}
};
let type_bytes = sh_type.to_le_bytes();
hdr[4..8].copy_from_slice(&type_bytes);
let flags_bytes = sh_flags.to_le_bytes();
hdr[8..16].copy_from_slice(&flags_bytes);
let off = offsets[i];
let off_bytes = off.to_le_bytes();
hdr[24..32].copy_from_slice(&off_bytes);
let size = sizes[i];
let size_bytes = size.to_le_bytes();
hdr[32..40].copy_from_slice(&size_bytes);
let link_bytes = sh_link.to_le_bytes();
hdr[40..44].copy_from_slice(&link_bytes);
let info_bytes = sh_info.to_le_bytes();
hdr[44..48].copy_from_slice(&info_bytes);
let align: u64 = if i <= user_section_count {
self.sections[sec_idx].alignment
} else {
8
};
let align_bytes = align.to_le_bytes();
hdr[48..56].copy_from_slice(&align_bytes);
let entsize_bytes = sh_entsize.to_le_bytes();
hdr[56..64].copy_from_slice(&entsize_bytes);
}
}
fn build_shstrtab(&self) -> Vec<u8> {
let mut names: Vec<String> = Vec::new();
names.push(String::new());
for sec in &self.sections {
names.push(sec.name.clone());
}
names.push(".shstrtab".to_string());
names.push(".strtab".to_string());
names.push(".symtab".to_string());
for (dbg_name, _) in &self.debug_sections {
names.push(dbg_name.clone());
}
let mut data = vec![0u8]; for name in &names {
data.extend_from_slice(name.as_bytes());
data.push(0u8);
}
data
}
fn shstrtab_offset(&self, name: &str, shstrtab: &[u8]) -> u32 {
let name_bytes = name.as_bytes();
for i in 0..shstrtab.len() {
if i + name_bytes.len() <= shstrtab.len()
&& shstrtab[i..i + name_bytes.len()] == *name_bytes
{
if i == 0 || shstrtab[i - 1] == 0 {
let after = i + name_bytes.len();
if after >= shstrtab.len() || shstrtab[after] == 0 {
return i as u32;
}
}
}
}
0
}
fn build_strtab(&self) -> Vec<u8> {
let mut data = vec![0u8]; for sym in &self.symbols {
data.extend_from_slice(sym.name.as_bytes());
data.push(0u8);
}
data
}
fn build_symtab(&self) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&[0u8; 24]);
for sym in &self.symbols {
let mut entry = [0u8; 24];
let name_offset = self.strtab_offset(&sym.name);
entry[0..4].copy_from_slice(&name_offset.to_le_bytes());
let bind = match sym.binding {
SymbolBinding::Local => 0u8,
SymbolBinding::Global => 1,
SymbolBinding::Weak => 2,
};
let stype = match sym.sym_type {
SymbolType::NoType => 0u8,
SymbolType::Object => 1,
SymbolType::Func => 2,
SymbolType::Section => 3,
};
entry[4] = (bind << 4) | stype;
entry[5] = 0;
entry[6..8].copy_from_slice(&sym.section_index.to_le_bytes());
entry[8..16].copy_from_slice(&sym.value.to_le_bytes());
entry[16..24].copy_from_slice(&sym.size.to_le_bytes());
data.extend_from_slice(&entry);
}
data
}
fn strtab_offset(&self, name: &str) -> u32 {
let mut offset = 1u32;
for sym in &self.symbols {
if sym.name == name {
return offset;
}
offset += sym.name.len() as u32 + 1;
}
0
}
fn build_rela_section(&self, section_index: usize) -> Vec<u8> {
let sec = &self.sections[section_index];
let mut data = Vec::new();
for reloc in &sec.relocations {
let mut entry = [0u8; 24];
entry[0..8].copy_from_slice(&reloc.offset.to_le_bytes());
let r_info: u64 =
((reloc.symbol_index as u64) << 32) | (reloc.rel_type.elf_value() as u64);
entry[8..16].copy_from_slice(&r_info.to_le_bytes());
entry[16..24].copy_from_slice(&reloc.addend.to_le_bytes());
data.extend_from_slice(&entry);
}
data
}
fn write_macho64(&self) -> Vec<u8> {
let mach_arch = if self.target_triple.contains("x86_64") {
MachOArch::X86_64
} else if self.target_triple.contains("aarch64") || self.target_triple.contains("arm64") {
MachOArch::ARM64
} else if self.target_triple.contains("arm") {
MachOArch::ARM
} else {
MachOArch::X86_64
};
let macho_syms: Vec<MachoSymbol> = self
.symbols
.iter()
.map(|s| MachoSymbol {
name: s.name.clone(),
n_type: if matches!(s.binding, SymbolBinding::Global) {
macho_const::N_SECT | macho_const::N_EXT
} else {
macho_const::N_SECT
},
n_sect: if s.section_index == 0 {
macho_const::N_UNDF
} else {
s.section_index as u8
},
n_desc: 0,
n_value: s.value,
})
.collect();
let macho_strtab_strings: Vec<String> = macho_syms.iter().map(|s| s.name.clone()).collect();
let mut segments: Vec<MachOSegment> = Vec::new();
if !self.sections.is_empty() {
let text_sections: Vec<usize> = self
.sections
.iter()
.enumerate()
.filter(|(_, s)| s.flags.exec && !s.flags.write)
.map(|(i, _)| i)
.collect();
let data_sections: Vec<usize> = self
.sections
.iter()
.enumerate()
.filter(|(_, s)| !s.flags.exec && s.flags.write)
.map(|(i, _)| i)
.collect();
let rodata_sections: Vec<usize> = self
.sections
.iter()
.enumerate()
.filter(|(_, s)| !s.flags.exec && !s.flags.write && s.section_type != 8)
.map(|(i, _)| i)
.collect();
if !text_sections.is_empty() {
segments.push(MachOSegment {
name: "__TEXT".to_string(),
vmaddr: 0,
vmsize: 0, fileoff: 0, filesize: 0,
maxprot: macho_const::VM_PROT_READ | macho_const::VM_PROT_EXECUTE,
initprot: macho_const::VM_PROT_READ | macho_const::VM_PROT_EXECUTE,
nsects: text_sections.len() as u32,
flags: 0,
});
}
if !rodata_sections.is_empty() {
segments.push(MachOSegment {
name: "__RODATA".to_string(),
vmaddr: 0,
vmsize: 0,
fileoff: 0,
filesize: 0,
maxprot: macho_const::VM_PROT_READ,
initprot: macho_const::VM_PROT_READ,
nsects: rodata_sections.len() as u32,
flags: 0,
});
}
if !data_sections.is_empty() {
segments.push(MachOSegment {
name: "__DATA".to_string(),
vmaddr: 0,
vmsize: 0,
fileoff: 0,
filesize: 0,
maxprot: macho_const::VM_PROT_READ | macho_const::VM_PROT_WRITE,
initprot: macho_const::VM_PROT_READ | macho_const::VM_PROT_WRITE,
nsects: data_sections.len() as u32,
flags: 0,
});
}
}
let header = self.write_macho_header(mach_arch);
let mut lc_data = Vec::new();
let lc_segments = self.write_macho_load_commands(
&segments,
&self.sections,
0, 0, macho_syms.len() as u32,
macho_strtab_strings
.iter()
.map(|s| s.len() as u32 + 1)
.sum(),
);
lc_data.extend_from_slice(&lc_segments);
let uuid = self.generate_uuid();
let mut uuid_cmd = Vec::new();
uuid_cmd.extend_from_slice(&macho_const::LC_UUID.to_le_bytes());
uuid_cmd.extend_from_slice(&24u32.to_le_bytes()); uuid_cmd.extend_from_slice(&uuid);
lc_data.extend_from_slice(&uuid_cmd);
let mut ver_cmd = Vec::new();
ver_cmd.extend_from_slice(&macho_const::LC_VERSION_MIN_MACOSX.to_le_bytes());
ver_cmd.extend_from_slice(&16u32.to_le_bytes()); ver_cmd.extend_from_slice(&0x000A0E00u32.to_le_bytes()); ver_cmd.extend_from_slice(&0x000A0E00u32.to_le_bytes()); lc_data.extend_from_slice(&ver_cmd);
let symtab_cmd = self.write_macho_symtab(&macho_syms, &macho_strtab_strings, 0, 0);
lc_data.extend_from_slice(&symtab_cmd);
let dysymtab_cmd = self.write_macho_dysymtab(&macho_syms);
lc_data.extend_from_slice(&dysymtab_cmd);
let ncmds: u32 = 4; let sizeofcmds: u32 = lc_data.len() as u32;
let mut out = Vec::new();
out.extend_from_slice(&header);
let ncmds_offset = 16;
out[ncmds_offset..ncmds_offset + 4].copy_from_slice(&ncmds.to_le_bytes());
out[ncmds_offset + 4..ncmds_offset + 8].copy_from_slice(&sizeofcmds.to_le_bytes());
out.extend_from_slice(&lc_data);
for sec in &self.sections {
out.extend_from_slice(&sec.data);
}
let st = self.write_macho_strtab(&macho_strtab_strings);
let symtab_bytes = self.write_macho_symtab_raw(&macho_syms);
let symtab_fileoff = out.len();
out.extend_from_slice(&symtab_bytes);
let strtab_fileoff = out.len();
out.extend_from_slice(&st);
let symtab_cmd_start = 32 + lc_segments.len() + 24 + 16;
if symtab_cmd_start + 24 <= out.len() {
out[symtab_cmd_start + 8..symtab_cmd_start + 12]
.copy_from_slice(&(symtab_fileoff as u32).to_le_bytes());
out[symtab_cmd_start + 12..symtab_cmd_start + 16]
.copy_from_slice(&(macho_syms.len() as u32).to_le_bytes());
out[symtab_cmd_start + 16..symtab_cmd_start + 20]
.copy_from_slice(&(strtab_fileoff as u32).to_le_bytes());
out[symtab_cmd_start + 20..symtab_cmd_start + 24]
.copy_from_slice(&(st.len() as u32).to_le_bytes());
}
out
}
pub fn write_macho_header(&self, arch: MachOArch) -> Vec<u8> {
let mut out = Vec::with_capacity(32);
out.extend_from_slice(&macho_const::MH_MAGIC_64.to_le_bytes()); out.extend_from_slice(&arch.cpu_type().to_le_bytes()); out.extend_from_slice(&arch.cpu_subtype().to_le_bytes()); out.extend_from_slice(&macho_const::MH_OBJECT.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes());
out
}
pub fn write_macho_load_commands(
&self,
segments: &[MachOSegment],
sections: &[ObjectSection],
_symtab_off: u32,
_strtab_off: u32,
_nsyms: u32,
_strsize: u32,
) -> Vec<u8> {
let mut out = Vec::new();
let mut current_fileoff: u64 = 0;
let text_sections: Vec<usize> = sections
.iter()
.enumerate()
.filter(|(_, s)| s.flags.exec && !s.flags.write)
.map(|(i, _)| i)
.collect();
let rodata_sections: Vec<usize> = sections
.iter()
.enumerate()
.filter(|(_, s)| !s.flags.exec && !s.flags.write && s.section_type != 8)
.map(|(i, _)| i)
.collect();
let data_sections: Vec<usize> = sections
.iter()
.enumerate()
.filter(|(_, s)| !s.flags.exec && s.flags.write)
.map(|(i, _)| i)
.collect();
for seg in segments {
let seg_sections: Vec<&ObjectSection> = match seg.name.as_str() {
"__TEXT" => text_sections.iter().map(|&i| §ions[i]).collect(),
"__RODATA" => rodata_sections.iter().map(|&i| §ions[i]).collect(),
"__DATA" => data_sections.iter().map(|&i| §ions[i]).collect(),
_ => Vec::new(),
};
self.write_macho_segment_command(seg, &seg_sections, current_fileoff, &mut out);
current_fileoff += seg_sections
.iter()
.map(|s| s.data.len() as u64)
.sum::<u64>();
}
out
}
fn write_macho_segment_command(
&self,
seg: &MachOSegment,
sections: &[&ObjectSection],
fileoff: u64,
out: &mut Vec<u8>,
) {
let nsects = sections.len() as u32;
let cmdsize = 72 + nsects * 80; let total_size: u64 = sections.iter().map(|s| s.data.len() as u64).sum();
out.extend_from_slice(&macho_const::LC_SEGMENT_64.to_le_bytes());
out.extend_from_slice(&cmdsize.to_le_bytes());
let mut segname_bytes = [0u8; 16];
let name_bytes = seg.name.as_bytes();
let copy_len = std::cmp::min(name_bytes.len(), 16);
segname_bytes[..copy_len].copy_from_slice(&name_bytes[..copy_len]);
out.extend_from_slice(&segname_bytes);
out.extend_from_slice(&0u64.to_le_bytes()); out.extend_from_slice(&total_size.to_le_bytes()); out.extend_from_slice(&fileoff.to_le_bytes());
out.extend_from_slice(&total_size.to_le_bytes()); out.extend_from_slice(&seg.maxprot.to_le_bytes());
out.extend_from_slice(&seg.initprot.to_le_bytes());
out.extend_from_slice(&nsects.to_le_bytes());
out.extend_from_slice(&seg.flags.to_le_bytes());
let mut section_offset = fileoff;
for sec in sections {
let sec_flags = if sec.flags.exec {
macho_const::S_ATTR_PURE_INSTRUCTIONS | macho_const::S_ATTR_SOME_INSTRUCTIONS
} else {
macho_const::S_REGULAR
};
self.write_macho_section_header(sec, section_offset, sec_flags, out);
section_offset += sec.data.len() as u64;
}
}
fn write_macho_section_header(
&self,
sec: &ObjectSection,
offset: u64,
flags: u32,
out: &mut Vec<u8>,
) {
let mut sectname = [0u8; 16];
let name_bytes = sec.name.as_bytes();
let copy_len = std::cmp::min(name_bytes.len(), 16);
sectname[..copy_len].copy_from_slice(&name_bytes[..copy_len]);
out.extend_from_slice(§name);
let mut segname = [0u8; 16];
let seg: &[u8] = if sec.flags.exec && !sec.flags.write {
b"__TEXT"
} else if sec.flags.write {
b"__DATA"
} else {
b"__RODATA"
};
let copy_len = std::cmp::min(seg.len(), 16);
segname[..copy_len].copy_from_slice(&seg[..copy_len]);
out.extend_from_slice(&segname);
out.extend_from_slice(&0u64.to_le_bytes()); out.extend_from_slice(&(sec.data.len() as u64).to_le_bytes()); out.extend_from_slice(&(offset as u32).to_le_bytes()); out.extend_from_slice(&(sec.alignment as u32).to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&flags.to_le_bytes());
out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); }
pub fn write_macho_section(&self, name: &str, segment: &str, data: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(data.len());
out.extend_from_slice(data);
let align_to = 4;
while out.len() % align_to != 0 {
out.push(0);
}
let _ = (name, segment);
out
}
pub fn write_macho_symtab(
&self,
_symbols: &[MachoSymbol],
_strings: &[String],
symoff: u32,
stroff: u32,
) -> Vec<u8> {
let mut out = Vec::with_capacity(24);
out.extend_from_slice(&macho_const::LC_SYMTAB.to_le_bytes());
out.extend_from_slice(&24u32.to_le_bytes()); out.extend_from_slice(&symoff.to_le_bytes());
out.extend_from_slice(&(_symbols.len() as u32).to_le_bytes());
out.extend_from_slice(&stroff.to_le_bytes());
let strsize: u32 = _strings.iter().map(|s| s.len() as u32 + 1).sum();
out.extend_from_slice(&(strsize + 1).to_le_bytes()); out
}
pub fn write_macho_symtab_raw(&self, symbols: &[MachoSymbol]) -> Vec<u8> {
let mut out = Vec::with_capacity(symbols.len() * 16);
for sym in symbols {
out.extend_from_slice(&0u32.to_le_bytes());
out.push(sym.n_type);
out.push(sym.n_sect);
out.extend_from_slice(&sym.n_desc.to_le_bytes());
out.extend_from_slice(&sym.n_value.to_le_bytes());
}
out
}
pub fn write_macho_strtab(&self, strings: &[String]) -> Vec<u8> {
let mut out = vec![0u8]; for s in strings {
out.extend_from_slice(s.as_bytes());
out.push(0);
}
out
}
pub fn write_macho_dysymtab(&self, symbols: &[MachoSymbol]) -> Vec<u8> {
let mut out = Vec::with_capacity(32);
let nlocals = symbols
.iter()
.filter(|s| s.n_type & macho_const::N_EXT == 0)
.count() as u32;
let nexdef = symbols
.iter()
.filter(|s| {
s.n_type & macho_const::N_EXT != 0
&& s.n_type & macho_const::N_TYPE == macho_const::N_SECT
})
.count() as u32;
let nundef = symbols
.iter()
.filter(|s| s.n_type & macho_const::N_TYPE == macho_const::N_UNDF)
.count() as u32;
out.extend_from_slice(&macho_const::LC_DYSYMTAB.to_le_bytes());
out.extend_from_slice(&80u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes());
out.extend_from_slice(&nlocals.to_le_bytes());
out.extend_from_slice(&nlocals.to_le_bytes());
out.extend_from_slice(&nexdef.to_le_bytes());
out.extend_from_slice(&(nlocals + nexdef).to_le_bytes());
out.extend_from_slice(&nundef.to_le_bytes());
out.extend_from_slice(&[0u8; 48]);
out
}
fn generate_uuid(&self) -> [u8; 16] {
let mut uuid = [0u8; 16];
let mut hash: u64 = 0x9E3779B97F4A7C15; for sec in &self.sections {
for &b in &sec.data {
hash = hash.wrapping_mul(0xC6A4A7935BD1E995).wrapping_add(b as u64);
}
}
for sym in &self.symbols {
for &b in sym.name.as_bytes() {
hash = hash.wrapping_mul(0xC6A4A7935BD1E995).wrapping_add(b as u64);
}
}
for i in 0..8 {
uuid[i] = (hash >> (i * 8)) as u8;
uuid[i + 8] = (hash.wrapping_mul(3) >> (i * 8)) as u8;
}
uuid[6] = (uuid[6] & 0x0F) | 0x40;
uuid[8] = (uuid[8] & 0x3F) | 0x80;
uuid
}
fn write_coff64(&self) -> Vec<u8> {
let machine = if self.target_triple.contains("x86_64") {
coff_const::IMAGE_FILE_MACHINE_AMD64
} else if self.target_triple.contains("aarch64") {
coff_const::IMAGE_FILE_MACHINE_ARM64
} else if self.target_triple.contains("arm") {
coff_const::IMAGE_FILE_MACHINE_ARM
} else {
coff_const::IMAGE_FILE_MACHINE_AMD64
};
let num_sections = self.sections.len() as u16;
let coff_syms: Vec<CoffSymbol> = self
.symbols
.iter()
.map(|s| CoffSymbol {
name: s.name.clone(),
value: s.value as u32,
section_number: s.section_index as i16,
sym_type: match s.sym_type {
SymbolType::Func => 0x20,
_ => 0,
},
storage_class: match s.binding {
SymbolBinding::Global => coff_const::IMAGE_SYM_CLASS_EXTERNAL,
SymbolBinding::Local => coff_const::IMAGE_SYM_CLASS_STATIC,
SymbolBinding::Weak => coff_const::IMAGE_SYM_CLASS_EXTERNAL,
},
aux_count: 0,
})
.collect();
let mut coff_relocs: Vec<(usize, Vec<CoffRelocation>)> = Vec::new();
for (i, sec) in self.sections.iter().enumerate() {
if !sec.relocations.is_empty() {
let relocs: Vec<CoffRelocation> = sec
.relocations
.iter()
.map(|r| CoffRelocation {
virtual_address: r.offset as u32,
symbol_index: r.symbol_index,
rel_type: match r.rel_type {
RelocationType::R_X86_64_64 => 1,
RelocationType::R_X86_64_32 => 3,
_ => 0,
},
})
.collect();
coff_relocs.push((i, relocs));
}
}
let header = self.write_coff_header(machine, num_sections, coff_syms.len() as u32);
let mut out = Vec::new();
out.extend_from_slice(&header);
let mut section_headers = Vec::new();
let mut data_offset = 20u32;
for sec in &self.sections {
let characteristics = if sec.flags.exec {
coff_const::IMAGE_SCN_CNT_CODE
| coff_const::IMAGE_SCN_MEM_EXECUTE
| coff_const::IMAGE_SCN_MEM_READ
} else if sec.flags.write {
coff_const::IMAGE_SCN_CNT_INITIALIZED_DATA
| coff_const::IMAGE_SCN_MEM_READ
| coff_const::IMAGE_SCN_MEM_WRITE
} else {
coff_const::IMAGE_SCN_CNT_INITIALIZED_DATA | coff_const::IMAGE_SCN_MEM_READ
};
let sec_header = self.write_coff_section_header(
&sec.name,
sec.data.len() as u32,
data_offset,
characteristics,
sec.relocations.len() as u16,
);
section_headers.push(sec_header);
data_offset += sec.data.len() as u32;
}
for sh in §ion_headers {
out.extend_from_slice(sh);
}
for sec in &self.sections {
out.extend_from_slice(&sec.data);
}
let mut reloc_offset = out.len() as u32;
for (sec_idx, relocs) in &coff_relocs {
let sec_header_offset = 20 + sec_idx * 40;
out[sec_header_offset + 24..sec_header_offset + 28]
.copy_from_slice(&reloc_offset.to_le_bytes());
let reloc_data = self.write_coff_relocations(relocs);
out.extend_from_slice(&reloc_data);
reloc_offset += reloc_data.len() as u32;
}
let symtab_offset = out.len() as u32;
out[8..12].copy_from_slice(&symtab_offset.to_le_bytes());
let symtab_data = self.write_coff_symtab(&coff_syms);
out.extend_from_slice(&symtab_data);
let strtab_size: u32 = coff_syms
.iter()
.filter(|s| s.name.len() > 8)
.map(|s| s.name.len() as u32 + 1)
.sum::<u32>()
+ 4; out.extend_from_slice(&strtab_size.to_le_bytes());
for sym in &coff_syms {
if sym.name.len() > 8 {
out.extend_from_slice(sym.name.as_bytes());
out.push(0);
}
}
out
}
pub fn write_coff_header(&self, machine: u16, num_sections: u16, num_symbols: u32) -> Vec<u8> {
let mut out = Vec::with_capacity(20);
out.extend_from_slice(&machine.to_le_bytes());
out.extend_from_slice(&num_sections.to_le_bytes());
out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&num_symbols.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out
}
pub fn write_coff_section_header(
&self,
name: &str,
data_size: u32,
data_offset: u32,
characteristics: u32,
num_relocs: u16,
) -> Vec<u8> {
let mut out = Vec::with_capacity(40);
let mut name_bytes = [0u8; 8];
let name_slice = name.as_bytes();
let copy_len = std::cmp::min(name_slice.len(), 8);
name_bytes[..copy_len].copy_from_slice(&name_slice[..copy_len]);
out.extend_from_slice(&name_bytes);
out.extend_from_slice(&data_size.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&data_size.to_le_bytes()); out.extend_from_slice(&data_offset.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&num_relocs.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&characteristics.to_le_bytes());
out
}
pub fn write_coff_section(&self, name: &str, data: &[u8], characteristics: u32) -> Vec<u8> {
let sec_header =
self.write_coff_section_header(name, data.len() as u32, 0, characteristics, 0);
let mut out = Vec::with_capacity(sec_header.len() + data.len());
out.extend_from_slice(&sec_header);
out.extend_from_slice(data);
out
}
pub fn write_coff_symtab(&self, symbols: &[CoffSymbol]) -> Vec<u8> {
let mut out = Vec::with_capacity(symbols.len() * 18);
for sym in symbols {
let mut name_bytes = [0u8; 8];
if sym.name.len() <= 8 {
let name_slice = sym.name.as_bytes();
name_bytes[..name_slice.len()].copy_from_slice(name_slice);
} else {
}
out.extend_from_slice(&name_bytes);
out.extend_from_slice(&sym.value.to_le_bytes());
out.extend_from_slice(&sym.section_number.to_le_bytes());
out.extend_from_slice(&sym.sym_type.to_le_bytes());
out.push(sym.storage_class);
out.push(sym.aux_count);
}
out
}
pub fn write_coff_relocations(&self, relocs: &[CoffRelocation]) -> Vec<u8> {
let mut out = Vec::with_capacity(relocs.len() * 10);
for reloc in relocs {
out.extend_from_slice(&reloc.virtual_address.to_le_bytes());
out.extend_from_slice(&reloc.symbol_index.to_le_bytes());
out.extend_from_slice(&reloc.rel_type.to_le_bytes());
}
out
}
fn write_wasm_module(&self) -> Vec<u8> {
let mut modules_sections = Vec::new();
for sec in &self.sections {
let section_type = match sec.name.as_str() {
".text" | "code" => 10u8, ".data" | "data" => 11u8, _ => 0u8, };
if section_type == 0 {
let mut custom_data = Vec::new();
write_leb128_u32(&mut custom_data, sec.name.len() as u32);
custom_data.extend_from_slice(sec.name.as_bytes());
custom_data.extend_from_slice(&sec.data);
modules_sections.push(WasmSection {
section_type: 0,
data: custom_data,
});
} else {
modules_sections.push(WasmSection {
section_type,
data: sec.data.clone(),
});
}
}
if !self.symbols.is_empty() {
let mut export_data = Vec::new();
write_leb128_u32(&mut export_data, self.symbols.len() as u32);
for (i, sym) in self.symbols.iter().enumerate() {
write_leb128_u32(&mut export_data, sym.name.len() as u32);
export_data.extend_from_slice(sym.name.as_bytes());
export_data.push(0x00);
write_leb128_u32(&mut export_data, i as u32);
}
modules_sections.push(WasmSection {
section_type: 7, data: export_data,
});
}
self.write_wasm_module_data(&modules_sections)
}
pub fn write_wasm_module_data(&self, sections: &[WasmSection]) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&[0x00, 0x61, 0x73, 0x6D]);
out.extend_from_slice(&1u32.to_le_bytes());
for section in sections {
out.push(section.section_type);
let mut section_data = Vec::new();
section_data.extend_from_slice(§ion.data);
let mut size_buf = Vec::new();
write_leb128_u32(&mut size_buf, section_data.len() as u32);
out.extend_from_slice(&size_buf);
out.extend_from_slice(§ion_data);
}
out
}
}
pub fn write_leb128_u32(buf: &mut Vec<u8>, mut value: u32) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
buf.push(byte);
if value == 0 {
break;
}
}
}
pub fn write_leb128_i32(buf: &mut Vec<u8>, mut value: i32) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if (value == 0 && (byte & 0x40) == 0) || (value == -1 && (byte & 0x40) != 0) {
buf.push(byte);
break;
}
byte |= 0x80;
buf.push(byte);
}
}
pub fn read_leb128_u32(data: &[u8]) -> Option<(u32, usize)> {
let mut result: u32 = 0;
let mut shift = 0;
for (i, &byte) in data.iter().enumerate() {
result |= ((byte & 0x7F) as u32) << shift;
if byte & 0x80 == 0 {
return Some((result, i + 1));
}
shift += 7;
if shift >= 35 {
return None; }
}
None
}
impl Default for ObjectWriter {
fn default() -> Self {
Self::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu")
}
}
impl ObjectWriter {
pub fn write_xcoff32(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.extend_from_slice(&0x01DFu16.to_be_bytes()); buf.extend_from_slice(&(self.sections.len() as u16).to_be_bytes()); buf.extend_from_slice(&0i32.to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&0i32.to_be_bytes()); buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(&0u16.to_be_bytes());
self.write_xcoff_section_headers(&mut buf);
self.write_xcoff_section_data(&mut buf);
self.write_xcoff_symbol_table(&mut buf);
self.write_xcoff_string_table(&mut buf);
buf
}
fn write_xcoff_section_headers(&self, buf: &mut Vec<u8>) {
for section in &self.sections {
let flags = if section.flags.exec == true {
0x20u32 } else if section.flags.write == true {
0x40u32 } else {
0x80u32 };
let mut name_bytes = [0u8; 8];
let name_slice = section.name.as_bytes();
let copy_len = name_slice.len().min(8);
name_bytes[..copy_len].copy_from_slice(&name_slice[..copy_len]);
buf.extend_from_slice(&name_bytes);
buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&((section.data.len() as u32).to_be_bytes())); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&(section.relocations.len() as u32).to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&flags.to_be_bytes());
}
}
fn write_xcoff_section_data(&self, buf: &mut Vec<u8>) {
for section in &self.sections {
buf.extend_from_slice(§ion.data);
}
}
fn write_xcoff_symbol_table(&self, buf: &mut Vec<u8>) {
for symbol in &self.symbols {
let mut name_bytes = [0u8; 8];
let name_slice = symbol.name.as_bytes();
let copy_len = name_slice.len().min(8);
name_bytes[..copy_len].copy_from_slice(&name_slice[..copy_len]);
buf.extend_from_slice(&name_bytes);
buf.extend_from_slice(&(symbol.value as u32).to_be_bytes());
buf.extend_from_slice(&(symbol.section_index as i16).to_be_bytes());
buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(&0u8.to_be_bytes()); buf.extend_from_slice(&0u8.to_be_bytes()); }
}
fn write_xcoff_string_table(&self, buf: &mut Vec<u8>) {
let mut strtab: Vec<u8> = Vec::new();
for symbol in &self.symbols {
if symbol.name.len() > 8 {
strtab.extend_from_slice(symbol.name.as_bytes());
strtab.push(0);
}
}
let len = (strtab.len() + 4) as u32;
buf.extend_from_slice(&len.to_be_bytes());
buf.extend_from_slice(&strtab);
}
}
const GOFF_END_REC: u8 = 0x04;
impl ObjectWriter {
pub fn write_goff(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.push(0x03); buf.extend_from_slice(&4u16.to_be_bytes());
buf.push(0x00);
self.write_goff_esd_records(&mut buf);
self.write_goff_txt_records(&mut buf);
self.write_goff_rld_records(&mut buf);
buf.push(GOFF_END_REC);
buf.extend_from_slice(&4u16.to_be_bytes());
buf.push(0x02); buf.push(0x00);
buf
}
fn write_goff_esd_records(&self, buf: &mut Vec<u8>) {
for (i, section) in self.sections.iter().enumerate() {
buf.push(0x00); let name_bytes = section.name.as_bytes();
let rec_len: u16 = (12 + name_bytes.len() as u16) + 1;
buf.extend_from_slice(&rec_len.to_be_bytes());
buf.extend_from_slice(&(i as u32).to_be_bytes()); buf.push(0x00); buf.push(0x00); buf.push(0x01); buf.push(0x01); buf.extend_from_slice(&(section.data.len() as u32).to_be_bytes()); buf.extend_from_slice(&(8u32).to_be_bytes()); buf.extend_from_slice(name_bytes);
buf.push(0x00); }
for sym in &self.symbols {
if sym.section_index == 0 {
buf.push(0x00); let name_bytes = sym.name.as_bytes();
let rec_len: u16 = (12 + name_bytes.len() as u16) + 1;
buf.extend_from_slice(&rec_len.to_be_bytes());
buf.extend_from_slice(&0u32.to_be_bytes()); buf.push(0x0A); buf.push(0x00); buf.push(0x01); buf.push(0x01); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(name_bytes);
buf.push(0x00);
}
}
}
fn write_goff_txt_records(&self, buf: &mut Vec<u8>) {
for (_i, section) in self.sections.iter().enumerate() {
if section.data.is_empty() {
continue;
}
buf.push(0x01); let data_len = section.data.len() as u16;
buf.extend_from_slice(&(data_len + 8).to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(§ion.data);
}
}
fn write_goff_rld_records(&self, buf: &mut Vec<u8>) {
for section in &self.sections {
for reloc in §ion.relocations {
buf.push(0x02); buf.extend_from_slice(&20u16.to_be_bytes()); buf.extend_from_slice(&(reloc.symbol_index as u32).to_be_bytes());
buf.extend_from_slice(&(reloc.offset as u32).to_be_bytes()); buf.push(8); buf.push(0x00); buf.extend_from_slice(&reloc.addend.to_be_bytes()); buf.extend_from_slice(&0u16.to_be_bytes()); }
}
}
}
const ARFMAG: &[u8; 2] = b"`\n";
impl ObjectWriter {
pub fn write_archive(&self, members: &[(String, Vec<u8>)]) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.extend_from_slice(b"!<arch>\n");
let mut offsets: Vec<u64> = Vec::new();
let mut strtab_data: Vec<u8> = Vec::new();
let mut long_names: Vec<(String, u64)> = Vec::new();
let mut current_offset: u64 = 8;
for (name, _data) in members {
let header_size: u64 = 60;
let data_size = _data.len() as u64;
let padded_size = if data_size % 2 != 0 {
data_size + 1
} else {
data_size
};
if name.len() > 15 {
let str_offset = strtab_data.len() as u64;
strtab_data.extend_from_slice(name.as_bytes());
strtab_data.extend_from_slice(b"/");
long_names.push((name.clone(), str_offset));
}
offsets.push(current_offset);
current_offset += header_size + padded_size;
}
if !members.is_empty() {
buf.extend_from_slice(format!("/ ").as_bytes().get(..16).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..12).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..8).unwrap());
let sym_count = members.len() as u32;
let mut symtab: Vec<u8> = Vec::new();
symtab.extend_from_slice(&sym_count.to_be_bytes());
for (name, _) in members {
symtab.extend_from_slice(&0u32.to_be_bytes());
}
for (name, _) in members {
symtab.extend_from_slice(name.as_bytes());
symtab.push(0);
}
let symtab_size = symtab.len();
buf.extend_from_slice(format!("{:<10}", symtab_size).as_bytes().get(..10).unwrap());
buf.extend_from_slice(ARFMAG);
buf.extend_from_slice(&symtab);
if symtab_size % 2 != 0 {
buf.push(b'\n');
}
}
for (i, (name, data)) in members.iter().enumerate() {
let name_field = if name.len() > 15 {
let offset = long_names
.iter()
.find(|(n, _)| n == name)
.map(|(_, o)| *o)
.unwrap_or(0);
format!("/{:<15}", offset)
} else {
format!("{:<16}", format!("{}/", name))
};
buf.extend_from_slice(name_field.as_bytes().get(..16).unwrap());
buf.extend_from_slice(format!("{:<12}", 0).as_bytes().get(..12).unwrap());
buf.extend_from_slice(format!("{:<6}", 0).as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("{:<6}", 0).as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("{:<8}", "100644").as_bytes().get(..8).unwrap());
buf.extend_from_slice(format!("{:<10}", data.len()).as_bytes().get(..10).unwrap());
buf.extend_from_slice(ARFMAG);
buf.extend_from_slice(data);
if data.len() % 2 != 0 {
buf.push(b'\n');
}
}
if !long_names.is_empty() {
buf.extend_from_slice(b"// "); buf.extend_from_slice(format!("0 ").as_bytes().get(..12).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..8).unwrap());
buf.extend_from_slice(
format!("{:<10}", strtab_data.len())
.as_bytes()
.get(..10)
.unwrap(),
);
buf.extend_from_slice(ARFMAG);
buf.extend_from_slice(&strtab_data);
if strtab_data.len() % 2 != 0 {
buf.push(b'\n');
}
}
buf
}
pub fn write_bsd_archive(&self, members: &[(String, Vec<u8>)]) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.extend_from_slice(b"!<arch>\n");
buf.extend_from_slice(b"__.SYMDEF "); buf.extend_from_slice(format!("0 ").as_bytes().get(..12).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("0 ").as_bytes().get(..8).unwrap());
let ranlib_entry_size = 8usize; let count = members.len();
let mut symdef: Vec<u8> = Vec::new();
let mut string_offsets: Vec<u32> = Vec::new();
let mut string_data: Vec<u8> = Vec::new();
for (i, (name, _)) in members.iter().enumerate() {
string_offsets.push(string_data.len() as u32);
string_data.extend_from_slice(name.as_bytes());
string_data.push(0);
}
let total_symdef: u32 = (count * ranlib_entry_size + string_data.len()) as u32;
symdef.extend_from_slice(&total_symdef.to_be_bytes());
for i in 0..count {
symdef.extend_from_slice(&string_offsets[i].to_be_bytes());
symdef.extend_from_slice(&0u32.to_be_bytes()); }
symdef.extend_from_slice(&string_data);
buf.extend_from_slice(
format!("{:<10}", symdef.len())
.as_bytes()
.get(..10)
.unwrap(),
);
buf.extend_from_slice(ARFMAG);
buf.extend_from_slice(&symdef);
if symdef.len() % 2 != 0 {
buf.push(b'\n');
}
for (name, data) in members {
let name_field = format!("{:<16}", format!("{}/", name));
buf.extend_from_slice(name_field.as_bytes().get(..16).unwrap());
buf.extend_from_slice(format!("{:<12}", 0).as_bytes().get(..12).unwrap());
buf.extend_from_slice(format!("{:<6}", 0).as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("{:<6}", 0).as_bytes().get(..6).unwrap());
buf.extend_from_slice(format!("{:<8}", "100644").as_bytes().get(..8).unwrap());
buf.extend_from_slice(format!("{:<10}", data.len()).as_bytes().get(..10).unwrap());
buf.extend_from_slice(ARFMAG);
buf.extend_from_slice(data);
if data.len() % 2 != 0 {
buf.push(b'\n');
}
}
buf
}
}
impl ObjectWriter {
pub fn emit_debug_info_dwo(&self, info_data: &[u8]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(info_data);
data
}
pub fn emit_debug_abbrev_dwo(&self, abbrev_data: &[u8]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(abbrev_data);
data
}
pub fn emit_debug_types_dwo(&self, types_data: &[u8]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(types_data);
data
}
pub fn emit_debug_str_offsets_dwo(&self, offsets: &[u32]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&5u16.to_le_bytes());
data.extend_from_slice(&0u16.to_le_bytes());
for &off in offsets {
data.extend_from_slice(&off.to_le_bytes());
}
data
}
pub fn emit_debug_str_dwo(&self, strings: &[&str]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
for s in strings {
data.extend_from_slice(s.as_bytes());
data.push(0); }
data
}
pub fn emit_debug_loc_dwo(&self, loc_data: &[u8]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(loc_data);
data
}
pub fn emit_debug_macro_dwo(&self, macro_data: &[u8]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(macro_data);
data
}
pub fn add_split_dwarf_sections(&mut self, dwo_sections: HashMap<String, Vec<u8>>) {
for (name, data) in dwo_sections {
self.debug_sections.push((name, data));
}
}
}
impl ObjectWriter {
pub fn write_xcoff64(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.extend_from_slice(&0x01F7u16.to_be_bytes()); buf.extend_from_slice(&(self.sections.len() as u16).to_be_bytes()); buf.extend_from_slice(&0i32.to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes());
buf.extend_from_slice(&0u32.to_be_bytes());
buf.extend_from_slice(&0i32.to_be_bytes()); buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(&0u16.to_be_bytes());
let mut scnptr_accum: u64 = 24; scnptr_accum += (self.sections.len() as u64) * 72;
for section in &self.sections {
let flags: u32 = if section.flags.exec {
0x20 } else if section.flags.write {
0x40 } else if section.section_type == 0x8 {
0x80 } else {
0x100 };
let mut name_bytes = [0u8; 8];
let name_slice = section.name.as_bytes();
let copy_len = name_slice.len().min(8);
name_bytes[..copy_len].copy_from_slice(&name_slice[..copy_len]);
buf.extend_from_slice(&name_bytes);
buf.extend_from_slice(&0u64.to_be_bytes());
buf.extend_from_slice(&0u64.to_be_bytes());
buf.extend_from_slice(&(section.data.len() as u64).to_be_bytes());
buf.extend_from_slice(&scnptr_accum.to_be_bytes());
buf.extend_from_slice(&0u64.to_be_bytes());
buf.extend_from_slice(&0u64.to_be_bytes());
buf.extend_from_slice(&(section.relocations.len() as u32).to_be_bytes());
buf.extend_from_slice(&0u32.to_be_bytes());
buf.extend_from_slice(&flags.to_be_bytes());
scnptr_accum += section.data.len() as u64;
}
for section in &self.sections {
buf.extend_from_slice(§ion.data);
}
for section in &self.sections {
for reloc in §ion.relocations {
buf.extend_from_slice(&(reloc.offset as u64).to_be_bytes()); buf.extend_from_slice(&(reloc.symbol_index as u32).to_be_bytes()); buf.push(0x00); buf.push(reloc.rel_type as u8); }
}
buf
}
pub fn write_xcoff_loader_section(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.extend_from_slice(&1i32.to_be_bytes()); buf.extend_from_slice(&(self.symbols.len() as i32).to_be_bytes()); buf.extend_from_slice(&0i32.to_be_bytes()); buf.extend_from_slice(&0i32.to_be_bytes()); buf.extend_from_slice(&0i32.to_be_bytes()); buf.extend_from_slice(&0u64.to_be_bytes()); buf.extend_from_slice(&0u64.to_be_bytes()); buf.extend_from_slice(&0u64.to_be_bytes()); buf
}
pub fn write_xcoff_loader_symbols(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
for symbol in &self.symbols {
let mut name_bytes = [0u8; 8];
let name_slice = symbol.name.as_bytes();
let copy_len = name_slice.len().min(8);
name_bytes[..copy_len].copy_from_slice(&name_slice[..copy_len]);
buf.extend_from_slice(&name_bytes);
buf.extend_from_slice(&symbol.value.to_be_bytes());
buf.extend_from_slice(&(symbol.section_index as u16).to_be_bytes());
buf.push(match symbol.sym_type {
SymbolType::Func => 0x20, SymbolType::Object => 0x01, _ => 0x00,
});
buf.push(0x00);
buf.push(0x00);
buf.push(0x00);
}
buf
}
pub fn write_goff_cm_esd(&self, name: &str, len: u64, align: u64) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.push(0x00); let name_bytes = name.as_bytes();
let rec_len: u16 = (14 + name_bytes.len() as u16) + 1;
buf.extend_from_slice(&rec_len.to_be_bytes());
buf.extend_from_slice(&0u32.to_be_bytes()); buf.push(0x06); buf.push(0x00); buf.push(0x01); buf.push(0x01); buf.extend_from_slice(&len.to_be_bytes());
buf.extend_from_slice(&align.to_be_bytes());
buf.extend_from_slice(name_bytes);
buf.push(0x00);
buf
}
pub fn write_goff_ld_esd(&self, name: &str, section_id: u32, offset: u64) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.push(0x00); let name_bytes = name.as_bytes();
let rec_len: u16 = (14 + name_bytes.len() as u16) + 1;
buf.extend_from_slice(&rec_len.to_be_bytes());
buf.extend_from_slice(&0u32.to_be_bytes()); buf.push(0x01); buf.push(0x00); buf.push(0x01); buf.push(0x01); buf.extend_from_slice(§ion_id.to_be_bytes()); buf.extend_from_slice(&offset.to_be_bytes()); buf.extend_from_slice(name_bytes);
buf.push(0x00);
buf
}
pub fn write_goff_ed_esd(&self, name: &str) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.push(0x00); let name_bytes = name.as_bytes();
let rec_len: u16 = (14 + name_bytes.len() as u16) + 1;
buf.extend_from_slice(&rec_len.to_be_bytes());
buf.extend_from_slice(&0u32.to_be_bytes()); buf.push(0x02); buf.push(0x00); buf.push(0x01); buf.push(0x01); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(&0u32.to_be_bytes()); buf.extend_from_slice(name_bytes);
buf.push(0x00);
buf
}
pub fn write_goff_pr_esd(&self, name: &str, section_id: u32, offset: u64) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.push(0x00); let name_bytes = name.as_bytes();
let rec_len: u16 = (14 + name_bytes.len() as u16) + 1;
buf.extend_from_slice(&rec_len.to_be_bytes());
buf.extend_from_slice(&0u32.to_be_bytes());
buf.push(0x04); buf.push(0x00);
buf.push(0x01);
buf.push(0x01);
buf.extend_from_slice(§ion_id.to_be_bytes());
buf.extend_from_slice(&offset.to_be_bytes());
buf.extend_from_slice(name_bytes);
buf.push(0x00);
buf
}
pub fn write_goff_pc_esd(&self, name: &str, section_id: u32, offset: u64) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.push(0x00);
let name_bytes = name.as_bytes();
let rec_len: u16 = (14 + name_bytes.len() as u16) + 1;
buf.extend_from_slice(&rec_len.to_be_bytes());
buf.extend_from_slice(&0u32.to_be_bytes());
buf.push(0x05); buf.push(0x00);
buf.push(0x01);
buf.push(0x01);
buf.extend_from_slice(§ion_id.to_be_bytes());
buf.extend_from_slice(&offset.to_be_bytes());
buf.extend_from_slice(name_bytes);
buf.push(0x00);
buf
}
pub fn write_goff_end_record(&self, reason_code: u8) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.push(GOFF_END_REC);
buf.extend_from_slice(&8u16.to_be_bytes()); buf.push(reason_code); buf.push(0x00);
buf.extend_from_slice(&0u32.to_be_bytes());
buf
}
pub fn emit_debug_line_dwo(&self, line_program: &[u8]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.push(0x05); data.push(0x08); data.push(0x00); data.extend_from_slice(line_program);
data
}
pub fn emit_debug_addr(&self, addresses: &[u64]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
let header_size: u32 = 8; data.extend_from_slice(&header_size.to_le_bytes());
data.extend_from_slice(&5u16.to_le_bytes()); data.push(8); data.push(0); for &addr in addresses {
data.extend_from_slice(&addr.to_le_bytes());
}
data
}
pub fn emit_debug_rnglists_dwo(&self, ranges: &[u8]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&5u16.to_le_bytes()); data.push(8); data.push(0); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(ranges);
data
}
}
impl ObjectWriter {
pub fn write_coff_aux_section_def(
&self,
sec_num: u16,
sec_size: u32,
num_relocs: u16,
num_linenos: u16,
) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&sec_size.to_le_bytes()); data.extend_from_slice(&num_relocs.to_le_bytes()); data.extend_from_slice(&num_linenos.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&sec_num.to_le_bytes()); data.push(0); data.extend_from_slice(&[0u8; 3]); data
}
pub fn write_coff_aux_function_def(
&self,
tag_index: u32,
total_size: u32,
pointer_to_linenumber: u32,
pointer_to_next_function: u32,
) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&tag_index.to_le_bytes());
data.extend_from_slice(&total_size.to_le_bytes());
data.extend_from_slice(&pointer_to_linenumber.to_le_bytes());
data.extend_from_slice(&pointer_to_next_function.to_le_bytes());
data.extend_from_slice(&[0u8; 2]); data
}
pub fn write_coff_aux_bf_ef(&self, unused1: u32, line_number: u16) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&unused1.to_le_bytes());
data.extend_from_slice(&line_number.to_le_bytes());
data.extend_from_slice(&[0u8; 6]); data.extend_from_slice(&0u16.to_le_bytes()); data.extend_from_slice(&[0u8; 2]); data
}
pub fn write_coff_aux_weak_external(&self, tag_index: u32, characteristics: u32) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&tag_index.to_le_bytes());
data.extend_from_slice(&characteristics.to_le_bytes());
data.extend_from_slice(&[0u8; 10]); data
}
pub fn write_coff_aux_file(&self, filename: &str) -> Vec<u8> {
let mut data: Vec<u8> = [0u8; 18].to_vec();
let bytes = filename.as_bytes();
let copy_len = bytes.len().min(18);
data[..copy_len].copy_from_slice(&bytes[..copy_len]);
data
}
pub fn write_coff_relocation_with_type(
&self,
vaddr: u32,
sym_idx: u32,
rel_type: u16,
) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&vaddr.to_le_bytes());
data.extend_from_slice(&sym_idx.to_le_bytes());
data.extend_from_slice(&rel_type.to_le_bytes());
data
}
pub fn write_coff_import_stub(&self, dll_name: &str, symbol_name: &str) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&[0xFFu8, 0x25, 0x00, 0x00, 0x00, 0x00]);
let _ = (dll_name, symbol_name);
data
}
pub fn write_pe_import_directory_entry(
&self,
import_lookup_table_rva: u32,
timestamp: u32,
forwarder_chain: u32,
name_rva: u32,
import_address_table_rva: u32,
) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&import_lookup_table_rva.to_le_bytes());
data.extend_from_slice(×tamp.to_le_bytes());
data.extend_from_slice(&forwarder_chain.to_le_bytes());
data.extend_from_slice(&name_rva.to_le_bytes());
data.extend_from_slice(&import_address_table_rva.to_le_bytes());
data
}
pub fn write_pe_export_directory(
&self,
characteristics: u32,
timestamp: u32,
major_ver: u16,
minor_ver: u16,
name_rva: u32,
ordinal_base: u32,
num_functions: u32,
num_names: u32,
addr_of_functions: u32,
addr_of_names: u32,
addr_of_ordinals: u32,
) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&characteristics.to_le_bytes());
data.extend_from_slice(×tamp.to_le_bytes());
data.extend_from_slice(&major_ver.to_le_bytes());
data.extend_from_slice(&minor_ver.to_le_bytes());
data.extend_from_slice(&name_rva.to_le_bytes());
data.extend_from_slice(&ordinal_base.to_le_bytes());
data.extend_from_slice(&num_functions.to_le_bytes());
data.extend_from_slice(&num_names.to_le_bytes());
data.extend_from_slice(&addr_of_functions.to_le_bytes());
data.extend_from_slice(&addr_of_names.to_le_bytes());
data.extend_from_slice(&addr_of_ordinals.to_le_bytes());
data
}
pub fn write_macho_uuid_command(&self, uuid: &[u8; 16]) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&0x1Bu32.to_le_bytes()); data.extend_from_slice(&24u32.to_le_bytes()); data.extend_from_slice(uuid);
data
}
pub fn write_macho_rpath_command(&self, path: &str) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
let path_bytes = path.as_bytes();
let cmdsize = 12 + path_bytes.len() + 1;
data.extend_from_slice(&0x8000001Cu32.to_le_bytes()); data.extend_from_slice(&(cmdsize as u32).to_le_bytes());
data.extend_from_slice(&(12u32.to_le_bytes())); data.extend_from_slice(path_bytes);
data.push(0);
data
}
pub fn write_macho_build_version_command(
&self,
platform: u32,
minos: u32,
sdk: u32,
ntools: u32,
) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&0x32u32.to_le_bytes()); let cmdsize = 24 + ntools * 8;
data.extend_from_slice(&cmdsize.to_le_bytes());
data.extend_from_slice(&platform.to_le_bytes());
data.extend_from_slice(&minos.to_le_bytes());
data.extend_from_slice(&sdk.to_le_bytes());
data.extend_from_slice(&ntools.to_le_bytes());
data
}
pub fn write_macho_linkedit_data_command(
&self,
cmd: u32,
dataoff: u32,
datasize: u32,
) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.extend_from_slice(&cmd.to_le_bytes());
buf.extend_from_slice(&16u32.to_le_bytes()); buf.extend_from_slice(&dataoff.to_le_bytes());
buf.extend_from_slice(&datasize.to_le_bytes());
buf
}
pub fn write_macho_dyld_environment(&self, env: &str) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&0x80000027u32.to_le_bytes()); let env_bytes = env.as_bytes();
data.extend_from_slice(&((12 + env_bytes.len() + 1) as u32).to_le_bytes());
data.extend_from_slice(&12u32.to_le_bytes()); data.extend_from_slice(env_bytes);
data.push(0);
data
}
pub fn write_elf_section_content(
&self,
name: &str,
content: &[u8],
sh_type: u32,
sh_flags: u64,
alignment: u64,
) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(content);
let _ = (name, sh_type, sh_flags, alignment);
data
}
pub fn write_elf_rela_entry(
&self,
r_offset: u64,
r_type: u32,
r_sym: u32,
r_addend: i64,
) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&r_offset.to_le_bytes());
let r_info: u64 = ((r_sym as u64) << 32) | (r_type as u64);
data.extend_from_slice(&r_info.to_le_bytes());
data.extend_from_slice(&r_addend.to_le_bytes());
data
}
pub fn write_elf_rel_entry(&self, r_offset: u64, r_type: u32, r_sym: u32) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&r_offset.to_le_bytes());
let r_info: u64 = ((r_sym as u64) << 32) | (r_type as u64);
data.extend_from_slice(&r_info.to_le_bytes());
data
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_object_writer() {
let writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
assert_eq!(writer.format, ObjectFormat::ELF);
assert_eq!(writer.sections.len(), 0);
assert_eq!(writer.symbols.len(), 0);
}
#[test]
fn test_add_section() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
let idx = writer.add_section(".text", vec![0x90, 0xC3], SectionFlags::text());
assert_eq!(idx, 0);
assert_eq!(writer.sections.len(), 1);
assert_eq!(writer.sections[0].name, ".text");
assert_eq!(writer.sections[0].data, vec![0x90, 0xC3]);
}
#[test]
fn test_add_symbol() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_symbol("main", 0, 2, SymbolBinding::Global, SymbolType::Func, 1);
assert_eq!(writer.symbols.len(), 1);
assert_eq!(writer.symbols[0].name, "main");
assert_eq!(writer.symbols[0].binding, SymbolBinding::Global);
assert_eq!(writer.symbols[0].sym_type, SymbolType::Func);
}
#[test]
fn test_add_relocation() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_section(".text", vec![0xE9, 0, 0, 0, 0], SectionFlags::text());
writer.add_symbol("target", 0, 0, SymbolBinding::Global, SymbolType::Func, 0);
writer.add_relocation(0, 1, 0, RelocationType::R_X86_64_PC32, -4);
assert_eq!(writer.sections[0].relocations.len(), 1);
assert_eq!(
writer.sections[0].relocations[0].rel_type,
RelocationType::R_X86_64_PC32
);
}
#[test]
fn test_write_elf_empty() {
let writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
let data = writer.write();
assert!(data.len() >= 64);
assert_eq!(&data[0..4], &[0x7F, b'E', b'L', b'F']);
assert_eq!(data[4], 2);
assert_eq!(u16::from_le_bytes([data[18], data[19]]), 62);
}
#[test]
fn test_write_elf_with_text_section() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_section(".text", vec![0x90, 0xC3], SectionFlags::text());
let data = writer.write();
assert!(data.len() >= 128); assert_eq!(&data[0..4], &[0x7F, b'E', b'L', b'F']);
let text_data = vec![0x90, 0xC3];
let found = data.windows(2).any(|w| w == text_data.as_slice());
assert!(found, ".text data should appear in the output");
}
#[test]
fn test_write_elf_multiple_sections() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_section(".text", vec![0x90, 0xC3], SectionFlags::text());
writer.add_section(".data", vec![0xDE, 0xAD], SectionFlags::data());
writer.add_section(".rodata", vec![0x01, 0x02, 0x03], SectionFlags::rodata());
let data = writer.write();
assert!(data.len() >= 256);
assert_eq!(&data[0..4], &[0x7F, b'E', b'L', b'F']);
}
#[test]
fn test_write_elf_aarch64() {
let writer = ObjectWriter::new(ObjectFormat::ELF, "aarch64-unknown-linux-gnu");
let data = writer.write();
assert_eq!(u16::from_le_bytes([data[18], data[19]]), 183);
}
#[test]
fn test_write_elf_arm32() {
let writer = ObjectWriter::new(ObjectFormat::ELF, "arm-unknown-linux-gnueabihf");
let data = writer.write();
assert_eq!(u16::from_le_bytes([data[18], data[19]]), 40);
}
#[test]
fn test_section_flags_to_elf() {
let text_flags = SectionFlags::text();
let elf_flags = text_flags.to_elf_flags();
assert_eq!(elf_flags & 2, 2); assert_eq!(elf_flags & 4, 4); assert_eq!(elf_flags & 1, 0);
let data_flags = SectionFlags::data();
let elf_flags = data_flags.to_elf_flags();
assert_eq!(elf_flags & 1, 1); assert_eq!(elf_flags & 2, 2); assert_eq!(elf_flags & 4, 0); }
#[test]
fn test_shstrtab_contains_section_names() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_section(".text", vec![0x90], SectionFlags::text());
writer.add_section(".data", vec![0x42], SectionFlags::data());
let shstrtab = writer.build_shstrtab();
let shstrtab_str = String::from_utf8_lossy(&shstrtab);
assert!(shstrtab_str.contains(".text"));
assert!(shstrtab_str.contains(".data"));
assert!(shstrtab_str.contains(".shstrtab"));
assert!(shstrtab_str.contains(".strtab"));
assert!(shstrtab_str.contains(".symtab"));
}
#[test]
fn test_strtab_contains_symbol_names() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_symbol("main", 0, 10, SymbolBinding::Global, SymbolType::Func, 1);
writer.add_symbol("printf", 0, 0, SymbolBinding::Global, SymbolType::Func, 0);
let strtab = writer.build_strtab();
let strtab_str = String::from_utf8_lossy(&strtab);
assert!(strtab_str.contains("main"));
assert!(strtab_str.contains("printf"));
}
#[test]
fn test_symtab_has_null_entry() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_symbol("main", 0, 10, SymbolBinding::Global, SymbolType::Func, 1);
let symtab = writer.build_symtab();
assert_eq!(symtab.len(), 48); assert_eq!(&symtab[0..24], &[0u8; 24]);
}
#[test]
fn test_relocation_types_have_correct_values() {
assert_eq!(RelocationType::R_X86_64_NONE.elf_value(), 0);
assert_eq!(RelocationType::R_X86_64_64.elf_value(), 1);
assert_eq!(RelocationType::R_X86_64_PC32.elf_value(), 2);
assert_eq!(RelocationType::R_X86_64_32.elf_value(), 10);
assert_eq!(RelocationType::R_AARCH64_ABS64.elf_value(), 257);
assert_eq!(RelocationType::R_AARCH64_CALL26.elf_value(), 283);
assert_eq!(RelocationType::R_ARM_ABS32.elf_value(), 2);
assert_eq!(RelocationType::R_ARM_CALL.elf_value(), 28);
}
#[test]
fn test_rela_section_data() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_section(".text", vec![0xE9, 0, 0, 0, 0], SectionFlags::text());
writer.add_symbol("main", 0, 0, SymbolBinding::Global, SymbolType::Func, 1);
writer.add_relocation(0, 1, 0, RelocationType::R_X86_64_PC32, -4);
let rela = writer.build_rela_section(0);
assert_eq!(rela.len(), 24);
assert_eq!(u64::from_le_bytes(rela[0..8].try_into().unwrap()), 1);
let r_info = u64::from_le_bytes(rela[8..16].try_into().unwrap());
assert_eq!(r_info & 0xFFFF_FFFF, 2); assert_eq!(r_info >> 32, 0); assert_eq!(i64::from_le_bytes(rela[16..24].try_into().unwrap()), -4);
}
#[test]
fn test_roundtrip_elf_write_parse() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_section(".text", vec![0x90, 0xC3], SectionFlags::text());
writer.add_symbol("main", 0, 2, SymbolBinding::Global, SymbolType::Func, 1);
let data = writer.write();
let obj = llvm_native_core::object_file::ObjectFile::parse(&data);
assert!(
obj.is_some(),
"ObjectFile::parse should succeed on our written ELF"
);
let obj = obj.unwrap();
assert_eq!(obj.machine_name(), "x86_64");
assert!(obj.is_valid());
}
#[test]
fn test_roundtrip_elf_aarch64_write_parse() {
let writer = ObjectWriter::new(ObjectFormat::ELF, "aarch64-unknown-linux-gnu");
let data = writer.write();
let obj = llvm_native_core::object_file::ObjectFile::parse(&data);
assert!(obj.is_some());
let obj = obj.unwrap();
assert_eq!(obj.machine_name(), "aarch64");
}
#[test]
fn test_write_macho_empty() {
let writer = ObjectWriter::new(ObjectFormat::MachO, "x86_64-apple-darwin");
let data = writer.write();
assert_eq!(&data[0..4], &[0xCF, 0xFA, 0xED, 0xFE]);
}
#[test]
fn test_write_macho_header_x86_64() {
let writer = ObjectWriter::new(ObjectFormat::MachO, "x86_64-apple-darwin");
let header = writer.write_macho_header(MachOArch::X86_64);
assert_eq!(header.len(), 32);
let magic = u32::from_le_bytes([header[0], header[1], header[2], header[3]]);
assert_eq!(magic, macho_const::MH_MAGIC_64);
let cputype = u32::from_le_bytes([header[4], header[5], header[6], header[7]]);
assert_eq!(cputype, macho_const::CPU_TYPE_X86_64);
}
#[test]
fn test_write_macho_header_arm64() {
let writer = ObjectWriter::new(ObjectFormat::MachO, "aarch64-apple-darwin");
let header = writer.write_macho_header(MachOArch::ARM64);
let cputype = u32::from_le_bytes([header[4], header[5], header[6], header[7]]);
assert_eq!(cputype, macho_const::CPU_TYPE_ARM64);
}
#[test]
fn test_write_macho_strtab() {
let writer = ObjectWriter::new(ObjectFormat::MachO, "x86_64-apple-darwin");
let strings = vec!["_main".to_string(), "_foo".to_string()];
let strtab = writer.write_macho_strtab(&strings);
assert_eq!(strtab[0], 0); let s = String::from_utf8_lossy(&strtab);
assert!(s.contains("_main"));
assert!(s.contains("_foo"));
}
#[test]
fn test_write_macho_symtab_raw() {
let writer = ObjectWriter::new(ObjectFormat::MachO, "x86_64-apple-darwin");
let syms = vec![
MachoSymbol {
name: "_main".to_string(),
n_type: macho_const::N_SECT | macho_const::N_EXT,
n_sect: 1,
n_desc: 0,
n_value: 0x1000,
},
MachoSymbol {
name: "_helper".to_string(),
n_type: macho_const::N_SECT,
n_sect: 1,
n_desc: 0,
n_value: 0x1010,
},
];
let raw = writer.write_macho_symtab_raw(&syms);
assert_eq!(raw.len(), 32);
}
#[test]
fn test_write_macho_dysymtab() {
let writer = ObjectWriter::new(ObjectFormat::MachO, "x86_64-apple-darwin");
let syms = vec![
MachoSymbol {
name: "local".to_string(),
n_type: macho_const::N_SECT,
n_sect: 1,
n_desc: 0,
n_value: 0x1000,
},
MachoSymbol {
name: "global".to_string(),
n_type: macho_const::N_SECT | macho_const::N_EXT,
n_sect: 1,
n_desc: 0,
n_value: 0x1010,
},
];
let dysymtab = writer.write_macho_dysymtab(&syms);
assert_eq!(dysymtab.len(), 80);
let cmd = u32::from_le_bytes([dysymtab[0], dysymtab[1], dysymtab[2], dysymtab[3]]);
assert_eq!(cmd, macho_const::LC_DYSYMTAB);
}
#[test]
fn test_write_macho_with_text_section() {
let mut writer = ObjectWriter::new(ObjectFormat::MachO, "x86_64-apple-darwin");
writer.add_section("__text", vec![0x90, 0xC3], SectionFlags::text());
let data = writer.write();
assert!(data.len() >= 32);
assert_eq!(&data[0..4], &[0xCF, 0xFA, 0xED, 0xFE]);
}
#[test]
fn test_macho_arch_cpu_types() {
assert_eq!(MachOArch::X86_64.cpu_type(), 0x01000007);
assert_eq!(MachOArch::ARM64.cpu_type(), 0x0100000C);
assert_eq!(MachOArch::ARM.cpu_type(), 0x0000000C);
}
#[test]
fn test_write_coff_empty() {
let writer = ObjectWriter::new(ObjectFormat::COFF, "x86_64-pc-windows-msvc");
let data = writer.write();
assert_eq!(u16::from_le_bytes([data[0], data[1]]), 0x8664);
}
#[test]
fn test_write_coff_with_sections() {
let mut writer = ObjectWriter::new(ObjectFormat::COFF, "x86_64-pc-windows-msvc");
writer.add_section(".text", vec![0x90, 0xC3], SectionFlags::text());
let data = writer.write();
assert!(data.len() >= 20); }
#[test]
fn test_write_coff_header_x86_64() {
let writer = ObjectWriter::new(ObjectFormat::COFF, "x86_64-pc-windows-msvc");
let header = writer.write_coff_header(coff_const::IMAGE_FILE_MACHINE_AMD64, 3, 0);
assert_eq!(header.len(), 20);
let machine = u16::from_le_bytes([header[0], header[1]]);
assert_eq!(machine, coff_const::IMAGE_FILE_MACHINE_AMD64);
let nsects = u16::from_le_bytes([header[2], header[3]]);
assert_eq!(nsects, 3);
}
#[test]
fn test_write_coff_header_arm64() {
let writer = ObjectWriter::new(ObjectFormat::COFF, "aarch64-pc-windows-msvc");
let header = writer.write_coff_header(coff_const::IMAGE_FILE_MACHINE_ARM64, 2, 0);
let machine = u16::from_le_bytes([header[0], header[1]]);
assert_eq!(machine, coff_const::IMAGE_FILE_MACHINE_ARM64);
}
#[test]
fn test_write_coff_section_header() {
let writer = ObjectWriter::new(ObjectFormat::COFF, "x86_64-pc-windows-msvc");
let header = writer.write_coff_section_header(
".text",
128,
64,
coff_const::IMAGE_SCN_CNT_CODE
| coff_const::IMAGE_SCN_MEM_EXECUTE
| coff_const::IMAGE_SCN_MEM_READ,
2,
);
assert_eq!(header.len(), 40);
let name = String::from_utf8_lossy(&header[0..8]);
assert!(name.starts_with(".text"));
let chars = u32::from_le_bytes([header[36], header[37], header[38], header[39]]);
assert!(chars & coff_const::IMAGE_SCN_CNT_CODE != 0);
}
#[test]
fn test_write_coff_section() {
let writer = ObjectWriter::new(ObjectFormat::COFF, "x86_64-pc-windows-msvc");
let data = writer.write_coff_section(
".text",
&[0x90, 0xC3],
coff_const::IMAGE_SCN_CNT_CODE
| coff_const::IMAGE_SCN_MEM_EXECUTE
| coff_const::IMAGE_SCN_MEM_READ,
);
assert!(data.len() >= 42); }
#[test]
fn test_write_coff_symtab() {
let writer = ObjectWriter::new(ObjectFormat::COFF, "x86_64-pc-windows-msvc");
let syms = vec![
CoffSymbol {
name: "_main".to_string(),
value: 0x1000,
section_number: 1,
sym_type: 0x20,
storage_class: coff_const::IMAGE_SYM_CLASS_EXTERNAL,
aux_count: 0,
},
CoffSymbol {
name: "helper".to_string(),
value: 0x1010,
section_number: 1,
sym_type: 0,
storage_class: coff_const::IMAGE_SYM_CLASS_STATIC,
aux_count: 0,
},
];
let symtab = writer.write_coff_symtab(&syms);
assert_eq!(symtab.len(), 36); }
#[test]
fn test_write_coff_relocations() {
let writer = ObjectWriter::new(ObjectFormat::COFF, "x86_64-pc-windows-msvc");
let relocs = vec![CoffRelocation {
virtual_address: 5,
symbol_index: 1,
rel_type: 1,
}];
let data = writer.write_coff_relocations(&relocs);
assert_eq!(data.len(), 10); }
#[test]
fn test_write_wasm_empty() {
let writer = ObjectWriter::new(ObjectFormat::Wasm, "wasm32-unknown-unknown");
let data = writer.write();
assert_eq!(&data[0..4], &[0x00, 0x61, 0x73, 0x6D]);
assert_eq!(&data[4..8], &1u32.to_le_bytes());
}
#[test]
fn test_write_wasm_module_data() {
let writer = ObjectWriter::new(ObjectFormat::Wasm, "wasm32-unknown-unknown");
let sections = vec![
WasmSection {
section_type: 1, data: vec![0x01, 0x60, 0x00, 0x00], },
WasmSection {
section_type: 3, data: vec![0x01, 0x00], },
];
let data = writer.write_wasm_module_data(§ions);
assert_eq!(&data[0..4], &[0x00, 0x61, 0x73, 0x6D]);
assert!(data.len() > 8);
}
#[test]
fn test_leb128_encoding_u32() {
let mut buf = Vec::new();
write_leb128_u32(&mut buf, 0);
assert_eq!(buf, vec![0x00]);
buf.clear();
write_leb128_u32(&mut buf, 1);
assert_eq!(buf, vec![0x01]);
buf.clear();
write_leb128_u32(&mut buf, 127);
assert_eq!(buf, vec![0x7F]);
buf.clear();
write_leb128_u32(&mut buf, 128);
assert_eq!(buf, vec![0x80, 0x01]);
buf.clear();
write_leb128_u32(&mut buf, 624485);
assert_eq!(buf, vec![0xE5, 0x8E, 0x26]);
}
#[test]
fn test_leb128_decoding_u32() {
let data = vec![0xE5, 0x8E, 0x26];
let (val, len) = read_leb128_u32(&data).unwrap();
assert_eq!(val, 624485);
assert_eq!(len, 3);
let (val, len) = read_leb128_u32(&[0x00]).unwrap();
assert_eq!(val, 0);
assert_eq!(len, 1);
}
#[test]
fn test_object_format_from_target_triple() {
assert_eq!(
ObjectFormat::from_target_triple("x86_64-unknown-linux-gnu"),
ObjectFormat::ELF
);
assert_eq!(
ObjectFormat::from_target_triple("x86_64-apple-darwin"),
ObjectFormat::MachO
);
assert_eq!(
ObjectFormat::from_target_triple("x86_64-pc-windows-msvc"),
ObjectFormat::COFF
);
assert_eq!(
ObjectFormat::from_target_triple("wasm32-unknown-unknown"),
ObjectFormat::Wasm
);
}
#[test]
fn test_set_target_arch() {
let mut writer = ObjectWriter::default();
writer.set_target_arch("aarch64");
assert!(writer.target_triple.contains("aarch64"));
assert_eq!(writer.format, ObjectFormat::ELF);
writer.set_target_arch("arm64");
assert!(writer.target_triple.contains("aarch64"));
}
#[test]
fn test_set_target_arch_wasm() {
let mut writer = ObjectWriter::default();
writer.set_target_arch("wasm32");
assert!(writer.target_triple.contains("wasm"));
assert_eq!(writer.format, ObjectFormat::Wasm);
}
#[test]
fn test_add_debug_section() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_debug_section(".debug_info", vec![0x01, 0x02, 0x03]);
assert_eq!(writer.debug_sections.len(), 1);
assert_eq!(writer.debug_sections[0].0, ".debug_info");
assert_eq!(writer.debug_sections[0].1, vec![0x01, 0x02, 0x03]);
}
#[test]
fn test_set_section_alignment() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_section(".text", vec![0x90], SectionFlags::text());
writer.set_section_alignment(".text", 64);
assert_eq!(writer.sections[0].alignment, 64);
}
#[test]
fn test_get_section_size() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
writer.add_section(".text", vec![0x90, 0xC3, 0xCC], SectionFlags::text());
let size = writer.get_section_size(".text");
assert_eq!(size, Some(3));
let size_none = writer.get_section_size(".nonexistent");
assert_eq!(size_none, None);
}
#[test]
fn test_add_bss_section() {
let mut writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
let idx = writer.add_bss_section(".bss", 1024);
assert_eq!(idx, 0);
assert_eq!(writer.sections[0].name, ".bss");
assert_eq!(writer.sections[0].section_type, 8); assert_eq!(writer.sections[0].flags.to_elf_flags() & 1, 1); assert_eq!(writer.sections[0].data.len(), 1024);
}
#[test]
fn test_write_to_file() {
let writer = ObjectWriter::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu");
let tmp_path = "/tmp/test_object_writer_write_to_file.o";
let result = writer.write_to_file(tmp_path);
assert!(result.is_ok());
let read_back = std::fs::read(tmp_path);
assert!(read_back.is_ok());
let data = read_back.unwrap();
assert_eq!(&data[0..4], &[0x7F, b'E', b'L', b'F']);
let _ = std::fs::remove_file(tmp_path);
}
}