use std::borrow::Cow;
use super::input::{DebugObject, Malformed, Reader, Section};
#[derive(Clone, Debug)]
pub(crate) struct NameEntry<'a> {
pub name: Cow<'a, [u8]>,
pub hash: u32,
pub value: u32,
}
#[must_use]
pub(crate) fn gdb_hash(name: &[u8]) -> u32 {
let mut h = 0u32;
for &c in name {
h = h
.wrapping_mul(67)
.wrapping_add(u32::from(c.to_ascii_lowercase()))
.wrapping_sub(113);
}
h
}
pub(crate) fn pub_sections<'a, F: crate::elf::read::ElfFormat>(
obj: &DebugObject<'_, 'a, F>,
cu_offsets: &[u64],
out: &mut Vec<NameEntry<'a>>,
problems: &mut Vec<(u32, Malformed)>,
) {
for section in [&obj.gnu_pubnames, &obj.gnu_pubtypes].into_iter().flatten() {
if let Err(e) = pub_section(obj, section, cu_offsets, out) {
problems.push((section.index, e));
}
}
}
fn pub_section<'a, F: crate::elf::read::ElfFormat>(
obj: &DebugObject<'_, 'a, F>,
section: &Section<'a, F>,
cu_offsets: &[u64],
out: &mut Vec<NameEntry<'a>>,
) -> Result<(), Malformed> {
let data = section.data;
let mut offset = 0usize;
while offset < data.len() {
let mut r = Reader::at(data, offset);
let (length, offset_size) = r.initial_length()?;
let next = usize::try_from(length)
.ok()
.and_then(|l| r.pos().checked_add(l))
.ok_or_else(|| r.error("name set length"))?;
let set = data.get(..next.min(data.len())).unwrap_or_default();
let mut r = Reader::at(set, r.pos());
let _version = r.u16()?;
let pos = r.pos();
let raw = r.uint(offset_size)?;
let (unit_offset, _) = obj.relocate(section, pos, raw);
let _unit_size = r.uint(offset_size)?;
let cu = cu_offsets.partition_point(|&o| o < unit_offset);
let cu = u32::try_from(cu).unwrap_or(u32::MAX) & 0x00ff_ffff;
loop {
let die = r.uint(offset_size)?;
if die == 0 {
break;
}
let descriptor = r.u8()?;
let name = r.cstr()?;
out.push(NameEntry {
name: Cow::Borrowed(name),
hash: gdb_hash(name),
value: (u32::from(descriptor & 0xf0) << 24) | cu,
});
}
if next > data.len() {
return Err(r.error("name set length (past the end of the section)"));
}
offset = next;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gdb_hash_matches_gdb() {
assert_eq!(gdb_hash(b""), 0);
assert_eq!(gdb_hash(b"a"), 97u32.wrapping_sub(113));
assert_eq!(gdb_hash(b"Main"), gdb_hash(b"main"));
}
}