use gimli::{
write::{EndianVec, Writer},
DebugStrOffsetsBase, DebugStrOffsetsIndex, DwarfFileType, Encoding, EndianSlice, Format,
Section,
};
use hashbrown::HashMap;
use itertools::Either;
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>>,
endian: E,
encoding: Encoding,
filter: Option<&std::collections::BTreeSet<u64>>,
) -> 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);
let num_elements = (debug_str_offsets.reader().len() - base.0) as u64 / entry_size;
let output_count =
filter.map_or(num_elements, |set| set.range(..num_elements).count() as u64);
if encoding.is_std_dwarf_package_format() {
let payload = 4 + output_count * entry_size;
match encoding.format {
Format::Dwarf32 => {
data.write_u32(
payload.try_into().expect("section size w/out header larger than u32"),
)?;
}
Format::Dwarf64 => {
data.write_u32(u32::MAX)?;
data.write_u64(payload)?;
}
};
data.write_u16(5)?;
data.write_u16(0)?;
}
debug!(?base);
let indices = filter.map_or(Either::Right(0..num_elements), |set| {
Either::Left(set.range(..num_elements).copied())
});
for i in indices {
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
}
}