diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/elf.rs | 120 | ||||
-rw-r--r-- | src/linker.rs | 258 | ||||
-rw-r--r-- | src/main.rs | 26 |
3 files changed, 183 insertions, 221 deletions
@@ -1,7 +1,7 @@ // basic ELF types and constants -use std::{io, mem, fmt}; use io::{BufRead, Seek}; +use std::{fmt, io, mem}; pub trait ToBytes<const N: usize> { fn to_bytes(self) -> [u8; N]; @@ -11,8 +11,6 @@ pub trait FromBytes<const N: usize> { fn from_bytes(bytes: [u8; N]) -> Self; } -// @TODO: make all of these constants private - // executable type pub const ET_REL: u16 = 1; pub const ET_EXEC: u16 = 2; @@ -40,9 +38,7 @@ pub const PT_INTERP: u32 = 3; pub const SHT_PROGBITS: u32 = 1; // Program data pub const SHT_SYMTAB: u32 = 2; // Symbol table -//pub const SHT_STRTAB: u32 = 3; // String table pub const SHT_RELA: u32 = 4; // Relocation entries with addends -//pub const SHT_DYNAMIC: u32 = 6; // Dynamic linking information pub const SHT_NOBITS: u32 = 8; // Program space with no data (bss) pub const SHT_REL: u32 = 9; // Relocation entries, no addends @@ -227,7 +223,6 @@ pub enum Error { NoStrtab, } - impl From<io::Error> for Error { fn from(e: io::Error) -> Error { Error::IO(e) @@ -278,7 +273,7 @@ fn bytes_to_string(bytes: Vec<u8>) -> Result<String> { String::from_utf8(bytes).map_err(|_| Error::BadUtf8) } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Machine { X86, Amd64, @@ -296,14 +291,14 @@ impl From<u16> for Machine { } } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SectionType { ProgBits, NoBits, Rel, Rela, Symtab, - Other(u32) + Other(u32), } impl From<u32> for SectionType { @@ -320,11 +315,11 @@ impl From<u32> for SectionType { } } -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Type { Rel, Exec, - Other(u16) + Other(u16), } impl From<u16> for Type { @@ -338,12 +333,12 @@ impl From<u16> for Type { } } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum SymbolBind { Global, Weak, Local, - Other(u8) + Other(u8), } impl From<u8> for SymbolBind { @@ -358,12 +353,12 @@ impl From<u8> for SymbolBind { } } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum SymbolType { Function, Object, Section, - Other(u8) + Other(u8), } impl From<u8> for SymbolType { @@ -403,8 +398,8 @@ pub enum RelType { impl RelType { fn from_u8(id: u8, machine: Machine) -> Self { - use RelType::*; use Machine::*; + use RelType::*; match (machine, id) { (X86, 1) => Direct32, (X86, 2) => Pc32, @@ -422,7 +417,6 @@ impl RelType { } } - pub struct Relocation { pub r#type: RelType, pub entry_offset: u64, // file offset of relocation metadata (for debugging) @@ -431,7 +425,10 @@ pub struct Relocation { pub addend: i64, } -pub trait Reader where Self: Sized { +pub trait Reader +where + Self: Sized, +{ fn new<T: BufRead + Seek>(reader: T) -> Result<Self>; fn r#type(&self) -> Type; fn machine(&self) -> Machine; @@ -466,11 +463,11 @@ impl Reader32LE { impl Reader for Reader32LE { fn new<T: BufRead + Seek>(mut reader: T) -> Result<Self> { use Error::*; - + let mut hdr_buf = [0; 0x34]; reader.read_exact(&mut hdr_buf)?; let ehdr = Ehdr32::from_bytes(hdr_buf); - + if ehdr.ident != [0x7f, b'E', b'L', b'F'] { return Err(NotAnElf); } @@ -480,29 +477,27 @@ impl Reader for Reader32LE { if ehdr.version != 1 || ehdr.version2 != 1 { return Err(BadVersion); } - + let mut shdrs = Vec::with_capacity(ehdr.shnum.into()); for i in 0..ehdr.shnum { let offset = u64::from(ehdr.shoff) + u64::from(ehdr.shentsize) * u64::from(i); reader.seek(io::SeekFrom::Start(offset))?; let mut shdr_buf = [0; 0x28]; reader.read_exact(&mut shdr_buf)?; - shdrs.push(Shdr32::from_bytes(shdr_buf)); + shdrs.push(Shdr32::from_bytes(shdr_buf)); } - + let mut symtabs = Vec::with_capacity(ehdr.shnum.into()); let mut symbols = vec![]; let mut section_data = Vec::with_capacity(ehdr.shnum.into()); let mut strtab_idx = None; - - - + for (s_idx, shdr) in shdrs.iter().enumerate() { let mut data = vec![0; shdr.size as usize]; reader.seek(io::SeekFrom::Start(shdr.offset.into()))?; reader.read_exact(&mut data)?; section_data.push(data); - + if let Some(shstrhdr) = shdrs.get(ehdr.shstrndx as usize) { // get name reader.seek(io::SeekFrom::Start( @@ -512,14 +507,12 @@ impl Reader for Reader32LE { reader.read_until(0, &mut bytes)?; bytes.pop(); // remove terminating \0 let name = bytes_to_string(bytes)?; - + if name == ".strtab" { strtab_idx = Some(s_idx as u16); } } - - - + let mut symtab = vec![]; if shdr.r#type == SHT_SYMTAB && shdr.entsize as usize >= mem::size_of::<Sym32>() { // read symbol table @@ -532,7 +525,7 @@ impl Reader for Reader32LE { let r#type = (sym.info & 0xf).into(); let bind = (sym.info >> 4).into(); let mut size = sym.size.into(); - + let value = match sym.shndx { SHN_UNDEF => SymbolValue::Undefined, SHN_ABS => SymbolValue::Absolute(sym.value.into()), @@ -544,11 +537,10 @@ impl Reader for Reader32LE { size = shdrs[idx as usize].size.into(); } SymbolValue::SectionOffset(idx, sym.value.into()) - }, + } x => return Err(BadSymShNdx(x)), }; - - + let symbol = Symbol { name: sym.name.into(), value, @@ -558,11 +550,11 @@ impl Reader for Reader32LE { }; symtab.push(symbols.len()); symbols.push(symbol); - } + } } symtabs.push(symtab); } - + // read relocations let mut relocations = vec![]; for shdr in shdrs.iter() { @@ -571,21 +563,20 @@ impl Reader for Reader32LE { continue; } let is_rela = r#type == SHT_RELA; - + if shdr.entsize < 8 { continue; } let count = shdr.size / shdr.entsize; - + reader.seek(io::SeekFrom::Start(shdr.offset.into()))?; - + let my_symbols = symtabs.get(shdr.link as usize).ok_or(BadLink(shdr.link))?; for r_idx in 0..count { - let info; let mut offset; let addend; - + if is_rela { let mut rela_buf = [0; 12]; reader.read_exact(&mut rela_buf)?; @@ -601,20 +592,20 @@ impl Reader for Reader32LE { offset = rel.offset; addend = 0; }; - - + if ehdr.r#type == ET_REL { // rel.offset is relative to section if let Some(info_hdr) = shdrs.get(shdr.info as usize) { offset += info_hdr.offset; } } - - + let sym_idx = info >> 8; - let symbols_idx = my_symbols.get(sym_idx as usize).ok_or(BadSymIndex(sym_idx.into()))?; + let symbols_idx = my_symbols + .get(sym_idx as usize) + .ok_or_else(|| BadSymIndex(sym_idx.into()))?; let symbol = &symbols[*symbols_idx]; - + relocations.push(Relocation { r#type: RelType::from_u8(info as u8, ehdr.machine.into()), entry_offset: shdr.offset as u64 + r_idx as u64 * shdr.entsize as u64, @@ -624,37 +615,37 @@ impl Reader for Reader32LE { }); } } - + Ok(Self { ehdr, shdrs, symbols, strtab_idx, relocations, - section_data + section_data, }) } - + fn r#type(&self) -> Type { self.ehdr.r#type.into() } - + fn machine(&self) -> Machine { self.ehdr.machine.into() } - + fn entry(&self) -> u64 { self.ehdr.entry.into() } - + fn relocations(&self) -> &[Relocation] { &self.relocations } - + fn symbols(&self) -> &[Symbol] { &self.symbols } - + fn symbol_name(&self, sym: &Symbol) -> Result<String> { let strtab = &self.section_data[self.strtab_idx.ok_or(Error::NoStrtab)? as usize]; let i = sym.name as usize; @@ -662,23 +653,26 @@ impl Reader for Reader32LE { while end < strtab.len() && strtab[end] != b'\0' { end += 1; } - bytes_to_string((&strtab[i..end]).to_vec()) + bytes_to_string(strtab[i..end].to_vec()) } - + fn section_type(&self, idx: u16) -> Option<SectionType> { self.shdrs.get(idx as usize).map(|shdr| shdr.r#type.into()) } - + fn read_section_data_exact(&self, idx: u16, offset: u64, data: &mut [u8]) -> Result<()> { - let section = self.section_data.get(usize::from(idx)).ok_or(Error::BadSectionIndex(idx))?; + let section = self + .section_data + .get(usize::from(idx)) + .ok_or(Error::BadSectionIndex(idx))?; if offset + data.len() as u64 > section.len() as u64 { return Err(Error::IO(io::Error::from(io::ErrorKind::UnexpectedEof))); } - + let offset = offset as usize; - + data.copy_from_slice(§ion[offset..offset + data.len()]); - + Ok(()) } } diff --git a/src/linker.rs b/src/linker.rs index b7069f6..e163cf2 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -1,17 +1,20 @@ use crate::{elf, util}; use io::{BufRead, Seek, Write}; use std::collections::{BTreeMap, HashMap}; -use std::{fmt, io, mem, fs, path}; +use std::{fmt, fs, io, mem, path}; -use elf::ToBytes; use elf::Reader as ELFReader; +use elf::ToBytes; use util::u32_from_le_slice; pub enum LinkError { IO(io::Error), + /// executable is too large (>4GB on 32-bit platforms) TooLarge, - NoEntry(String), // no entry point - EntryNotDefined(String), // entry point is declared, but not defined + /// entry point not found + NoEntry(String), + /// entry point was declared, and (probably) used, but not defined + EntryNotDefined(String), } type LinkResult<T> = Result<T, LinkError>; @@ -41,24 +44,23 @@ impl From<&LinkError> for String { } pub enum LinkWarning { - RelSymNotFound { source: String, name: String }, + /// unsupported relocation type RelUnsupported(u8), + /// relocation is too large to fit inside its owner RelOOB(String, u64), + /// relocation is in a BSS section or some shit RelNoData(String, u64), - RelNoValue(String), } impl fmt::Display for LinkWarning { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use LinkWarning::*; match self { - RelSymNotFound { source, name } => write!(f, "undefined symbol '{name}' (in {source}) (relocation ignored)."), RelOOB(text, offset) => write!(f, "relocation applied to {text}+0x{offset:x}, which goes outside of the symbol (it will be ignored)."), RelNoData(source, offset) => write!( f, "relocation {source}+0x{offset:x} not in a data/text section. it will be ignored." ), - RelNoValue(name) => write!(f, "can't figure out value of symbol '{name}' (relocation ignored)."), RelUnsupported(x) => write!(f, "Unsupported relocation type {x} (relocation ignored)."), } } @@ -70,16 +72,12 @@ impl From<&LinkWarning> for String { } } +/// error produced by [Linker::add_object] pub enum ObjectError { + /// ELF format error Elf(elf::Error), + /// wrong type of ELF file BadType, - BadUtf8, - BadSymtab, - BadLink(u64), - BadRelHeader, - UnsupportedRelocation(u8), - BadSymIdx(u64), - NoStrtab, } impl From<elf::Error> for ObjectError { @@ -102,21 +100,16 @@ impl fmt::Display for ObjectError { // ("failed to fill whole buffer") Elf(e) => write!(f, "{e}"), BadType => write!(f, "wrong type of ELF file (not an object file)"), - BadUtf8 => write!(f, "bad UTF-8 in ELF file"), - BadSymtab => write!(f, "bad ELF symbol table"), - BadRelHeader => write!(f, "bad ELF relocation header"), - UnsupportedRelocation(x) => write!(f, "unsupported relocation type: {x}"), - BadLink(i) => write!(f, "bad ELF link: {i}"), - BadSymIdx(i) => write!(f, "bad symbol index: {i}"), - NoStrtab => write!(f, "object has no .strtab section"), } } } -// to be more efficientâ„¢, we use integers to keep track of symbol names. type SymbolNameType = u32; +/// To be more efficientâ„¢, we use integers to keep track of symbol names. +/// A SymbolName doesn't need to refer to a symbol which has been defined. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] struct SymbolName(SymbolNameType); +/// Keeps track of string-[SymbolName] conversion. struct SymbolNames { count: SymbolNameType, to_string: Vec<String>, @@ -146,12 +139,10 @@ impl SymbolNames { } } - #[allow(dead_code)] fn get_str(&self, id: SymbolName) -> Option<&str> { self.to_string.get(id.0 as usize).map(|s| &s[..]) } - #[allow(dead_code)] fn get(&self, name: &str) -> Option<SymbolName> { self.by_string.get(name).copied() } @@ -168,14 +159,6 @@ type SymbolIdType = u32; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] struct SymbolId(SymbolIdType); -#[derive(Copy, Clone, Debug)] -#[allow(dead_code)] // @TODO @TEMPORARY -enum SymbolType { - Function, - Object, - Other, -} - #[derive(Debug)] enum SymbolValue { Bss(u64), @@ -183,12 +166,10 @@ enum SymbolValue { Absolute(u64), } -#[allow(dead_code)] // @TODO @TEMPORARY #[derive(Debug)] struct SymbolInfo { - r#type: elf::SymbolType, - value: Option<SymbolValue>, - size: u64, + // (currently this is all we need) + value: SymbolValue, } struct Symbols { @@ -239,8 +220,8 @@ impl Symbols { self.info.get_mut(id.0 as usize) } - fn get_info_from_id(&self, id: SymbolId) -> Option<&SymbolInfo> { - self.info.get(id.0 as usize) + fn get_info_from_id(&self, id: SymbolId) -> &SymbolInfo { + self.info.get(id.0 as usize).expect("bad symbol ID") } fn get_id_from_name(&self, source: SourceId, name: SymbolName) -> Option<SymbolId> { @@ -269,18 +250,18 @@ struct Relocation { addend: i64, } -pub struct Linker { +pub struct Linker<'a> { symbols: Symbols, symbol_names: SymbolNames, relocations: Vec<Relocation>, undefined_relocations: Vec<Relocation>, // library relocations - sources: Vec<String>, // object files + sources: Vec<String>, // object files libraries: Vec<String>, bss_size: u64, // output bss size bss_addr: u64, // output bss address data_addr: u64, // output data address symbol_data_offsets: HashMap<SymbolId, u64>, // for symbols with data, this holds the offsets into the data segment. - warn: fn(LinkWarning), + warn: Box<dyn Fn(LinkWarning) + 'a>, } // this maps between offsets in an object file and symbols defined in that file. @@ -402,7 +383,7 @@ impl Executable { self.load_addr + self.data_offset() } - pub fn write<T: Write + Seek>(&self, data: &[u8], mut out: T) -> LinkResult<()> { + pub fn write(&self, data: &[u8], mut out: impl Write + Seek) -> LinkResult<()> { let load_addr = self.load_addr as u32; // start by writing data. @@ -428,7 +409,7 @@ impl Executable { 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 + // @TODO: allow STT_OBJECT as well let sym = elf::Sym32 { name: *strtab_offset as u32, info: elf::STB_GLOBAL << 4 | elf::STT_FUNC, @@ -571,18 +552,15 @@ impl Executable { } } -impl Linker { +impl<'a> Linker<'a> { fn default_warn_handler(warning: LinkWarning) { eprintln!("warning: {warning}"); } - // why use fn of all things to transmit warnings? - // well, it's very nice for stuff to not need a mutable reference - // to emit warnings, and this is basically the only way of doing it. - // if you need to mutate state in your warning handler, you can always - // use a mutex. - pub fn _set_warning_handler(&mut self, warn: fn(LinkWarning)) { - self.warn = warn; + /// Set function to be called when there is a warning. + /// By default, warnings are sent to stderr. + pub fn set_warning_handler<T: Fn(LinkWarning) + 'a>(&mut self, warn: T) { + self.warn = Box::new(warn); } pub fn new() -> Self { @@ -597,7 +575,7 @@ impl Linker { sources: vec![], libraries: vec![], symbol_data_offsets: HashMap::new(), - warn: Self::default_warn_handler, + warn: Box::new(Self::default_warn_handler), } } @@ -626,31 +604,29 @@ impl Linker { data_offset = Some(elf.section_offset(shndx).unwrap() + offset); elf.read_section_data_exact(shndx, offset, &mut data)?; Some(SymbolValue::Data(data)) - }, + } Some(elf::SectionType::NoBits) => { let p = self.bss_size; self.bss_size += symbol.size; Some(SymbolValue::Bss(p)) - }, + } _ => None, // huh } } }; - let info = SymbolInfo { - r#type: symbol.r#type, - value, - size: symbol.size, - }; - let symbol_id = match symbol.bind { - elf::SymbolBind::Local => self.symbols.add_local(source, name_id, info), - elf::SymbolBind::Global => self.symbols.add_global(source, name_id, info), - elf::SymbolBind::Weak => self.symbols.add_weak(source, name_id, info), - _ => return Ok(()), // eh - }; + if let Some(value) = value { + let info = SymbolInfo { value }; + let symbol_id = match symbol.bind { + elf::SymbolBind::Local => self.symbols.add_local(source, name_id, info), + elf::SymbolBind::Global => self.symbols.add_global(source, name_id, info), + elf::SymbolBind::Weak => self.symbols.add_weak(source, name_id, info), + _ => return Ok(()), // eh + }; - if let Some(offset) = data_offset { - offset_map.add_symbol(offset, symbol.size, symbol_id); + if let Some(offset) = data_offset { + offset_map.add_symbol(offset, symbol.size, symbol_id); + } } Ok(()) } @@ -674,11 +650,11 @@ impl Linker { if elf.r#type() != elf::Type::Rel { return Err(BadType); } - + for symbol in elf.symbols() { self.add_symbol(source_id, &elf, &mut offset_map, symbol)?; } - + for rel in elf.relocations() { if let Some(r#where) = offset_map.get(rel.offset) { let sym = self.symbol_names.add(elf.symbol_name(&rel.symbol)?); @@ -692,14 +668,14 @@ impl Linker { } else { self.emit_warning(LinkWarning::RelNoData( self.source_name(source_id).into(), - rel.entry_offset + rel.entry_offset, )); } } Ok(()) } - + pub fn add_library(&mut self, name: &str) -> Result<(), ObjectError> { self.libraries.push(name.into()); Ok(()) @@ -713,22 +689,10 @@ impl Linker { (self.warn)(warning); } - fn emit_warning_rel_sym_not_found(&self, source: SourceId, name: SymbolName) { - let warn = LinkWarning::RelSymNotFound { - source: self.source_name(source).into(), - name: self.symbol_name_str(name).into(), - }; - self.emit_warning(warn); - } - - // get symbol ID, producing a warning if it does not exist. + /// get symbol ID from symbol name + /// returns 0 if the ssymbol is not defined. fn get_symbol_id(&self, source_id: SourceId, name: SymbolName) -> Option<SymbolId> { - // @TODO: don't warn about the same symbol twice - let sym = self.symbols.get_id_from_name(source_id, name); - if sym.is_none() { - self.emit_warning_rel_sym_not_found(source_id, name); - } - sym + self.symbols.get_id_from_name(source_id, name) } // generates a string like main.c:some_function @@ -743,16 +707,19 @@ impl Linker { "???".into() } - fn get_symbol_value(&self, sym: SymbolId) -> Option<u64> { - let info = self.symbols.get_info_from_id(sym)?; + fn get_symbol_value(&self, sym: SymbolId) -> u64 { + let info = self.symbols.get_info_from_id(sym); use SymbolValue::*; - match info.value.as_ref()? { - Data(_) => self + match info.value { + Data(_) => { + self .symbol_data_offsets .get(&sym) - .map(|&o| o + self.data_addr), - Bss(x) => Some(self.bss_addr + *x), - Absolute(a) => Some(*a), + .unwrap() // @TODO: can this panic? + + self.data_addr + } + Bss(x) => self.bss_addr + x, + Absolute(a) => a, } } @@ -771,20 +738,16 @@ impl Linker { let pc = apply_offset + self.data_addr; let symbol = match self.get_symbol_id(rel.source_id, rel.sym) { - None => return Ok(()), // we emitted a warning in get_symbol_id - Some(sym) => sym, - }; - - let symbol_value = match self.get_symbol_value(symbol) { None => { - // this symbol is defined in a library - //self.emit_warning(LinkWarning::RelNoValue(self.symbol_id_location_string(symbol))); + // symbol not defined. it should come from a library. self.undefined_relocations.push(rel); return Ok(()); } - Some(v) => v, + Some(sym) => sym, }; + let symbol_value = self.get_symbol_value(symbol); + let addend = rel.addend; enum Value { @@ -796,25 +759,24 @@ impl Linker { let value = match rel.r#type { Direct32 => U32(symbol_value as u32 + addend as u32), Pc32 => U32(symbol_value as u32 + addend as u32 - pc as u32), - Other(x) => {self.emit_warning(LinkWarning::RelUnsupported(x)); return Ok(()) }, - }; - - let apply_symbol_info = match self.symbols.get_mut_info_from_id(apply_symbol) { - Some(info) => info, - None => { - // this shouldn't happen. - self.emit_warning_rel_sym_not_found(rel.source_id, rel.sym); + Other(x) => { + self.emit_warning(LinkWarning::RelUnsupported(x)); return Ok(()); } }; + let apply_symbol_info = self + .symbols + .get_mut_info_from_id(apply_symbol) + .expect("bad symbol ID"); + use SymbolValue::*; // guarantee failure if apply_offset can't be converted to usize. let apply_start = apply_offset.try_into().unwrap_or(usize::MAX - 32); match apply_symbol_info.value { - Some(Data(_)) => { + Data(_) => { let mut in_bounds = true; match value { U32(u) => { @@ -844,7 +806,7 @@ impl Linker { Ok(()) } - + /// "easy" input API. /// infers the file type of input, and calls the appropriate function (e.g. `add_object`) /// if there return value is `Err(s)`, `s` will be a nicely formatted error string. @@ -852,11 +814,11 @@ impl Linker { enum FileType { Object, DynamicLibrary, - Other + Other, } - + use FileType::*; - + fn file_type(input: &str) -> FileType { if input.ends_with(".o") { return Object; @@ -864,32 +826,28 @@ impl Linker { if input.ends_with(".so") { return DynamicLibrary; } - if input.find(".so.").is_some() { + if input.contains(".so.") { // e.g. libc.so.6, some_library.so.12.7.3 return DynamicLibrary; } Other } - + match file_type(input) { Object => { - let file = fs::File::open(input) - .map_err(|e| format!("Error opening {input}: {e}"))?; + let file = + fs::File::open(input).map_err(|e| format!("Couldn't open {input}: {e}"))?; let mut file = io::BufReader::new(file); self.add_object(input, &mut file) - .map_err(|e| format!("Error processing object file {input}: {e}")) - }, - DynamicLibrary => { - self.add_library(input) - .map_err(|e| format!("Error processing library file {input}: {e}")) - }, - Other => { - Err(format!("Unrecognized file type: {input}")) + .map_err(|e| format!("Failed to process object file {input}: {e}")) } + DynamicLibrary => self + .add_library(input) + .map_err(|e| format!("Failed to process library file {input}: {e}")), + Other => Err(format!("Unrecognized file type: {input}")), } } - // we don't want to link unused symbols. // we start by calling this on the entry function, then it recursively calls itself for each symbol used. fn add_data_for_symbol( @@ -903,13 +861,12 @@ impl Linker { return Ok(()); } - if let Some(info) = self.symbols.get_info_from_id(id) { - if let Some(SymbolValue::Data(d)) = &info.value { - // set address - self.symbol_data_offsets.insert(id, data.len() as u64); - // add data - data.extend(d); - } + let info = self.symbols.get_info_from_id(id); + if let SymbolValue::Data(d) = &info.value { + // set address + self.symbol_data_offsets.insert(id, data.len() as u64); + // add data + data.extend(d); } for reference in symbol_graph.get(&id).unwrap_or(&vec![]) { @@ -919,11 +876,11 @@ impl Linker { Ok(()) } - pub fn link<T: Write + Seek>(mut self, out: T, entry: &str) -> LinkResult<()> { + pub fn link(mut self, out: impl Write + Seek, entry: &str) -> LinkResult<()> { let mut symbol_graph = SymbolGraph::with_capacity(self.symbols.count()); - + let relocations = mem::take(&mut self.relocations); - + // compute symbol graph for rel in relocations.iter() { use std::collections::hash_map::Entry; @@ -949,7 +906,7 @@ impl Linker { for lib in self.libraries.iter() { exec.add_lib(lib); } - + self.data_addr = exec.data_addr(); let entry_name_id = self @@ -976,26 +933,31 @@ impl Linker { exec.write(&data, out) } - + /// "easy" linking API. - pub fn link_to_file<P: AsRef<path::Path>>(self, path: P, entry: &str) -> Result<(), String> { + pub fn link_to_file(self, path: impl AsRef<path::Path>, entry: &str) -> Result<(), String> { let path = path.as_ref(); let mut out_options = fs::OpenOptions::new(); - out_options - .write(true) - .create(true) - .truncate(true); + out_options.write(true).create(true).truncate(true); #[cfg(unix)] { use std::os::unix::fs::OpenOptionsExt; out_options.mode(0o755); } - - let output = out_options.open(path) + + let output = out_options + .open(path) .map_err(|e| format!("Error opening output file {}: {e}", path.to_string_lossy()))?; let mut output = io::BufWriter::new(output); - + self.link(&mut output, entry) .map_err(|e| format!("Error linking {}: {e}", path.to_string_lossy())) } } + +impl<'a> Default for Linker<'a> { + /// mostly so clippy doesn't complain + fn default() -> Self { + Self::new() + } +} diff --git a/src/main.rs b/src/main.rs index bae6d71..c6c45a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,8 @@ use clap::Parser; 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; +pub mod linker; mod util; -mod linker; #[derive(Parser, Debug)] struct Args { @@ -29,21 +29,29 @@ struct Args { entry: String, /// :3 #[arg(long = "nya")] - nya: bool + nya: bool, } fn main_() -> Result<(), String> { let args = Args::parse(); - + if args.nya { println!("hai uwu ^_^"); return Ok(()); } - + let inputs = &args.inputs; - + let mut linker = linker::Linker::new(); - + + let warning_handler = |w| { + // termcolor is a goddamn nightmare to use + // I DONT FUCKING CARE IF WRITING TO STDOUT FAILS + // DONT MAKE ME UNWRAP EVERYTHING + eprintln!("\x1b[93mwarning:\x1b[0m {w}"); + }; + linker.set_warning_handler(warning_handler); + if inputs.is_empty() { if cfg!(debug_assertions) { // ease of use when debugging @@ -53,7 +61,6 @@ fn main_() -> Result<(), String> { } } - if !args.no_std_lib { linker.add_input("libc.so.6")?; } @@ -61,13 +68,12 @@ fn main_() -> Result<(), String> { for input in inputs.iter() { linker.add_input(input)?; } - + linker.link_to_file(&args.output, &args.entry) } fn main() { if let Err(e) = main_() { - eprintln!("{e}"); + eprintln!("\x1b[91merror:\x1b[0m {e}"); } } - |