summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-11-06 16:05:10 -0500
committerpommicket <pommicket@gmail.com>2022-11-06 16:05:10 -0500
commit18a8c0d9e4d8241bd06548b892947a51a2833f40 (patch)
treec0b682f1bd78a568d8b05a38a79f8107c9833495 /src
parentf5f2bae2a43a8508235e5635d3d19d9e488bc029 (diff)
cleaning up and some documentatino
Diffstat (limited to 'src')
-rw-r--r--src/elf.rs120
-rw-r--r--src/linker.rs258
-rw-r--r--src/main.rs26
3 files changed, 183 insertions, 221 deletions
diff --git a/src/elf.rs b/src/elf.rs
index 033910a..4fe2759 100644
--- a/src/elf.rs
+++ b/src/elf.rs
@@ -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(&section[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}");
}
}
-