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