summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/elf.rs62
-rw-r--r--src/main.rs98
2 files changed, 116 insertions, 44 deletions
diff --git a/src/elf.rs b/src/elf.rs
index aeeecc9..1f9f0dd 100644
--- a/src/elf.rs
+++ b/src/elf.rs
@@ -2,6 +2,14 @@
use std::{io, mem};
+pub trait ToBytes<const N: usize> {
+ fn to_bytes(self) -> [u8; N];
+}
+
+pub trait FromBytes<const N: usize> {
+ fn from_bytes(bytes: [u8; N]) -> Self;
+}
+
// executable type
pub const ET_REL: u16 = 1;
pub const ET_EXEC: u16 = 2;
@@ -13,7 +21,7 @@ pub const PF_X: u32 = 1 << 0;
pub const PF_W: u32 = 1 << 1;
pub const PF_R: u32 = 1 << 2;
-
+pub const DT_NULL: u32 = 0;
pub const DT_NEEDED: u32 = 1;
pub const DT_HASH: u32 = 4;
pub const DT_STRTAB: u32 = 5;
@@ -123,10 +131,6 @@ impl Ehdr32 {
pub fn section_seek(&self, ndx: u16) -> io::SeekFrom {
io::SeekFrom::Start(self.section_offset(ndx))
}
-
- pub fn to_bytes(self) -> [u8; 0x34] {
- unsafe { mem::transmute(self) }
- }
}
#[repr(C)]
@@ -175,8 +179,50 @@ impl Phdr32 {
pub fn size_of() -> usize {
mem::size_of::<Self>()
}
-
- pub fn to_bytes(self) -> [u8; 0x20] {
- unsafe { mem::transmute(self) }
+}
+
+#[repr(C)]
+pub struct Sym32 {
+ pub name: u32,
+ pub value: u32,
+ pub size: u32,
+ pub info: u8,
+ pub other: u8,
+ pub shndx: u16,
+}
+
+#[repr(C)]
+pub struct Rela32 {
+ pub offset: u32,
+ pub info: u32,
+ pub addend: i32,
+}
+
+#[repr(C)]
+pub struct Rel32 {
+ pub offset: u32,
+ pub info: u32,
+}
+
+macro_rules! impl_bytes {
+ ($r#type: ident, $n: literal) => {
+ impl FromBytes<$n> for $r#type {
+ fn from_bytes(bytes: [u8; $n]) -> Self {
+ unsafe { mem::transmute(bytes) }
+ }
+ }
+
+ impl ToBytes<$n> for $r#type {
+ fn to_bytes(self) -> [u8; $n] {
+ unsafe { mem::transmute(self) }
+ }
+ }
+
}
}
+
+impl_bytes!(Ehdr32, 0x34);
+impl_bytes!(Phdr32, 0x20);
+impl_bytes!(Sym32, 16);
+impl_bytes!(Rela32, 12);
+impl_bytes!(Rel32, 8);
diff --git a/src/main.rs b/src/main.rs
index 5e22eed..c36df98 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ use std::{fmt, fs, io, mem, ptr};
compile_error! {"WHY do you have a big endian machine???? it's the 21st century, buddy. this program won't work fuck you"}
mod elf;
+use elf::{FromBytes, ToBytes};
pub enum LinkError {
IO(io::Error),
@@ -290,6 +291,14 @@ impl RelocationType {
_ => return Err(ElfError::UnsupportedRelocation(id)),
})
}
+
+ fn to_x86_u8(self) -> u8 {
+ use RelocationType::*;
+ match self {
+ Direct32 => 1,
+ Pc32 => 2,
+ }
+ }
}
#[derive(Debug, Clone)]
@@ -440,7 +449,7 @@ impl Executable {
}
pub fn write<T: Write + Seek>(&self, data: &[u8], out: &mut T) -> LinkResult<()> {
- let load_addr = 0x400000;// @TODO: make field
+ let load_addr = self.load_addr as u32;
let data_addr: u32 = self.data_addr() as u32;
// start by writing data.
@@ -461,26 +470,67 @@ impl Executable {
out.write_all(&self.strtab)?;
// now symtab
let symtab_offset = out.stream_position()?;
+ let null_symbol = [0; mem::size_of::<elf::Sym32>()];
+ out.write_all(&null_symbol)?;
+ let mut symbols: HashMap<SymbolName, u32> = HashMap::new();
+ for (i, (sym, strtab_offset)) in self.symbol_strtab_offsets.iter().enumerate() {
+ symbols.insert(*sym, (i + 1) as u32);
+ // @TODO: allow STT_OBJECT as fell
+ let sym = elf::Sym32 {
+ name: *strtab_offset as u32,
+ info: elf::STB_GLOBAL << 4 | elf::STT_FUNC,
+ value: 0,
+ size: 0,
+ other: 0,
+ shndx: 0,
+ };
+ out.write_all(&sym.to_bytes())?;
+ }
// now reltab
let reltab_offset = out.stream_position()?;
+ for reloc in self.relocations.iter() {
+ let index = *symbols.get(&reloc.sym).unwrap();
+ println!("{:x}", index << 8 | u32::from(reloc.r#type.to_x86_u8()));
+ let rel = elf::Rel32 {
+ offset: load_addr, // @TODO
+ info: index << 8 | u32::from(reloc.r#type.to_x86_u8()),
+ };
+ out.write_all(&rel.to_bytes())?;
+ }
let reltab_size = out.stream_position()? - reltab_offset;
// now hash
let hashtab_offset = out.stream_position()?;
+ // put everything in a single bucket
+ let nsymbols = symbols.len() as u32;
+ out.write_all(&u32::to_le_bytes(1))?;
+ out.write_all(&u32::to_le_bytes(nsymbols))?;
+ out.write_all(&u32::to_le_bytes(0))?; // bucket begins at 0
+ // chain 1 -> 2 -> 3 -> ... -> n -> 0
+ for i in 1..nsymbols {
+ out.write_all(&u32::to_le_bytes(i))?;
+ }
+ // @OPTIMIZE: can be removed with potentially disastrous results if
+ // a symbol is not found.
+ out.write_all(&u32::to_le_bytes(0))?;
+
+
+
// now dyntab
dyntab_offset = out.stream_position()?;
let mut dyn_data = vec![
elf::DT_RELSZ, reltab_size as u32,
elf::DT_RELENT, 8,
- elf::DT_REL, reltab_offset as u32,
+ elf::DT_REL, load_addr + reltab_offset as u32,
elf::DT_STRSZ, self.strtab.len() as u32,
- elf::DT_STRTAB, strtab_offset as u32,
+ elf::DT_STRTAB, load_addr + strtab_offset as u32,
elf::DT_SYMENT, 16,
- elf::DT_SYMTAB, symtab_offset as u32,
- elf::DT_HASH, hashtab_offset as u32,
+ elf::DT_SYMTAB, load_addr + symtab_offset as u32,
+ elf::DT_HASH, load_addr + hashtab_offset as u32,
];
for lib in &self.lib_strtab_offsets {
dyn_data.extend([elf::DT_NEEDED, *lib as u32]);
}
+ dyn_data.extend([elf::DT_NULL, 0]);
let mut dyn_bytes = Vec::with_capacity(dyn_data.len() * 4);
for x in dyn_data {
dyn_bytes.extend(u32::to_le_bytes(x));
@@ -615,19 +665,9 @@ impl Linker {
addr_map: &mut AddrMap,
reader: &mut BufReader<File>,
) -> Result<SymbolName, ElfError> {
- #[repr(C)]
- pub struct ElfSym {
- name: u32,
- value: u32,
- size: u32,
- info: u8,
- other: u8,
- shndx: u16,
- }
-
let mut sym_buf = [0u8; 16];
reader.read_exact(&mut sym_buf)?;
- let sym: ElfSym = unsafe { mem::transmute(sym_buf) };
+ let sym = elf::Sym32::from_bytes(sym_buf);
let r#type = sym.info & 0xf;
let bind = sym.info >> 4;
let name = self.get_strtab(reader, sym.name)?;
@@ -812,7 +852,7 @@ impl Linker {
}
}
- fn read_relocations<RelType>(
+ fn read_relocations<const N: usize, RelType: FromBytes<N> + ToBytes<N>>(
reader: &mut BufReader<File>,
shdr: &elf::Shdr32,
) -> Result<Vec<RelType>, ElfError> {
@@ -824,19 +864,16 @@ impl Linker {
}
let count = size / entsize;
let mut relocations = Vec::with_capacity(count as _);
- // annoyingly, array sizes can't depend on the size of a type parameter.
- // if they could, we could just use transmute and everyone would be happier.
- let mut rel_buf = [0; 32];
- let rel_data = &mut rel_buf[..mem::size_of::<RelType>()];
+ let mut rel_buf = [0; N];
for rel_idx in 0..count {
reader.seek(io::SeekFrom::Start(offset + rel_idx * entsize))?;
- reader.read_exact(rel_data)?;
+ reader.read_exact(&mut rel_buf)?;
let mut rel = mem::MaybeUninit::uninit();
let rel = unsafe {
ptr::copy_nonoverlapping(
- (&rel_data[0]) as *const u8,
+ (&rel_buf[0]) as *const u8,
rel.as_mut_ptr() as *mut u8,
mem::size_of::<RelType>(),
);
@@ -868,13 +905,7 @@ impl Linker {
match shdr.r#type {
elf::SHT_RELA => {
- #[repr(C)]
- struct ElfRela {
- offset: u32,
- info: u32,
- addend: i32,
- }
- let rels: Vec<ElfRela> = read_relocations(reader, shdr)?;
+ let rels: Vec<elf::Rela32> = read_relocations(reader, shdr)?;
for rela in rels {
add_relocation_x86(
self,
@@ -885,12 +916,7 @@ impl Linker {
}
}
elf::SHT_REL => {
- #[repr(C)]
- struct ElfRel {
- offset: u32,
- info: u32,
- }
- let rels: Vec<ElfRel> = read_relocations(reader, shdr)?;
+ let rels: Vec<elf::Rel32> = read_relocations(reader, shdr)?;
for rel in rels {
add_relocation_x86(self, rel.offset as _, rel.info as _, 0)?;
}