summaryrefslogtreecommitdiffstats
path: root/library/backtrace/src/symbolize
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /library/backtrace/src/symbolize
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/backtrace/src/symbolize')
-rw-r--r--library/backtrace/src/symbolize/gimli.rs2
-rw-r--r--library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs20
-rw-r--r--library/backtrace/src/symbolize/gimli/libs_macos.rs4
-rw-r--r--library/backtrace/src/symbolize/gimli/macho.rs4
-rw-r--r--library/backtrace/src/symbolize/gimli/mmap_windows.rs2
-rw-r--r--library/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs242
6 files changed, 268 insertions, 6 deletions
diff --git a/library/backtrace/src/symbolize/gimli.rs b/library/backtrace/src/symbolize/gimli.rs
index 5f10122dd..cd4cec58c 100644
--- a/library/backtrace/src/symbolize/gimli.rs
+++ b/library/backtrace/src/symbolize/gimli.rs
@@ -184,6 +184,8 @@ cfg_if::cfg_if! {
))] {
mod libs_dl_iterate_phdr;
use libs_dl_iterate_phdr::native_libraries;
+ #[path = "gimli/parse_running_mmaps_unix.rs"]
+ mod parse_running_mmaps;
} else if #[cfg(target_env = "libnx")] {
mod libs_libnx;
use libs_libnx::native_libraries;
diff --git a/library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs b/library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
index a011e6080..9f0304ce8 100644
--- a/library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
+++ b/library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
@@ -17,6 +17,20 @@ pub(super) fn native_libraries() -> Vec<Library> {
return ret;
}
+fn infer_current_exe(base_addr: usize) -> OsString {
+ if let Ok(entries) = super::parse_running_mmaps::parse_maps() {
+ let opt_path = entries
+ .iter()
+ .find(|e| e.ip_matches(base_addr) && e.pathname().len() > 0)
+ .map(|e| e.pathname())
+ .cloned();
+ if let Some(path) = opt_path {
+ return path;
+ }
+ }
+ env::current_exe().map(|e| e.into()).unwrap_or_default()
+}
+
// `info` should be a valid pointers.
// `vec` should be a valid pointer to a `std::Vec`.
unsafe extern "C" fn callback(
@@ -28,8 +42,12 @@ unsafe extern "C" fn callback(
let libs = &mut *(vec as *mut Vec<Library>);
let is_main_prog = info.dlpi_name.is_null() || *info.dlpi_name == 0;
let name = if is_main_prog {
+ // The man page for dl_iterate_phdr says that the first object visited by
+ // callback is the main program; so the first time we encounter a
+ // nameless entry, we can assume its the main program and try to infer its path.
+ // After that, we cannot continue that assumption, and we use an empty string.
if libs.is_empty() {
- env::current_exe().map(|e| e.into()).unwrap_or_default()
+ infer_current_exe(info.dlpi_addr as usize)
} else {
OsString::new()
}
diff --git a/library/backtrace/src/symbolize/gimli/libs_macos.rs b/library/backtrace/src/symbolize/gimli/libs_macos.rs
index 17703b88a..438bbff6f 100644
--- a/library/backtrace/src/symbolize/gimli/libs_macos.rs
+++ b/library/backtrace/src/symbolize/gimli/libs_macos.rs
@@ -113,8 +113,8 @@ fn native_library(i: u32) -> Option<Library> {
// file offset 0 with a nonzero size. For whatever reason when this
// is present it appears to mean that the symbol table is relative
// to just the vmaddr slide for the library. If it's *not* present
- // then the symbol table is relative to the the vmaddr slide plus
- // the segment's stated address.
+ // then the symbol table is relative to the vmaddr slide plus the
+ // segment's stated address.
//
// To handle this situation if we *don't* find a text section at
// file offset zero then we increase the bias by the first text
diff --git a/library/backtrace/src/symbolize/gimli/macho.rs b/library/backtrace/src/symbolize/gimli/macho.rs
index ec5673843..adea97a09 100644
--- a/library/backtrace/src/symbolize/gimli/macho.rs
+++ b/library/backtrace/src/symbolize/gimli/macho.rs
@@ -13,8 +13,8 @@ type MachSection = <Mach as MachHeader>::Section;
type MachNlist = <Mach as MachHeader>::Nlist;
impl Mapping {
- // The loading path for OSX is is so different we just have a completely
- // different implementation of the function here. On OSX we need to go
+ // The loading path for macOS is so different we just have a completely
+ // different implementation of the function here. On macOS we need to go
// probing the filesystem for a bunch of files.
pub fn new(path: &Path) -> Option<Mapping> {
// First up we need to load the unique UUID which is stored in the macho
diff --git a/library/backtrace/src/symbolize/gimli/mmap_windows.rs b/library/backtrace/src/symbolize/gimli/mmap_windows.rs
index 22f53fe03..b39509d8c 100644
--- a/library/backtrace/src/symbolize/gimli/mmap_windows.rs
+++ b/library/backtrace/src/symbolize/gimli/mmap_windows.rs
@@ -6,7 +6,7 @@ use core::ptr;
use core::slice;
pub struct Mmap {
- // keep the file alive to prevent it from ebeing deleted which would cause
+ // keep the file alive to prevent it from being deleted which would cause
// us to read bad data.
_file: File,
ptr: *mut c_void,
diff --git a/library/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs b/library/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs
new file mode 100644
index 000000000..a196ffcfb
--- /dev/null
+++ b/library/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs
@@ -0,0 +1,242 @@
+// Note: This file is only currently used on targets that call out to the code
+// in `mod libs_dl_iterate_phdr` (e.g. linux, freebsd, ...); it may be more
+// general purpose, but it hasn't been tested elsewhere.
+
+use super::mystd::fs::File;
+use super::mystd::io::Read;
+use super::mystd::str::FromStr;
+use super::{OsString, String, Vec};
+
+#[derive(PartialEq, Eq, Debug)]
+pub(super) struct MapsEntry {
+ /// start (inclusive) and limit (exclusive) of address range.
+ address: (usize, usize),
+ /// The perms field are the permissions for the entry
+ ///
+ /// r = read
+ /// w = write
+ /// x = execute
+ /// s = shared
+ /// p = private (copy on write)
+ perms: [char; 4],
+ /// Offset into the file (or "whatever").
+ offset: usize,
+ /// device (major, minor)
+ dev: (usize, usize),
+ /// inode on the device. 0 indicates that no inode is associated with the memory region (e.g. uninitalized data aka BSS).
+ inode: usize,
+ /// Usually the file backing the mapping.
+ ///
+ /// Note: The man page for proc includes a note about "coordination" by
+ /// using readelf to see the Offset field in ELF program headers. pnkfelix
+ /// is not yet sure if that is intended to be a comment on pathname, or what
+ /// form/purpose such coordination is meant to have.
+ ///
+ /// There are also some pseudo-paths:
+ /// "[stack]": The initial process's (aka main thread's) stack.
+ /// "[stack:<tid>]": a specific thread's stack. (This was only present for a limited range of Linux verisons; it was determined to be too expensive to provide.)
+ /// "[vdso]": Virtual dynamically linked shared object
+ /// "[heap]": The process's heap
+ ///
+ /// The pathname can be blank, which means it is an anonymous mapping
+ /// obtained via mmap.
+ ///
+ /// Newlines in pathname are replaced with an octal escape sequence.
+ ///
+ /// The pathname may have "(deleted)" appended onto it if the file-backed
+ /// path has been deleted.
+ ///
+ /// Note that modifications like the latter two indicated above imply that
+ /// in general the pathname may be ambiguous. (I.e. you cannot tell if the
+ /// denoted filename actually ended with the text "(deleted)", or if that
+ /// was added by the maps rendering.
+ pathname: OsString,
+}
+
+pub(super) fn parse_maps() -> Result<Vec<MapsEntry>, &'static str> {
+ let mut v = Vec::new();
+ let mut proc_self_maps =
+ File::open("/proc/self/maps").map_err(|_| "Couldn't open /proc/self/maps")?;
+ let mut buf = String::new();
+ let _bytes_read = proc_self_maps
+ .read_to_string(&mut buf)
+ .map_err(|_| "Couldn't read /proc/self/maps")?;
+ for line in buf.lines() {
+ v.push(line.parse()?);
+ }
+
+ Ok(v)
+}
+
+impl MapsEntry {
+ pub(super) fn pathname(&self) -> &OsString {
+ &self.pathname
+ }
+
+ pub(super) fn ip_matches(&self, ip: usize) -> bool {
+ self.address.0 <= ip && ip < self.address.1
+ }
+}
+
+impl FromStr for MapsEntry {
+ type Err = &'static str;
+
+ // Format: address perms offset dev inode pathname
+ // e.g.: "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]"
+ // e.g.: "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
+ // e.g.: "35b1a21000-35b1a22000 rw-p 00000000 00:00 0"
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let mut parts = s
+ .split(' ') // space-separated fields
+ .filter(|s| s.len() > 0); // multiple spaces implies empty strings that need to be skipped.
+ let range_str = parts.next().ok_or("Couldn't find address")?;
+ let perms_str = parts.next().ok_or("Couldn't find permissions")?;
+ let offset_str = parts.next().ok_or("Couldn't find offset")?;
+ let dev_str = parts.next().ok_or("Couldn't find dev")?;
+ let inode_str = parts.next().ok_or("Couldn't find inode")?;
+ let pathname_str = parts.next().unwrap_or(""); // pathname may be omitted.
+
+ let hex = |s| usize::from_str_radix(s, 16).map_err(|_| "Couldn't parse hex number");
+ let address = {
+ // This could use `range_str.split_once('-')` once the MSRV passes 1.52.
+ if let Some(idx) = range_str.find('-') {
+ let (start, rest) = range_str.split_at(idx);
+ let (_div, limit) = rest.split_at(1);
+ (hex(start)?, hex(limit)?)
+ } else {
+ return Err("Couldn't parse address range");
+ }
+ };
+ let perms: [char; 4] = {
+ let mut chars = perms_str.chars();
+ let mut c = || chars.next().ok_or("insufficient perms");
+ let perms = [c()?, c()?, c()?, c()?];
+ if chars.next().is_some() {
+ return Err("too many perms");
+ }
+ perms
+ };
+ let offset = hex(offset_str)?;
+ let dev = {
+ // This could use `dev_str.split_once(':')` once the MSRV passes 1.52.
+ if let Some(idx) = dev_str.find(':') {
+ let (major, rest) = dev_str.split_at(idx);
+ let (_div, minor) = rest.split_at(1);
+ (hex(major)?, hex(minor)?)
+ } else {
+ return Err("Couldn't parse dev")?;
+ }
+ };
+ let inode = hex(inode_str)?;
+ let pathname = pathname_str.into();
+
+ Ok(MapsEntry {
+ address,
+ perms,
+ offset,
+ dev,
+ inode,
+ pathname,
+ })
+ }
+}
+
+// Make sure we can parse 64-bit sample output if we're on a 64-bit target.
+#[cfg(target_pointer_width = "64")]
+#[test]
+fn check_maps_entry_parsing_64bit() {
+ assert_eq!(
+ "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 \
+ [vsyscall]"
+ .parse::<MapsEntry>()
+ .unwrap(),
+ MapsEntry {
+ address: (0xffffffffff600000, 0xffffffffff601000),
+ perms: ['-', '-', 'x', 'p'],
+ offset: 0x00000000,
+ dev: (0x00, 0x00),
+ inode: 0x0,
+ pathname: "[vsyscall]".into(),
+ }
+ );
+
+ assert_eq!(
+ "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 \
+ /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
+ .parse::<MapsEntry>()
+ .unwrap(),
+ MapsEntry {
+ address: (0x7f5985f46000, 0x7f5985f48000),
+ perms: ['r', 'w', '-', 'p'],
+ offset: 0x00039000,
+ dev: (0x103, 0x06),
+ inode: 0x76021795,
+ pathname: "/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2".into(),
+ }
+ );
+ assert_eq!(
+ "35b1a21000-35b1a22000 rw-p 00000000 00:00 0"
+ .parse::<MapsEntry>()
+ .unwrap(),
+ MapsEntry {
+ address: (0x35b1a21000, 0x35b1a22000),
+ perms: ['r', 'w', '-', 'p'],
+ offset: 0x00000000,
+ dev: (0x00, 0x00),
+ inode: 0x0,
+ pathname: Default::default(),
+ }
+ );
+}
+
+// (This output was taken from a 32-bit machine, but will work on any target)
+#[test]
+fn check_maps_entry_parsing_32bit() {
+ /* Example snippet of output:
+ 08056000-08077000 rw-p 00000000 00:00 0 [heap]
+ b7c79000-b7e02000 r--p 00000000 08:01 60662705 /usr/lib/locale/locale-archive
+ b7e02000-b7e03000 rw-p 00000000 00:00 0
+ */
+ assert_eq!(
+ "08056000-08077000 rw-p 00000000 00:00 0 \
+ [heap]"
+ .parse::<MapsEntry>()
+ .unwrap(),
+ MapsEntry {
+ address: (0x08056000, 0x08077000),
+ perms: ['r', 'w', '-', 'p'],
+ offset: 0x00000000,
+ dev: (0x00, 0x00),
+ inode: 0x0,
+ pathname: "[heap]".into(),
+ }
+ );
+
+ assert_eq!(
+ "b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
+ /usr/lib/locale/locale-archive"
+ .parse::<MapsEntry>()
+ .unwrap(),
+ MapsEntry {
+ address: (0xb7c79000, 0xb7e02000),
+ perms: ['r', '-', '-', 'p'],
+ offset: 0x00000000,
+ dev: (0x08, 0x01),
+ inode: 0x60662705,
+ pathname: "/usr/lib/locale/locale-archive".into(),
+ }
+ );
+ assert_eq!(
+ "b7e02000-b7e03000 rw-p 00000000 00:00 0"
+ .parse::<MapsEntry>()
+ .unwrap(),
+ MapsEntry {
+ address: (0xb7e02000, 0xb7e03000),
+ perms: ['r', 'w', '-', 'p'],
+ offset: 0x00000000,
+ dev: (0x00, 0x00),
+ inode: 0x0,
+ pathname: Default::default(),
+ }
+ );
+}