Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for QNX Neutrino #507

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/symbolize/gimli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"),
))] {
Expand Down
3 changes: 2 additions & 1 deletion src/symbolize/gimli/libs_dl_iterate_phdr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +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(
info: *mut 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 {
Expand Down
152 changes: 141 additions & 11 deletions src/symbolize/gimli/parse_running_mmaps_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,34 @@ pub(super) struct MapsEntry {
pathname: OsString,
}

pub(super) fn parse_maps() -> Result<Vec<MapsEntry>, &'static str> {
let mut v = Vec::new();
fn concat_str(a: &str, b: &str) -> String {
let mut e = String::from(a);
e += b;
e
}

fn read_file(file: &str) -> Result<String, String> {
let mut proc_self_maps =
File::open("/proc/self/maps").map_err(|_| "Couldn't open /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(|_| "Couldn't read /proc/self/maps")?;
for line in buf.lines() {
v.push(line.parse()?);
}
.map_err(|_| concat_str("Couldn't read file: ", file))?;
Ok(buf)
}
Comment on lines +56 to +70
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This machinery is a reimplementation of fs::read_to_string. You can import fs by changing

- use super::mystd::fs::File;
+ use super::mystd::fs::{self, File};

If you must, you can use map_err for the Err variant.


Ok(v)
pub(super) fn parse_maps() -> Result<Vec<MapsEntry>, String> {
let (file, skip) = config();
let content = read_file(file)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let content = read_file(file)?;
let content = fs::read_to_string(file).map_err(|e| e.to_string())?;

parse_maps_lines(&content, skip)
}

fn parse_maps_lines(content: &str, skip: usize) -> Result<Vec<MapsEntry>, String> {
Comment on lines +75 to +78
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is still functionally identical if we simply delete these lines?

Suggested change
parse_maps_lines(&content, skip)
}
fn parse_maps_lines(content: &str, skip: usize) -> Result<Vec<MapsEntry>, String> {

let mut data = Vec::new();
for line in content.lines().skip(skip) {
data.push(line.parse()?);
}
Ok(data)
}

impl MapsEntry {
Expand All @@ -78,6 +93,12 @@ impl MapsEntry {
}
}

#[cfg(not(target_os = "nto"))]
pub fn config() -> (&'static str, usize) {
("/proc/self/maps", 0)
}
Comment on lines +96 to +99
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This moves to runtime what can be known at compile-time. Please describe a static and a const instead, with appropriate names. If you wish, you can put them in a mod config {}.


#[cfg(not(target_os = "nto"))]
impl FromStr for MapsEntry {
type Err = &'static str;

Expand Down Expand Up @@ -141,13 +162,73 @@ impl FromStr for MapsEntry {
}
}

#[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<Self, Self::Err> {
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")?;
Comment on lines +179 to +190
Copy link
Member

@workingjubilee workingjubilee Jun 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be clear, on QNX Neutrino these are all required fields? Is there somewhere this format is documented for Neutrino? It seems to be a superset of the other format described here?

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
Copy link
Member

@workingjubilee workingjubilee Jun 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could probably cook something up using array::from_fn but I'm not going to block this on that. If you decide not to, though, please remove the TODO.

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")]
#[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]"
[vsyscall]"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for diffing this line?

.parse::<MapsEntry>()
.unwrap(),
MapsEntry {
Expand All @@ -162,7 +243,7 @@ fn check_maps_entry_parsing_64bit() {

assert_eq!(
"7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 \
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for diffing this line?

.parse::<MapsEntry>()
.unwrap(),
MapsEntry {
Expand Down Expand Up @@ -190,6 +271,7 @@ fn check_maps_entry_parsing_64bit() {
}

// (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:
Expand Down Expand Up @@ -240,3 +322,51 @@ fn check_maps_entry_parsing_32bit() {
}
);
}

#[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::<MapsEntry>()
.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::<MapsEntry>()
.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::<MapsEntry>()
.unwrap(),
MapsEntry {
address: (0x35b1a21000, 0x35b1a22000),
perms: ['r', 'w', '-', '-'],
offset: 0x00000000,
dev: (0x00, 0x00),
inode: 0x0,
pathname: Default::default(),
}
);
}