#![allow(dead_code)]
pub struct Symbol<'a> {
pub name: &'a str,
pub offset: Option<u64>,
}
pub struct Reloc<'a> {
pub offset: u64,
pub symbol: &'a str,
pub kind: u32,
pub addend: i64,
}
pub const R_X86_64_PLT32: u32 = 4;
pub fn object(text: &[u8], symbols: &[Symbol<'_>], relocs: &[Reloc<'_>]) -> Vec<u8> {
const TEXT: u32 = 1;
const SYMTAB: u32 = 4;
const STRTAB: u32 = 5;
const SHSTRTAB: u16 = 6;
let mut shstrtab = vec![0u8];
let name = |table: &mut Vec<u8>, text: &str| {
let offset = table.len() as u32;
table.extend_from_slice(text.as_bytes());
table.push(0);
offset
};
let names = [
".text",
".rela.text",
".note.GNU-stack",
".symtab",
".strtab",
".shstrtab",
]
.map(|section| name(&mut shstrtab, section));
let mut strtab = vec![0u8];
let mut symtab = vec![0u8; 24]; for symbol in symbols {
let st_name = name(&mut strtab, symbol.name);
let (info, shndx, value) = match symbol.offset {
Some(offset) => (0x12u8, TEXT as u16, offset),
None => (0x10, 0, 0),
};
symtab.extend_from_slice(&st_name.to_le_bytes());
symtab.push(info);
symtab.push(0); symtab.extend_from_slice(&shndx.to_le_bytes());
symtab.extend_from_slice(&value.to_le_bytes());
symtab.extend_from_slice(&0u64.to_le_bytes());
}
let mut rela = Vec::new();
for reloc in relocs {
let index = symbols
.iter()
.position(|symbol| symbol.name == reloc.symbol)
.expect("relocation against an unknown symbol") as u64
+ 1;
rela.extend_from_slice(&reloc.offset.to_le_bytes());
rela.extend_from_slice(&(index << 32 | u64::from(reloc.kind)).to_le_bytes());
rela.extend_from_slice(&reloc.addend.to_le_bytes());
}
let mut out = vec![0u8; 64];
let place = |out: &mut Vec<u8>, bytes: &[u8]| {
out.resize(out.len().next_multiple_of(8), 0);
let offset = out.len() as u64;
out.extend_from_slice(bytes);
offset
};
let text_at = place(&mut out, text);
let rela_at = place(&mut out, &rela);
let note_at = place(&mut out, &[]);
let symtab_at = place(&mut out, &symtab);
let strtab_at = place(&mut out, &strtab);
let shstrtab_at = place(&mut out, &shstrtab);
out.resize(out.len().next_multiple_of(8), 0);
let shoff = out.len() as u64;
type Header = (u32, u32, u64, u64, usize, u32, u32, u64, u64);
let headers: [Header; 7] = [
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(names[0], 1, 0x6, text_at, text.len(), 0, 0, 16, 0),
(names[1], 4, 0x40, rela_at, rela.len(), SYMTAB, TEXT, 8, 24),
(names[2], 1, 0, note_at, 0, 0, 0, 1, 0),
(names[3], 2, 0, symtab_at, symtab.len(), STRTAB, 1, 8, 24),
(names[4], 3, 0, strtab_at, strtab.len(), 0, 0, 1, 0),
(names[5], 3, 0, shstrtab_at, shstrtab.len(), 0, 0, 1, 0),
];
for (name, kind, flags, offset, size, link, info, align, entsize) in headers {
out.extend_from_slice(&name.to_le_bytes());
out.extend_from_slice(&kind.to_le_bytes());
out.extend_from_slice(&flags.to_le_bytes());
out.extend_from_slice(&0u64.to_le_bytes()); out.extend_from_slice(&offset.to_le_bytes());
out.extend_from_slice(&(size as u64).to_le_bytes());
out.extend_from_slice(&link.to_le_bytes());
out.extend_from_slice(&info.to_le_bytes());
out.extend_from_slice(&align.to_le_bytes());
out.extend_from_slice(&entsize.to_le_bytes());
}
let header = &mut out[..64];
header[..16].copy_from_slice(b"\x7fELF\x02\x01\x01\0\0\0\0\0\0\0\0\0");
header[16..18].copy_from_slice(&1u16.to_le_bytes()); header[18..20].copy_from_slice(&62u16.to_le_bytes()); header[20..24].copy_from_slice(&1u32.to_le_bytes()); header[40..48].copy_from_slice(&shoff.to_le_bytes());
header[52..54].copy_from_slice(&64u16.to_le_bytes()); header[58..60].copy_from_slice(&64u16.to_le_bytes()); header[60..62].copy_from_slice(&(headers.len() as u16).to_le_bytes());
header[62..64].copy_from_slice(&SHSTRTAB.to_le_bytes());
out
}
pub fn main_object() -> Vec<u8> {
object(
&[
0xe8, 0, 0, 0, 0, 0x89, 0xc7, 0xb8, 60, 0, 0, 0, 0x0f, 0x05, ],
&[
Symbol {
name: "_start",
offset: Some(0),
},
Symbol {
name: "answer",
offset: None,
},
],
&[Reloc {
offset: 1,
symbol: "answer",
kind: R_X86_64_PLT32,
addend: -4,
}],
)
}
pub fn answer_object(value: u8) -> Vec<u8> {
object(
&[0xb8, value, 0, 0, 0, 0xc3], &[Symbol {
name: "answer",
offset: Some(0),
}],
&[],
)
}
pub fn archive(members: &[(&str, &[u8], &[&str])]) -> Vec<u8> {
fn header(out: &mut Vec<u8>, name: &str, size: usize) {
let field = |text: String, width: usize| format!("{text:<width$}");
let header = field(name.to_string(), 16)
+ &field("0".into(), 12)
+ &field("0".into(), 6)
+ &field("0".into(), 6)
+ &field("644".into(), 8)
+ &field(size.to_string(), 10)
+ "`\n";
out.extend_from_slice(header.as_bytes());
}
let symbols: Vec<(usize, &str)> = members
.iter()
.enumerate()
.flat_map(|(index, (_, _, names))| names.iter().map(move |name| (index, *name)))
.collect();
let names_size: usize = symbols.iter().map(|(_, name)| name.len() + 1).sum();
let index_size = 4 + 4 * symbols.len() + names_size;
let mut offset = 8 + 60 + index_size.next_multiple_of(2);
let mut member_offsets = Vec::new();
for (_, data, _) in members {
member_offsets.push(offset as u32);
offset += 60 + data.len().next_multiple_of(2);
}
let mut out = b"!<arch>\n".to_vec();
header(&mut out, "/", index_size);
out.extend_from_slice(&(symbols.len() as u32).to_be_bytes());
for (member, _) in &symbols {
out.extend_from_slice(&member_offsets[*member].to_be_bytes());
}
for (_, name) in &symbols {
out.extend_from_slice(name.as_bytes());
out.push(0);
}
if out.len() % 2 == 1 {
out.push(b'\n');
}
for (name, data, _) in members {
header(&mut out, &format!("{name}/"), data.len());
out.extend_from_slice(data);
if out.len() % 2 == 1 {
out.push(b'\n');
}
}
out
}
pub fn entry_point(image: &[u8]) -> Option<u64> {
if !image.starts_with(b"\x7fELF\x02\x01") {
return None;
}
Some(u64::from_le_bytes(image.get(24..32)?.try_into().ok()?))
}