Skip to content

Commit db32da1

Browse files
authored
Improve documentation for converting relocatable object files (#837)
This includes a restructure of the logic in the range list and location list conversion, but the result should be the same. Also add subprogram range list handling to convert example.
1 parent 6f93e39 commit db32da1

File tree

4 files changed

+103
-44
lines changed

4 files changed

+103
-44
lines changed

crates/examples/src/bin/convert.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,53 @@ fn need_entry<R: gimli::Reader<Offset = usize>>(
264264
if let Some(address) = entry.unit.attr_address(attr)? {
265265
return Ok(!is_tombstone_address(entry, address));
266266
}
267+
} else if let Some(attr) = entry.attr_value(gimli::DW_AT_ranges) {
268+
if let Some(offset) = entry.unit.attr_ranges_offset(attr)? {
269+
let mut base = if entry.unit.low_pc != 0 {
270+
// Have a base and it is valid.
271+
Some(true)
272+
} else {
273+
// Don't have a base.
274+
None
275+
};
276+
let mut ranges = entry.unit.raw_ranges(offset)?;
277+
while let Some(range) = ranges.next()? {
278+
match range {
279+
gimli::RawRngListEntry::AddressOrOffsetPair { begin, .. } => {
280+
if base == Some(true)
281+
|| (base.is_none() && !is_tombstone_address(entry, begin))
282+
{
283+
return Ok(true);
284+
}
285+
}
286+
gimli::RawRngListEntry::OffsetPair { .. } => {
287+
if base == Some(true) {
288+
return Ok(true);
289+
}
290+
}
291+
gimli::RawRngListEntry::BaseAddress { addr } => {
292+
base = Some(!is_tombstone_address(entry, addr));
293+
}
294+
gimli::RawRngListEntry::BaseAddressx { addr } => {
295+
let addr = entry.unit.address(addr)?;
296+
base = Some(!is_tombstone_address(entry, addr));
297+
}
298+
gimli::RawRngListEntry::StartEnd { begin, .. }
299+
| gimli::RawRngListEntry::StartLength { begin, .. } => {
300+
if !is_tombstone_address(entry, begin) {
301+
return Ok(true);
302+
}
303+
}
304+
gimli::RawRngListEntry::StartxEndx { begin, .. }
305+
| gimli::RawRngListEntry::StartxLength { begin, .. } => {
306+
let begin = entry.unit.address(begin)?;
307+
if !is_tombstone_address(entry, begin) {
308+
return Ok(true);
309+
}
310+
}
311+
}
312+
}
313+
}
267314
}
268315
}
269316
gimli::DW_TAG_variable => {

src/write/dwarf.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,20 @@ pub(crate) mod convert {
126126
impl Dwarf {
127127
/// Create a `write::Dwarf` by converting a `read::Dwarf`.
128128
///
129-
/// `convert_address` is a function to convert read addresses into the `Address`
130-
/// type. For non-relocatable addresses, this function may simply return
131-
/// `Address::Constant(address)`. For relocatable addresses, it is the caller's
132-
/// responsibility to determine the symbol and addend corresponding to the address
133-
/// and return `Address::Symbol { symbol, addend }`.
129+
/// `convert_address` is a function to convert addresses read by
130+
/// `Reader::read_address` into the `Address` type. For executable files,
131+
/// it is sufficient to simply map the address to `Address::Constant`.
132+
///
133+
/// Relocatable object files are more complicated because there are relocations
134+
/// associated with the address. To handle this, you can use a `Reader`
135+
/// implementation for which `Reader::read_address` stores the relocation
136+
/// information in a map and returns the map key instead of an address. Then
137+
/// `convert_address` can look up the mapping to produce an `Address`.
138+
///
139+
/// Note that `convert_address` is also used for address and offset pairs in
140+
/// DWARF v2-v4 range lists and location lists. In order for the parser to
141+
/// correctly handle these, `Reader::read_address` must return the values 0 and -1
142+
/// unchanged.
134143
///
135144
/// `convert_address` should not be used for complex address transformations, as it
136145
/// will not be called for address offsets (such as in `DW_AT_high_pc`, line programs,

src/write/loc.rs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -334,30 +334,23 @@ mod convert {
334334
while let Some(from_loc) = from.next()? {
335335
let loc = match from_loc {
336336
read::RawLocListEntry::AddressOrOffsetPair { begin, end, data } => {
337-
// These were parsed as addresses, even if they are offsets.
337+
// This matches the logic in `RangeList::from`. See the comments there.
338338
let begin = convert_address(begin)?;
339339
let end = convert_address(end)?;
340340
let data = convert_expression(data)?;
341-
match (begin, end) {
342-
(Address::Constant(begin_offset), Address::Constant(end_offset)) => {
343-
if have_base_address {
344-
Location::OffsetPair {
345-
begin: begin_offset,
346-
end: end_offset,
347-
data,
348-
}
349-
} else {
350-
Location::StartEnd { begin, end, data }
351-
}
352-
}
353-
_ => {
354-
if have_base_address {
355-
// At least one of begin/end is an address, but we also have
356-
// a base address. Adding addresses is undefined.
357-
return Err(ConvertError::InvalidRangeRelativeAddress);
358-
}
359-
Location::StartEnd { begin, end, data }
341+
if have_base_address {
342+
let (Address::Constant(begin_offset), Address::Constant(end_offset)) =
343+
(begin, end)
344+
else {
345+
return Err(ConvertError::InvalidRangeRelativeAddress);
346+
};
347+
Location::OffsetPair {
348+
begin: begin_offset,
349+
end: end_offset,
350+
data,
360351
}
352+
} else {
353+
Location::StartEnd { begin, end, data }
361354
}
362355
}
363356
read::RawLocListEntry::BaseAddress { addr } => {

src/write/range.rs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -235,33 +235,43 @@ mod convert {
235235
convert_address: &dyn Fn(u64) -> Option<Address>,
236236
) -> ConvertResult<Self> {
237237
let convert_address = |x| convert_address(x).ok_or(ConvertError::InvalidAddress);
238+
// The CU DW_AT_low_pc was parsed with `Reader::read_address`.
239+
// We could pass it to `convert_address`, but we rely on `read_address`
240+
// returning 0 if and only if it was an unrelocated 0 value.
241+
// If it is 0, then DWARF v2-4 ranges are address pairs unless there is a
242+
// base address entry, otherwise they must be offset pairs.
243+
// We don't handle the possibility of this being a tombstone since I don't
244+
// think that can occur.
238245
let mut have_base_address = from_unit.low_pc != 0;
239246
let mut ranges = Vec::new();
240247
while let Some(from_range) = from.next()? {
241248
let range = match from_range {
242249
read::RawRngListEntry::AddressOrOffsetPair { begin, end } => {
243-
// These were parsed as addresses, even if they are offsets.
250+
// These were parsed with `Reader::read_address`, even if they are
251+
// offsets, so we need to apply the conversion function.
252+
// For executables, the converted values will be `Address::Constant`
253+
// for both offsets and addresses.
254+
// For relocatable objects, we expect offsets to be `Address::Constant`
255+
// and addresses to be `Address::Symbol`.
244256
let begin = convert_address(begin)?;
245257
let end = convert_address(end)?;
246-
match (begin, end) {
247-
(Address::Constant(begin_offset), Address::Constant(end_offset)) => {
248-
if have_base_address {
249-
Range::OffsetPair {
250-
begin: begin_offset,
251-
end: end_offset,
252-
}
253-
} else {
254-
Range::StartEnd { begin, end }
255-
}
256-
}
257-
_ => {
258-
if have_base_address {
259-
// At least one of begin/end is an address, but we also have
260-
// a base address. Adding addresses is undefined.
261-
return Err(ConvertError::InvalidRangeRelativeAddress);
262-
}
263-
Range::StartEnd { begin, end }
258+
// We must use the presence of a base address to disambiguate between
259+
// offsets and addresses for both executables and relocatable objects.
260+
// (This logic is also used in `LocationList::from`.)
261+
if have_base_address {
262+
let (Address::Constant(begin_offset), Address::Constant(end_offset)) =
263+
(begin, end)
264+
else {
265+
// We have a relocatable object file that uses both a base address
266+
// and an address pair.
267+
return Err(ConvertError::InvalidRangeRelativeAddress);
268+
};
269+
Range::OffsetPair {
270+
begin: begin_offset,
271+
end: end_offset,
264272
}
273+
} else {
274+
Range::StartEnd { begin, end }
265275
}
266276
}
267277
read::RawRngListEntry::BaseAddress { addr } => {

0 commit comments

Comments
 (0)