#![deny(clippy::arithmetic_side_effects)]
use crate::elf::read::consts::{STB_WEAK, STT_GNU_IFUNC, STT_TLS};
use crate::elf::read::{RawSymbol, SectionIndex};
use crate::ids::{FileId, SectionId, SymbolId};
use crate::symbols::{DefinitionKind, Resolution, SymbolTable};
use super::inputs::ElfInput;
use super::sections::Sections;
pub const LINKER_FILE: FileId = FileId::from_u32(u32::MAX - 1);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Def {
Section {
file: usize,
section: u32,
value: u64,
},
Absolute(u64),
Common(SymbolId),
Linker(SymbolId),
Shared(SymbolId),
Undefined {
weak: bool,
},
}
#[derive(Clone, Copy, Debug)]
pub struct Target {
pub global: Option<SymbolId>,
pub def: Def,
pub raw: Option<RawSymbol>,
}
impl Target {
#[must_use]
pub fn is_ifunc(&self) -> bool {
self.raw.is_some_and(|raw| raw.kind() == STT_GNU_IFUNC)
&& matches!(self.def, Def::Section { .. })
}
#[must_use]
pub fn is_tls(&self) -> bool {
self.raw.is_some_and(|raw| raw.kind() == STT_TLS)
}
#[must_use]
pub fn is_section_symbol(&self) -> bool {
self.raw
.is_some_and(|raw| raw.kind() == crate::elf::read::consts::STT_SECTION)
}
}
#[derive(Clone, Copy)]
pub struct Refs<'r, 'a, F: crate::elf::read::ElfFormat = crate::elf::read::Elf64Le> {
pub files: &'r [ElfInput<'a, F>],
pub symbols: &'r SymbolTable<'a>,
pub resolution: &'r Resolution<'a>,
pub sections: &'r Sections,
}
impl<'a, F: crate::elf::read::ElfFormat> Refs<'_, 'a, F> {
#[inline]
#[must_use]
pub fn global_id(&self, file: usize, index: usize) -> Option<SymbolId> {
let object = self.files.get(file)?.object.as_ref()?;
let local = index.checked_sub(object.first_global)?;
self.resolution
.symbol_ids(FileId::new(file))
.get(local)
.copied()
}
#[inline(always)]
#[must_use]
pub fn target(&self, file: usize, index: usize) -> Option<Target> {
let object = self.files.get(file)?.object.as_ref()?;
let symbols = object.elf.symbols();
let raw = symbols.get_raw(index)?;
if index < object.first_global {
let def = local_def(file, index, &raw, symbols)?;
return Some(Target {
global: None,
def,
raw: Some(raw),
});
}
let id = self.global_id(file, index)?;
Some(self.global_target(id, raw.binding() == STB_WEAK))
}
#[inline(always)]
#[must_use]
pub fn global_target(&self, id: SymbolId, weak: bool) -> Target {
let def = self.symbols.definition(id);
let undefined = Target {
global: Some(id),
def: Def::Undefined { weak },
raw: None,
};
match def.kind {
DefinitionKind::Undefined | DefinitionKind::Lazy => undefined,
DefinitionKind::Shared => {
let raw = self
.files
.get(def.file.index())
.and_then(|f| f.shared.as_ref())
.and_then(|s| {
let index = *s.symbols.get(def.index as usize)?;
s.elf.symbols().get_raw(index as usize)
});
Target {
global: Some(id),
def: Def::Shared(id),
raw,
}
}
DefinitionKind::Common => Target {
global: Some(id),
def: Def::Common(id),
raw: None,
},
DefinitionKind::Regular | DefinitionKind::Weak => {
if def.file == LINKER_FILE {
return Target {
global: Some(id),
def: Def::Linker(id),
raw: None,
};
}
let file = def.file.index();
let Some(object) = self.files.get(file).and_then(|f| f.object.as_ref()) else {
return Target {
global: Some(id),
def: Def::Linker(id),
raw: None,
};
};
let symbols = object.elf.symbols();
let Some(index) = (def.index as usize).checked_add(object.first_global) else {
return undefined;
};
let Some(raw) = symbols.get_raw(index) else {
return undefined;
};
match local_def(file, index, &raw, symbols) {
Some(d) => Target {
global: Some(id),
def: d,
raw: Some(raw),
},
None => undefined,
}
}
}
}
#[must_use]
pub fn symbol_name(&self, file: usize, symbol: u32) -> Option<String> {
let object = self.files.get(file)?.object.as_ref()?;
let symbol = object.elf.symbols().get(symbol as usize).ok()?;
Some(String::from_utf8_lossy(symbol.name).into_owned())
}
#[must_use]
pub fn target_section(&self, target: &Target) -> Option<SectionId> {
match target.def {
Def::Section { file, section, .. } => self.sections.id(file, section),
_ => None,
}
}
}
#[inline(always)]
fn local_def<F: crate::elf::read::ElfFormat>(
file: usize,
index: usize,
raw: &RawSymbol,
symbols: &crate::elf::read::SymbolTable<'_, F>,
) -> Option<Def> {
Some(match symbols.section_of(index, raw)? {
SectionIndex::Section(section) => Def::Section {
file,
section,
value: raw.st_value,
},
SectionIndex::Absolute => Def::Absolute(raw.st_value),
SectionIndex::Undefined => Def::Undefined {
weak: raw.binding() == STB_WEAK,
},
SectionIndex::Common | SectionIndex::Reserved(_) => Def::Absolute(0),
})
}