From 5c368199f4bb26a42eab7427255ae3c7828eba97 Mon Sep 17 00:00:00 2001 From: gh-tr Date: Tue, 8 Nov 2022 10:19:18 -0500 Subject: [PATCH 1/6] Add support for QNX/Neutrino --- src/symbolize/gimli.rs | 2 ++ src/symbolize/gimli/libs_dl_iterate_phdr.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/symbolize/gimli.rs b/src/symbolize/gimli.rs index cd4cec58c..129741e82 100644 --- a/src/symbolize/gimli.rs +++ b/src/symbolize/gimli.rs @@ -41,6 +41,7 @@ cfg_if::cfg_if! { target_os = "openbsd", target_os = "solaris", target_os = "illumos", + target_os = "nto", ))] { #[path = "gimli/mmap_unix.rs"] mod mmap; @@ -179,6 +180,7 @@ cfg_if::cfg_if! { target_os = "freebsd", target_os = "openbsd", all(target_os = "android", feature = "dl_iterate_phdr"), + target_os = "nto", ), not(target_env = "uclibc"), ))] { diff --git a/src/symbolize/gimli/libs_dl_iterate_phdr.rs b/src/symbolize/gimli/libs_dl_iterate_phdr.rs index 9f0304ce8..25e9df940 100644 --- a/src/symbolize/gimli/libs_dl_iterate_phdr.rs +++ b/src/symbolize/gimli/libs_dl_iterate_phdr.rs @@ -34,7 +34,10 @@ fn infer_current_exe(base_addr: usize) -> OsString { // `info` should be a valid pointers. // `vec` should be a valid pointer to a `std::Vec`. unsafe extern "C" fn callback( + #[cfg(not(target_os = "nto"))] info: *mut libc::dl_phdr_info, + #[cfg(target_os = "nto")] + info: *const libc::dl_phdr_info, _size: libc::size_t, vec: *mut libc::c_void, ) -> libc::c_int { From 12afb1164b6ac46c3f5bc8bc55eb93c086f17fa6 Mon Sep 17 00:00:00 2001 From: gh-tr Date: Tue, 20 Dec 2022 13:49:12 -0500 Subject: [PATCH 2/6] Initial attempt at adding process map parsing --- .../gimli/parse_running_mmaps_unix.rs | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/symbolize/gimli/parse_running_mmaps_unix.rs b/src/symbolize/gimli/parse_running_mmaps_unix.rs index a196ffcfb..f475b2cf2 100644 --- a/src/symbolize/gimli/parse_running_mmaps_unix.rs +++ b/src/symbolize/gimli/parse_running_mmaps_unix.rs @@ -53,6 +53,7 @@ pub(super) struct MapsEntry { pathname: OsString, } +#[cfg(not(target_os = "nto"))] pub(super) fn parse_maps() -> Result, &'static str> { let mut v = Vec::new(); let mut proc_self_maps = @@ -68,6 +69,26 @@ pub(super) fn parse_maps() -> Result, &'static str> { Ok(v) } +// TODO: This could be merged with the above block but seems to require +// creating a couple of extra strings to pass to map_err(). Is +// there a way to pass it paramenters without adding a bunch of +// lines of code? +#[cfg(target_os = "nto")] +pub(super) fn parse_maps() -> Result, &'static str> { + let mut v = Vec::new(); + let mut proc_self_maps = + File::open("/proc/self/pmap").map_err(|_| "Couldn't open /proc/self/pmap")?; + let mut buf = String::new(); + let _bytes_read = proc_self_maps + .read_to_string(&mut buf) + .map_err(|_| "Couldn't read /proc/self/pmap")?; + for line in buf.lines() { + v.push(line.parse()?); + } + + Ok(v) +} + impl MapsEntry { pub(super) fn pathname(&self) -> &OsString { &self.pathname @@ -78,6 +99,7 @@ impl MapsEntry { } } +#[cfg(not(target_os = "nto"))] impl FromStr for MapsEntry { type Err = &'static str; @@ -141,9 +163,64 @@ impl FromStr for MapsEntry { } } +#[cfg(target_os = "nto")] +impl FromStr for MapsEntry { + type Err = &'static str; + + // Format: vaddr,size,flags,prot,maxprot,dev,ino,offset,rsv,guardsize,refcnt,mapcnt,path + // e.g.: "0x00000022fa36b000,0x0000000000002000,0x00000071,0x05,0x0f,0x0000040b,0x00000000000000dd, + // 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/cat" + fn from_str(s: &str) -> Result { + let mut parts = s.split(','); + let vaddr_str = parts.next().ok_or("Couldn't find virtual address")?; + let size_str = parts.next().ok_or("Couldn't find size")?; + let _flags_str = parts.next().ok_or("Couldn't find flags")?; + let prot_str = parts.next().ok_or("Couldn't find protection")?; + let _maxprot_str = parts.next().ok_or("Couldn't find maximum protection")?; + let dev_str = parts.next().ok_or("Couldn't find device")?; + let ino_str = parts.next().ok_or("Couldn't find inode")?; + let offset_str = parts.next().ok_or("Couldn't find offset")?; + let _rsv_str = parts.next().ok_or("Couldn't find reserved pages")?; + let _guardsize_str = parts.next().ok_or("Couldn't find guard size")?; + let _refcnt_str = parts.next().ok_or("Couldn't find reference count")?; + let _mapcnt_str = parts.next().ok_or("Couldn't find mapped count")?; + let pathname_str = parts.next().unwrap_or(""); // pathname may be omitted. + + let hex = |s: &str| usize::from_str_radix(&s[2..], 16).map_err(|_| "Couldn't parse hex number"); + let address = { (hex(vaddr_str)?, hex(vaddr_str)? + hex(size_str)?) }; + + // TODO: Probably a rust'ier way of doing this + let mut perms: [char; 4] = ['-', '-', '-', '-']; + let perm_str: [char; 3] = ['r', 'w', 'x']; + let perm_bits: [usize; 3] = [0x1, 0x2, 0x4]; + + for (pos, val) in perm_bits.iter().enumerate() { + let prot = hex(prot_str)?; + if val & prot != 0 { + perms[pos] = perm_str[pos] + } + } + + let offset = hex(offset_str)?; + let dev = { (hex(dev_str)?, 0x00000000) }; + let inode = hex(ino_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] +#[cfg(not(target_os = "nto"))] fn check_maps_entry_parsing_64bit() { assert_eq!( "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 \ @@ -189,7 +266,55 @@ fn check_maps_entry_parsing_64bit() { ); } +#[cfg(target_os = "nto")] +fn check_maps_entry_parsing_64bit() { + assert_eq!( + "0xffffffffff600000,0x0000000000001000,0x00000071,0x04,0x0f,0x00000000,0x0000000000000000,\ + 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/foo" + .parse::() + .unwrap(), + MapsEntry { + address: (0xffffffffff600000, 0xffffffffff601000), + perms: ['-', '-', 'x', '-'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: "/proc/boot/foo".into(), + } + ); + + assert_eq!( + "0x00007f5985f46000,0x0000000000002000,0x00000071,0x03,0x0f,0x00000103,0x0000000076021795,\ + 0x0000000000039000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/usr/lib/ldqnx-64.so.2" + .parse::() + .unwrap(), + MapsEntry { + address: (0x7f5985f46000, 0x7f5985f48000), + perms: ['r', 'w', '-', '-'], + offset: 0x00039000, + dev: (0x103, 0x0), + inode: 0x76021795, + pathname: "/usr/lib/ldqnx-64.so.2".into(), + } + ); + assert_eq!( + "0x00000035b1a21000,0x0000000000001000,0x00000071,0x03,0x0f,0x00000000,0x0000000000000000,\ + 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003," + .parse::() + .unwrap(), + MapsEntry { + address: (0x35b1a21000, 0x35b1a22000), + perms: ['r', 'w', '-', '-'], + 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) +#[cfg(not(target_os = "nto"))] #[test] fn check_maps_entry_parsing_32bit() { /* Example snippet of output: From d528ac425f2b0c0b7fe5468cb56b9b58c0a0afde Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Thu, 22 Dec 2022 14:29:17 +0100 Subject: [PATCH 3/6] Split Linux and nto code into separate mods --- Cargo.toml | 2 +- .../gimli/parse_running_mmaps_unix.rs | 336 +++--------------- .../gimli/parse_running_mmaps_unix_default.rs | 158 ++++++++ .../gimli/parse_running_mmaps_unix_nto.rs | 100 ++++++ 4 files changed, 300 insertions(+), 296 deletions(-) create mode 100644 src/symbolize/gimli/parse_running_mmaps_unix_default.rs create mode 100644 src/symbolize/gimli/parse_running_mmaps_unix_nto.rs diff --git a/Cargo.toml b/Cargo.toml index 477909111..9fc8b3c83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ exclude = [ [dependencies] cfg-if = "1.0" rustc-demangle = "0.1.4" -libc = { version = "0.2.94", default-features = false } +libc = { version = "0.2.139", default-features = false } # Optionally enable the ability to serialize a `Backtrace`, controlled through # the `serialize-*` features below. diff --git a/src/symbolize/gimli/parse_running_mmaps_unix.rs b/src/symbolize/gimli/parse_running_mmaps_unix.rs index f475b2cf2..804d82eb3 100644 --- a/src/symbolize/gimli/parse_running_mmaps_unix.rs +++ b/src/symbolize/gimli/parse_running_mmaps_unix.rs @@ -4,11 +4,11 @@ use super::mystd::fs::File; use super::mystd::io::Read; -use super::mystd::str::FromStr; -use super::{OsString, String, Vec}; +use super::OsString; +use super::{String, Vec}; #[derive(PartialEq, Eq, Debug)] -pub(super) struct MapsEntry { +pub struct MapsEntry { /// start (inclusive) and limit (exclusive) of address range. address: (usize, usize), /// The perms field are the permissions for the entry @@ -53,42 +53,6 @@ pub(super) struct MapsEntry { pathname: OsString, } -#[cfg(not(target_os = "nto"))] -pub(super) fn parse_maps() -> Result, &'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) -} - -// TODO: This could be merged with the above block but seems to require -// creating a couple of extra strings to pass to map_err(). Is -// there a way to pass it paramenters without adding a bunch of -// lines of code? -#[cfg(target_os = "nto")] -pub(super) fn parse_maps() -> Result, &'static str> { - let mut v = Vec::new(); - let mut proc_self_maps = - File::open("/proc/self/pmap").map_err(|_| "Couldn't open /proc/self/pmap")?; - let mut buf = String::new(); - let _bytes_read = proc_self_maps - .read_to_string(&mut buf) - .map_err(|_| "Couldn't read /proc/self/pmap")?; - for line in buf.lines() { - v.push(line.parse()?); - } - - Ok(v) -} - impl MapsEntry { pub(super) fn pathname(&self) -> &OsString { &self.pathname @@ -99,269 +63,51 @@ impl MapsEntry { } } -#[cfg(not(target_os = "nto"))] -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 { - 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, - }) - } +fn concat_str(a: &str, b: &str) -> String { + let mut e = String::from(a); + e += b; + e } -#[cfg(target_os = "nto")] -impl FromStr for MapsEntry { - type Err = &'static str; - - // Format: vaddr,size,flags,prot,maxprot,dev,ino,offset,rsv,guardsize,refcnt,mapcnt,path - // e.g.: "0x00000022fa36b000,0x0000000000002000,0x00000071,0x05,0x0f,0x0000040b,0x00000000000000dd, - // 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/cat" - fn from_str(s: &str) -> Result { - let mut parts = s.split(','); - let vaddr_str = parts.next().ok_or("Couldn't find virtual address")?; - let size_str = parts.next().ok_or("Couldn't find size")?; - let _flags_str = parts.next().ok_or("Couldn't find flags")?; - let prot_str = parts.next().ok_or("Couldn't find protection")?; - let _maxprot_str = parts.next().ok_or("Couldn't find maximum protection")?; - let dev_str = parts.next().ok_or("Couldn't find device")?; - let ino_str = parts.next().ok_or("Couldn't find inode")?; - let offset_str = parts.next().ok_or("Couldn't find offset")?; - let _rsv_str = parts.next().ok_or("Couldn't find reserved pages")?; - let _guardsize_str = parts.next().ok_or("Couldn't find guard size")?; - let _refcnt_str = parts.next().ok_or("Couldn't find reference count")?; - let _mapcnt_str = parts.next().ok_or("Couldn't find mapped count")?; - let pathname_str = parts.next().unwrap_or(""); // pathname may be omitted. - - let hex = |s: &str| usize::from_str_radix(&s[2..], 16).map_err(|_| "Couldn't parse hex number"); - let address = { (hex(vaddr_str)?, hex(vaddr_str)? + hex(size_str)?) }; - - // TODO: Probably a rust'ier way of doing this - let mut perms: [char; 4] = ['-', '-', '-', '-']; - let perm_str: [char; 3] = ['r', 'w', 'x']; - let perm_bits: [usize; 3] = [0x1, 0x2, 0x4]; - - for (pos, val) in perm_bits.iter().enumerate() { - let prot = hex(prot_str)?; - if val & prot != 0 { - perms[pos] = perm_str[pos] - } - } - - let offset = hex(offset_str)?; - let dev = { (hex(dev_str)?, 0x00000000) }; - let inode = hex(ino_str)?; - let pathname = pathname_str.into(); - - Ok(MapsEntry { - address, - perms, - offset, - dev, - inode, - pathname, - }) - } +fn read_file(file: &str) -> Result { + let mut proc_self_maps = + File::open(file).map_err(|_| concat_str("Couldn't open file: ", file))?; + let mut buf = String::new(); + let _bytes_read = proc_self_maps + .read_to_string(&mut buf) + .map_err(|_| concat_str("Couldn't read file: ", file))?; + Ok(buf) } -// Make sure we can parse 64-bit sample output if we're on a 64-bit target. -#[cfg(target_pointer_width = "64")] -#[test] -#[cfg(not(target_os = "nto"))] -fn check_maps_entry_parsing_64bit() { - assert_eq!( - "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 \ - [vsyscall]" - .parse::() - .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::() - .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::() - .unwrap(), - MapsEntry { - address: (0x35b1a21000, 0x35b1a22000), - perms: ['r', 'w', '-', 'p'], - offset: 0x00000000, - dev: (0x00, 0x00), - inode: 0x0, - pathname: Default::default(), - } - ); +pub fn parse_maps() -> Result, String> { + let (file, skip) = config(); + let content = read_file(file)?; + parse_maps_lines(&content, skip) } -#[cfg(target_os = "nto")] -fn check_maps_entry_parsing_64bit() { - assert_eq!( - "0xffffffffff600000,0x0000000000001000,0x00000071,0x04,0x0f,0x00000000,0x0000000000000000,\ - 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/foo" - .parse::() - .unwrap(), - MapsEntry { - address: (0xffffffffff600000, 0xffffffffff601000), - perms: ['-', '-', 'x', '-'], - offset: 0x00000000, - dev: (0x00, 0x00), - inode: 0x0, - pathname: "/proc/boot/foo".into(), - } - ); - - assert_eq!( - "0x00007f5985f46000,0x0000000000002000,0x00000071,0x03,0x0f,0x00000103,0x0000000076021795,\ - 0x0000000000039000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/usr/lib/ldqnx-64.so.2" - .parse::() - .unwrap(), - MapsEntry { - address: (0x7f5985f46000, 0x7f5985f48000), - perms: ['r', 'w', '-', '-'], - offset: 0x00039000, - dev: (0x103, 0x0), - inode: 0x76021795, - pathname: "/usr/lib/ldqnx-64.so.2".into(), - } - ); - assert_eq!( - "0x00000035b1a21000,0x0000000000001000,0x00000071,0x03,0x0f,0x00000000,0x0000000000000000,\ - 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003," - .parse::() - .unwrap(), - MapsEntry { - address: (0x35b1a21000, 0x35b1a22000), - perms: ['r', 'w', '-', '-'], - offset: 0x00000000, - dev: (0x00, 0x00), - inode: 0x0, - pathname: Default::default(), - } - ); +fn parse_maps_lines(content: &str, skip: usize) -> Result, String> { + let mut data = Vec::new(); + for line in content.lines().skip(skip) { + data.push(line.parse()?); + } + Ok(data) } -// (This output was taken from a 32-bit machine, but will work on any target) -#[cfg(not(target_os = "nto"))] -#[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::() - .unwrap(), - MapsEntry { - address: (0x08056000, 0x08077000), - perms: ['r', 'w', '-', 'p'], - offset: 0x00000000, - dev: (0x00, 0x00), - inode: 0x0, - pathname: "[heap]".into(), - } - ); +cfg_if::cfg_if! { + if #[cfg(target_os = "nto")] { + mod parse_running_mmaps_unix_nto; + use parse_running_mmaps_unix_nto::*; + } else { + mod parse_running_mmaps_unix_default; + use parse_running_mmaps_unix_default::*; + } +} - assert_eq!( - "b7c79000-b7e02000 r--p 00000000 08:01 60662705 \ - /usr/lib/locale/locale-archive" - .parse::() - .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::() - .unwrap(), - MapsEntry { - address: (0xb7e02000, 0xb7e03000), - perms: ['r', 'w', '-', 'p'], - offset: 0x00000000, - dev: (0x00, 0x00), - inode: 0x0, - pathname: Default::default(), - } - ); +#[cfg(test)] +mod test { + #[test] + fn test_parse_maps() { + use super::*; + assert!(parse_maps().is_ok()); + } } diff --git a/src/symbolize/gimli/parse_running_mmaps_unix_default.rs b/src/symbolize/gimli/parse_running_mmaps_unix_default.rs new file mode 100644 index 000000000..834319a61 --- /dev/null +++ b/src/symbolize/gimli/parse_running_mmaps_unix_default.rs @@ -0,0 +1,158 @@ +use super::super::mystd::str::FromStr; +use super::MapsEntry; + +pub fn config() -> (&'static str, usize) { + ("/proc/self/maps", 0) +} + +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 { + 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::() + .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::() + .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::().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::() + .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::() + .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::().unwrap(), + MapsEntry { + address: (0xb7e02000, 0xb7e03000), + perms: ['r', 'w', '-', 'p'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: Default::default(), + } + ); +} diff --git a/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs b/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs new file mode 100644 index 000000000..cf4c856c4 --- /dev/null +++ b/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs @@ -0,0 +1,100 @@ +use super::super::mystd::str::FromStr; +use super::MapsEntry; + +pub fn config() -> (&'static str, usize) { + ("/proc/self/pmap", 1) +} + +impl FromStr for MapsEntry { + type Err = &'static str; + + // Format: vaddr,size,flags,prot,maxprot,dev,ino,offset,rsv,guardsize,refcnt,mapcnt,path + // e.g.: "0x00000022fa36b000,0x0000000000002000,0x00000071,0x05,0x0f,0x0000040b,0x00000000000000dd, + // 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/cat" + fn from_str(s: &str) -> Result { + let mut parts = s.split(','); + let vaddr_str = parts.next().ok_or("Couldn't find virtual address")?; + let size_str = parts.next().ok_or("Couldn't find size")?; + let _flags_str = parts.next().ok_or("Couldn't find flags")?; + let prot_str = parts.next().ok_or("Couldn't find protection")?; + let _maxprot_str = parts.next().ok_or("Couldn't find maximum protection")?; + let dev_str = parts.next().ok_or("Couldn't find device")?; + let ino_str = parts.next().ok_or("Couldn't find inode")?; + let offset_str = parts.next().ok_or("Couldn't find offset")?; + let _rsv_str = parts.next().ok_or("Couldn't find reserved pages")?; + let _guardsize_str = parts.next().ok_or("Couldn't find guard size")?; + let _refcnt_str = parts.next().ok_or("Couldn't find reference count")?; + let _mapcnt_str = parts.next().ok_or("Couldn't find mapped count")?; + let pathname_str = parts.next().unwrap_or(""); // pathname may be omitted. + + let hex = + |s: &str| usize::from_str_radix(&s[2..], 16).map_err(|_| "Couldn't parse hex number"); + let address = { (hex(vaddr_str)?, hex(vaddr_str)? + hex(size_str)?) }; + + // TODO: Probably a rust'ier way of doing this + let mut perms: [char; 4] = ['-', '-', '-', '-']; + let perm_str: [char; 3] = ['r', 'w', 'x']; + let perm_bits: [usize; 3] = [0x1, 0x2, 0x4]; + + for (pos, val) in perm_bits.iter().enumerate() { + let prot = hex(prot_str)?; + if val & prot != 0 { + perms[pos] = perm_str[pos] + } + } + + let offset = hex(offset_str)?; + let dev = { (hex(dev_str)?, 0x00000000) }; + let inode = hex(ino_str)?; + let pathname = pathname_str.into(); + + Ok(MapsEntry { address, perms, offset, dev, inode, pathname }) + } +} + +#[test] +fn check_maps_entry_parsing_64bit() { + assert_eq!( + "0xffffffffff600000,0x0000000000001000,0x00000071,0x04,0x0f,0x00000000,0x0000000000000000,\ + 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/foo" + .parse::() + .unwrap(), + MapsEntry { + address: (0xffffffffff600000, 0xffffffffff601000), + perms: ['-', '-', 'x', '-'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: "/proc/boot/foo".into(), + } + ); + + assert_eq!( + "0x00007f5985f46000,0x0000000000002000,0x00000071,0x03,0x0f,0x00000103,0x0000000076021795,\ + 0x0000000000039000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/usr/lib/ldqnx-64.so.2" + .parse::() + .unwrap(), + MapsEntry { + address: (0x7f5985f46000, 0x7f5985f48000), + perms: ['r', 'w', '-', '-'], + offset: 0x00039000, + dev: (0x103, 0x0), + inode: 0x76021795, + pathname: "/usr/lib/ldqnx-64.so.2".into(), + } + ); + assert_eq!( + "0x00000035b1a21000,0x0000000000001000,0x00000071,0x03,0x0f,0x00000000,0x0000000000000000,\ + 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003," + .parse::() + .unwrap(), + MapsEntry { + address: (0x35b1a21000, 0x35b1a22000), + perms: ['r', 'w', '-', '-'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: Default::default(), + } + ); +} From 42045303679e9073cc91ced2de58bca194a5a90b Mon Sep 17 00:00:00 2001 From: gh-tr Date: Thu, 22 Dec 2022 09:36:10 -0500 Subject: [PATCH 4/6] Fixing automated formatting --- src/symbolize/gimli/libs_dl_iterate_phdr.rs | 6 ++---- .../gimli/parse_running_mmaps_unix_default.rs | 17 ++++++++++++++--- .../gimli/parse_running_mmaps_unix_nto.rs | 9 ++++++++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/symbolize/gimli/libs_dl_iterate_phdr.rs b/src/symbolize/gimli/libs_dl_iterate_phdr.rs index 25e9df940..71ec5776f 100644 --- a/src/symbolize/gimli/libs_dl_iterate_phdr.rs +++ b/src/symbolize/gimli/libs_dl_iterate_phdr.rs @@ -34,10 +34,8 @@ fn infer_current_exe(base_addr: usize) -> OsString { // `info` should be a valid pointers. // `vec` should be a valid pointer to a `std::Vec`. unsafe extern "C" fn callback( - #[cfg(not(target_os = "nto"))] - info: *mut libc::dl_phdr_info, - #[cfg(target_os = "nto")] - info: *const libc::dl_phdr_info, + #[cfg(not(target_os = "nto"))] info: *mut libc::dl_phdr_info, + #[cfg(target_os = "nto")] info: *const libc::dl_phdr_info, _size: libc::size_t, vec: *mut libc::c_void, ) -> libc::c_int { diff --git a/src/symbolize/gimli/parse_running_mmaps_unix_default.rs b/src/symbolize/gimli/parse_running_mmaps_unix_default.rs index 834319a61..480f0a3fd 100644 --- a/src/symbolize/gimli/parse_running_mmaps_unix_default.rs +++ b/src/symbolize/gimli/parse_running_mmaps_unix_default.rs @@ -57,7 +57,14 @@ impl FromStr for MapsEntry { let inode = hex(inode_str)?; let pathname = pathname_str.into(); - Ok(MapsEntry { address, perms, offset, dev, inode, pathname }) + Ok(MapsEntry { + address, + perms, + offset, + dev, + inode, + pathname, + }) } } @@ -95,7 +102,9 @@ fn check_maps_entry_parsing_64bit() { } ); assert_eq!( - "35b1a21000-35b1a22000 rw-p 00000000 00:00 0".parse::().unwrap(), + "35b1a21000-35b1a22000 rw-p 00000000 00:00 0" + .parse::() + .unwrap(), MapsEntry { address: (0x35b1a21000, 0x35b1a22000), perms: ['r', 'w', '-', 'p'], @@ -145,7 +154,9 @@ fn check_maps_entry_parsing_32bit() { } ); assert_eq!( - "b7e02000-b7e03000 rw-p 00000000 00:00 0".parse::().unwrap(), + "b7e02000-b7e03000 rw-p 00000000 00:00 0" + .parse::() + .unwrap(), MapsEntry { address: (0xb7e02000, 0xb7e03000), perms: ['r', 'w', '-', 'p'], diff --git a/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs b/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs index cf4c856c4..c8462ec1b 100644 --- a/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs +++ b/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs @@ -48,7 +48,14 @@ impl FromStr for MapsEntry { let inode = hex(ino_str)?; let pathname = pathname_str.into(); - Ok(MapsEntry { address, perms, offset, dev, inode, pathname }) + Ok(MapsEntry { + address, + perms, + offset, + dev, + inode, + pathname, + }) } } From 75e65e7b5a53ccaa63206f8da99d21771d510561 Mon Sep 17 00:00:00 2001 From: gh-tr Date: Fri, 23 Dec 2022 14:48:43 -0500 Subject: [PATCH 5/6] Merged separate OS implementations into the original source file as the automated build system is unhappy about naming conventions on 1.42.0 --- .../gimli/parse_running_mmaps_unix.rs | 309 ++++++++++++++++-- .../gimli/parse_running_mmaps_unix_default.rs | 169 ---------- .../gimli/parse_running_mmaps_unix_nto.rs | 107 ------ 3 files changed, 284 insertions(+), 301 deletions(-) delete mode 100644 src/symbolize/gimli/parse_running_mmaps_unix_default.rs delete mode 100644 src/symbolize/gimli/parse_running_mmaps_unix_nto.rs diff --git a/src/symbolize/gimli/parse_running_mmaps_unix.rs b/src/symbolize/gimli/parse_running_mmaps_unix.rs index 804d82eb3..c356d21e6 100644 --- a/src/symbolize/gimli/parse_running_mmaps_unix.rs +++ b/src/symbolize/gimli/parse_running_mmaps_unix.rs @@ -4,8 +4,8 @@ use super::mystd::fs::File; use super::mystd::io::Read; -use super::OsString; -use super::{String, Vec}; +use super::mystd::str::FromStr; +use super::{OsString, String, Vec}; #[derive(PartialEq, Eq, Debug)] pub struct MapsEntry { @@ -53,16 +53,6 @@ pub struct MapsEntry { pathname: OsString, } -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 - } -} - fn concat_str(a: &str, b: &str) -> String { let mut e = String::from(a); e += b; @@ -93,21 +83,290 @@ fn parse_maps_lines(content: &str, skip: usize) -> Result, String Ok(data) } -cfg_if::cfg_if! { - if #[cfg(target_os = "nto")] { - mod parse_running_mmaps_unix_nto; - use parse_running_mmaps_unix_nto::*; - } else { - mod parse_running_mmaps_unix_default; - use parse_running_mmaps_unix_default::*; +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 + } +} + +#[cfg(not(target_os = "nto"))] +pub fn config() -> (&'static str, usize) { + ("/proc/self/maps", 0) +} + +#[cfg(not(target_os = "nto"))] +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 { + 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, + }) } } -#[cfg(test)] -mod test { - #[test] - fn test_parse_maps() { - use super::*; - assert!(parse_maps().is_ok()); +#[cfg(target_os = "nto")] +pub fn config() -> (&'static str, usize) { + ("/proc/self/pmap", 1) +} + +#[cfg(target_os = "nto")] +impl FromStr for MapsEntry { + type Err = &'static str; + + // Format: vaddr,size,flags,prot,maxprot,dev,ino,offset,rsv,guardsize,refcnt,mapcnt,path + // e.g.: "0x00000022fa36b000,0x0000000000002000,0x00000071,0x05,0x0f,0x0000040b,0x00000000000000dd, + // 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/cat" + fn from_str(s: &str) -> Result { + let mut parts = s.split(','); + let vaddr_str = parts.next().ok_or("Couldn't find virtual address")?; + let size_str = parts.next().ok_or("Couldn't find size")?; + let _flags_str = parts.next().ok_or("Couldn't find flags")?; + let prot_str = parts.next().ok_or("Couldn't find protection")?; + let _maxprot_str = parts.next().ok_or("Couldn't find maximum protection")?; + let dev_str = parts.next().ok_or("Couldn't find device")?; + let ino_str = parts.next().ok_or("Couldn't find inode")?; + let offset_str = parts.next().ok_or("Couldn't find offset")?; + let _rsv_str = parts.next().ok_or("Couldn't find reserved pages")?; + let _guardsize_str = parts.next().ok_or("Couldn't find guard size")?; + let _refcnt_str = parts.next().ok_or("Couldn't find reference count")?; + let _mapcnt_str = parts.next().ok_or("Couldn't find mapped count")?; + let pathname_str = parts.next().unwrap_or(""); // pathname may be omitted. + + let hex = + |s: &str| usize::from_str_radix(&s[2..], 16).map_err(|_| "Couldn't parse hex number"); + let address = { (hex(vaddr_str)?, hex(vaddr_str)? + hex(size_str)?) }; + + // TODO: Probably a rust'ier way of doing this + let mut perms: [char; 4] = ['-', '-', '-', '-']; + let perm_str: [char; 3] = ['r', 'w', 'x']; + let perm_bits: [usize; 3] = [0x1, 0x2, 0x4]; + + for (pos, val) in perm_bits.iter().enumerate() { + let prot = hex(prot_str)?; + if val & prot != 0 { + perms[pos] = perm_str[pos] + } + } + + let offset = hex(offset_str)?; + let dev = { (hex(dev_str)?, 0x00000000) }; + let inode = hex(ino_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(all(target_pointer_width = "64", not(target_os = "nto")))] +#[test] +fn check_maps_entry_parsing_64bit() { + assert_eq!( + "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 \ + [vsyscall]" + .parse::() + .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::() + .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::() + .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) +#[cfg(not(target_os = "nto"))] +#[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::() + .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::() + .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::() + .unwrap(), + MapsEntry { + address: (0xb7e02000, 0xb7e03000), + perms: ['r', 'w', '-', 'p'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: Default::default(), + } + ); +} + +#[cfg(target_os = "nto")] +#[test] +fn check_maps_entry_parsing_64bit() { + assert_eq!( + "0xffffffffff600000,0x0000000000001000,0x00000071,0x04,0x0f,0x00000000,0x0000000000000000,\ + 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/foo" + .parse::() + .unwrap(), + MapsEntry { + address: (0xffffffffff600000, 0xffffffffff601000), + perms: ['-', '-', 'x', '-'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: "/proc/boot/foo".into(), + } + ); + + assert_eq!( + "0x00007f5985f46000,0x0000000000002000,0x00000071,0x03,0x0f,0x00000103,0x0000000076021795,\ + 0x0000000000039000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/usr/lib/ldqnx-64.so.2" + .parse::() + .unwrap(), + MapsEntry { + address: (0x7f5985f46000, 0x7f5985f48000), + perms: ['r', 'w', '-', '-'], + offset: 0x00039000, + dev: (0x103, 0x0), + inode: 0x76021795, + pathname: "/usr/lib/ldqnx-64.so.2".into(), + } + ); + assert_eq!( + "0x00000035b1a21000,0x0000000000001000,0x00000071,0x03,0x0f,0x00000000,0x0000000000000000,\ + 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003," + .parse::() + .unwrap(), + MapsEntry { + address: (0x35b1a21000, 0x35b1a22000), + perms: ['r', 'w', '-', '-'], + offset: 0x00000000, + dev: (0x00, 0x00), + inode: 0x0, + pathname: Default::default(), + } + ); +} diff --git a/src/symbolize/gimli/parse_running_mmaps_unix_default.rs b/src/symbolize/gimli/parse_running_mmaps_unix_default.rs deleted file mode 100644 index 480f0a3fd..000000000 --- a/src/symbolize/gimli/parse_running_mmaps_unix_default.rs +++ /dev/null @@ -1,169 +0,0 @@ -use super::super::mystd::str::FromStr; -use super::MapsEntry; - -pub fn config() -> (&'static str, usize) { - ("/proc/self/maps", 0) -} - -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 { - 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::() - .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::() - .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::() - .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::() - .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::() - .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::() - .unwrap(), - MapsEntry { - address: (0xb7e02000, 0xb7e03000), - perms: ['r', 'w', '-', 'p'], - offset: 0x00000000, - dev: (0x00, 0x00), - inode: 0x0, - pathname: Default::default(), - } - ); -} diff --git a/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs b/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs deleted file mode 100644 index c8462ec1b..000000000 --- a/src/symbolize/gimli/parse_running_mmaps_unix_nto.rs +++ /dev/null @@ -1,107 +0,0 @@ -use super::super::mystd::str::FromStr; -use super::MapsEntry; - -pub fn config() -> (&'static str, usize) { - ("/proc/self/pmap", 1) -} - -impl FromStr for MapsEntry { - type Err = &'static str; - - // Format: vaddr,size,flags,prot,maxprot,dev,ino,offset,rsv,guardsize,refcnt,mapcnt,path - // e.g.: "0x00000022fa36b000,0x0000000000002000,0x00000071,0x05,0x0f,0x0000040b,0x00000000000000dd, - // 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/cat" - fn from_str(s: &str) -> Result { - let mut parts = s.split(','); - let vaddr_str = parts.next().ok_or("Couldn't find virtual address")?; - let size_str = parts.next().ok_or("Couldn't find size")?; - let _flags_str = parts.next().ok_or("Couldn't find flags")?; - let prot_str = parts.next().ok_or("Couldn't find protection")?; - let _maxprot_str = parts.next().ok_or("Couldn't find maximum protection")?; - let dev_str = parts.next().ok_or("Couldn't find device")?; - let ino_str = parts.next().ok_or("Couldn't find inode")?; - let offset_str = parts.next().ok_or("Couldn't find offset")?; - let _rsv_str = parts.next().ok_or("Couldn't find reserved pages")?; - let _guardsize_str = parts.next().ok_or("Couldn't find guard size")?; - let _refcnt_str = parts.next().ok_or("Couldn't find reference count")?; - let _mapcnt_str = parts.next().ok_or("Couldn't find mapped count")?; - let pathname_str = parts.next().unwrap_or(""); // pathname may be omitted. - - let hex = - |s: &str| usize::from_str_radix(&s[2..], 16).map_err(|_| "Couldn't parse hex number"); - let address = { (hex(vaddr_str)?, hex(vaddr_str)? + hex(size_str)?) }; - - // TODO: Probably a rust'ier way of doing this - let mut perms: [char; 4] = ['-', '-', '-', '-']; - let perm_str: [char; 3] = ['r', 'w', 'x']; - let perm_bits: [usize; 3] = [0x1, 0x2, 0x4]; - - for (pos, val) in perm_bits.iter().enumerate() { - let prot = hex(prot_str)?; - if val & prot != 0 { - perms[pos] = perm_str[pos] - } - } - - let offset = hex(offset_str)?; - let dev = { (hex(dev_str)?, 0x00000000) }; - let inode = hex(ino_str)?; - let pathname = pathname_str.into(); - - Ok(MapsEntry { - address, - perms, - offset, - dev, - inode, - pathname, - }) - } -} - -#[test] -fn check_maps_entry_parsing_64bit() { - assert_eq!( - "0xffffffffff600000,0x0000000000001000,0x00000071,0x04,0x0f,0x00000000,0x0000000000000000,\ - 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/proc/boot/foo" - .parse::() - .unwrap(), - MapsEntry { - address: (0xffffffffff600000, 0xffffffffff601000), - perms: ['-', '-', 'x', '-'], - offset: 0x00000000, - dev: (0x00, 0x00), - inode: 0x0, - pathname: "/proc/boot/foo".into(), - } - ); - - assert_eq!( - "0x00007f5985f46000,0x0000000000002000,0x00000071,0x03,0x0f,0x00000103,0x0000000076021795,\ - 0x0000000000039000,0x0000000000000000,0x00000000,0x00000005,0x00000003,/usr/lib/ldqnx-64.so.2" - .parse::() - .unwrap(), - MapsEntry { - address: (0x7f5985f46000, 0x7f5985f48000), - perms: ['r', 'w', '-', '-'], - offset: 0x00039000, - dev: (0x103, 0x0), - inode: 0x76021795, - pathname: "/usr/lib/ldqnx-64.so.2".into(), - } - ); - assert_eq!( - "0x00000035b1a21000,0x0000000000001000,0x00000071,0x03,0x0f,0x00000000,0x0000000000000000,\ - 0x0000000000000000,0x0000000000000000,0x00000000,0x00000005,0x00000003," - .parse::() - .unwrap(), - MapsEntry { - address: (0x35b1a21000, 0x35b1a22000), - perms: ['r', 'w', '-', '-'], - offset: 0x00000000, - dev: (0x00, 0x00), - inode: 0x0, - pathname: Default::default(), - } - ); -} From 84fd7c8441b07b40d01868ca0a64870c9a561e45 Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Mon, 13 Mar 2023 14:47:16 +0100 Subject: [PATCH 6/6] Reduce visibility back to pub(super) --- src/symbolize/gimli/parse_running_mmaps_unix.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/symbolize/gimli/parse_running_mmaps_unix.rs b/src/symbolize/gimli/parse_running_mmaps_unix.rs index c356d21e6..ea7064c72 100644 --- a/src/symbolize/gimli/parse_running_mmaps_unix.rs +++ b/src/symbolize/gimli/parse_running_mmaps_unix.rs @@ -8,7 +8,7 @@ use super::mystd::str::FromStr; use super::{OsString, String, Vec}; #[derive(PartialEq, Eq, Debug)] -pub struct MapsEntry { +pub(super) struct MapsEntry { /// start (inclusive) and limit (exclusive) of address range. address: (usize, usize), /// The perms field are the permissions for the entry @@ -69,7 +69,7 @@ fn read_file(file: &str) -> Result { Ok(buf) } -pub fn parse_maps() -> Result, String> { +pub(super) fn parse_maps() -> Result, String> { let (file, skip) = config(); let content = read_file(file)?; parse_maps_lines(&content, skip)