summary refs log tree commit diff
path: root/ripple
diff options
context:
space:
mode:
authoredef <edef@unfathomable.blue>2022-07-30 02:07:13 +0000
committeredef <edef@unfathomable.blue>2022-07-30 02:23:50 +0000
commit9300a465016c7474ee61d6cefa97c662c190aeff (patch)
tree065a35b888364d2607fd3a7afc49f700baf5327e /ripple
parentdb58b80ac7d1cc4f16ef1bc21146de9ab6e90302 (diff)
ripple/minitrace: dump early memory mappings
These are from right after exec(2), so they are all done by the kernel.

Change-Id: Ic76aa9c40acac64462fa6ab5c33cabcee3e096e5
Diffstat (limited to 'ripple')
-rw-r--r--ripple/Cargo.lock18
-rw-r--r--ripple/minitrace/Cargo.toml2
-rw-r--r--ripple/minitrace/src/main.rs17
-rw-r--r--ripple/minitrace/src/maps_file.rs186
4 files changed, 223 insertions, 0 deletions
diff --git a/ripple/Cargo.lock b/ripple/Cargo.lock
index ceb0235..bb49f57 100644
--- a/ripple/Cargo.lock
+++ b/ripple/Cargo.lock
@@ -668,12 +668,19 @@ dependencies = [
 ]
 
 [[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"
 dependencies = [
  "anyhow",
  "bitflags",
  "nix",
+ "nom",
 ]
 
 [[package]]
@@ -696,6 +703,17 @@ dependencies = [
 ]
 
 [[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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
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<Vec<maps_file::Mapping>> {
+		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::<Result<_, _>>()?;
+		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 <edef@unfathomable.blue>
+// 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<Mapping> {
+	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);
+}