New module 'vma-iter'.
[gnulib.git] / lib / vma-iter.c
1 /* Iteration over virtual memory areas.
2    Copyright (C) 2011 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2011.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include "vma-iter.h"
22
23 #include <errno.h> /* errno */
24 #include <stdlib.h> /* size_t */
25 #include <fcntl.h> /* open, O_RDONLY */
26 #include <unistd.h> /* getpagesize, read, close */
27
28 #if defined __sgi || defined __osf__ /* IRIX, OSF/1 */
29 # include <string.h> /* memcpy */
30 # include <sys/types.h>
31 # include <sys/mman.h> /* mmap, munmap */
32 # include <sys/procfs.h> /* PIOC*, prmap_t */
33 #endif
34
35 #if defined __APPLE__ && defined __MACH__ /* MacOS X */
36 # include <mach/mach.h>
37 #endif
38
39 #if (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ /* Windows */
40 # include <windows.h>
41 #endif
42
43 #if defined __BEOS__ /* BeOS */
44 # include <OS.h>
45 #endif
46
47
48 /* Support for reading text files in the /proc file system.  */
49
50 #if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ /* || defined __CYGWIN__ */
51
52 /* Buffered read-only streams.
53    We cannot use <stdio.h> here, because fopen() calls malloc(), and a malloc()
54    call may call mmap() and thus pre-allocate available memory.  */
55
56 struct rofile
57   {
58     int fd;
59     size_t position;
60     size_t filled;
61     int eof_seen;
62     char buffer[1024];
63   };
64
65 /* Open a read-only file stream.  */
66 static int
67 rof_open (struct rofile *rof, const char *filename)
68 {
69   int fd = open (filename, O_RDONLY);
70   if (fd < 0)
71     return -1;
72   rof->fd = fd;
73   rof->position = 0;
74   rof->filled = 0;
75   rof->eof_seen = 0;
76   return 0;
77 }
78
79 /* Return the next byte from a read-only file stream without consuming it,
80    or -1 at EOF.  */
81 static int
82 rof_peekchar (struct rofile *rof)
83 {
84   if (rof->position == rof->filled)
85     {
86       if (rof->eof_seen)
87         return -1;
88       else
89         for (;;)
90           {
91             int n = read (rof->fd, rof->buffer, sizeof (rof->buffer));
92 # ifdef EINTR
93             if (n < 0 && errno == EINTR)
94               continue;
95 # endif
96             if (n <= 0)
97               {
98                 rof->eof_seen = 1;
99                 return -1;
100               }
101             rof->filled = n;
102             rof->position = 0;
103             break;
104           }
105     }
106   return (unsigned char) rof->buffer[rof->position];
107 }
108
109 /* Return the next byte from a read-only file stream, or -1 at EOF.  */
110 static int
111 rof_getchar (struct rofile *rof)
112 {
113   int c = rof_peekchar (rof);
114   if (c >= 0)
115     rof->position++;
116   return c;
117 }
118
119 /* Parse an unsigned hexadecimal number from a read-only file stream.  */
120 static int
121 rof_scanf_lx (struct rofile *rof, unsigned long *valuep)
122 {
123   unsigned long value = 0;
124   unsigned int numdigits = 0;
125   for (;;)
126     {
127       int c = rof_peekchar (rof);
128       if (c >= '0' && c <= '9')
129         value = (value << 4) + (c - '0');
130       else if (c >= 'A' && c <= 'F')
131         value = (value << 4) + (c - 'A' + 10);
132       else if (c >= 'a' && c <= 'f')
133         value = (value << 4) + (c - 'a' + 10);
134       else
135         break;
136       rof_getchar (rof);
137       numdigits++;
138     }
139   if (numdigits == 0)
140     return -1;
141   *valuep = value;
142   return 0;
143 }
144
145 /* Close a read-only file stream.  */
146 static void
147 rof_close (struct rofile *rof)
148 {
149   close (rof->fd);
150 }
151
152 #endif
153
154
155 void
156 vma_iterate (vma_iterate_callback_fn callback, void *data)
157 {
158 #if defined __linux__ /* || defined __CYGWIN__ */
159
160   struct rofile rof;
161   int c;
162
163   /* Open the current process' maps file.  It describes one VMA per line.  */
164   if (rof_open (&rof, "/proc/self/maps") < 0)
165     return;
166
167   for (;;)
168     {
169       unsigned long start, end;
170       unsigned int flags;
171
172       /* Parse one line.  First start and end.  */
173       if (!(rof_scanf_lx (&rof, &start) >= 0
174             && rof_getchar (&rof) == '-'
175             && rof_scanf_lx (&rof, &end) >= 0))
176         break;
177       /* Then the flags.  */
178       do
179         c = rof_getchar (&rof);
180       while (c == ' ');
181       flags = 0;
182       if (c == 'r')
183         flags |= VMA_PROT_READ;
184       c = rof_getchar (&rof);
185       if (c == 'w')
186         flags |= VMA_PROT_WRITE;
187       c = rof_getchar (&rof);
188       if (c == 'x')
189         flags |= VMA_PROT_EXECUTE;
190       while (c = rof_getchar (&rof), c != -1 && c != '\n')
191         ;
192
193       if (callback (data, start, end, flags))
194         break;
195     }
196   rof_close (&rof);
197
198 #elif defined __FreeBSD__ || defined __NetBSD__
199
200   struct rofile rof;
201   int c;
202
203   /* Open the current process' maps file.  It describes one VMA per line.  */
204   if (rof_open (&rof, "/proc/curproc/map") < 0)
205     return;
206
207   for (;;)
208     {
209       unsigned long start, end;
210       unsigned int flags;
211
212       /* Parse one line.  First start.  */
213       if (!(rof_getchar (&rof) == '0'
214             && rof_getchar (&rof) == 'x'
215             && rof_scanf_lx (&rof, &start) >= 0))
216         break;
217       while (c = rof_peekchar (&rof), c == ' ' || c == '\t')
218         rof_getchar (&rof);
219       /* Then end.  */
220       if (!(rof_getchar (&rof) == '0'
221             && rof_getchar (&rof) == 'x'
222             && rof_scanf_lx (&rof, &end) >= 0))
223         break;
224       /* Then the flags.  */
225       do
226         c = rof_getchar (&rof);
227       while (c == ' ');
228       flags = 0;
229       if (c == 'r')
230         flags |= VMA_PROT_READ;
231       c = rof_getchar (&rof);
232       if (c == 'w')
233         flags |= VMA_PROT_WRITE;
234       c = rof_getchar (&rof);
235       if (c == 'x')
236         flags |= VMA_PROT_EXECUTE;
237       while (c = rof_getchar (&rof), c != -1 && c != '\n')
238         ;
239
240       if (callback (data, start, end, flags))
241         break;
242     }
243   rof_close (&rof);
244
245 #elif defined __sgi || defined __osf__ /* IRIX, OSF/1 */
246
247   size_t pagesize;
248   char fnamebuf[6+10+1];
249   char *fname;
250   int fd;
251   int nmaps;
252   size_t memneed;
253 # if HAVE_MAP_ANONYMOUS
254 #  define zero_fd -1
255 #  define map_flags MAP_ANONYMOUS
256 # else
257   int zero_fd;
258 #  define map_flags 0
259 # endif
260   void *auxmap;
261   unsigned long auxmap_start;
262   unsigned long auxmap_end;
263   prmap_t* maps;
264   prmap_t* mp;
265
266   pagesize = getpagesize ();
267
268   /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()).  */
269   fname = fnamebuf + sizeof (fnamebuf) - 1;
270   *fname = '\0';
271   {
272     unsigned int value = getpid ();
273     do
274       *--fname = (value % 10) + '0';
275     while ((value = value / 10) > 0);
276   }
277   fname -= 6;
278   memcpy (fname, "/proc/", 6);
279
280   fd = open (fname, O_RDONLY);
281   if (fd < 0)
282     return;
283
284   if (ioctl (fd, PIOCNMAP, &nmaps) < 0)
285     goto fail2;
286
287   memneed = (nmaps + 10) * sizeof (prmap_t);
288   /* Allocate memneed bytes of memory.
289      We cannot use alloca here, because not much stack space is guaranteed.
290      We also cannot use malloc here, because a malloc() call may call mmap()
291      and thus pre-allocate available memory.
292      So use mmap(), and ignore the resulting VMA.  */
293   memneed = ((memneed - 1) / pagesize + 1) * pagesize;
294 # if !HAVE_MAP_ANONYMOUS
295   zero_fd = open ("/dev/zero", O_RDONLY, 0644);
296   if (zero_fd < 0)
297     goto fail2;
298 # endif
299   auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE,
300                           map_flags | MAP_PRIVATE, zero_fd, 0);
301 # if !HAVE_MAP_ANONYMOUS
302   close (zero_fd);
303 # endif
304   if (auxmap == (void *) -1)
305     goto fail2;
306   auxmap_start = (unsigned long) auxmap;
307   auxmap_end = auxmap_start + memneed;
308   maps = (prmap_t *) auxmap;
309
310   if (ioctl (fd, PIOCMAP, maps) < 0)
311     goto fail1;
312
313   for (mp = maps;;)
314     {
315       unsigned long start, end;
316       unsigned int flags;
317
318       start = (unsigned long) mp->pr_vaddr;
319       end = start + mp->pr_size;
320       if (start == 0 && end == 0)
321         break;
322       flags = 0;
323       if (mp->pr_mflags & MA_READ)
324         flags |= VMA_PROT_READ;
325       if (mp->pr_mflags & MA_WRITE)
326         flags |= VMA_PROT_WRITE;
327       if (mp->pr_mflags & MA_EXEC)
328         flags |= VMA_PROT_EXECUTE;
329       mp++;
330       if (start <= auxmap_start && auxmap_end - 1 <= end - 1)
331         {
332           /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1]
333              = [start,auxmap_start-1] u [auxmap_end,end-1].  */
334           if (callback (data, start, auxmap_start, flags))
335             break;
336           if (callback (data, auxmap_end, end, flags))
337             break;
338         }
339       else
340         {
341           if (callback (data, start, end, flags))
342             break;
343         }
344     }
345   munmap (auxmap, memneed);
346   close (fd);
347   return;
348
349  fail1:
350   munmap (auxmap, memneed);
351  fail2:
352   close (fd);
353   return;
354
355 #elif defined __APPLE__ && defined __MACH__ /* MacOS X */
356
357   task_t task = mach_task_self ();
358   vm_address_t address;
359   vm_size_t size;
360
361   for (address = VM_MIN_ADDRESS;; address += size)
362     {
363       int more;
364       mach_port_t object_name;
365       unsigned int flags;
366       /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
367          32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas
368          mach_vm_address_t and mach_vm_size_t are always 64 bits large.
369          MacOS X 10.5 has three vm_region like methods:
370            - vm_region. It has arguments that depend on whether the current
371              process is 32-bit or 64-bit. When linking dynamically, this
372              function exists only in 32-bit processes. Therefore we use it only
373              in 32-bit processes.
374            - vm_region_64. It has arguments that depend on whether the current
375              process is 32-bit or 64-bit. It interprets a flavor
376              VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is
377              dangerous since 'struct vm_region_basic_info_64' is larger than
378              'struct vm_region_basic_info'; therefore let's write
379              VM_REGION_BASIC_INFO_64 explicitly.
380            - mach_vm_region. It has arguments that are 64-bit always. This
381              function is useful when you want to access the VM of a process
382              other than the current process.
383          In 64-bit processes, we could use vm_region_64 or mach_vm_region.
384          I choose vm_region_64 because it uses the same types as vm_region,
385          resulting in less conditional code.  */
386 # if defined __ppc64__ || defined __x86_64__
387       struct vm_region_basic_info_64 info;
388       mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
389
390       more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64,
391                             (vm_region_info_t)&info, &info_count, &object_name)
392               == KERN_SUCCESS);
393 # else
394       struct vm_region_basic_info info;
395       mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
396
397       more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
398                          (vm_region_info_t)&info, &info_count, &object_name)
399               == KERN_SUCCESS);
400 # endif
401       if (object_name != MACH_PORT_NULL)
402         mach_port_deallocate (mach_task_self (), object_name);
403       if (!more)
404         break;
405       flags = 0;
406       if (info.protection & VM_PROT_READ)
407         flags |= VMA_PROT_READ;
408       if (info.protection & VM_PROT_WRITE)
409         flags |= VMA_PROT_WRITE;
410       if (info.protection & VM_PROT_EXECUTE)
411         flags |= VMA_PROT_EXECUTE;
412       if (callback (data, address, address + size, flags))
413         break;
414     }
415
416 #elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__
417   /* Windows platform.  Use the native Windows API.  */
418
419   MEMORY_BASIC_INFORMATION info;
420   unsigned long address = 0;
421
422   while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info))
423     {
424       if (info.State != MEM_FREE)
425         /* Ignore areas where info.State has the value MEM_RESERVE or,
426            equivalently, info.Protect has the undocumented value 0.
427            This is needed, so that on Cygwin, areas used by malloc() are
428            distinguished from areas reserved for future malloc().  */
429         if (info.State != MEM_RESERVE)
430           {
431             unsigned long start, end;
432             unsigned int flags;
433
434             start = (unsigned long)info.BaseAddress;
435             end = start + info.RegionSize;
436             switch (info.Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
437               {
438               case PAGE_READONLY:
439                 flags = VMA_PROT_READ;
440                 break;
441               case PAGE_READWRITE:
442               case PAGE_WRITECOPY:
443                 flags = VMA_PROT_READ | VMA_PROT_WRITE;
444                 break;
445               case PAGE_EXECUTE:
446                 flags = VMA_PROT_EXECUTE;
447                 break;
448               case PAGE_EXECUTE_READ:
449                 flags = VMA_PROT_READ | VMA_PROT_EXECUTE;
450                 break;
451               case PAGE_EXECUTE_READWRITE:
452               case PAGE_EXECUTE_WRITECOPY:
453                 flags = VMA_PROT_READ | VMA_PROT_WRITE | VMA_PROT_EXECUTE;
454                 break;
455               case PAGE_NOACCESS:
456               default:
457                 flags = 0;
458                 break;
459               }
460
461             if (callback (data, start, end, flags))
462               break;
463           }
464       address = (unsigned long)info.BaseAddress + info.RegionSize;
465     }
466
467 #elif defined __BEOS__
468   /* Use the BeOS specific API.  */
469
470   area_info info;
471   int32 cookie;
472
473   cookie = 0;
474   while (get_next_area_info (0, &cookie, &info) == B_OK)
475     {
476       unsigned long start, end;
477       unsigned int flags;
478
479       start = (unsigned long) info.address;
480       end = start + info.size;
481       flags = 0;
482       if (info.protection & B_READ_AREA)
483         flags |= VMA_PROT_READ | VMA_PROT_EXECUTE;
484       if (info.protection & B_WRITE_AREA)
485         flags |= VMA_PROT_WRITE;
486
487       if (callback (data, start, end, flags))
488         break;
489     }
490
491 #endif
492 }
493
494
495 #ifdef TEST
496
497 #include <stdio.h>
498
499 /* Output the VMAs of the current process in a format similar to the Linux
500    /proc/$pid/maps file.  */
501
502 static int
503 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
504                       unsigned int flags)
505 {
506   printf ("%08lx-%08lx %c%c%c\n",
507           (unsigned long) start, (unsigned long) end,
508           flags & VMA_PROT_READ ? 'r' : '-',
509           flags & VMA_PROT_WRITE ? 'w' : '-',
510           flags & VMA_PROT_EXECUTE ? 'x' : '-');
511   return 0;
512 }
513
514 int
515 main ()
516 {
517   vma_iterate (vma_iterate_callback, NULL);
518
519   /* Let the user interactively look at the /proc file system.  */
520   sleep (10);
521
522   return 0;
523 }
524
525 #endif /* TEST */