Skip to content

Commit

Permalink
Add simple_write example (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
philipc authored Apr 5, 2024
1 parent bbd6aa8 commit 16b441f
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 1 deletion.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ For reading files, it provides multiple levels of support:
* a higher level unified API for accessing common features of object files, such
as sections and symbols ([example](crates/examples/src/objdump.rs))

Supported file formats: ELF, Mach-O, Windows PE/COFF, Wasm, XCOFF, and Unix archive.
Supported file formats for reading: ELF, Mach-O, Windows PE/COFF, Wasm, XCOFF, and Unix archive.

For writing files, it provides:

* low level writers for ELF, PE, and COFF
* higher level builder for ELF ([example](crates/rewrite/src))
* a unified API for writing relocatable object files (ELF, Mach-O, COFF, XCOFF)
([example](crates/examples/src/bin/simple_write.rs))

## Example for unified read API
```rust
Expand Down
4 changes: 4 additions & 0 deletions crates/examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ required-features = ["object/read_core", "object/write_core", "object/pe", "obje
[[bin]]
name = "readobj"
required-features = ["read"]

[[bin]]
name = "simple_write"
required-features = ["write"]
128 changes: 128 additions & 0 deletions crates/examples/src/bin/simple_write.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/// This example demonstrates how to create an object file with a simple main function that
/// calls puts("Hello, world!").
///
/// The resulting object file can be linked with a C runtime to create a complete executable:
/// ```sh
/// $ cargo run --features write --bin simple_write
/// $ gcc -o hello hello.o
/// $ ./hello
/// Hello, world!
/// ```
use object::write::{Object, Relocation, StandardSection, Symbol, SymbolScope, SymbolSection};
use object::{
Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationFlags, RelocationKind,
SymbolFlags, SymbolKind,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut obj = Object::new(
BinaryFormat::native_object(),
Architecture::X86_64,
Endianness::Little,
);

// Add a file symbol (STT_FILE or equivalent).
obj.add_file_symbol(b"hello.c".into());

// Generate code for the equivalent of this C function:
// int main() {
// puts("Hello, world!");
// return 0;
// }
let mut main_data = Vec::new();
// sub $0x28, %rsp
main_data.extend_from_slice(&[0x48, 0x83, 0xec, 0x28]);
// Handle different calling convention on Windows.
if cfg!(target_os = "windows") {
// lea 0x0(%rip), %rcx
main_data.extend_from_slice(&[0x48, 0x8d, 0x0d, 0x00, 0x00, 0x00, 0x00]);
} else {
// lea 0x0(%rip), %rdi
main_data.extend_from_slice(&[0x48, 0x8d, 0x3d, 0x00, 0x00, 0x00, 0x00]);
}
// R_X86_64_PC32 .rodata-0x4
let s_reloc_offset = main_data.len() - 4;
let s_reloc_addend = -4;
let s_reloc_flags = RelocationFlags::Generic {
kind: RelocationKind::Relative,
encoding: RelocationEncoding::Generic,
size: 32,
};
// call 14 <main+0x14>
main_data.extend_from_slice(&[0xe8, 0x00, 0x00, 0x00, 0x00]);
// R_X86_64_PLT32 puts-0x4
let puts_reloc_offset = main_data.len() - 4;
let puts_reloc_addend = -4;
let puts_reloc_flags = RelocationFlags::Generic {
kind: RelocationKind::PltRelative,
encoding: RelocationEncoding::X86Branch,
size: 32,
};
// xor %eax, %eax
main_data.extend_from_slice(&[0x31, 0xc0]);
// add $0x28, %rsp
main_data.extend_from_slice(&[0x48, 0x83, 0xc4, 0x28]);
// ret
main_data.extend_from_slice(&[0xc3]);

// Add the main function in its own subsection (equivalent to -ffunction-sections).
let (main_section, main_offset) =
obj.add_subsection(StandardSection::Text, b"main", &main_data, 1);
// Add a globally visible symbol for the main function.
obj.add_symbol(Symbol {
name: b"main".into(),
value: main_offset,
size: main_data.len() as u64,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: SymbolSection::Section(main_section),
flags: SymbolFlags::None,
});

// Add a read only string constant for the puts argument.
// We don't create a symbol for the constant, but instead refer to it by
// the section symbol and section offset.
let rodata_section = obj.section_id(StandardSection::ReadOnlyData);
let rodata_symbol = obj.section_symbol(rodata_section);
let s_offset = obj.append_section_data(rodata_section, b"Hello, world!\0", 1);

// Relocation for the string constant.
obj.add_relocation(
main_section,
Relocation {
offset: s_reloc_offset as u64,
symbol: rodata_symbol,
addend: s_offset as i64 + s_reloc_addend,
flags: s_reloc_flags,
},
)?;

// External symbol for puts.
let puts_symbol = obj.add_symbol(Symbol {
name: b"puts".into(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});

// Relocation for the call to puts.
obj.add_relocation(
main_section,
Relocation {
offset: puts_reloc_offset as u64,
symbol: puts_symbol,
addend: puts_reloc_addend,
flags: puts_reloc_flags,
},
)?;

// Finally, write the object file.
let file = std::fs::File::create("hello.o")?;
obj.write_stream(file)?;
Ok(())
}

0 comments on commit 16b441f

Please sign in to comment.