Skip to content

Commit

Permalink
fix(debuginfo): Improve .o handling on MachO (#173)
Browse files Browse the repository at this point in the history
* Change how `MachObject::raw_section` finds segments.

Currently `raw_section` finds the given section by assuming that it's
within a segment named `__TEXT` or `__DWARF`, finding such a segment,
and then searching within that segment.

However, this fails for `.o` files, because they have a single nameless
segment. As
https://github.com/aidansteele/osx-abi-macho-file-format-reference says:

> For compactness, an intermediate object file contains only one
> segment. This segment has no name; it contains all the sections
> destined ultimately for different segments in the final object file.

This commit changes `raw_section` so it ignores segment names, and
simply searches through all segments.

As well as working with `.o` files, the resulting code is a little
shorter and simpler.

* Allow zero addresses in DWARF units.

Because they occur in `.o` files.

Without the first change in this commit, debuginfo for entire `.o` files
can be missed. Without the second change, debuginfo for the first
function (which can start at 0x0) can be missed.
  • Loading branch information
nnethercote committed Mar 17, 2020
1 parent 0f31615 commit e4b3939
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 46 deletions.
12 changes: 1 addition & 11 deletions debuginfo/src/dwarf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,13 +399,6 @@ impl<'d, 'a> DwarfUnit<'d, 'a> {
None => return Err(gimli::read::Error::MissingUnitDie.into()),
};

// Clang's LLD might eliminate an entire compilation unit and simply set the low_pc to zero
// and remove all range entries to indicate that it is missing. Skip such a unit, as it does
// not contain any code that can be executed.
if unit.low_pc == 0 && entry.attr(constants::DW_AT_ranges)?.is_none() {
return Ok(None);
}

let language = match entry.attr_value(constants::DW_AT_language)? {
Some(AttributeValue::Language(lang)) => language_from_dwarf(lang),
_ => Language::Unknown,
Expand Down Expand Up @@ -483,11 +476,8 @@ impl<'d, 'a> DwarfUnit<'d, 'a> {
return Ok(tuple);
}

// to go by the logic in dwarf2read a low_pc of 0 can indicate an
// eliminated duplicate when the GNU linker is used.
// TODO: *technically* there could be a relocatable section placed at VA 0
let low_pc = match low_pc {
Some(low_pc) if low_pc != 0 => low_pc,
Some(low_pc) => low_pc,
_ => return Ok(tuple),
};

Expand Down
54 changes: 19 additions & 35 deletions debuginfo/src/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,17 +238,6 @@ impl<'d> MachObject<'d> {
self.symbols()
.any(|s| s.name().map_or(false, |n| n.starts_with("__?hidden#")))
}

/// Locates a segment by its name.
fn find_segment(&self, name: &str) -> Option<&mach::segment::Segment<'d>> {
for segment in &self.macho.segments {
if segment.name().map(|seg| seg == name).unwrap_or(false) {
return Some(segment);
}
}

None
}
}

impl fmt::Debug for MachObject<'_> {
Expand Down Expand Up @@ -353,31 +342,26 @@ impl<'d> Dwarf<'d> for MachObject<'d> {
}

fn raw_section(&self, section_name: &str) -> Option<DwarfSection<'d>> {
let segment_name = match section_name {
"eh_frame" => "__TEXT",
_ => "__DWARF",
};

let segment = self.find_segment(segment_name)?;

for section in segment {
if let Ok((header, data)) = section {
if let Ok(sec) = header.name() {
if sec.len() >= 2 && &sec[2..] == section_name {
// In some cases, dsymutil leaves sections headers but removes their data
// from the file. While the addr and size parameters are still set,
// `header.offset` is 0 in that case. We skip them just like the section was
// missing to avoid loading invalid data.
if header.offset == 0 {
return None;
for segment in &self.macho.segments {
for section in segment {
if let Ok((header, data)) = section {
if let Ok(sec) = header.name() {
if sec.len() >= 2 && &sec[2..] == section_name {
// In some cases, dsymutil leaves sections headers but removes their
// data from the file. While the addr and size parameters are still
// set, `header.offset` is 0 in that case. We skip them just like the
// section was missing to avoid loading invalid data.
if header.offset == 0 {
return None;
}

return Some(DwarfSection {
data: Cow::Borrowed(data),
address: header.addr,
offset: u64::from(header.offset),
align: u64::from(header.align),
});
}

return Some(DwarfSection {
data: Cow::Borrowed(data),
address: header.addr,
offset: u64::from(header.offset),
align: u64::from(header.align),
});
}
}
}
Expand Down

0 comments on commit e4b3939

Please sign in to comment.