Skip to content

Commit

Permalink
read/macho/file: support __zdebug_ section name prefixes
Browse files Browse the repository at this point in the history
When using compression, sections in Mach-O produced by the go
compiler have a __zdebug_ prefix (only the debug sections are
compressed).
  • Loading branch information
ajwerner committed Jun 21, 2024
1 parent 7b58f78 commit 211bfce
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 33 deletions.
50 changes: 25 additions & 25 deletions src/read/macho/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,32 +287,32 @@ where
&'file self,
section_name: &[u8],
) -> Option<MachOSection<'data, 'file, Mach, R>> {
// Translate the "." prefix to the "__" prefix used by OSX/Mach-O, eg
// ".debug_info" to "__debug_info", and limit to 16 bytes total.
let system_name = if section_name.starts_with(b".") {
if section_name.len() > 15 {
Some(&section_name[1..15])
} else {
Some(&section_name[1..])
}
} else {
None
};
let cmp_section_name = |section: &MachOSection<'data, 'file, Mach, R>| {
section
.name_bytes()
.map(|name| {
section_name == name
|| system_name
.filter(|system_name| {
name.starts_with(b"__") && name[2..] == **system_name
})
.is_some()
})
.unwrap_or(false)
// Translate the section_name by stripping the query_prefix to construct
// a function that matches names starting with name_prefix, taking into
// consideration the maximum section name length.
let make_prefix_matcher = |query_prefix: &'static [u8], name_prefix: &'static [u8]| {
const MAX_SECTION_NAME_LEN: usize = 16;
let suffix = section_name.strip_prefix(query_prefix).map(|suffix| {
let max_len = MAX_SECTION_NAME_LEN - name_prefix.len();
&suffix[..suffix.len().min(max_len)]
});
move |name: &[u8]| suffix.is_some() && name.strip_prefix(name_prefix) == suffix
};

self.sections().find(cmp_section_name)
// Matches "__text" when searching for ".text" and "__debug_str_offs"
// when searching for ".debug_str_offsets", as is common in
// macOS/Mach-O.
let matches_underscores_prefix = make_prefix_matcher(b".", b"__");
// Matches "__zdebug_info" when searching for ".debug_info" and
// "__zdebug_str_off" when searching for ".debug_str_offsets", as is
// used by Go when using GNU-style compression.
let matches_zdebug_prefix = make_prefix_matcher(b".debug_", b"__zdebug_");
self.sections().find(|section| {
section.name_bytes().map_or(false, |name| {
name == section_name
|| matches_underscores_prefix(name)
|| matches_zdebug_prefix(name)
})
})
}

fn section_by_index(&self, index: SectionIndex) -> Result<MachOSection<'data, '_, Mach, R>> {
Expand Down
18 changes: 10 additions & 8 deletions src/read/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,18 @@ pub trait Object<'data>: read::private::Sealed {

/// Get the section named `section_name`, if such a section exists.
///
/// If `section_name` starts with a '.' then it is treated as a system section name,
/// and is compared using the conventions specific to the object file format. This
/// includes:
/// - if ".debug_str_offsets" is requested for a Mach-O object file, then the actual
/// section name that is searched for is "__debug_str_offs".
/// If `section_name` starts with a '.' then it is treated as a system
/// section name, and is compared using the conventions specific to the
/// object file format. This includes:
/// - if ".debug_str_offsets" is requested for a Mach-O object file, then
/// the actual section name that is searched for is "__debug_str_offs".
/// - if ".debug_info" is requested for an ELF object file, then
/// ".zdebug_info" may be returned (and similarly for other debug sections).
/// ".zdebug_info" may be returned (and similarly for other debug
/// sections). Similarly, if ".debug_info" is requested for a Mach-O
/// object file, then "__zdebug_info" may be returned.
///
/// For some object files, multiple segments may contain sections with the same
/// name. In this case, the first matching section will be used.
/// For some object files, multiple segments may contain sections with the
/// same name. In this case, the first matching section will be used.
///
/// This method skips over sections with invalid names.
fn section_by_name(&self, section_name: &str) -> Option<Self::Section<'_>> {
Expand Down

0 comments on commit 211bfce

Please sign in to comment.