@@ -13,20 +13,8 @@ use core::str::FromStr;
13
13
pub ( super ) struct MapsEntry {
14
14
/// start (inclusive) and limit (exclusive) of address range.
15
15
address : ( usize , usize ) ,
16
- /// The perms field are the permissions for the entry
17
- ///
18
- /// r = read
19
- /// w = write
20
- /// x = execute
21
- /// s = shared
22
- /// p = private (copy on write)
23
- perms : [ char ; 4 ] ,
24
16
/// Offset into the file (or "whatever").
25
17
offset : u64 ,
26
- /// device (major, minor)
27
- dev : ( usize , usize ) ,
28
- /// inode on the device. 0 indicates that no inode is associated with the memory region (e.g. uninitalized data aka BSS).
29
- inode : usize ,
30
18
/// Usually the file backing the mapping.
31
19
///
32
20
/// Note: The man page for proc includes a note about "coordination" by
@@ -56,15 +44,21 @@ pub(super) struct MapsEntry {
56
44
}
57
45
58
46
pub ( super ) fn parse_maps ( ) -> Result < Vec < MapsEntry > , & ' static str > {
59
- let mut v = Vec :: new ( ) ;
60
47
let mut proc_self_maps =
61
48
File :: open ( "/proc/self/maps" ) . map_err ( |_| "Couldn't open /proc/self/maps" ) ?;
62
49
let mut buf = String :: new ( ) ;
63
50
let _bytes_read = proc_self_maps
64
51
. read_to_string ( & mut buf)
65
52
. map_err ( |_| "Couldn't read /proc/self/maps" ) ?;
66
- for line in buf. lines ( ) {
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) } ;
58
+
67
59
v. push ( line. parse ( ) ?) ;
60
+
61
+ buf = unsafe { buf. get_unchecked ( ( match_idx + 1 ) ..) } ;
68
62
}
69
63
70
64
Ok ( v)
@@ -92,70 +86,86 @@ impl FromStr for MapsEntry {
92
86
// e.g.: "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]"
93
87
// e.g.: "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
94
88
// e.g.: "35b1a21000-35b1a22000 rw-p 00000000 00:00 0"
95
- //
96
- // Note that paths may contain spaces, so we can't use `str::split` for parsing (until
97
- // Split::remainder is stabilized #77998).
98
89
fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
99
- let ( range_str, s) = s. trim_start ( ) . split_once ( ' ' ) . unwrap_or ( ( s, "" ) ) ;
90
+ // While there are nicer standard library APIs available for this, we aim for minimal code size.
91
+
92
+ let mut state = s;
93
+
94
+ fn parse_start < ' a > ( state : & mut & ' a str ) -> & ' a str {
95
+ let start_idx = state. bytes ( ) . position ( |b| b != b' ' ) ;
96
+ if let Some ( start_idx) = start_idx {
97
+ // SAFETY: It comes from position, so it's in bounds.
98
+ // It must be on a UTF-8 boundary as it's the first byte that isn't ' '.
99
+ * state = unsafe { state. get_unchecked ( start_idx..) } ;
100
+ }
101
+ let match_idx = state. bytes ( ) . position ( |b| b == b' ' ) ;
102
+ match match_idx {
103
+ None => {
104
+ let result = * state;
105
+ * state = "" ;
106
+ result
107
+ }
108
+ Some ( match_idx) => {
109
+ // SAFETY: match_index comes from .bytes().position() of an ASCII character,
110
+ // so it's both in bounds and a UTF-8 boundary
111
+ let result = unsafe { state. get_unchecked ( ..match_idx) } ;
112
+ // SAFETY: Since match_idx is the ' ', there must be at least the end after it.
113
+ * state = unsafe { state. get_unchecked ( ( match_idx + 1 ) ..) } ;
114
+ result
115
+ }
116
+ }
117
+ }
118
+
119
+ fn error ( msg : & str ) -> & str {
120
+ if cfg ! ( debug_assertions) {
121
+ msg
122
+ } else {
123
+ "invalid map entry"
124
+ }
125
+ }
126
+
127
+ let range_str = parse_start ( & mut state) ;
100
128
if range_str. is_empty ( ) {
101
- return Err ( "Couldn't find address" ) ;
129
+ return Err ( error ( "Couldn't find address" ) ) ;
102
130
}
103
131
104
- let ( perms_str, s ) = s . trim_start ( ) . split_once ( ' ' ) . unwrap_or ( ( s , "" ) ) ;
132
+ let perms_str = parse_start ( & mut state ) ;
105
133
if perms_str. is_empty ( ) {
106
- return Err ( "Couldn't find permissions" ) ;
134
+ return Err ( error ( "Couldn't find permissions" ) ) ;
107
135
}
108
136
109
- let ( offset_str, s ) = s . trim_start ( ) . split_once ( ' ' ) . unwrap_or ( ( s , "" ) ) ;
137
+ let offset_str = parse_start ( & mut state ) ;
110
138
if offset_str. is_empty ( ) {
111
- return Err ( "Couldn't find offset" ) ;
139
+ return Err ( error ( "Couldn't find offset" ) ) ;
112
140
}
113
141
114
- let ( dev_str, s ) = s . trim_start ( ) . split_once ( ' ' ) . unwrap_or ( ( s , "" ) ) ;
142
+ let dev_str = parse_start ( & mut state ) ;
115
143
if dev_str. is_empty ( ) {
116
- return Err ( "Couldn't find dev" ) ;
144
+ return Err ( error ( "Couldn't find dev" ) ) ;
117
145
}
118
146
119
- let ( inode_str, s ) = s . trim_start ( ) . split_once ( ' ' ) . unwrap_or ( ( s , "" ) ) ;
147
+ let inode_str = parse_start ( & mut state ) ;
120
148
if inode_str. is_empty ( ) {
121
149
return Err ( "Couldn't find inode" ) ;
122
150
}
123
151
124
152
// Pathname may be omitted in which case it will be empty
125
- let pathname_str = s . trim_start ( ) ;
153
+ let pathname_str = state . trim_ascii_start ( ) ;
126
154
127
- let hex = |s| usize:: from_str_radix ( s, 16 ) . map_err ( |_| "Couldn't parse hex number" ) ;
128
- let hex64 = |s| u64:: from_str_radix ( s, 16 ) . map_err ( |_| "Couldn't parse hex number" ) ;
155
+ let hex = |s| usize:: from_str_radix ( s, 16 ) . map_err ( |_| error ( "Couldn't parse hex number" ) ) ;
156
+ let hex64 = |s| u64:: from_str_radix ( s, 16 ) . map_err ( |_| error ( "Couldn't parse hex number" ) ) ;
129
157
130
158
let address = if let Some ( ( start, limit) ) = range_str. split_once ( '-' ) {
131
159
( hex ( start) ?, hex ( limit) ?)
132
160
} else {
133
- return Err ( "Couldn't parse address range" ) ;
134
- } ;
135
- let perms: [ char ; 4 ] = {
136
- let mut chars = perms_str. chars ( ) ;
137
- let mut c = || chars. next ( ) . ok_or ( "insufficient perms" ) ;
138
- let perms = [ c ( ) ?, c ( ) ?, c ( ) ?, c ( ) ?] ;
139
- if chars. next ( ) . is_some ( ) {
140
- return Err ( "too many perms" ) ;
141
- }
142
- perms
161
+ return Err ( error ( "Couldn't parse address range" ) ) ;
143
162
} ;
144
163
let offset = hex64 ( offset_str) ?;
145
- let dev = if let Some ( ( major, minor) ) = dev_str. split_once ( ':' ) {
146
- ( hex ( major) ?, hex ( minor) ?)
147
- } else {
148
- return Err ( "Couldn't parse dev" ) ;
149
- } ;
150
- let inode = hex ( inode_str) ?;
151
164
let pathname = pathname_str. into ( ) ;
152
165
153
166
Ok ( MapsEntry {
154
167
address,
155
- perms,
156
168
offset,
157
- dev,
158
- inode,
159
169
pathname,
160
170
} )
161
171
}
@@ -172,10 +182,7 @@ fn check_maps_entry_parsing_64bit() {
172
182
. unwrap( ) ,
173
183
MapsEntry {
174
184
address: ( 0xffffffffff600000 , 0xffffffffff601000 ) ,
175
- perms: [ '-' , '-' , 'x' , 'p' ] ,
176
185
offset: 0x00000000 ,
177
- dev: ( 0x00 , 0x00 ) ,
178
- inode: 0x0 ,
179
186
pathname: "[vsyscall]" . into( ) ,
180
187
}
181
188
) ;
@@ -187,10 +194,7 @@ fn check_maps_entry_parsing_64bit() {
187
194
. unwrap( ) ,
188
195
MapsEntry {
189
196
address: ( 0x7f5985f46000 , 0x7f5985f48000 ) ,
190
- perms: [ 'r' , 'w' , '-' , 'p' ] ,
191
197
offset: 0x00039000 ,
192
- dev: ( 0x103 , 0x06 ) ,
193
- inode: 0x76021795 ,
194
198
pathname: "/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2" . into( ) ,
195
199
}
196
200
) ;
@@ -200,10 +204,7 @@ fn check_maps_entry_parsing_64bit() {
200
204
. unwrap( ) ,
201
205
MapsEntry {
202
206
address: ( 0x35b1a21000 , 0x35b1a22000 ) ,
203
- perms: [ 'r' , 'w' , '-' , 'p' ] ,
204
207
offset: 0x00000000 ,
205
- dev: ( 0x00 , 0x00 ) ,
206
- inode: 0x0 ,
207
208
pathname: Default :: default ( ) ,
208
209
}
209
210
) ;
@@ -224,10 +225,7 @@ fn check_maps_entry_parsing_32bit() {
224
225
. unwrap( ) ,
225
226
MapsEntry {
226
227
address: ( 0x08056000 , 0x08077000 ) ,
227
- perms: [ 'r' , 'w' , '-' , 'p' ] ,
228
228
offset: 0x00000000 ,
229
- dev: ( 0x00 , 0x00 ) ,
230
- inode: 0x0 ,
231
229
pathname: "[heap]" . into( ) ,
232
230
}
233
231
) ;
@@ -239,10 +237,7 @@ fn check_maps_entry_parsing_32bit() {
239
237
. unwrap( ) ,
240
238
MapsEntry {
241
239
address: ( 0xb7c79000 , 0xb7e02000 ) ,
242
- perms: [ 'r' , '-' , '-' , 'p' ] ,
243
240
offset: 0x00000000 ,
244
- dev: ( 0x08 , 0x01 ) ,
245
- inode: 0x60662705 ,
246
241
pathname: "/usr/lib/locale/locale-archive" . into( ) ,
247
242
}
248
243
) ;
@@ -252,10 +247,7 @@ fn check_maps_entry_parsing_32bit() {
252
247
. unwrap( ) ,
253
248
MapsEntry {
254
249
address: ( 0xb7e02000 , 0xb7e03000 ) ,
255
- perms: [ 'r' , 'w' , '-' , 'p' ] ,
256
250
offset: 0x00000000 ,
257
- dev: ( 0x00 , 0x00 ) ,
258
- inode: 0x0 ,
259
251
pathname: Default :: default ( ) ,
260
252
}
261
253
) ;
@@ -266,10 +258,7 @@ fn check_maps_entry_parsing_32bit() {
266
258
. unwrap( ) ,
267
259
MapsEntry {
268
260
address: ( 0xb7c79000 , 0xb7e02000 ) ,
269
- perms: [ 'r' , '-' , '-' , 'p' ] ,
270
261
offset: 0x00000000 ,
271
- dev: ( 0x08 , 0x01 ) ,
272
- inode: 0x60662705 ,
273
262
pathname: "/executable/path/with some spaces" . into( ) ,
274
263
}
275
264
) ;
@@ -280,10 +269,7 @@ fn check_maps_entry_parsing_32bit() {
280
269
. unwrap( ) ,
281
270
MapsEntry {
282
271
address: ( 0xb7c79000 , 0xb7e02000 ) ,
283
- perms: [ 'r' , '-' , '-' , 'p' ] ,
284
272
offset: 0x00000000 ,
285
- dev: ( 0x08 , 0x01 ) ,
286
- inode: 0x60662705 ,
287
273
pathname: "/executable/path/with multiple-continuous spaces " . into( ) ,
288
274
}
289
275
) ;
@@ -294,10 +280,7 @@ fn check_maps_entry_parsing_32bit() {
294
280
. unwrap( ) ,
295
281
MapsEntry {
296
282
address: ( 0xb7c79000 , 0xb7e02000 ) ,
297
- perms: [ 'r' , '-' , '-' , 'p' ] ,
298
283
offset: 0x00000000 ,
299
- dev: ( 0x08 , 0x01 ) ,
300
- inode: 0x60662705 ,
301
284
pathname: "/executable/path/starts-with-spaces" . into( ) ,
302
285
}
303
286
) ;
0 commit comments