use std::borrow::Cow;
use super::reader::{DwarfError, DwarfResult};
use crate::debug::section::section_contents;
use crate::elf::read::consts::{
EM_386, EM_AARCH64, EM_ARM, EM_LOONGARCH, EM_MIPS, EM_PPC, EM_PPC64, EM_RISCV, EM_S390,
EM_SPARC, EM_SPARCV9, EM_X86_64,
};
use crate::elf::read::{ElfFormat, Endian, ObjectFile, Relocations, SectionIndex};
use crate::error::Result;
use crate::target::Endianness;
#[derive(Clone, Copy, Debug)]
struct Reloc {
offset: u64,
symbol: u32,
r_type: u32,
addend: i64,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Op {
Absolute,
Add,
Sub,
Ignore,
}
fn classify(machine: u16, r_type: u32) -> Op {
match machine {
EM_X86_64 => match r_type {
1 | 10 | 11 => Op::Absolute, _ => Op::Ignore,
},
EM_386 => match r_type {
1 => Op::Absolute, _ => Op::Ignore,
},
EM_AARCH64 => match r_type {
257 | 258 => Op::Absolute, _ => Op::Ignore,
},
EM_ARM => match r_type {
2 => Op::Absolute, _ => Op::Ignore,
},
EM_RISCV => match r_type {
1 | 2 | 54..=56 | 60 => Op::Absolute, 33..=36 => Op::Add, 37..=40 | 61 => Op::Sub, _ => Op::Ignore,
},
EM_LOONGARCH => match r_type {
1 | 2 => Op::Absolute, 47..=51 => Op::Add, 52..=56 => Op::Sub, _ => Op::Ignore,
},
EM_PPC64 => match r_type {
1 | 38 => Op::Absolute, _ => Op::Ignore,
},
EM_PPC => match r_type {
1 => Op::Absolute, _ => Op::Ignore,
},
EM_S390 => match r_type {
4 | 22 => Op::Absolute, _ => Op::Ignore,
},
EM_MIPS => match r_type {
2 | 18 => Op::Absolute, _ => Op::Ignore,
},
EM_SPARC | EM_SPARCV9 => match r_type {
3 | 23 | 32 | 54 => Op::Absolute, _ => Op::Ignore,
},
_ => Op::Absolute,
}
}
pub(super) struct Loaded<'a> {
pub(super) data: Cow<'a, [u8]>,
pub(super) file_offset: u64,
relocs: Vec<Reloc>,
rela: bool,
}
pub(super) struct Context<'a, F: ElfFormat> {
object: ObjectFile<'a, F>,
pub(super) big_endian: bool,
machine: u16,
sections: Vec<Option<Loaded<'a>>>,
pub(super) info: Vec<u32>,
pub(super) line: Vec<u32>,
debug_str: Option<u32>,
line_str: Option<u32>,
pub(super) str_offsets: Option<u32>,
abbrev: Option<u32>,
}
const NAMES: [&[u8]; 6] = [
b".debug_info",
b".debug_line",
b".debug_str",
b".debug_line_str",
b".debug_str_offsets",
b".debug_abbrev",
];
impl<'a, F: ElfFormat> Context<'a, F> {
pub(super) fn load(object: &ObjectFile<'a, F>) -> Result<Self> {
let count = object.section_count();
let mut sections: Vec<Option<Loaded<'a>>> = Vec::new();
sections.resize_with(count, || None);
let mut ctx = Self {
object: *object,
big_endian: <F::Endian as Endian>::ENDIANNESS == Endianness::Big,
machine: object.elf().header().e_machine,
sections,
info: Vec::new(),
line: Vec::new(),
debug_str: None,
line_str: None,
str_offsets: None,
abbrev: None,
};
let reloc_map = object.relocation_map()?;
for (index, header) in object.elf().enumerate_sections() {
let name = object.section_name(&header)?;
let name = crate::debug::section::decompressed_name(name);
let Some(which) = NAMES.iter().position(|n| **n == *name) else {
continue;
};
let data = section_contents(object, &header)?;
let mut relocs = Vec::new();
let mut rela = true;
let rel_index = reloc_map
.get(usize::try_from(index).unwrap_or(usize::MAX))
.copied()
.unwrap_or(0);
if rel_index != 0 {
let rel_header = object.section_header(rel_index)?;
if let Some(rel) = object.relocation_section(rel_index, &rel_header)? {
match rel.relocations {
Relocations::Rela(list) => {
relocs.extend(list.iter().map(|r| Reloc {
offset: r.offset,
symbol: r.symbol,
r_type: r.r_type,
addend: r.addend,
}));
}
Relocations::Rel(list) => {
rela = false;
relocs.extend(list.iter().map(|r| Reloc {
offset: r.offset,
symbol: r.symbol,
r_type: r.r_type,
addend: 0,
}));
}
}
}
}
relocs.sort_by_key(|r| r.offset);
if let Some(slot) = ctx
.sections
.get_mut(usize::try_from(index).unwrap_or(usize::MAX))
{
*slot = Some(Loaded {
data,
file_offset: header.sh_offset,
relocs,
rela,
});
}
match which {
0 => ctx.info.push(index),
1 => ctx.line.push(index),
2 => {
ctx.debug_str.get_or_insert(index);
}
3 => {
ctx.line_str.get_or_insert(index);
}
4 => {
ctx.str_offsets.get_or_insert(index);
}
_ => {
ctx.abbrev.get_or_insert(index);
}
}
}
Ok(ctx)
}
pub(super) fn section(&self, index: u32) -> Option<&Loaded<'a>> {
self.sections
.get(usize::try_from(index).ok()?)
.and_then(Option::as_ref)
}
pub(super) fn abbrev_section(&self, target: Option<u32>) -> Option<u32> {
target.or(self.abbrev)
}
pub(super) fn relocate(&self, index: u32, offset: usize, raw: u64) -> (u64, Option<u32>) {
let Some(section) = self.section(index) else {
return (raw, None);
};
let offset = u64::try_from(offset).unwrap_or(u64::MAX);
let start = section.relocs.partition_point(|r| r.offset < offset);
let mut value = raw;
let mut target = None;
for r in section.relocs.get(start..).unwrap_or_default() {
if r.offset != offset {
break;
}
let op = classify(self.machine, r.r_type);
if op == Op::Ignore {
continue;
}
let Some(sym) = self.object.symbols().get_raw(r.symbol as usize) else {
continue;
};
let sym_section = match self.object.symbols().section(r.symbol as usize, &sym) {
Ok(SectionIndex::Section(s)) => Some(s),
_ => None,
};
let addend = if section.rela { r.addend as u64 } else { raw };
let s_plus_a = sym.st_value.wrapping_add(addend);
match op {
Op::Absolute => {
value = s_plus_a;
if target.is_none() {
target = sym_section;
}
}
Op::Add => value = value.wrapping_add(s_plus_a),
Op::Sub => value = value.wrapping_sub(s_plus_a),
Op::Ignore => {}
}
}
(value, target)
}
pub(super) fn string_at(&self, index: u32, offset: u64) -> Option<&[u8]> {
let data = &self.section(index)?.data;
let rest = data.get(usize::try_from(offset).ok()?..)?;
let len = rest.iter().position(|&b| b == 0)?;
rest.get(..len)
}
pub(super) fn str_section(&self, target: Option<u32>) -> Option<u32> {
target.or(self.debug_str)
}
pub(super) fn line_str_section(&self, target: Option<u32>) -> Option<u32> {
target.or(self.line_str)
}
pub(super) fn error(&self, index: u32, error: DwarfError) -> crate::Error {
let base = self.section(index).map_or(0, |s| s.file_offset);
self.object.source().malformed(
base.saturating_add(u64::try_from(error.offset).unwrap_or(u64::MAX)),
error.what,
)
}
}
pub(super) fn read_relocated<F: ElfFormat>(
ctx: &Context<'_, F>,
index: u32,
reader: &mut super::reader::Reader<'_>,
size: usize,
) -> DwarfResult<(u64, Option<u32>)> {
let pos = reader.pos();
let raw = reader.uint(size)?;
Ok(ctx.relocate(index, pos, raw))
}