blob: 4ab8421996f4505194334a9c5dba2a112938711e [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
sewardj55f9d1a2005-04-25 11:11:44 +00003/*--- A simple parser for /proc/self/maps on Linux 2.4.X/2.6.X ---*/
4/*--- read_procselfmaps.c ---*/
sewardjde4a1d02002-03-22 01:27:54 +00005/*--------------------------------------------------------------------*/
6
7/*
njnb9c427c2004-12-01 14:14:42 +00008 This file is part of Valgrind, a dynamic binary instrumentation
9 framework.
sewardjde4a1d02002-03-22 01:27:54 +000010
njn53612422005-03-12 16:22:54 +000011 Copyright (C) 2000-2005 Julian Seward
sewardjde4a1d02002-03-22 01:27:54 +000012 jseward@acm.org
sewardjde4a1d02002-03-22 01:27:54 +000013
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 02111-1307, USA.
28
njn25e49d8e72002-09-23 09:36:25 +000029 The GNU General Public License is contained in the file COPYING.
sewardjde4a1d02002-03-22 01:27:54 +000030*/
31
32
nethercotef1e5e152004-09-01 23:58:16 +000033#include "core.h"
sewardj55f9d1a2005-04-25 11:11:44 +000034#include "pub_core_aspacemgr.h"
35
sewardjde4a1d02002-03-22 01:27:54 +000036
njn14319cc2005-03-13 06:26:22 +000037/* Size of a smallish table used to read /proc/self/map entries. */
38#define M_PROCMAP_BUF 50000
39
sewardjde4a1d02002-03-22 01:27:54 +000040/* static ... to keep it out of the stack frame. */
sewardjde4a1d02002-03-22 01:27:54 +000041static Char procmap_buf[M_PROCMAP_BUF];
42
njn3e884182003-04-15 13:03:23 +000043/* Records length of /proc/self/maps read into procmap_buf. */
44static Int buf_n_tot;
45
sewardjde4a1d02002-03-22 01:27:54 +000046
47/* Helper fns. */
48
49static Int hexdigit ( Char c )
50{
51 if (c >= '0' && c <= '9') return (Int)(c - '0');
52 if (c >= 'a' && c <= 'f') return 10 + (Int)(c - 'a');
53 if (c >= 'A' && c <= 'F') return 10 + (Int)(c - 'A');
54 return -1;
55}
56
fitzhardinge98abfc72003-12-16 02:05:15 +000057static Int decdigit ( Char c )
58{
59 if (c >= '0' && c <= '9') return (Int)(c - '0');
60 return -1;
61}
62
sewardjb5f6f512005-03-10 23:59:00 +000063static Int readchar ( const Char* buf, Char* ch )
sewardjde4a1d02002-03-22 01:27:54 +000064{
65 if (*buf == 0) return 0;
66 *ch = *buf;
67 return 1;
68}
69
sewardjb5f6f512005-03-10 23:59:00 +000070static Int readhex ( const Char* buf, UWord* val )
sewardjde4a1d02002-03-22 01:27:54 +000071{
72 Int n = 0;
73 *val = 0;
74 while (hexdigit(*buf) >= 0) {
75 *val = (*val << 4) + hexdigit(*buf);
76 n++; buf++;
77 }
78 return n;
79}
80
sewardjb5f6f512005-03-10 23:59:00 +000081static Int readdec ( const Char* buf, UInt* val )
fitzhardinge98abfc72003-12-16 02:05:15 +000082{
83 Int n = 0;
84 *val = 0;
85 while (hexdigit(*buf) >= 0) {
86 *val = (*val * 10) + decdigit(*buf);
87 n++; buf++;
88 }
89 return n;
90}
91
sewardjde4a1d02002-03-22 01:27:54 +000092
sewardjb5f6f512005-03-10 23:59:00 +000093/* Read /proc/self/maps, store the contents into a static buffer. If
94 there's a syntax error or other failure, just abort. */
95
96static void read_procselfmaps ( void )
njn3e884182003-04-15 13:03:23 +000097{
98 Int n_chunk, fd;
99
100 /* Read the initial memory mapping from the /proc filesystem. */
101 fd = VG_(open) ( "/proc/self/maps", VKI_O_RDONLY, 0 );
jsgff3c3f1a2003-10-14 22:13:28 +0000102 if (fd < 0) {
njn3e884182003-04-15 13:03:23 +0000103 VG_(message)(Vg_UserMsg, "FATAL: can't open /proc/self/maps");
104 VG_(exit)(1);
105 }
106 buf_n_tot = 0;
107 do {
sewardjb5f6f512005-03-10 23:59:00 +0000108 n_chunk = VG_(read) ( fd, &procmap_buf[buf_n_tot],
njn3e884182003-04-15 13:03:23 +0000109 M_PROCMAP_BUF - buf_n_tot );
110 buf_n_tot += n_chunk;
111 } while ( n_chunk > 0 && buf_n_tot < M_PROCMAP_BUF );
112 VG_(close)(fd);
113 if (buf_n_tot >= M_PROCMAP_BUF-5) {
114 VG_(message)(Vg_UserMsg, "FATAL: M_PROCMAP_BUF is too small; "
115 "increase it and recompile");
116 VG_(exit)(1);
117 }
118 if (buf_n_tot == 0) {
119 VG_(message)(Vg_UserMsg, "FATAL: I/O error on /proc/self/maps" );
120 VG_(exit)(1);
121 }
122 procmap_buf[buf_n_tot] = 0;
123}
sewardjde4a1d02002-03-22 01:27:54 +0000124
njn3e884182003-04-15 13:03:23 +0000125/* Parse /proc/self/maps. For each map entry, call
sewardjde4a1d02002-03-22 01:27:54 +0000126 record_mapping, passing it, in this order:
127
128 start address in memory
129 length
sewardjb5f6f512005-03-10 23:59:00 +0000130 page protections (using the VKI_PROT_* flags)
131 mapped file device and inode
sewardjde4a1d02002-03-22 01:27:54 +0000132 offset in file, or zero if no file
133 filename, zero terminated, or NULL if no file
134
135 So the sig of the called fn might be
136
sewardjb5f6f512005-03-10 23:59:00 +0000137 void (*record_mapping)( Addr start, SizeT size, UInt prot,
138 UInt dev, UInt info,
nethercoted9255482004-10-26 10:19:30 +0000139 ULong foffset, UChar* filename )
sewardjde4a1d02002-03-22 01:27:54 +0000140
141 Note that the supplied filename is transiently stored; record_mapping
142 should make a copy if it wants to keep it.
143
njn3e884182003-04-15 13:03:23 +0000144 Nb: it is important that this function does not alter the contents of
145 procmap_buf!
sewardjde4a1d02002-03-22 01:27:54 +0000146*/
njnfa1016e2003-09-25 17:54:11 +0000147void VG_(parse_procselfmaps) (
sewardjb5f6f512005-03-10 23:59:00 +0000148 void (*record_mapping)( Addr addr, SizeT len, UInt prot,
fitzhardinge98abfc72003-12-16 02:05:15 +0000149 UInt dev, UInt ino, ULong foff, const UChar* filename )
150 )
sewardjde4a1d02002-03-22 01:27:54 +0000151{
njn3e884182003-04-15 13:03:23 +0000152 Int i, j, i_eol;
sewardjde4a1d02002-03-22 01:27:54 +0000153 Addr start, endPlusOne;
154 UChar* filename;
njn3e884182003-04-15 13:03:23 +0000155 UChar rr, ww, xx, pp, ch, tmp;
sewardjb5f6f512005-03-10 23:59:00 +0000156 UInt ino, prot;
nethercote6a27d832004-09-07 10:17:02 +0000157 UWord foffset, maj, min;
sewardjde4a1d02002-03-22 01:27:54 +0000158
sewardjb5f6f512005-03-10 23:59:00 +0000159 read_procselfmaps();
160
njnca82cc02004-11-22 17:18:48 +0000161 tl_assert( '\0' != procmap_buf[0] && 0 != buf_n_tot);
njn3e884182003-04-15 13:03:23 +0000162
sewardjde4a1d02002-03-22 01:27:54 +0000163 if (0)
164 VG_(message)(Vg_DebugMsg, "raw:\n%s", procmap_buf );
165
166 /* Ok, it's safely aboard. Parse the entries. */
sewardjde4a1d02002-03-22 01:27:54 +0000167 i = 0;
168 while (True) {
njn3e884182003-04-15 13:03:23 +0000169 if (i >= buf_n_tot) break;
sewardjde4a1d02002-03-22 01:27:54 +0000170
sewardj0a54cef2005-02-17 09:29:33 +0000171 /* Read (without fscanf :) the pattern %16x-%16x %c%c%c%c %16x %2x:%2x %d */
sewardjde4a1d02002-03-22 01:27:54 +0000172 j = readhex(&procmap_buf[i], &start);
173 if (j > 0) i += j; else goto syntaxerror;
174 j = readchar(&procmap_buf[i], &ch);
175 if (j == 1 && ch == '-') i += j; else goto syntaxerror;
176 j = readhex(&procmap_buf[i], &endPlusOne);
177 if (j > 0) i += j; else goto syntaxerror;
178
179 j = readchar(&procmap_buf[i], &ch);
180 if (j == 1 && ch == ' ') i += j; else goto syntaxerror;
181
182 j = readchar(&procmap_buf[i], &rr);
183 if (j == 1 && (rr == 'r' || rr == '-')) i += j; else goto syntaxerror;
184 j = readchar(&procmap_buf[i], &ww);
185 if (j == 1 && (ww == 'w' || ww == '-')) i += j; else goto syntaxerror;
186 j = readchar(&procmap_buf[i], &xx);
187 if (j == 1 && (xx == 'x' || xx == '-')) i += j; else goto syntaxerror;
fitzhardinge98abfc72003-12-16 02:05:15 +0000188 /* This field is the shared/private flag */
sewardjde4a1d02002-03-22 01:27:54 +0000189 j = readchar(&procmap_buf[i], &pp);
190 if (j == 1 && (pp == 'p' || pp == '-' || pp == 's'))
191 i += j; else goto syntaxerror;
192
193 j = readchar(&procmap_buf[i], &ch);
194 if (j == 1 && ch == ' ') i += j; else goto syntaxerror;
195
196 j = readhex(&procmap_buf[i], &foffset);
197 if (j > 0) i += j; else goto syntaxerror;
fitzhardinge98abfc72003-12-16 02:05:15 +0000198
199 j = readchar(&procmap_buf[i], &ch);
200 if (j == 1 && ch == ' ') i += j; else goto syntaxerror;
201
202 j = readhex(&procmap_buf[i], &maj);
203 if (j > 0) i += j; else goto syntaxerror;
204 j = readchar(&procmap_buf[i], &ch);
205 if (j == 1 && ch == ':') i += j; else goto syntaxerror;
206 j = readhex(&procmap_buf[i], &min);
207 if (j > 0) i += j; else goto syntaxerror;
208
209 j = readchar(&procmap_buf[i], &ch);
210 if (j == 1 && ch == ' ') i += j; else goto syntaxerror;
211
212 j = readdec(&procmap_buf[i], &ino);
213 if (j > 0) i += j; else goto syntaxerror;
214
sewardjde4a1d02002-03-22 01:27:54 +0000215 goto read_line_ok;
216
217 syntaxerror:
218 VG_(message)(Vg_UserMsg, "FATAL: syntax error reading /proc/self/maps");
219 { Int k;
220 VG_(printf)("last 50 chars: `");
221 for (k = i-50; k <= i; k++) VG_(printf)("%c", procmap_buf[k]);
222 VG_(printf)("'\n");
223 }
sewardjb5f6f512005-03-10 23:59:00 +0000224 VG_(exit)(1);
sewardjde4a1d02002-03-22 01:27:54 +0000225
226 read_line_ok:
njn25e49d8e72002-09-23 09:36:25 +0000227
sewardjde4a1d02002-03-22 01:27:54 +0000228 /* Try and find the name of the file mapped to this segment, if
229 it exists. */
sewardjb5f6f512005-03-10 23:59:00 +0000230 while (procmap_buf[i] != '\n' && i < buf_n_tot-1) i++;
sewardjde4a1d02002-03-22 01:27:54 +0000231 i_eol = i;
232 i--;
233 while (!VG_(isspace)(procmap_buf[i]) && i >= 0) i--;
234 i++;
235 if (i < i_eol-1 && procmap_buf[i] == '/') {
njn3e884182003-04-15 13:03:23 +0000236 /* Minor hack: put a '\0' at the filename end for the call to
237 `record_mapping', then restore the old char with `tmp'. */
sewardjde4a1d02002-03-22 01:27:54 +0000238 filename = &procmap_buf[i];
njn3e884182003-04-15 13:03:23 +0000239 tmp = filename[i_eol - i];
sewardjde4a1d02002-03-22 01:27:54 +0000240 filename[i_eol - i] = '\0';
241 } else {
sewardjb5f6f512005-03-10 23:59:00 +0000242 tmp = 0;
sewardjde4a1d02002-03-22 01:27:54 +0000243 filename = NULL;
244 foffset = 0;
245 }
246
sewardjb5f6f512005-03-10 23:59:00 +0000247 prot = 0;
248 if (rr == 'r') prot |= VKI_PROT_READ;
249 if (ww == 'w') prot |= VKI_PROT_WRITE;
250 if (xx == 'x') prot |= VKI_PROT_EXEC;
251
252 //if (start < VG_(valgrind_last))
sewardjde4a1d02002-03-22 01:27:54 +0000253 (*record_mapping) ( start, endPlusOne-start,
sewardjb5f6f512005-03-10 23:59:00 +0000254 prot, maj * 256 + min, ino,
sewardjde4a1d02002-03-22 01:27:54 +0000255 foffset, filename );
256
njn3e884182003-04-15 13:03:23 +0000257 if ('\0' != tmp) {
258 filename[i_eol - i] = tmp;
259 }
260
sewardjde4a1d02002-03-22 01:27:54 +0000261 i = i_eol + 1;
262 }
sewardjde4a1d02002-03-22 01:27:54 +0000263}
264
265/*--------------------------------------------------------------------*/
sewardj55f9d1a2005-04-25 11:11:44 +0000266/*--- end read_procselfmaps.c ---*/
sewardjde4a1d02002-03-22 01:27:54 +0000267/*--------------------------------------------------------------------*/