vma-iter: Treat Haiku like BeOS.
[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__ || defined __HAIKU__ /* BeOS, Haiku */
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 (start < auxmap_start)
335             if (callback (data, start, auxmap_start, flags))
336               break;
337           if (auxmap_end - 1 < end - 1)
338             if (callback (data, auxmap_end, end, flags))
339               break;
340         }
341       else
342         {
343           if (callback (data, start, end, flags))
344             break;
345         }
346     }
347   munmap (auxmap, memneed);
348   close (fd);
349   return;
350
351  fail1:
352   munmap (auxmap, memneed);
353  fail2:
354   close (fd);
355   return;
356
357 #elif defined __APPLE__ && defined __MACH__ /* MacOS X */
358
359   task_t task = mach_task_self ();
360   vm_address_t address;
361   vm_size_t size;
362
363   for (address = VM_MIN_ADDRESS;; address += size)
364     {
365       int more;
366       mach_port_t object_name;
367       unsigned int flags;
368       /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
369          32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas
370          mach_vm_address_t and mach_vm_size_t are always 64 bits large.
371          MacOS X 10.5 has three vm_region like methods:
372            - vm_region. It has arguments that depend on whether the current
373              process is 32-bit or 64-bit. When linking dynamically, this
374              function exists only in 32-bit processes. Therefore we use it only
375              in 32-bit processes.
376            - vm_region_64. It has arguments that depend on whether the current
377              process is 32-bit or 64-bit. It interprets a flavor
378              VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is
379              dangerous since 'struct vm_region_basic_info_64' is larger than
380              'struct vm_region_basic_info'; therefore let's write
381              VM_REGION_BASIC_INFO_64 explicitly.
382            - mach_vm_region. It has arguments that are 64-bit always. This
383              function is useful when you want to access the VM of a process
384              other than the current process.
385          In 64-bit processes, we could use vm_region_64 or mach_vm_region.
386          I choose vm_region_64 because it uses the same types as vm_region,
387          resulting in less conditional code.  */
388 # if defined __ppc64__ || defined __x86_64__
389       struct vm_region_basic_info_64 info;
390       mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
391
392       more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64,
393                             (vm_region_info_t)&info, &info_count, &object_name)
394               == KERN_SUCCESS);
395 # else
396       struct vm_region_basic_info info;
397       mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
398
399       more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
400                          (vm_region_info_t)&info, &info_count, &object_name)
401               == KERN_SUCCESS);
402 # endif
403       if (object_name != MACH_PORT_NULL)
404         mach_port_deallocate (mach_task_self (), object_name);
405       if (!more)
406         break;
407       flags = 0;
408       if (info.protection & VM_PROT_READ)
409         flags |= VMA_PROT_READ;
410       if (info.protection & VM_PROT_WRITE)
411         flags |= VMA_PROT_WRITE;
412       if (info.protection & VM_PROT_EXECUTE)
413         flags |= VMA_PROT_EXECUTE;
414       if (callback (data, address, address + size, flags))
415         break;
416     }
417
418 #elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__
419   /* Windows platform.  Use the native Windows API.  */
420
421   MEMORY_BASIC_INFORMATION info;
422   unsigned long address = 0;
423
424   while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info))
425     {
426       if (info.State != MEM_FREE)
427         /* Ignore areas where info.State has the value MEM_RESERVE or,
428            equivalently, info.Protect has the undocumented value 0.
429            This is needed, so that on Cygwin, areas used by malloc() are
430            distinguished from areas reserved for future malloc().  */
431         if (info.State != MEM_RESERVE)
432           {
433             unsigned long start, end;
434             unsigned int flags;
435
436             start = (unsigned long)info.BaseAddress;
437             end = start + info.RegionSize;
438             switch (info.Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
439               {
440               case PAGE_READONLY:
441                 flags = VMA_PROT_READ;
442                 break;
443               case PAGE_READWRITE:
444               case PAGE_WRITECOPY:
445                 flags = VMA_PROT_READ | VMA_PROT_WRITE;
446                 break;
447               case PAGE_EXECUTE:
448                 flags = VMA_PROT_EXECUTE;
449                 break;
450               case PAGE_EXECUTE_READ:
451                 flags = VMA_PROT_READ | VMA_PROT_EXECUTE;
452                 break;
453               case PAGE_EXECUTE_READWRITE:
454               case PAGE_EXECUTE_WRITECOPY:
455                 flags = VMA_PROT_READ | VMA_PROT_WRITE | VMA_PROT_EXECUTE;
456                 break;
457               case PAGE_NOACCESS:
458               default:
459                 flags = 0;
460                 break;
461               }
462
463             if (callback (data, start, end, flags))
464               break;
465           }
466       address = (unsigned long)info.BaseAddress + info.RegionSize;
467     }
468
469 #elif defined __BEOS__ || defined __HAIKU__
470   /* Use the BeOS specific API.  */
471
472   area_info info;
473   int32 cookie;
474
475   cookie = 0;
476   while (get_next_area_info (0, &cookie, &info) == B_OK)
477     {
478       unsigned long start, end;
479       unsigned int flags;
480
481       start = (unsigned long) info.address;
482       end = start + info.size;
483       flags = 0;
484       if (info.protection & B_READ_AREA)
485         flags |= VMA_PROT_READ | VMA_PROT_EXECUTE;
486       if (info.protection & B_WRITE_AREA)
487         flags |= VMA_PROT_WRITE;
488
489       if (callback (data, start, end, flags))
490         break;
491     }
492
493 #endif
494 }
495
496
497 #ifdef TEST
498
499 #include <stdio.h>
500
501 /* Output the VMAs of the current process in a format similar to the Linux
502    /proc/$pid/maps file.  */
503
504 static int
505 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
506                       unsigned int flags)
507 {
508   printf ("%08lx-%08lx %c%c%c\n",
509           (unsigned long) start, (unsigned long) end,
510           flags & VMA_PROT_READ ? 'r' : '-',
511           flags & VMA_PROT_WRITE ? 'w' : '-',
512           flags & VMA_PROT_EXECUTE ? 'x' : '-');
513   return 0;
514 }
515
516 int
517 main ()
518 {
519   vma_iterate (vma_iterate_callback, NULL);
520
521   /* Let the user interactively look at the /proc file system.  */
522   sleep (10);
523
524   return 0;
525 }
526
527 #endif /* TEST */