maint: update copyright
[gnulib.git] / lib / vma-iter.c
1 /* Iteration over virtual memory areas.
2    Copyright (C) 2011-2014 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__ /* Mac OS 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 #if HAVE_MQUERY /* OpenBSD */
48 # include <sys/types.h>
49 # include <sys/mman.h> /* mquery */
50 #endif
51
52
53 /* Support for reading text files in the /proc file system.  */
54
55 #if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ /* || defined __CYGWIN__ */
56
57 /* Buffered read-only streams.
58    We cannot use <stdio.h> here, because fopen() calls malloc(), and a malloc()
59    call may call mmap() and thus pre-allocate available memory.  */
60
61 struct rofile
62   {
63     int fd;
64     size_t position;
65     size_t filled;
66     int eof_seen;
67     char buffer[1024];
68   };
69
70 /* Open a read-only file stream.  */
71 static int
72 rof_open (struct rofile *rof, const char *filename)
73 {
74   int fd = open (filename, O_RDONLY);
75   if (fd < 0)
76     return -1;
77   rof->fd = fd;
78   rof->position = 0;
79   rof->filled = 0;
80   rof->eof_seen = 0;
81   return 0;
82 }
83
84 /* Return the next byte from a read-only file stream without consuming it,
85    or -1 at EOF.  */
86 static int
87 rof_peekchar (struct rofile *rof)
88 {
89   if (rof->position == rof->filled)
90     {
91       if (rof->eof_seen)
92         return -1;
93       else
94         for (;;)
95           {
96             int n = read (rof->fd, rof->buffer, sizeof (rof->buffer));
97 # ifdef EINTR
98             if (n < 0 && errno == EINTR)
99               continue;
100 # endif
101             if (n <= 0)
102               {
103                 rof->eof_seen = 1;
104                 return -1;
105               }
106             rof->filled = n;
107             rof->position = 0;
108             break;
109           }
110     }
111   return (unsigned char) rof->buffer[rof->position];
112 }
113
114 /* Return the next byte from a read-only file stream, or -1 at EOF.  */
115 static int
116 rof_getchar (struct rofile *rof)
117 {
118   int c = rof_peekchar (rof);
119   if (c >= 0)
120     rof->position++;
121   return c;
122 }
123
124 /* Parse an unsigned hexadecimal number from a read-only file stream.  */
125 static int
126 rof_scanf_lx (struct rofile *rof, unsigned long *valuep)
127 {
128   unsigned long value = 0;
129   unsigned int numdigits = 0;
130   for (;;)
131     {
132       int c = rof_peekchar (rof);
133       if (c >= '0' && c <= '9')
134         value = (value << 4) + (c - '0');
135       else if (c >= 'A' && c <= 'F')
136         value = (value << 4) + (c - 'A' + 10);
137       else if (c >= 'a' && c <= 'f')
138         value = (value << 4) + (c - 'a' + 10);
139       else
140         break;
141       rof_getchar (rof);
142       numdigits++;
143     }
144   if (numdigits == 0)
145     return -1;
146   *valuep = value;
147   return 0;
148 }
149
150 /* Close a read-only file stream.  */
151 static void
152 rof_close (struct rofile *rof)
153 {
154   close (rof->fd);
155 }
156
157 #endif
158
159
160 void
161 vma_iterate (vma_iterate_callback_fn callback, void *data)
162 {
163 #if defined __linux__ /* || defined __CYGWIN__ */
164
165   struct rofile rof;
166   int c;
167
168   /* Open the current process' maps file.  It describes one VMA per line.  */
169   if (rof_open (&rof, "/proc/self/maps") < 0)
170     return;
171
172   for (;;)
173     {
174       unsigned long start, end;
175       unsigned int flags;
176
177       /* Parse one line.  First start and end.  */
178       if (!(rof_scanf_lx (&rof, &start) >= 0
179             && rof_getchar (&rof) == '-'
180             && rof_scanf_lx (&rof, &end) >= 0))
181         break;
182       /* Then the flags.  */
183       do
184         c = rof_getchar (&rof);
185       while (c == ' ');
186       flags = 0;
187       if (c == 'r')
188         flags |= VMA_PROT_READ;
189       c = rof_getchar (&rof);
190       if (c == 'w')
191         flags |= VMA_PROT_WRITE;
192       c = rof_getchar (&rof);
193       if (c == 'x')
194         flags |= VMA_PROT_EXECUTE;
195       while (c = rof_getchar (&rof), c != -1 && c != '\n')
196         ;
197
198       if (callback (data, start, end, flags))
199         break;
200     }
201   rof_close (&rof);
202
203 #elif defined __FreeBSD__ || defined __NetBSD__
204
205   struct rofile rof;
206   int c;
207
208   /* Open the current process' maps file.  It describes one VMA per line.  */
209   if (rof_open (&rof, "/proc/curproc/map") < 0)
210     return;
211
212   for (;;)
213     {
214       unsigned long start, end;
215       unsigned int flags;
216
217       /* Parse one line.  First start.  */
218       if (!(rof_getchar (&rof) == '0'
219             && rof_getchar (&rof) == 'x'
220             && rof_scanf_lx (&rof, &start) >= 0))
221         break;
222       while (c = rof_peekchar (&rof), c == ' ' || c == '\t')
223         rof_getchar (&rof);
224       /* Then end.  */
225       if (!(rof_getchar (&rof) == '0'
226             && rof_getchar (&rof) == 'x'
227             && rof_scanf_lx (&rof, &end) >= 0))
228         break;
229       /* Then the flags.  */
230       do
231         c = rof_getchar (&rof);
232       while (c == ' ');
233       flags = 0;
234       if (c == 'r')
235         flags |= VMA_PROT_READ;
236       c = rof_getchar (&rof);
237       if (c == 'w')
238         flags |= VMA_PROT_WRITE;
239       c = rof_getchar (&rof);
240       if (c == 'x')
241         flags |= VMA_PROT_EXECUTE;
242       while (c = rof_getchar (&rof), c != -1 && c != '\n')
243         ;
244
245       if (callback (data, start, end, flags))
246         break;
247     }
248   rof_close (&rof);
249
250 #elif defined __sgi || defined __osf__ /* IRIX, OSF/1 */
251
252   size_t pagesize;
253   char fnamebuf[6+10+1];
254   char *fname;
255   int fd;
256   int nmaps;
257   size_t memneed;
258 # if HAVE_MAP_ANONYMOUS
259 #  define zero_fd -1
260 #  define map_flags MAP_ANONYMOUS
261 # else
262   int zero_fd;
263 #  define map_flags 0
264 # endif
265   void *auxmap;
266   unsigned long auxmap_start;
267   unsigned long auxmap_end;
268   prmap_t* maps;
269   prmap_t* mp;
270
271   pagesize = getpagesize ();
272
273   /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()).  */
274   fname = fnamebuf + sizeof (fnamebuf) - 1;
275   *fname = '\0';
276   {
277     unsigned int value = getpid ();
278     do
279       *--fname = (value % 10) + '0';
280     while ((value = value / 10) > 0);
281   }
282   fname -= 6;
283   memcpy (fname, "/proc/", 6);
284
285   fd = open (fname, O_RDONLY);
286   if (fd < 0)
287     return;
288
289   if (ioctl (fd, PIOCNMAP, &nmaps) < 0)
290     goto fail2;
291
292   memneed = (nmaps + 10) * sizeof (prmap_t);
293   /* Allocate memneed bytes of memory.
294      We cannot use alloca here, because not much stack space is guaranteed.
295      We also cannot use malloc here, because a malloc() call may call mmap()
296      and thus pre-allocate available memory.
297      So use mmap(), and ignore the resulting VMA.  */
298   memneed = ((memneed - 1) / pagesize + 1) * pagesize;
299 # if !HAVE_MAP_ANONYMOUS
300   zero_fd = open ("/dev/zero", O_RDONLY, 0644);
301   if (zero_fd < 0)
302     goto fail2;
303 # endif
304   auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE,
305                           map_flags | MAP_PRIVATE, zero_fd, 0);
306 # if !HAVE_MAP_ANONYMOUS
307   close (zero_fd);
308 # endif
309   if (auxmap == (void *) -1)
310     goto fail2;
311   auxmap_start = (unsigned long) auxmap;
312   auxmap_end = auxmap_start + memneed;
313   maps = (prmap_t *) auxmap;
314
315   if (ioctl (fd, PIOCMAP, maps) < 0)
316     goto fail1;
317
318   for (mp = maps;;)
319     {
320       unsigned long start, end;
321       unsigned int flags;
322
323       start = (unsigned long) mp->pr_vaddr;
324       end = start + mp->pr_size;
325       if (start == 0 && end == 0)
326         break;
327       flags = 0;
328       if (mp->pr_mflags & MA_READ)
329         flags |= VMA_PROT_READ;
330       if (mp->pr_mflags & MA_WRITE)
331         flags |= VMA_PROT_WRITE;
332       if (mp->pr_mflags & MA_EXEC)
333         flags |= VMA_PROT_EXECUTE;
334       mp++;
335       if (start <= auxmap_start && auxmap_end - 1 <= end - 1)
336         {
337           /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1]
338              = [start,auxmap_start-1] u [auxmap_end,end-1].  */
339           if (start < auxmap_start)
340             if (callback (data, start, auxmap_start, flags))
341               break;
342           if (auxmap_end - 1 < end - 1)
343             if (callback (data, auxmap_end, end, flags))
344               break;
345         }
346       else
347         {
348           if (callback (data, start, end, flags))
349             break;
350         }
351     }
352   munmap (auxmap, memneed);
353   close (fd);
354   return;
355
356  fail1:
357   munmap (auxmap, memneed);
358  fail2:
359   close (fd);
360   return;
361
362 #elif defined __APPLE__ && defined __MACH__ /* Mac OS X */
363
364   task_t task = mach_task_self ();
365   vm_address_t address;
366   vm_size_t size;
367
368   for (address = VM_MIN_ADDRESS;; address += size)
369     {
370       int more;
371       mach_port_t object_name;
372       unsigned int flags;
373       /* In Mac OS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
374          32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas
375          mach_vm_address_t and mach_vm_size_t are always 64 bits large.
376          Mac OS X 10.5 has three vm_region like methods:
377            - vm_region. It has arguments that depend on whether the current
378              process is 32-bit or 64-bit. When linking dynamically, this
379              function exists only in 32-bit processes. Therefore we use it only
380              in 32-bit processes.
381            - vm_region_64. It has arguments that depend on whether the current
382              process is 32-bit or 64-bit. It interprets a flavor
383              VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is
384              dangerous since 'struct vm_region_basic_info_64' is larger than
385              'struct vm_region_basic_info'; therefore let's write
386              VM_REGION_BASIC_INFO_64 explicitly.
387            - mach_vm_region. It has arguments that are 64-bit always. This
388              function is useful when you want to access the VM of a process
389              other than the current process.
390          In 64-bit processes, we could use vm_region_64 or mach_vm_region.
391          I choose vm_region_64 because it uses the same types as vm_region,
392          resulting in less conditional code.  */
393 # if defined __ppc64__ || defined __x86_64__
394       struct vm_region_basic_info_64 info;
395       mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
396
397       more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64,
398                             (vm_region_info_t)&info, &info_count, &object_name)
399               == KERN_SUCCESS);
400 # else
401       struct vm_region_basic_info info;
402       mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
403
404       more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
405                          (vm_region_info_t)&info, &info_count, &object_name)
406               == KERN_SUCCESS);
407 # endif
408       if (object_name != MACH_PORT_NULL)
409         mach_port_deallocate (mach_task_self (), object_name);
410       if (!more)
411         break;
412       flags = 0;
413       if (info.protection & VM_PROT_READ)
414         flags |= VMA_PROT_READ;
415       if (info.protection & VM_PROT_WRITE)
416         flags |= VMA_PROT_WRITE;
417       if (info.protection & VM_PROT_EXECUTE)
418         flags |= VMA_PROT_EXECUTE;
419       if (callback (data, address, address + size, flags))
420         break;
421     }
422
423 #elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__
424   /* Windows platform.  Use the native Windows API.  */
425
426   MEMORY_BASIC_INFORMATION info;
427   unsigned long address = 0;
428
429   while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info))
430     {
431       if (info.State != MEM_FREE)
432         /* Ignore areas where info.State has the value MEM_RESERVE or,
433            equivalently, info.Protect has the undocumented value 0.
434            This is needed, so that on Cygwin, areas used by malloc() are
435            distinguished from areas reserved for future malloc().  */
436         if (info.State != MEM_RESERVE)
437           {
438             unsigned long start, end;
439             unsigned int flags;
440
441             start = (unsigned long)info.BaseAddress;
442             end = start + info.RegionSize;
443             switch (info.Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
444               {
445               case PAGE_READONLY:
446                 flags = VMA_PROT_READ;
447                 break;
448               case PAGE_READWRITE:
449               case PAGE_WRITECOPY:
450                 flags = VMA_PROT_READ | VMA_PROT_WRITE;
451                 break;
452               case PAGE_EXECUTE:
453                 flags = VMA_PROT_EXECUTE;
454                 break;
455               case PAGE_EXECUTE_READ:
456                 flags = VMA_PROT_READ | VMA_PROT_EXECUTE;
457                 break;
458               case PAGE_EXECUTE_READWRITE:
459               case PAGE_EXECUTE_WRITECOPY:
460                 flags = VMA_PROT_READ | VMA_PROT_WRITE | VMA_PROT_EXECUTE;
461                 break;
462               case PAGE_NOACCESS:
463               default:
464                 flags = 0;
465                 break;
466               }
467
468             if (callback (data, start, end, flags))
469               break;
470           }
471       address = (unsigned long)info.BaseAddress + info.RegionSize;
472     }
473
474 #elif defined __BEOS__ || defined __HAIKU__
475   /* Use the BeOS specific API.  */
476
477   area_info info;
478   int32 cookie;
479
480   cookie = 0;
481   while (get_next_area_info (0, &cookie, &info) == B_OK)
482     {
483       unsigned long start, end;
484       unsigned int flags;
485
486       start = (unsigned long) info.address;
487       end = start + info.size;
488       flags = 0;
489       if (info.protection & B_READ_AREA)
490         flags |= VMA_PROT_READ | VMA_PROT_EXECUTE;
491       if (info.protection & B_WRITE_AREA)
492         flags |= VMA_PROT_WRITE;
493
494       if (callback (data, start, end, flags))
495         break;
496     }
497
498 #elif HAVE_MQUERY /* OpenBSD */
499
500   uintptr_t pagesize;
501   uintptr_t address;
502   int /*bool*/ address_known_mapped;
503
504   pagesize = getpagesize ();
505   /* Avoid calling mquery with a NULL first argument, because this argument
506      value has a specific meaning.  We know the NULL page is unmapped.  */
507   address = pagesize;
508   address_known_mapped = 0;
509   for (;;)
510     {
511       /* Test whether the page at address is mapped.  */
512       if (address_known_mapped
513           || mquery ((void *) address, pagesize, 0, MAP_FIXED, -1, 0)
514              == (void *) -1)
515         {
516           /* The page at address is mapped.
517              This is the start of an interval.  */
518           uintptr_t start = address;
519           uintptr_t end;
520
521           /* Find the end of the interval.  */
522           end = (uintptr_t) mquery ((void *) address, pagesize, 0, 0, -1, 0);
523           if (end == (uintptr_t) (void *) -1)
524             end = 0; /* wrap around */
525           address = end;
526
527           /* It's too complicated to find out about the flags.  Just pass 0.  */
528           if (callback (data, start, end, 0))
529             break;
530
531           if (address < pagesize) /* wrap around? */
532             break;
533         }
534       /* Here we know that the page at address is unmapped.  */
535       {
536         uintptr_t query_size = pagesize;
537
538         address += pagesize;
539
540         /* Query larger and larger blocks, to get through the unmapped address
541            range with few mquery() calls.  */
542         for (;;)
543           {
544             if (2 * query_size > query_size)
545               query_size = 2 * query_size;
546             if (address + query_size - 1 < query_size) /* wrap around? */
547               {
548                 address_known_mapped = 0;
549                 break;
550               }
551             if (mquery ((void *) address, query_size, 0, MAP_FIXED, -1, 0)
552                 == (void *) -1)
553               {
554                 /* Not all the interval [address .. address + query_size - 1]
555                    is unmapped.  */
556                 address_known_mapped = (query_size == pagesize);
557                 break;
558               }
559             /* The interval [address .. address + query_size - 1] is
560                unmapped.  */
561             address += query_size;
562           }
563         /* Reduce the query size again, to determine the precise size of the
564            unmapped interval that starts at address.  */
565         while (query_size > pagesize)
566           {
567             query_size = query_size / 2;
568             if (address + query_size - 1 >= query_size)
569               {
570                 if (mquery ((void *) address, query_size, 0, MAP_FIXED, -1, 0)
571                     != (void *) -1)
572                   {
573                     /* The interval [address .. address + query_size - 1] is
574                        unmapped.  */
575                     address += query_size;
576                     address_known_mapped = 0;
577                   }
578                 else
579                   address_known_mapped = (query_size == pagesize);
580               }
581           }
582         /* Here again query_size = pagesize, and
583            either address + pagesize - 1 < pagesize, or
584            mquery ((void *) address, pagesize, 0, MAP_FIXED, -1, 0) fails.
585            So, the unmapped area ends at address.  */
586       }
587       if (address + pagesize - 1 < pagesize) /* wrap around? */
588         break;
589     }
590
591 #endif
592 }
593
594
595 #ifdef TEST
596
597 #include <stdio.h>
598
599 /* Output the VMAs of the current process in a format similar to the Linux
600    /proc/$pid/maps file.  */
601
602 static int
603 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
604                       unsigned int flags)
605 {
606   printf ("%08lx-%08lx %c%c%c\n",
607           (unsigned long) start, (unsigned long) end,
608           flags & VMA_PROT_READ ? 'r' : '-',
609           flags & VMA_PROT_WRITE ? 'w' : '-',
610           flags & VMA_PROT_EXECUTE ? 'x' : '-');
611   return 0;
612 }
613
614 int
615 main ()
616 {
617   vma_iterate (vma_iterate_callback, NULL);
618
619   /* Let the user interactively look at the /proc file system.  */
620   sleep (10);
621
622   return 0;
623 }
624
625 #endif /* TEST */