Skip to content

Commit

Permalink
macho: add some objcopy tests (gimli-rs#583)
Browse files Browse the repository at this point in the history
Currently only x86-64 and AArch64 work.

i386 and ARM are missing support for scattered relocations and
paired relocations.

Also add support for an environment variable called OBJECT_TESTFILES_UPDATE
which causes the tests to update the output files.
  • Loading branch information
philipc authored Oct 25, 2023
1 parent fc2fd5c commit 9f222ec
Show file tree
Hide file tree
Showing 9 changed files with 972 additions and 167 deletions.
5 changes: 3 additions & 2 deletions crates/examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ glob = "0.3"

[features]
read = ["object/read"]
write = ["object/write"]
wasm = ["object/wasm"]
xcoff = ["object/xcoff"]
all = ["read", "wasm", "xcoff"]
all = ["read", "write", "wasm", "xcoff"]
unstable-all = ["all"]
default = ["read"]

Expand All @@ -36,7 +37,7 @@ required-features = ["object/read_core", "object/write_core", "object/elf", "obj

[[bin]]
name = "objcopy"
required-features = ["object/read", "object/write"]
required-features = ["read", "write"]

[[bin]]
name = "objdump"
Expand Down
164 changes: 4 additions & 160 deletions crates/examples/src/bin/objcopy.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
use std::collections::HashMap;
use std::{env, fs, process};
use object_examples::objcopy;

use object::{
write, Object, ObjectComdat, ObjectKind, ObjectSection, ObjectSymbol, RelocationTarget,
SectionKind, SymbolFlags, SymbolKind, SymbolSection,
};
use std::{env, fs, process};

fn main() {
let mut args = env::args();
Expand All @@ -24,168 +20,16 @@ fn main() {
process::exit(1);
}
};
let in_file = match unsafe { memmap2::Mmap::map(&in_file) } {
let in_data = match unsafe { memmap2::Mmap::map(&in_file) } {
Ok(mmap) => mmap,
Err(err) => {
eprintln!("Failed to map file '{}': {}", in_file_path, err,);
process::exit(1);
}
};
let in_object = match object::File::parse(&*in_file) {
Ok(object) => object,
Err(err) => {
eprintln!("Failed to parse file '{}': {}", in_file_path, err);
process::exit(1);
}
};
if in_object.kind() != ObjectKind::Relocatable {
eprintln!("Unsupported object kind: {:?}", in_object.kind());
process::exit(1);
}

let mut out_object = write::Object::new(
in_object.format(),
in_object.architecture(),
in_object.endianness(),
);
out_object.mangling = write::Mangling::None;
out_object.flags = in_object.flags();

let mut out_sections = HashMap::new();
for in_section in in_object.sections() {
if in_section.kind() == SectionKind::Metadata {
continue;
}
let section_id = out_object.add_section(
in_section
.segment_name()
.unwrap()
.unwrap_or("")
.as_bytes()
.to_vec(),
in_section.name().unwrap().as_bytes().to_vec(),
in_section.kind(),
);
let out_section = out_object.section_mut(section_id);
if out_section.is_bss() {
out_section.append_bss(in_section.size(), in_section.align());
} else {
out_section.set_data(in_section.data().unwrap(), in_section.align());
}
out_section.flags = in_section.flags();
out_sections.insert(in_section.index(), section_id);
}

let mut out_symbols = HashMap::new();
for in_symbol in in_object.symbols() {
if in_symbol.kind() == SymbolKind::Null {
continue;
}
let (section, value) = match in_symbol.section() {
SymbolSection::None => (write::SymbolSection::None, in_symbol.address()),
SymbolSection::Undefined => (write::SymbolSection::Undefined, in_symbol.address()),
SymbolSection::Absolute => (write::SymbolSection::Absolute, in_symbol.address()),
SymbolSection::Common => (write::SymbolSection::Common, in_symbol.address()),
SymbolSection::Section(index) => {
if let Some(out_section) = out_sections.get(&index) {
(
write::SymbolSection::Section(*out_section),
in_symbol.address() - in_object.section_by_index(index).unwrap().address(),
)
} else {
// Ignore symbols for sections that we have skipped.
assert_eq!(in_symbol.kind(), SymbolKind::Section);
continue;
}
}
_ => panic!("unknown symbol section for {:?}", in_symbol),
};
let flags = match in_symbol.flags() {
SymbolFlags::None => SymbolFlags::None,
SymbolFlags::Elf { st_info, st_other } => SymbolFlags::Elf { st_info, st_other },
SymbolFlags::MachO { n_desc } => SymbolFlags::MachO { n_desc },
SymbolFlags::CoffSection {
selection,
associative_section,
} => {
let associative_section =
associative_section.map(|index| *out_sections.get(&index).unwrap());
SymbolFlags::CoffSection {
selection,
associative_section,
}
}
SymbolFlags::Xcoff {
n_sclass,
x_smtyp,
x_smclas,
containing_csect,
} => {
let containing_csect =
containing_csect.map(|index| *out_symbols.get(&index).unwrap());
SymbolFlags::Xcoff {
n_sclass,
x_smtyp,
x_smclas,
containing_csect,
}
}
_ => panic!("unknown symbol flags for {:?}", in_symbol),
};
let out_symbol = write::Symbol {
name: in_symbol.name().unwrap_or("").as_bytes().to_vec(),
value,
size: in_symbol.size(),
kind: in_symbol.kind(),
scope: in_symbol.scope(),
weak: in_symbol.is_weak(),
section,
flags,
};
let symbol_id = out_object.add_symbol(out_symbol);
out_symbols.insert(in_symbol.index(), symbol_id);
}

for in_section in in_object.sections() {
if in_section.kind() == SectionKind::Metadata {
continue;
}
let out_section = *out_sections.get(&in_section.index()).unwrap();
for (offset, in_relocation) in in_section.relocations() {
let symbol = match in_relocation.target() {
RelocationTarget::Symbol(symbol) => *out_symbols.get(&symbol).unwrap(),
RelocationTarget::Section(section) => {
out_object.section_symbol(*out_sections.get(&section).unwrap())
}
_ => panic!("unknown relocation target for {:?}", in_relocation),
};
let out_relocation = write::Relocation {
offset,
size: in_relocation.size(),
kind: in_relocation.kind(),
encoding: in_relocation.encoding(),
symbol,
addend: in_relocation.addend(),
};
out_object
.add_relocation(out_section, out_relocation)
.unwrap();
}
}

for in_comdat in in_object.comdats() {
let mut sections = Vec::new();
for in_section in in_comdat.sections() {
sections.push(*out_sections.get(&in_section).unwrap());
}
out_object.add_comdat(write::Comdat {
kind: in_comdat.kind(),
symbol: *out_symbols.get(&in_comdat.symbol()).unwrap(),
sections,
});
}
let out_data = objcopy::copy(&in_data);

let out_data = out_object.write().unwrap();
if let Err(err) = fs::write(&out_file_path, out_data) {
eprintln!("Failed to write file '{}': {}", out_file_path, err);
process::exit(1);
Expand Down
3 changes: 3 additions & 0 deletions crates/examples/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Style.
#![allow(clippy::single_match)]

#[cfg(all(feature = "read", feature = "write"))]
pub mod objcopy;

#[cfg(feature = "read")]
pub mod objdump;

Expand Down
165 changes: 165 additions & 0 deletions crates/examples/src/objcopy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use std::collections::HashMap;
use std::process;

use object::{
write, Object, ObjectComdat, ObjectKind, ObjectSection, ObjectSymbol, RelocationTarget,
SectionKind, SymbolFlags, SymbolKind, SymbolSection,
};

pub fn copy(in_data: &[u8]) -> Vec<u8> {
let in_object = match object::File::parse(in_data) {
Ok(object) => object,
Err(err) => {
eprintln!("Failed to parse file: {}", err);
process::exit(1);
}
};
if in_object.kind() != ObjectKind::Relocatable {
eprintln!("Unsupported object kind: {:?}", in_object.kind());
process::exit(1);
}

let mut out_object = write::Object::new(
in_object.format(),
in_object.architecture(),
in_object.endianness(),
);
out_object.mangling = write::Mangling::None;
out_object.flags = in_object.flags();

let mut out_sections = HashMap::new();
for in_section in in_object.sections() {
if in_section.kind() == SectionKind::Metadata {
continue;
}
let section_id = out_object.add_section(
in_section
.segment_name()
.unwrap()
.unwrap_or("")
.as_bytes()
.to_vec(),
in_section.name().unwrap().as_bytes().to_vec(),
in_section.kind(),
);
let out_section = out_object.section_mut(section_id);
if out_section.is_bss() {
out_section.append_bss(in_section.size(), in_section.align());
} else {
out_section.set_data(in_section.data().unwrap(), in_section.align());
}
out_section.flags = in_section.flags();
out_sections.insert(in_section.index(), section_id);
}

let mut out_symbols = HashMap::new();
for in_symbol in in_object.symbols() {
if in_symbol.kind() == SymbolKind::Null {
continue;
}
let (section, value) = match in_symbol.section() {
SymbolSection::None => (write::SymbolSection::None, in_symbol.address()),
SymbolSection::Undefined => (write::SymbolSection::Undefined, in_symbol.address()),
SymbolSection::Absolute => (write::SymbolSection::Absolute, in_symbol.address()),
SymbolSection::Common => (write::SymbolSection::Common, in_symbol.address()),
SymbolSection::Section(index) => {
if let Some(out_section) = out_sections.get(&index) {
(
write::SymbolSection::Section(*out_section),
in_symbol.address() - in_object.section_by_index(index).unwrap().address(),
)
} else {
// Ignore symbols for sections that we have skipped.
assert_eq!(in_symbol.kind(), SymbolKind::Section);
continue;
}
}
_ => panic!("unknown symbol section for {:?}", in_symbol),
};
let flags = match in_symbol.flags() {
SymbolFlags::None => SymbolFlags::None,
SymbolFlags::Elf { st_info, st_other } => SymbolFlags::Elf { st_info, st_other },
SymbolFlags::MachO { n_desc } => SymbolFlags::MachO { n_desc },
SymbolFlags::CoffSection {
selection,
associative_section,
} => {
let associative_section =
associative_section.map(|index| *out_sections.get(&index).unwrap());
SymbolFlags::CoffSection {
selection,
associative_section,
}
}
SymbolFlags::Xcoff {
n_sclass,
x_smtyp,
x_smclas,
containing_csect,
} => {
let containing_csect =
containing_csect.map(|index| *out_symbols.get(&index).unwrap());
SymbolFlags::Xcoff {
n_sclass,
x_smtyp,
x_smclas,
containing_csect,
}
}
_ => panic!("unknown symbol flags for {:?}", in_symbol),
};
let out_symbol = write::Symbol {
name: in_symbol.name().unwrap_or("").as_bytes().to_vec(),
value,
size: in_symbol.size(),
kind: in_symbol.kind(),
scope: in_symbol.scope(),
weak: in_symbol.is_weak(),
section,
flags,
};
let symbol_id = out_object.add_symbol(out_symbol);
out_symbols.insert(in_symbol.index(), symbol_id);
}

for in_section in in_object.sections() {
if in_section.kind() == SectionKind::Metadata {
continue;
}
let out_section = *out_sections.get(&in_section.index()).unwrap();
for (offset, in_relocation) in in_section.relocations() {
let symbol = match in_relocation.target() {
RelocationTarget::Symbol(symbol) => *out_symbols.get(&symbol).unwrap(),
RelocationTarget::Section(section) => {
out_object.section_symbol(*out_sections.get(&section).unwrap())
}
_ => panic!("unknown relocation target for {:?}", in_relocation),
};
let out_relocation = write::Relocation {
offset,
size: in_relocation.size(),
kind: in_relocation.kind(),
encoding: in_relocation.encoding(),
symbol,
addend: in_relocation.addend(),
};
out_object
.add_relocation(out_section, out_relocation)
.unwrap();
}
}

for in_comdat in in_object.comdats() {
let mut sections = Vec::new();
for in_section in in_comdat.sections() {
sections.push(*out_sections.get(&in_section).unwrap());
}
out_object.add_comdat(write::Comdat {
kind: in_comdat.kind(),
symbol: *out_symbols.get(&in_comdat.symbol()).unwrap(),
sections,
});
}

out_object.write().unwrap()
}
Loading

0 comments on commit 9f222ec

Please sign in to comment.