std/backtrace/src/symbolize/gimli/
parse_running_mmaps_unix.rs1use super::mystd::ffi::OsString;
6use super::mystd::fs::File;
7use super::mystd::io::Read;
8use alloc::string::String;
9use alloc::vec::Vec;
10use core::str::FromStr;
11
12#[derive(PartialEq, Eq, Debug)]
13pub(super) struct MapsEntry {
14 address: (usize, usize),
16 offset: u64,
18 pathname: OsString,
44}
45
46pub(super) fn parse_maps() -> Result<Vec<MapsEntry>, &'static str> {
47 let mut proc_self_maps =
48 File::open("/proc/self/maps").map_err(|_| "Couldn't open /proc/self/maps")?;
49 let mut buf = String::new();
50 let _bytes_read = proc_self_maps
51 .read_to_string(&mut buf)
52 .map_err(|_| "Couldn't read /proc/self/maps")?;
53
54 let mut v = Vec::new();
55 let mut buf = buf.as_str();
56 while let Some(match_idx) = buf.bytes().position(|b| b == b'\n') {
57 let line = unsafe { buf.get_unchecked(..match_idx) };
61
62 v.push(line.parse()?);
63
64 buf = unsafe { buf.get_unchecked((match_idx + 1)..) };
66 }
67
68 Ok(v)
69}
70
71impl MapsEntry {
72 pub(super) fn pathname(&self) -> &OsString {
73 &self.pathname
74 }
75
76 pub(super) fn ip_matches(&self, ip: usize) -> bool {
77 self.address.0 <= ip && ip < self.address.1
78 }
79
80 #[cfg(target_os = "android")]
81 pub(super) fn offset(&self) -> u64 {
82 self.offset
83 }
84}
85
86impl FromStr for MapsEntry {
87 type Err = &'static str;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 let mut state = s;
97
98 fn parse_start<'a>(state: &mut &'a str) -> &'a str {
99 let start_idx = state.bytes().position(|b| b != b' ');
102 if let Some(start_idx) = start_idx {
103 *state = unsafe { state.get_unchecked(start_idx..) };
106 }
107 let match_idx = state.bytes().position(|b| b == b' ');
108 match match_idx {
109 None => {
110 let result = *state;
111 *state = "";
112 result
113 }
114 Some(match_idx) => {
115 let result = unsafe { state.get_unchecked(..match_idx) };
118 *state = unsafe { state.get_unchecked((match_idx + 1)..) };
120 result
121 }
122 }
123 }
124
125 fn error(msg: &str) -> &str {
126 if cfg!(debug_assertions) {
127 msg
128 } else {
129 "invalid map entry"
130 }
131 }
132
133 let range_str = parse_start(&mut state);
134 if range_str.is_empty() {
135 return Err(error("Couldn't find address"));
136 }
137
138 let perms_str = parse_start(&mut state);
139 if perms_str.is_empty() {
140 return Err(error("Couldn't find permissions"));
141 }
142
143 let offset_str = parse_start(&mut state);
144 if offset_str.is_empty() {
145 return Err(error("Couldn't find offset"));
146 }
147
148 let dev_str = parse_start(&mut state);
149 if dev_str.is_empty() {
150 return Err(error("Couldn't find dev"));
151 }
152
153 let inode_str = parse_start(&mut state);
154 if inode_str.is_empty() {
155 return Err(error("Couldn't find inode"));
156 }
157
158 let pathname_str = state.trim_ascii_start();
160
161 let hex = |s| usize::from_str_radix(s, 16).map_err(|_| error("Couldn't parse hex number"));
162 let hex64 = |s| u64::from_str_radix(s, 16).map_err(|_| error("Couldn't parse hex number"));
163
164 let address = if let Some((start, limit)) = range_str.split_once('-') {
165 (hex(start)?, hex(limit)?)
166 } else {
167 return Err(error("Couldn't parse address range"));
168 };
169 let offset = hex64(offset_str)?;
170 let pathname = pathname_str.into();
171
172 Ok(MapsEntry {
173 address,
174 offset,
175 pathname,
176 })
177 }
178}
179
180#[cfg(target_pointer_width = "64")]
182#[test]
183fn check_maps_entry_parsing_64bit() {
184 assert_eq!(
185 "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 \
186 [vsyscall]"
187 .parse::<MapsEntry>()
188 .unwrap(),
189 MapsEntry {
190 address: (0xffffffffff600000, 0xffffffffff601000),
191 offset: 0x00000000,
192 pathname: "[vsyscall]".into(),
193 }
194 );
195
196 assert_eq!(
197 "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 \
198 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
199 .parse::<MapsEntry>()
200 .unwrap(),
201 MapsEntry {
202 address: (0x7f5985f46000, 0x7f5985f48000),
203 offset: 0x00039000,
204 pathname: "/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2".into(),
205 }
206 );
207 assert_eq!(
208 "35b1a21000-35b1a22000 rw-p 00000000 00:00 0"
209 .parse::<MapsEntry>()
210 .unwrap(),
211 MapsEntry {
212 address: (0x35b1a21000, 0x35b1a22000),
213 offset: 0x00000000,
214 pathname: Default::default(),
215 }
216 );
217}
218
219#[test]
221fn check_maps_entry_parsing_32bit() {
222 assert_eq!(
228 "08056000-08077000 rw-p 00000000 00:00 0 \
229 [heap]"
230 .parse::<MapsEntry>()
231 .unwrap(),
232 MapsEntry {
233 address: (0x08056000, 0x08077000),
234 offset: 0x00000000,
235 pathname: "[heap]".into(),
236 }
237 );
238
239 assert_eq!(
240 "b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
241 /usr/lib/locale/locale-archive"
242 .parse::<MapsEntry>()
243 .unwrap(),
244 MapsEntry {
245 address: (0xb7c79000, 0xb7e02000),
246 offset: 0x00000000,
247 pathname: "/usr/lib/locale/locale-archive".into(),
248 }
249 );
250 assert_eq!(
251 "b7e02000-b7e03000 rw-p 00000000 00:00 0"
252 .parse::<MapsEntry>()
253 .unwrap(),
254 MapsEntry {
255 address: (0xb7e02000, 0xb7e03000),
256 offset: 0x00000000,
257 pathname: Default::default(),
258 }
259 );
260 assert_eq!(
261 "b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
262 /executable/path/with some spaces"
263 .parse::<MapsEntry>()
264 .unwrap(),
265 MapsEntry {
266 address: (0xb7c79000, 0xb7e02000),
267 offset: 0x00000000,
268 pathname: "/executable/path/with some spaces".into(),
269 }
270 );
271 assert_eq!(
272 "b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
273 /executable/path/with multiple-continuous spaces "
274 .parse::<MapsEntry>()
275 .unwrap(),
276 MapsEntry {
277 address: (0xb7c79000, 0xb7e02000),
278 offset: 0x00000000,
279 pathname: "/executable/path/with multiple-continuous spaces ".into(),
280 }
281 );
282 assert_eq!(
283 " b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
284 /executable/path/starts-with-spaces"
285 .parse::<MapsEntry>()
286 .unwrap(),
287 MapsEntry {
288 address: (0xb7c79000, 0xb7e02000),
289 offset: 0x00000000,
290 pathname: "/executable/path/starts-with-spaces".into(),
291 }
292 );
293}