use std::str::FromStr;
use debugid::DebugId;
use object::{FileFlags, Object, ObjectSection};
use uuid::Uuid;
pub(crate) fn code_id_for_object<'data>(obj: &impl Object<'data>) -> Option<CodeId> {
if let Ok(Some(build_id)) = obj.build_id() {
return Some(CodeId::ElfBuildId(ElfBuildId::from_bytes(build_id)));
}
None
}
pub(crate) fn debug_id_for_object<'data>(obj: &impl Object<'data>) -> Option<DebugId> {
if let Ok(Some(build_id)) = obj.build_id() {
return Some(DebugId::from_identifier(build_id, obj.is_little_endian()));
}
let text_section = obj
.section_by_name(".text")
.or_else(|| obj.section_by_name("text"));
if let Some(section) = text_section {
let data_len = section.size().min(4096);
if let Ok(Some(first_page_data)) = section.data_range(section.address(), data_len) {
return Some(DebugId::from_text_first_page(
first_page_data,
obj.is_little_endian(),
));
}
}
None
}
pub(crate) fn relative_address_base<'data>(obj: &impl Object<'data>) -> u64 {
use object::read::ObjectSegment;
if let FileFlags::Elf { .. } = obj.flags() {
if let Some(first_segment) = obj.segments().next() {
return first_segment.address();
}
}
obj.relative_address_base()
}
trait DebugIdExt {
fn from_identifier(identifier: &[u8], little_endian: bool) -> Self;
fn from_text_first_page(text_first_page: &[u8], little_endian: bool) -> Self;
}
impl DebugIdExt for DebugId {
fn from_identifier(identifier: &[u8], little_endian: bool) -> Self {
let mut d = [0u8; 16];
let shared_len = identifier.len().min(d.len());
d[0..shared_len].copy_from_slice(&identifier[0..shared_len]);
let (d1, d2, d3) = if little_endian {
(
u32::from_le_bytes([d[0], d[1], d[2], d[3]]),
u16::from_le_bytes([d[4], d[5]]),
u16::from_le_bytes([d[6], d[7]]),
)
} else {
(
u32::from_be_bytes([d[0], d[1], d[2], d[3]]),
u16::from_be_bytes([d[4], d[5]]),
u16::from_be_bytes([d[6], d[7]]),
)
};
let uuid = Uuid::from_fields(d1, d2, d3, d[8..16].try_into().unwrap());
DebugId::from_uuid(uuid)
}
fn from_text_first_page(text_first_page: &[u8], little_endian: bool) -> Self {
const UUID_SIZE: usize = 16;
const PAGE_SIZE: usize = 4096;
let mut hash = [0; UUID_SIZE];
for (i, byte) in text_first_page.iter().cloned().take(PAGE_SIZE).enumerate() {
hash[i % UUID_SIZE] ^= byte;
}
DebugId::from_identifier(&hash, little_endian)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) enum CodeId {
ElfBuildId(ElfBuildId),
}
impl FromStr for CodeId {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(CodeId::ElfBuildId(ElfBuildId::from_str(s)?))
}
}
impl std::fmt::Display for CodeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CodeId::ElfBuildId(elf) => std::fmt::Display::fmt(elf, f),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) struct ElfBuildId(Vec<u8>);
impl ElfBuildId {
fn from_bytes(bytes: &[u8]) -> Self {
Self(bytes.to_owned())
}
}
impl FromStr for ElfBuildId {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let byte_count = s.len() / 2;
let mut bytes = Vec::with_capacity(byte_count);
for i in 0..byte_count {
let hex_byte = &s[i * 2..i * 2 + 2];
let b = u8::from_str_radix(hex_byte, 16).map_err(|_| ())?;
bytes.push(b);
}
Ok(Self(bytes))
}
}
impl std::fmt::Display for ElfBuildId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for byte in &self.0 {
f.write_fmt(format_args!("{byte:02x}"))?;
}
Ok(())
}
}