From 9300a465016c7474ee61d6cefa97c662c190aeff Mon Sep 17 00:00:00 2001 From: edef Date: Sat, 30 Jul 2022 02:07:13 +0000 Subject: ripple/minitrace: dump early memory mappings These are from right after exec(2), so they are all done by the kernel. Change-Id: Ic76aa9c40acac64462fa6ab5c33cabcee3e096e5 --- ripple/Cargo.lock | 18 ++++ ripple/minitrace/Cargo.toml | 2 + ripple/minitrace/src/main.rs | 17 ++++ ripple/minitrace/src/maps_file.rs | 186 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 223 insertions(+) create mode 100644 ripple/minitrace/src/maps_file.rs (limited to 'ripple') diff --git a/ripple/Cargo.lock b/ripple/Cargo.lock index ceb0235..bb49f57 100644 --- a/ripple/Cargo.lock +++ b/ripple/Cargo.lock @@ -667,6 +667,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" + [[package]] name = "minitrace" version = "0.0.0" @@ -674,6 +680,7 @@ dependencies = [ "anyhow", "bitflags", "nix", + "nom", ] [[package]] @@ -695,6 +702,17 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nom" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + [[package]] name = "num-traits" version = "0.2.14" diff --git a/ripple/minitrace/Cargo.toml b/ripple/minitrace/Cargo.toml index 92f3d41..261e2c2 100644 --- a/ripple/minitrace/Cargo.toml +++ b/ripple/minitrace/Cargo.toml @@ -10,3 +10,5 @@ edition = "2021" nix = "0.23.1" anyhow = "1.0.53" bitflags = "1.3.2" +# TODO(edef): upgrade nom, we're stuck with 7.0 for MSRV reasons +nom = ">= 7.0.0, <7.1" diff --git a/ripple/minitrace/src/main.rs b/ripple/minitrace/src/main.rs index cf23034..0f9cb0d 100644 --- a/ripple/minitrace/src/main.rs +++ b/ripple/minitrace/src/main.rs @@ -27,6 +27,8 @@ use { }, }; +mod maps_file; + // TODO(edef): consider implementing this in terms of TID? // tgids are a strict subset of tids #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -92,6 +94,19 @@ impl Process { Ok(CString::from_vec_with_nul(buf).expect("logic error")) } + + fn read_mappings(&self) -> Result> { + let contents = std::fs::read_to_string(format!("/proc/{}/maps", self.tgid.0))?; + + // TODO(edef): consult /proc/$pid/map_files/* for pathnames, since /proc/$pid/maps is unreliable with odd paths + // we'll want to verify the two against each other, just to be sure they're congruent + + let mappings = contents + .lines() + .map(maps_file::parse_mapping_line) + .collect::>()?; + Ok(mappings) + } } macro_rules! define_syscalls { @@ -384,6 +399,8 @@ fn main() -> Result<()> { cmd })?; + println!("{:#?}", process.read_mappings()?); + let options = ptrace::Options::PTRACE_O_TRACESYSGOOD | ptrace::Options::PTRACE_O_TRACECLONE | ptrace::Options::PTRACE_O_EXITKILL; diff --git a/ripple/minitrace/src/maps_file.rs b/ripple/minitrace/src/maps_file.rs new file mode 100644 index 0000000..84fc0fb --- /dev/null +++ b/ripple/minitrace/src/maps_file.rs @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: edef +// SPDX-License-Identifier: OSL-3.0 + +use { + anyhow::bail, + nom::{ + branch::alt, + bytes::complete::{tag, take_while}, + combinator::map_res, + sequence::tuple, + IResult, + }, + std::fmt::{self, Debug}, +}; + +fn whitespace(s: &str) -> IResult<&str, &str> { + take_while(|c| c == ' ')(s) +} + +fn hex_u32(s: &str) -> IResult<&str, u32> { + map_res(take_while(|c: char| c.is_ascii_hexdigit()), |src| { + u32::from_str_radix(src, 16) + })(s) +} + +fn hex_u64(s: &str) -> IResult<&str, u64> { + map_res(take_while(|c: char| c.is_ascii_hexdigit()), |src| { + u64::from_str_radix(src, 16) + })(s) +} + +fn dec_u64(s: &str) -> IResult<&str, u64> { + map_res(take_while(|c: char| c.is_ascii_hexdigit()), |src| { + u64::from_str_radix(src, 10) + })(s) +} + +fn perm(c: &'static str) -> impl Fn(&str) -> IResult<&str, bool> { + move |input| { + let (input, tag) = alt((tag(c), tag("-")))(input)?; + Ok((input, tag != "-")) + } +} + +fn shared(input: &str) -> IResult<&str, bool> { + let (input, tag) = alt((tag("p"), tag("s")))(input)?; + Ok((input, tag == "s")) +} + +fn pathname(input: &str) -> IResult<&str, &str> { + take_while(|c| c != '\n')(input) +} + +fn mapping(input: &str) -> IResult<&str, Mapping> { + let ( + input, + ( + start, + _, + end, + _, + perm_read, + perm_write, + perm_exec, + shared, + _, + offset, + _, + dev_maj, + _, + dev_min, + _, + inode, + _, + pathname, + ), + ) = tuple(( + hex_u64, + tag("-"), + hex_u64, + whitespace, + perm("r"), + perm("w"), + perm("x"), + shared, + whitespace, + hex_u64, + whitespace, + hex_u32, + tag(":"), + hex_u32, + whitespace, + dec_u64, + whitespace, + pathname, + ))(input)?; + Ok(( + input, + Mapping { + start, + end, + perm_read, + perm_write, + perm_exec, + shared, + offset, + dev: (dev_maj, dev_min), + inode, + pathname: pathname.to_owned(), + }, + )) +} + +pub(crate) fn parse_mapping_line(line: &str) -> anyhow::Result { + match mapping(line) { + Ok(("", mapping)) => Ok(mapping), + Ok(_) => unreachable!(), + Err(err) => bail!("Cannot parse mapping: {err}"), + } +} + +#[derive(Eq, PartialEq)] +pub(crate) struct Mapping { + start: u64, + end: u64, + perm_read: bool, + perm_write: bool, + perm_exec: bool, + shared: bool, + offset: u64, + dev: (u32, u32), + inode: u64, + pathname: String, +} + +impl Debug for Mapping { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use std::fmt::Write; + + let Mapping { + start, + end, + perm_read, + perm_write, + perm_exec, + shared, + offset, + dev: (dev_maj, dev_min), + inode, + ref pathname, + } = *self; + + write!(f, "{start:016x}-{end:016x} ")?; + + for (c, p) in [('r', perm_read), ('w', perm_write), ('x', perm_exec)] { + f.write_char(if p { c } else { '-' })?; + } + f.write_char(if shared { 's' } else { 'p' })?; + f.write_char(' ')?; + + write!( + f, + "{offset:016x} {dev_maj:04x}:{dev_min:04x} {inode:20} {pathname}" + ) + } +} + +#[test] +fn golden_mapping() { + let line = + "00400000-00407000 r--p 00000000 00:1e 7507895 /nix/store/hgl0ydlkgs6y6hx9h7k209shw3v7z77j-coreutils-9.0/bin/coreutils"; + let golden = Mapping { + start: 0x00400000, + end: 0x00407000, + perm_read: true, + perm_write: false, + perm_exec: false, + shared: false, + offset: 0, + dev: (0x00, 0x1e), + inode: 7507895, + pathname: "/nix/store/hgl0ydlkgs6y6hx9h7k209shw3v7z77j-coreutils-9.0/bin/coreutils" + .to_owned(), + }; + assert_eq!(mapping(line).unwrap().1, golden); +} -- cgit 1.4.1