Skip to content

Commit

Permalink
Bug 1847098 - Add version number of so files (#103)
Browse files Browse the repository at this point in the history
* Bug 1847098 - Add version number of so files

* Simplify

The original code would have paniced on non-utf8 paths, and would fail to return any version components if a single one failed to be parsed as a number.

The new code will now gracefully fail if the filename (the rest of the path is inconsequential) is non-utf8, and now tries to handle as many version components as it can, including partial parsing of mixed alphanumeric components such as, eg 2rc5 (which was aready a test case) would be parsed as `25`

---------

Co-authored-by: Alexandre Lissy <lissyx@lissyx.dyndns.org>
Co-authored-by: Jake Shadle <jake.shadle@embark-studios.com>
  • Loading branch information
3 people authored Feb 23, 2024
1 parent a098f7c commit c81300e
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 9 deletions.
82 changes: 77 additions & 5 deletions src/linux/maps_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,44 @@ impl MappingInfo {
Ok(soname.to_string())
}

pub fn get_mapping_effective_path_and_name(&self) -> Result<(PathBuf, String)> {
/// Attempts to retrieve the .so version of the elf via its filename as a
/// `(major, minor, release)` triplet
fn elf_file_so_version(&self) -> (u32, u32, u32) {
const DEF: (u32, u32, u32) = (0, 0, 0);
let Some(so_name) = self.name.as_deref() else {
return DEF;
};
let Some(filename) = std::path::Path::new(so_name).file_name() else {
return DEF;
};

// Avoid an allocation unless the string contains non-utf8
let filename = filename.to_string_lossy();

let Some((_, version)) = filename.split_once(".so.") else {
return DEF;
};

let mut triplet = [0, 0, 0];

for (so, trip) in version.split('.').zip(triplet.iter_mut()) {
// In some cases the release/patch version is alphanumeric (eg. '2rc5'),
// so try to parse as much as we can rather than completely ignoring
for digit in so
.chars()
.filter_map(|c: char| c.is_ascii_digit().then_some(c as u8 - b'0'))
{
*trip *= 10;
*trip += digit as u32;
}
}

(triplet[0], triplet[1], triplet[2])
}

pub fn get_mapping_effective_path_name_and_version(
&self,
) -> Result<(PathBuf, String, (u32, u32, u32))> {
let mut file_path = PathBuf::from(self.name.clone().unwrap_or_default());

// Tools such as minidump_stackwalk use the name of the module to look up
Expand All @@ -321,7 +358,7 @@ impl MappingInfo {
.file_name()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or_default();
return Ok((file_path, file_name));
return Ok((file_path, file_name, self.elf_file_so_version()));
};

if self.is_executable() && self.offset != 0 {
Expand All @@ -337,7 +374,7 @@ impl MappingInfo {
file_path.set_file_name(&file_name);
}

Ok((file_path, file_name))
Ok((file_path, file_name, self.elf_file_so_version()))
}

pub fn is_contained_in(&self, user_mapping_list: &MappingList) -> bool {
Expand Down Expand Up @@ -628,13 +665,48 @@ a4840000-a4873000 rw-p 09021000 08:12 393449 /data/app/org.mozilla.firefox-1
);
assert_eq!(mappings.len(), 1);

let (file_path, file_name) = mappings[0]
.get_mapping_effective_path_and_name()
let (file_path, file_name, _version) = mappings[0]
.get_mapping_effective_path_name_and_version()
.expect("Couldn't get effective name for mapping");
assert_eq!(file_name, "libmozgtk.so");
assert_eq!(file_path, PathBuf::from("/home/martin/Documents/mozilla/devel/mozilla-central/obj/widget/gtk/mozgtk/gtk3/libmozgtk.so"));
}

#[test]
fn test_elf_file_so_version() {
let mappings = get_mappings_for(
"\
7f877ab9f000-7f877aba0000 rw-p 0001f000 00:1b 100457459 /home/alex/bin/firefox/libmozsandbox.so
7f877ae65000-7f877ae68000 rw-p 00265000 00:1b 90432393 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.32
7f877ae76000-7f877ae77000 rw-p 0000a000 00:1b 90443112 /usr/lib/x86_64-linux-gnu/libcairo-gobject.so.2.11800.0
7f877ae7c000-7f877ae8c000 r--p 00000000 00:1b 93439971 /usr/lib/x86_64-linux-gnu/libm.so.6
7f877af70000-7f877af71000 rw-p 00003000 00:1b 93439980 /usr/lib/x86_64-linux-gnu/libpthread.so.0
7f877af78000-7f877af79000 rw-p 00005000 00:1b 90423049 /usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0.7800.0
7f877ae7c000-7f877ae8c000 rw-p 00000000 00:1b 93439971 /usr/lib/x86_64-linux-gnu/libabsl_time_zone.so.20220623.0.0
7f877ae7c000-7f877ae8c000 rw-p 00000000 00:1b 93439971 /usr/lib/x86_64-linux-gnu/libdbus-1.so.3.34.2rc5
7f877ae7c000-7f877ae8c000 rw-p 00000000 00:1b 93439971 /usr/lib/x86_64-linux-gnu/libtoto.so.AAA",
0x7ffe091bf000,
);
assert_eq!(mappings.len(), 9);

let expected = [
(0, 0, 0),
(6, 0, 32),
(2, 11800, 0),
(6, 0, 0),
(0, 0, 0),
(0, 7800, 0),
(20220623, 0, 0),
(3, 34, 25),
(0, 0, 0),
];

for (i, (map, exp)) in mappings.into_iter().zip(expected).enumerate() {
let version = map.elf_file_so_version();
assert_eq!(version, exp, "{i}");
}
}

#[test]
fn test_whitespaces_in_name() {
let mappings = get_mappings_for(
Expand Down
16 changes: 12 additions & 4 deletions src/linux/sections/mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,24 @@ fn fill_raw_module(
sig_section.location()
};

let (file_path, _) = mapping
.get_mapping_effective_path_and_name()
let (file_path, _, (major, minor, release)) = mapping
.get_mapping_effective_path_name_and_version()
.map_err(|e| errors::SectionMappingsError::GetEffectivePathError(mapping.clone(), e))?;
let name_header = write_string_to_location(buffer, file_path.to_string_lossy().as_ref())?;

Ok(MDRawModule {
let mut raw_module = MDRawModule {
base_of_image: mapping.start_address as u64,
size_of_image: mapping.size as u32,
cv_record,
module_name_rva: name_header.rva,
..Default::default()
})
};

raw_module.version_info.signature = format::VS_FFI_SIGNATURE;
raw_module.version_info.struct_version = format::VS_FFI_STRUCVERSION;
raw_module.version_info.file_version_hi = major;
raw_module.version_info.file_version_lo = minor;
raw_module.version_info.product_version_hi = release;

Ok(raw_module)
}

0 comments on commit c81300e

Please sign in to comment.