diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /third_party/rust/minidump-writer/src/linux/maps_reader.rs | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/minidump-writer/src/linux/maps_reader.rs')
-rw-r--r-- | third_party/rust/minidump-writer/src/linux/maps_reader.rs | 147 |
1 files changed, 135 insertions, 12 deletions
diff --git a/third_party/rust/minidump-writer/src/linux/maps_reader.rs b/third_party/rust/minidump-writer/src/linux/maps_reader.rs index 4d0d3b5aaa..b5b7fb23e6 100644 --- a/third_party/rust/minidump-writer/src/linux/maps_reader.rs +++ b/third_party/rust/minidump-writer/src/linux/maps_reader.rs @@ -289,10 +289,9 @@ impl MappingInfo { true } - fn elf_file_so_name(&self) -> Result<String> { - // Find the shared object name (SONAME) by examining the ELF information - // for |mapping|. If the SONAME is found copy it into the passed buffer - // |soname| and return true. The size of the buffer is |soname_size|. + /// Find the shared object name (SONAME) by examining the ELF information + /// for the mapping. + fn so_name(&self) -> Result<String> { let mapped_file = MappingInfo::get_mmap(&self.name, self.offset)?; let elf_obj = elf::Elf::parse(&mapped_file)?; @@ -303,7 +302,14 @@ impl MappingInfo { Ok(soname.to_string()) } - pub fn get_mapping_effective_path_and_name(&self) -> Result<(PathBuf, String)> { + #[inline] + fn so_version(&self) -> Option<SoVersion> { + SoVersion::parse(self.name.as_deref()?) + } + + pub fn get_mapping_effective_path_name_and_version( + &self, + ) -> Result<(PathBuf, String, Option<SoVersion>)> { 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 @@ -312,16 +318,15 @@ impl MappingInfo { // filesystem name of the module. // Just use the filesystem name if no SONAME is present. - let file_name = if let Ok(name) = self.elf_file_so_name() { - name - } else { + let Ok(file_name) = self.so_name() else { // file_path := /path/to/libname.so // file_name := libname.so let file_name = file_path .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.so_version())); }; if self.is_executable() && self.offset != 0 { @@ -337,7 +342,7 @@ impl MappingInfo { file_path.set_file_name(&file_name); } - Ok((file_path, file_name)) + Ok((file_path, file_name, self.so_version())) } pub fn is_contained_in(&self, user_mapping_list: &MappingList) -> bool { @@ -382,6 +387,97 @@ impl MappingInfo { } } +/// Version metadata retrieved from an .so filename +/// +/// There is no standard for .so version numbers so this implementation just +/// does a best effort to pull as much data as it can based on real .so schemes +/// seen +/// +/// That being said, the [libtool](https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html) +/// versioning scheme is fairly common +#[cfg_attr(test, derive(Debug))] +pub struct SoVersion { + /// Might be non-zero if there is at least one non-zero numeric component after .so. + /// + /// Equivalent to `current` in libtool versions + pub major: u32, + /// The numeric component after the major version, if any + /// + /// Equivalent to `revision` in libtool versions + pub minor: u32, + /// The numeric component after the minor version, if any + /// + /// Equivalent to `age` in libtool versions + pub patch: u32, + /// The patch component may contain additional non-numeric metadata similar + /// to a semver prelease, this is any numeric data that suffixes that prerelease + /// string + pub prerelease: u32, +} + +impl SoVersion { + /// Attempts to retrieve the .so version of the elf path via its filename + fn parse(so_path: &OsStr) -> Option<Self> { + let filename = std::path::Path::new(so_path).file_name()?; + + // Avoid an allocation unless the string contains non-utf8 + let filename = filename.to_string_lossy(); + + let (_, version) = filename.split_once(".so.")?; + + let mut sov = Self { + major: 0, + minor: 0, + patch: 0, + prerelease: 0, + }; + + let comps = [ + &mut sov.major, + &mut sov.minor, + &mut sov.patch, + &mut sov.prerelease, + ]; + + for (i, comp) in version.split('.').enumerate() { + if i <= 1 { + *comps[i] = comp.parse().unwrap_or_default(); + } else if i >= 4 { + break; + } else { + // In some cases the release/patch version is alphanumeric (eg. '2rc5'), + // so try to parse either a single or two numbers + if let Some(pend) = comp.find(|c: char| !c.is_ascii_digit()) { + if let Ok(patch) = comp[..pend].parse() { + *comps[i] = patch; + } + + if i >= comps.len() - 1 { + break; + } + if let Some(pre) = comp.rfind(|c: char| !c.is_ascii_digit()) { + if let Ok(pre) = comp[pre + 1..].parse() { + *comps[i + 1] = pre; + break; + } + } + } else { + *comps[i] = comp.parse().unwrap_or_default(); + } + } + } + + Some(sov) + } +} + +#[cfg(test)] +impl PartialEq<(u32, u32, u32, u32)> for SoVersion { + fn eq(&self, o: &(u32, u32, u32, u32)) -> bool { + self.major == o.0 && self.minor == o.1 && self.patch == o.2 && self.prerelease == o.3 + } +} + #[cfg(test)] #[cfg(target_pointer_width = "64")] // All addresses are 64 bit and I'm currently too lazy to adjust it to work for both mod tests { @@ -628,14 +724,41 @@ 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() { + #[rustfmt::skip] + let test_cases = [ + ("/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.32", (6, 0, 32, 0)), + ("/usr/lib/x86_64-linux-gnu/libcairo-gobject.so.2.11800.0", (2, 11800, 0, 0)), + ("/usr/lib/x86_64-linux-gnu/libm.so.6", (6, 0, 0, 0)), + ("/usr/lib/x86_64-linux-gnu/libpthread.so.0", (0, 0, 0, 0)), + ("/usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0.7800.0", (0, 7800, 0, 0)), + ("/usr/lib/x86_64-linux-gnu/libabsl_time_zone.so.20220623.0.0", (20220623, 0, 0, 0)), + ("/usr/lib/x86_64-linux-gnu/libdbus-1.so.3.34.2rc5", (3, 34, 2, 5)), + ("/usr/lib/x86_64-linux-gnu/libdbus-1.so.3.34.2rc", (3, 34, 2, 0)), + ("/usr/lib/x86_64-linux-gnu/libdbus-1.so.3.34.rc5", (3, 34, 0, 5)), + ("/usr/lib/x86_64-linux-gnu/libtoto.so.AAA", (0, 0, 0, 0)), + ("/usr/lib/x86_64-linux-gnu/libsemver-1.so.1.2.alpha.1", (1, 2, 0, 1)), + ("/usr/lib/x86_64-linux-gnu/libboop.so.1.2.3.4.5", (1, 2, 3, 4)), + ("/usr/lib/x86_64-linux-gnu/libboop.so.1.2.3pre4.5", (1, 2, 3, 4)), + ]; + + assert!(SoVersion::parse(OsStr::new("/home/alex/bin/firefox/libmozsandbox.so")).is_none()); + + for (path, expected) in test_cases { + let actual = SoVersion::parse(OsStr::new(path)).unwrap(); + assert_eq!(actual, expected); + } + } + + #[test] fn test_whitespaces_in_name() { let mappings = get_mappings_for( "\ |