| 1 | use gimli::{ |
| 2 | write::{EndianVec, Writer}, |
| 3 | DebugStrOffsetsBase, DebugStrOffsetsIndex, DwarfFileType, Encoding, EndianSlice, Format, |
| 4 | Section, |
| 5 | }; |
| 6 | use hashbrown::HashMap; |
| 7 | use tracing::debug; |
| 8 | |
| 9 | use crate::{ |
| 10 | error::{Error, Result}, |
| 11 | ext::PackageFormatExt, |
| 12 | }; |
| 13 | |
| 14 | /// New-type'd offset into `.debug_str` section. |
| 15 | #[derive (Copy, Clone, Debug, Eq, Hash, PartialEq)] |
| 16 | pub(crate) struct PackageStringOffset(usize); |
| 17 | |
| 18 | /// DWARF packages need to merge the `.debug_str` sections of input DWARF objects. |
| 19 | /// `.debug_str_offsets` sections then need to be rebuilt with offsets into the new merged |
| 20 | /// `.debug_str` section and then concatenated (indices into each dwarf object's offset list will |
| 21 | /// therefore still refer to the same string). |
| 22 | /// |
| 23 | /// Gimli's `StringTable` produces a `.debug_str` section with a single `.debug_str_offsets` |
| 24 | /// section, but `PackageStringTable` accumulates a single `.debug_str` section and can be used to |
| 25 | /// produce multiple `.debug_str_offsets` sections (which will be concatenated) which all offset |
| 26 | /// into the same `.debug_str`. |
| 27 | pub(crate) struct PackageStringTable { |
| 28 | data: Vec<u8>, |
| 29 | strings: HashMap<Vec<u8>, PackageStringOffset>, |
| 30 | } |
| 31 | |
| 32 | impl PackageStringTable { |
| 33 | /// Create a new `PackageStringTable` with a given endianity. |
| 34 | pub(crate) fn new() -> Self { |
| 35 | Self { data: Vec::new(), strings: HashMap::new() } |
| 36 | } |
| 37 | |
| 38 | /// Insert a string into the string table and return its offset in the table. If the string is |
| 39 | /// already in the table, returns its offset. |
| 40 | pub(crate) fn get_or_insert(&mut self, bytes: &[u8]) -> PackageStringOffset { |
| 41 | debug_assert!(!bytes.contains(&0)); |
| 42 | if let Some(offset) = self.strings.get(bytes) { |
| 43 | return *offset; |
| 44 | } |
| 45 | |
| 46 | // Keep track of the offset for this string, it might be referenced by the next compilation |
| 47 | // unit too. |
| 48 | let offset = PackageStringOffset(self.data.len()); |
| 49 | self.strings.insert(bytes.into(), offset); |
| 50 | |
| 51 | // Insert into the string table. |
| 52 | self.data.extend_from_slice(bytes); |
| 53 | self.data.push(0); |
| 54 | |
| 55 | offset |
| 56 | } |
| 57 | |
| 58 | /// Adds strings from input `.debug_str_offsets` and `.debug_str` into the string table, returns |
| 59 | /// data for a equivalent `.debug_str_offsets` section with offsets pointing into the new |
| 60 | /// `.debug_str` section. |
| 61 | pub(crate) fn remap_str_offsets_section<E: gimli::Endianity>( |
| 62 | &mut self, |
| 63 | debug_str: gimli::DebugStr<EndianSlice<E>>, |
| 64 | debug_str_offsets: gimli::DebugStrOffsets<EndianSlice<E>>, |
| 65 | section_size: u64, |
| 66 | endian: E, |
| 67 | encoding: Encoding, |
| 68 | ) -> Result<EndianVec<E>> { |
| 69 | let entry_size = match encoding.format { |
| 70 | Format::Dwarf32 => 4, |
| 71 | Format::Dwarf64 => 8, |
| 72 | }; |
| 73 | |
| 74 | // Reduce the number of allocations needed. |
| 75 | self.data.reserve(debug_str.reader().len()); |
| 76 | |
| 77 | let mut data = EndianVec::new(endian); |
| 78 | |
| 79 | // `DebugStrOffsetsBase` knows to skip past the header with DWARF 5. |
| 80 | let base: gimli::DebugStrOffsetsBase<usize> = |
| 81 | DebugStrOffsetsBase::default_for_encoding_and_file(encoding, DwarfFileType::Dwo); |
| 82 | |
| 83 | if encoding.is_std_dwarf_package_format() { |
| 84 | match encoding.format { |
| 85 | Format::Dwarf32 => { |
| 86 | // Unit length (4 bytes): size of the offsets section without this |
| 87 | // header (8 bytes total). |
| 88 | data.write_u32( |
| 89 | (section_size - 8) |
| 90 | .try_into() |
| 91 | .expect("section size w/out header larger than u32" ), |
| 92 | )?; |
| 93 | } |
| 94 | Format::Dwarf64 => { |
| 95 | // Unit length (4 bytes then 8 bytes): size of the offsets section without |
| 96 | // this header (16 bytes total). |
| 97 | data.write_u32(u32::MAX)?; |
| 98 | data.write_u64(section_size - 16)?; |
| 99 | } |
| 100 | }; |
| 101 | // Version (2 bytes): DWARF 5 |
| 102 | data.write_u16(5)?; |
| 103 | // Reserved padding (2 bytes) |
| 104 | data.write_u16(0)?; |
| 105 | } |
| 106 | debug!(?base); |
| 107 | |
| 108 | let base_offset: u64 = base.0.try_into().expect("base offset larger than u64" ); |
| 109 | let num_elements = (section_size - base_offset) / entry_size; |
| 110 | debug!(?section_size, ?base_offset, ?num_elements); |
| 111 | |
| 112 | for i in 0..num_elements { |
| 113 | let dwo_index = DebugStrOffsetsIndex(i as usize); |
| 114 | let dwo_offset = debug_str_offsets |
| 115 | .get_str_offset(encoding.format, base, dwo_index) |
| 116 | .map_err(|e| Error::OffsetAtIndex(e, i))?; |
| 117 | let dwo_str = |
| 118 | debug_str.get_str(dwo_offset).map_err(|e| Error::StrAtOffset(e, dwo_offset.0))?; |
| 119 | |
| 120 | let dwp_offset = self.get_or_insert(&dwo_str); |
| 121 | |
| 122 | match encoding.format { |
| 123 | Format::Dwarf32 => { |
| 124 | let dwp_offset = |
| 125 | dwp_offset.0.try_into().expect("string offset larger than u32" ); |
| 126 | data.write_u32(dwp_offset)?; |
| 127 | } |
| 128 | Format::Dwarf64 => { |
| 129 | let dwp_offset = |
| 130 | dwp_offset.0.try_into().expect("string offset larger than u64" ); |
| 131 | data.write_u64(dwp_offset)?; |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | Ok(data) |
| 137 | } |
| 138 | |
| 139 | /// Returns the accumulated `.debug_str` section data |
| 140 | pub(crate) fn finish(self) -> Vec<u8> { |
| 141 | self.data |
| 142 | } |
| 143 | } |
| 144 | |