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 | |