use gimli::{
write::{EndianVec, Writer},
DebugStrOffsetsBase, DebugStrOffsetsIndex, DwarfFileType, Encoding, EndianSlice, Format,
Section,
};
use hashbrown::HashMap;
use tracing::debug;
use crate::{
error::{Error, Result},
ext::PackageFormatExt,
};
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) struct PackageStringOffset(usize);
pub(crate) struct PackageStringTable {
data: Vec<u8>,
strings: HashMap<Vec<u8>, PackageStringOffset>,
}
impl PackageStringTable {
pub(crate) fn new() -> Self {
Self { data: Vec::new(), strings: HashMap::new() }
}
pub(crate) fn get_or_insert(&mut self, bytes: &[u8]) -> PackageStringOffset {
debug_assert!(!bytes.contains(&0));
if let Some(offset) = self.strings.get(bytes) {
return *offset;
}
let offset = PackageStringOffset(self.data.len());
self.strings.insert(bytes.into(), offset);
self.data.extend_from_slice(bytes);
self.data.push(0);
offset
}
pub(crate) fn remap_str_offsets_section<E: gimli::Endianity>(
&mut self,
debug_str: gimli::DebugStr<EndianSlice<E>>,
debug_str_offsets: gimli::DebugStrOffsets<EndianSlice<E>>,
section_size: u64,
endian: E,
encoding: Encoding,
) -> Result<EndianVec<E>> {
let entry_size = match encoding.format {
Format::Dwarf32 => 4,
Format::Dwarf64 => 8,
};
self.data.reserve(debug_str.reader().len());
let mut data = EndianVec::new(endian);
let base: gimli::DebugStrOffsetsBase<usize> =
DebugStrOffsetsBase::default_for_encoding_and_file(encoding, DwarfFileType::Dwo);
if encoding.is_std_dwarf_package_format() {
match encoding.format {
Format::Dwarf32 => {
data.write_u32(
(section_size - 8)
.try_into()
.expect("section size w/out header larger than u32"),
)?;
}
Format::Dwarf64 => {
data.write_u32(u32::MAX)?;
data.write_u64(section_size - 16)?;
}
};
data.write_u16(5)?;
data.write_u16(0)?;
}
debug!(?base);
let base_offset: u64 = base.0.try_into().expect("base offset larger than u64");
let num_elements = (section_size - base_offset) / entry_size;
debug!(?section_size, ?base_offset, ?num_elements);
for i in 0..num_elements {
let dwo_index = DebugStrOffsetsIndex(i as usize);
let dwo_offset = debug_str_offsets
.get_str_offset(encoding.format, base, dwo_index)
.map_err(|e| Error::OffsetAtIndex(e, i))?;
let dwo_str =
debug_str.get_str(dwo_offset).map_err(|e| Error::StrAtOffset(e, dwo_offset.0))?;
let dwp_offset = self.get_or_insert(&dwo_str);
match encoding.format {
Format::Dwarf32 => {
let dwp_offset =
dwp_offset.0.try_into().expect("string offset larger than u32");
data.write_u32(dwp_offset)?;
}
Format::Dwarf64 => {
let dwp_offset =
dwp_offset.0.try_into().expect("string offset larger than u64");
data.write_u64(dwp_offset)?;
}
}
}
Ok(data)
}
pub(crate) fn finish(self) -> Vec<u8> {
self.data
}
}