1 | use alloc::vec::Vec; |
2 | use indexmap::IndexSet; |
3 | use std::ops::{Deref, DerefMut}; |
4 | |
5 | use crate::common::{Encoding, LocationListsOffset, SectionId}; |
6 | use crate::write::{ |
7 | Address, BaseId, DebugInfoReference, Error, Expression, Result, Section, Sections, UnitOffsets, |
8 | Writer, |
9 | }; |
10 | |
11 | define_section!( |
12 | DebugLoc, |
13 | LocationListsOffset, |
14 | "A writable `.debug_loc` section." |
15 | ); |
16 | define_section!( |
17 | DebugLocLists, |
18 | LocationListsOffset, |
19 | "A writable `.debug_loclists` section." |
20 | ); |
21 | |
22 | define_offsets!( |
23 | LocationListOffsets: LocationListId => LocationListsOffset, |
24 | "The section offsets of a series of location lists within the `.debug_loc` or `.debug_loclists` sections." |
25 | ); |
26 | |
27 | define_id!( |
28 | LocationListId, |
29 | "An identifier for a location list in a `LocationListTable`." |
30 | ); |
31 | |
32 | /// A table of location lists that will be stored in a `.debug_loc` or `.debug_loclists` section. |
33 | #[derive (Debug, Default)] |
34 | pub struct LocationListTable { |
35 | base_id: BaseId, |
36 | locations: IndexSet<LocationList>, |
37 | } |
38 | |
39 | impl LocationListTable { |
40 | /// Add a location list to the table. |
41 | pub fn add(&mut self, loc_list: LocationList) -> LocationListId { |
42 | let (index, _) = self.locations.insert_full(loc_list); |
43 | LocationListId::new(self.base_id, index) |
44 | } |
45 | |
46 | /// Get a reference to a location list. |
47 | /// |
48 | /// # Panics |
49 | /// |
50 | /// Panics if `id` is invalid. |
51 | #[inline ] |
52 | pub fn get(&self, id: LocationListId) -> &LocationList { |
53 | debug_assert_eq!(self.base_id, id.base_id); |
54 | &self.locations[id.index] |
55 | } |
56 | |
57 | /// Write the location list table to the appropriate section for the given DWARF version. |
58 | pub(crate) fn write<W: Writer>( |
59 | &self, |
60 | sections: &mut Sections<W>, |
61 | encoding: Encoding, |
62 | unit_offsets: Option<&UnitOffsets>, |
63 | ) -> Result<LocationListOffsets> { |
64 | if self.locations.is_empty() { |
65 | return Ok(LocationListOffsets::none()); |
66 | } |
67 | |
68 | match encoding.version { |
69 | 2..=4 => self.write_loc( |
70 | &mut sections.debug_loc, |
71 | &mut sections.debug_loc_refs, |
72 | encoding, |
73 | unit_offsets, |
74 | ), |
75 | 5 => self.write_loclists( |
76 | &mut sections.debug_loclists, |
77 | &mut sections.debug_loclists_refs, |
78 | encoding, |
79 | unit_offsets, |
80 | ), |
81 | _ => Err(Error::UnsupportedVersion(encoding.version)), |
82 | } |
83 | } |
84 | |
85 | /// Write the location list table to the `.debug_loc` section. |
86 | fn write_loc<W: Writer>( |
87 | &self, |
88 | w: &mut DebugLoc<W>, |
89 | refs: &mut Vec<DebugInfoReference>, |
90 | encoding: Encoding, |
91 | unit_offsets: Option<&UnitOffsets>, |
92 | ) -> Result<LocationListOffsets> { |
93 | let address_size = encoding.address_size; |
94 | let mut offsets = Vec::new(); |
95 | for loc_list in self.locations.iter() { |
96 | offsets.push(w.offset()); |
97 | for loc in &loc_list.0 { |
98 | // Note that we must ensure none of the ranges have both begin == 0 and end == 0. |
99 | // We do this by ensuring that begin != end, which is a bit more restrictive |
100 | // than required, but still seems reasonable. |
101 | match *loc { |
102 | Location::BaseAddress { address } => { |
103 | let marker = !0 >> (64 - address_size * 8); |
104 | w.write_udata(marker, address_size)?; |
105 | w.write_address(address, address_size)?; |
106 | } |
107 | Location::OffsetPair { |
108 | begin, |
109 | end, |
110 | ref data, |
111 | } => { |
112 | if begin == end { |
113 | return Err(Error::InvalidRange); |
114 | } |
115 | w.write_udata(begin, address_size)?; |
116 | w.write_udata(end, address_size)?; |
117 | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
118 | } |
119 | Location::StartEnd { |
120 | begin, |
121 | end, |
122 | ref data, |
123 | } => { |
124 | if begin == end { |
125 | return Err(Error::InvalidRange); |
126 | } |
127 | w.write_address(begin, address_size)?; |
128 | w.write_address(end, address_size)?; |
129 | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
130 | } |
131 | Location::StartLength { |
132 | begin, |
133 | length, |
134 | ref data, |
135 | } => { |
136 | let end = match begin { |
137 | Address::Constant(begin) => Address::Constant(begin + length), |
138 | Address::Symbol { symbol, addend } => Address::Symbol { |
139 | symbol, |
140 | addend: addend + length as i64, |
141 | }, |
142 | }; |
143 | if begin == end { |
144 | return Err(Error::InvalidRange); |
145 | } |
146 | w.write_address(begin, address_size)?; |
147 | w.write_address(end, address_size)?; |
148 | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
149 | } |
150 | Location::DefaultLocation { .. } => { |
151 | return Err(Error::InvalidRange); |
152 | } |
153 | } |
154 | } |
155 | w.write_udata(0, address_size)?; |
156 | w.write_udata(0, address_size)?; |
157 | } |
158 | Ok(LocationListOffsets { |
159 | base_id: self.base_id, |
160 | offsets, |
161 | }) |
162 | } |
163 | |
164 | /// Write the location list table to the `.debug_loclists` section. |
165 | fn write_loclists<W: Writer>( |
166 | &self, |
167 | w: &mut DebugLocLists<W>, |
168 | refs: &mut Vec<DebugInfoReference>, |
169 | encoding: Encoding, |
170 | unit_offsets: Option<&UnitOffsets>, |
171 | ) -> Result<LocationListOffsets> { |
172 | let mut offsets = Vec::new(); |
173 | |
174 | if encoding.version != 5 { |
175 | return Err(Error::NeedVersion(5)); |
176 | } |
177 | |
178 | let length_offset = w.write_initial_length(encoding.format)?; |
179 | let length_base = w.len(); |
180 | |
181 | w.write_u16(encoding.version)?; |
182 | w.write_u8(encoding.address_size)?; |
183 | w.write_u8(0)?; // segment_selector_size |
184 | w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28) |
185 | // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list |
186 | |
187 | for loc_list in self.locations.iter() { |
188 | offsets.push(w.offset()); |
189 | for loc in &loc_list.0 { |
190 | match *loc { |
191 | Location::BaseAddress { address } => { |
192 | w.write_u8(crate::constants::DW_LLE_base_address.0)?; |
193 | w.write_address(address, encoding.address_size)?; |
194 | } |
195 | Location::OffsetPair { |
196 | begin, |
197 | end, |
198 | ref data, |
199 | } => { |
200 | w.write_u8(crate::constants::DW_LLE_offset_pair.0)?; |
201 | w.write_uleb128(begin)?; |
202 | w.write_uleb128(end)?; |
203 | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
204 | } |
205 | Location::StartEnd { |
206 | begin, |
207 | end, |
208 | ref data, |
209 | } => { |
210 | w.write_u8(crate::constants::DW_LLE_start_end.0)?; |
211 | w.write_address(begin, encoding.address_size)?; |
212 | w.write_address(end, encoding.address_size)?; |
213 | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
214 | } |
215 | Location::StartLength { |
216 | begin, |
217 | length, |
218 | ref data, |
219 | } => { |
220 | w.write_u8(crate::constants::DW_LLE_start_length.0)?; |
221 | w.write_address(begin, encoding.address_size)?; |
222 | w.write_uleb128(length)?; |
223 | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
224 | } |
225 | Location::DefaultLocation { ref data } => { |
226 | w.write_u8(crate::constants::DW_LLE_default_location.0)?; |
227 | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
228 | } |
229 | } |
230 | } |
231 | |
232 | w.write_u8(crate::constants::DW_LLE_end_of_list.0)?; |
233 | } |
234 | |
235 | let length = (w.len() - length_base) as u64; |
236 | w.write_initial_length_at(length_offset, length, encoding.format)?; |
237 | |
238 | Ok(LocationListOffsets { |
239 | base_id: self.base_id, |
240 | offsets, |
241 | }) |
242 | } |
243 | } |
244 | |
245 | /// A locations list that will be stored in a `.debug_loc` or `.debug_loclists` section. |
246 | #[derive (Clone, Debug, Eq, PartialEq, Hash)] |
247 | pub struct LocationList(pub Vec<Location>); |
248 | |
249 | /// A single location. |
250 | #[derive (Clone, Debug, Eq, PartialEq, Hash)] |
251 | pub enum Location { |
252 | /// DW_LLE_base_address |
253 | BaseAddress { |
254 | /// Base address. |
255 | address: Address, |
256 | }, |
257 | /// DW_LLE_offset_pair |
258 | OffsetPair { |
259 | /// Start of range relative to base address. |
260 | begin: u64, |
261 | /// End of range relative to base address. |
262 | end: u64, |
263 | /// Location description. |
264 | data: Expression, |
265 | }, |
266 | /// DW_LLE_start_end |
267 | StartEnd { |
268 | /// Start of range. |
269 | begin: Address, |
270 | /// End of range. |
271 | end: Address, |
272 | /// Location description. |
273 | data: Expression, |
274 | }, |
275 | /// DW_LLE_start_length |
276 | StartLength { |
277 | /// Start of range. |
278 | begin: Address, |
279 | /// Length of range. |
280 | length: u64, |
281 | /// Location description. |
282 | data: Expression, |
283 | }, |
284 | /// DW_LLE_default_location |
285 | DefaultLocation { |
286 | /// Location description. |
287 | data: Expression, |
288 | }, |
289 | } |
290 | |
291 | fn write_expression<W: Writer>( |
292 | w: &mut W, |
293 | refs: &mut Vec<DebugInfoReference>, |
294 | encoding: Encoding, |
295 | unit_offsets: Option<&UnitOffsets>, |
296 | val: &Expression, |
297 | ) -> Result<()> { |
298 | let size: u64 = val.size(encoding, unit_offsets) as u64; |
299 | if encoding.version <= 4 { |
300 | w.write_udata(val:size, size:2)?; |
301 | } else { |
302 | w.write_uleb128(val:size)?; |
303 | } |
304 | val.write(w, refs:Some(refs), encoding, unit_offsets)?; |
305 | Ok(()) |
306 | } |
307 | |
308 | #[cfg (feature = "read" )] |
309 | mod convert { |
310 | use super::*; |
311 | |
312 | use crate::read::{self, Reader}; |
313 | use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; |
314 | |
315 | impl LocationList { |
316 | /// Create a location list by reading the data from the give location list iter. |
317 | pub(crate) fn from<R: Reader<Offset = usize>>( |
318 | mut from: read::RawLocListIter<R>, |
319 | context: &ConvertUnitContext<'_, R>, |
320 | ) -> ConvertResult<Self> { |
321 | let mut have_base_address = context.base_address != Address::Constant(0); |
322 | let convert_address = |
323 | |x| (context.convert_address)(x).ok_or(ConvertError::InvalidAddress); |
324 | let convert_expression = |x| { |
325 | Expression::from( |
326 | x, |
327 | context.unit.encoding(), |
328 | Some(context.dwarf), |
329 | Some(context.unit), |
330 | Some(context.entry_ids), |
331 | context.convert_address, |
332 | ) |
333 | }; |
334 | let mut loc_list = Vec::new(); |
335 | while let Some(from_loc) = from.next()? { |
336 | let loc = match from_loc { |
337 | read::RawLocListEntry::AddressOrOffsetPair { begin, end, data } => { |
338 | // These were parsed as addresses, even if they are offsets. |
339 | let begin = convert_address(begin)?; |
340 | let end = convert_address(end)?; |
341 | let data = convert_expression(data)?; |
342 | match (begin, end) { |
343 | (Address::Constant(begin_offset), Address::Constant(end_offset)) => { |
344 | if have_base_address { |
345 | Location::OffsetPair { |
346 | begin: begin_offset, |
347 | end: end_offset, |
348 | data, |
349 | } |
350 | } else { |
351 | Location::StartEnd { begin, end, data } |
352 | } |
353 | } |
354 | _ => { |
355 | if have_base_address { |
356 | // At least one of begin/end is an address, but we also have |
357 | // a base address. Adding addresses is undefined. |
358 | return Err(ConvertError::InvalidRangeRelativeAddress); |
359 | } |
360 | Location::StartEnd { begin, end, data } |
361 | } |
362 | } |
363 | } |
364 | read::RawLocListEntry::BaseAddress { addr } => { |
365 | have_base_address = true; |
366 | let address = convert_address(addr)?; |
367 | Location::BaseAddress { address } |
368 | } |
369 | read::RawLocListEntry::BaseAddressx { addr } => { |
370 | have_base_address = true; |
371 | let address = convert_address(context.dwarf.address(context.unit, addr)?)?; |
372 | Location::BaseAddress { address } |
373 | } |
374 | read::RawLocListEntry::StartxEndx { begin, end, data } => { |
375 | let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; |
376 | let end = convert_address(context.dwarf.address(context.unit, end)?)?; |
377 | let data = convert_expression(data)?; |
378 | Location::StartEnd { begin, end, data } |
379 | } |
380 | read::RawLocListEntry::StartxLength { |
381 | begin, |
382 | length, |
383 | data, |
384 | } => { |
385 | let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; |
386 | let data = convert_expression(data)?; |
387 | Location::StartLength { |
388 | begin, |
389 | length, |
390 | data, |
391 | } |
392 | } |
393 | read::RawLocListEntry::OffsetPair { begin, end, data } => { |
394 | let data = convert_expression(data)?; |
395 | Location::OffsetPair { begin, end, data } |
396 | } |
397 | read::RawLocListEntry::StartEnd { begin, end, data } => { |
398 | let begin = convert_address(begin)?; |
399 | let end = convert_address(end)?; |
400 | let data = convert_expression(data)?; |
401 | Location::StartEnd { begin, end, data } |
402 | } |
403 | read::RawLocListEntry::StartLength { |
404 | begin, |
405 | length, |
406 | data, |
407 | } => { |
408 | let begin = convert_address(begin)?; |
409 | let data = convert_expression(data)?; |
410 | Location::StartLength { |
411 | begin, |
412 | length, |
413 | data, |
414 | } |
415 | } |
416 | read::RawLocListEntry::DefaultLocation { data } => { |
417 | let data = convert_expression(data)?; |
418 | Location::DefaultLocation { data } |
419 | } |
420 | }; |
421 | // In some cases, existing data may contain begin == end, filtering |
422 | // these out. |
423 | match loc { |
424 | Location::StartLength { length: 0, .. } => continue, |
425 | Location::StartEnd { begin, end, .. } if begin == end => continue, |
426 | Location::OffsetPair { begin, end, .. } if begin == end => continue, |
427 | _ => (), |
428 | } |
429 | loc_list.push(loc); |
430 | } |
431 | Ok(LocationList(loc_list)) |
432 | } |
433 | } |
434 | } |
435 | |
436 | #[cfg (test)] |
437 | #[cfg (feature = "read" )] |
438 | mod tests { |
439 | use super::*; |
440 | use crate::common::{ |
441 | DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, |
442 | DebugStrOffsetsBase, Format, |
443 | }; |
444 | use crate::read; |
445 | use crate::write::{ |
446 | ConvertUnitContext, EndianVec, LineStringTable, RangeListTable, StringTable, |
447 | }; |
448 | use crate::LittleEndian; |
449 | use std::collections::HashMap; |
450 | use std::sync::Arc; |
451 | |
452 | #[test ] |
453 | fn test_loc_list() { |
454 | let mut line_strings = LineStringTable::default(); |
455 | let mut strings = StringTable::default(); |
456 | let mut expression = Expression::new(); |
457 | expression.op_constu(0); |
458 | |
459 | for &version in &[2, 3, 4, 5] { |
460 | for &address_size in &[4, 8] { |
461 | for &format in &[Format::Dwarf32, Format::Dwarf64] { |
462 | let encoding = Encoding { |
463 | format, |
464 | version, |
465 | address_size, |
466 | }; |
467 | |
468 | let mut loc_list = LocationList(vec![ |
469 | Location::StartLength { |
470 | begin: Address::Constant(6666), |
471 | length: 7777, |
472 | data: expression.clone(), |
473 | }, |
474 | Location::StartEnd { |
475 | begin: Address::Constant(4444), |
476 | end: Address::Constant(5555), |
477 | data: expression.clone(), |
478 | }, |
479 | Location::BaseAddress { |
480 | address: Address::Constant(1111), |
481 | }, |
482 | Location::OffsetPair { |
483 | begin: 2222, |
484 | end: 3333, |
485 | data: expression.clone(), |
486 | }, |
487 | ]); |
488 | if version >= 5 { |
489 | loc_list.0.push(Location::DefaultLocation { |
490 | data: expression.clone(), |
491 | }); |
492 | } |
493 | |
494 | let mut locations = LocationListTable::default(); |
495 | let loc_list_id = locations.add(loc_list.clone()); |
496 | |
497 | let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
498 | let loc_list_offsets = locations.write(&mut sections, encoding, None).unwrap(); |
499 | assert!(sections.debug_loc_refs.is_empty()); |
500 | assert!(sections.debug_loclists_refs.is_empty()); |
501 | |
502 | let read_debug_loc = |
503 | read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); |
504 | let read_debug_loclists = |
505 | read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); |
506 | let read_loc = read::LocationLists::new(read_debug_loc, read_debug_loclists); |
507 | let offset = loc_list_offsets.get(loc_list_id); |
508 | let read_loc_list = read_loc.raw_locations(offset, encoding).unwrap(); |
509 | |
510 | let dwarf = read::Dwarf { |
511 | locations: read_loc, |
512 | ..Default::default() |
513 | }; |
514 | let unit = read::Unit { |
515 | header: read::UnitHeader::new( |
516 | encoding, |
517 | 0, |
518 | read::UnitType::Compilation, |
519 | DebugAbbrevOffset(0), |
520 | DebugInfoOffset(0).into(), |
521 | read::EndianSlice::default(), |
522 | ), |
523 | abbreviations: Arc::new(read::Abbreviations::default()), |
524 | name: None, |
525 | comp_dir: None, |
526 | low_pc: 0, |
527 | str_offsets_base: DebugStrOffsetsBase(0), |
528 | addr_base: DebugAddrBase(0), |
529 | loclists_base: DebugLocListsBase(0), |
530 | rnglists_base: DebugRngListsBase(0), |
531 | line_program: None, |
532 | dwo_id: None, |
533 | }; |
534 | let context = ConvertUnitContext { |
535 | dwarf: &dwarf, |
536 | unit: &unit, |
537 | line_strings: &mut line_strings, |
538 | strings: &mut strings, |
539 | ranges: &mut RangeListTable::default(), |
540 | locations: &mut locations, |
541 | convert_address: &|address| Some(Address::Constant(address)), |
542 | base_address: Address::Constant(0), |
543 | line_program_offset: None, |
544 | line_program_files: Vec::new(), |
545 | entry_ids: &HashMap::new(), |
546 | }; |
547 | let convert_loc_list = LocationList::from(read_loc_list, &context).unwrap(); |
548 | |
549 | if version <= 4 { |
550 | loc_list.0[0] = Location::StartEnd { |
551 | begin: Address::Constant(6666), |
552 | end: Address::Constant(6666 + 7777), |
553 | data: expression.clone(), |
554 | }; |
555 | } |
556 | assert_eq!(loc_list, convert_loc_list); |
557 | } |
558 | } |
559 | } |
560 | } |
561 | } |
562 | |