ld: Add PR ld/25593 tests
[deliverable/binutils-gdb.git] / bfd / bfdio.c
CommitLineData
93509525 1/* Low-level I/O routines for BFDs.
7c192733 2
b3adc24a 3 Copyright (C) 1990-2020 Free Software Foundation, Inc.
7c192733 4
93509525
KD
5 Written by Cygnus Support.
6
cd123cb7 7 This file is part of BFD, the Binary File Descriptor library.
93509525 8
cd123cb7
NC
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
93509525 13
cd123cb7
NC
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
93509525 18
cd123cb7
NC
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
22 MA 02110-1301, USA. */
93509525
KD
23
24#include "sysdep.h"
3db64b00 25#include <limits.h>
93509525
KD
26#include "bfd.h"
27#include "libbfd.h"
b570b954 28#include "aout/ar.h"
93509525 29
93509525
KD
30#ifndef S_IXUSR
31#define S_IXUSR 0100 /* Execute by owner. */
32#endif
33#ifndef S_IXGRP
34#define S_IXGRP 0010 /* Execute by group. */
35#endif
36#ifndef S_IXOTH
37#define S_IXOTH 0001 /* Execute by others. */
38#endif
39
428b207a
TT
40#ifndef FD_CLOEXEC
41#define FD_CLOEXEC 1
42#endif
43
7c192733 44file_ptr
c7c3d11b 45_bfd_real_ftell (FILE *file)
7c192733
AC
46{
47#if defined (HAVE_FTELLO64)
48 return ftello64 (file);
49#elif defined (HAVE_FTELLO)
50 return ftello (file);
51#else
52 return ftell (file);
53#endif
54}
55
56int
c7c3d11b 57_bfd_real_fseek (FILE *file, file_ptr offset, int whence)
7c192733
AC
58{
59#if defined (HAVE_FSEEKO64)
60 return fseeko64 (file, offset, whence);
61#elif defined (HAVE_FSEEKO)
62 return fseeko (file, offset, whence);
63#else
64 return fseek (file, offset, whence);
65#endif
66}
67
428b207a
TT
68/* Mark FILE as close-on-exec. Return FILE. FILE may be NULL, in
69 which case nothing is done. */
70static FILE *
71close_on_exec (FILE *file)
72{
73#if defined (HAVE_FILENO) && defined (F_GETFD)
74 if (file)
75 {
76 int fd = fileno (file);
77 int old = fcntl (fd, F_GETFD, 0);
78 if (old >= 0)
79 fcntl (fd, F_SETFD, old | FD_CLOEXEC);
80 }
81#endif
82 return file;
83}
84
2e6f4fae 85FILE *
c7c3d11b 86_bfd_real_fopen (const char *filename, const char *modes)
2e6f4fae 87{
d387240a 88#ifdef VMS
d387240a
TG
89 char *vms_attr;
90
9aff4b7a 91 /* On VMS, fopen allows file attributes as optional arguments.
d387240a
TG
92 We need to use them but we'd better to use the common prototype.
93 In fopen-vms.h, they are separated from the mode with a comma.
94 Split here. */
95 vms_attr = strchr (modes, ',');
96 if (vms_attr == NULL)
97 {
98 /* No attributes. */
99 return close_on_exec (fopen (filename, modes));
100 }
101 else
102 {
0c376465
TG
103 /* Attributes found. Split. */
104 size_t modes_len = strlen (modes) + 1;
105 char attrs[modes_len + 1];
106 char *at[3];
107 int i;
108
109 memcpy (attrs, modes, modes_len);
110 at[0] = attrs;
111 for (i = 0; i < 2; i++)
112 {
113 at[i + 1] = strchr (at[i], ',');
114 BFD_ASSERT (at[i + 1] != NULL);
115 *(at[i + 1]++) = 0; /* Replace ',' with a nul, and skip it. */
116 }
117 return close_on_exec (fopen (filename, at[0], at[1], at[2]));
d387240a
TG
118 }
119#else /* !VMS */
2e6f4fae 120#if defined (HAVE_FOPEN64)
428b207a 121 return close_on_exec (fopen64 (filename, modes));
2e6f4fae 122#else
428b207a 123 return close_on_exec (fopen (filename, modes));
2e6f4fae 124#endif
d387240a 125#endif /* !VMS */
2e6f4fae
DJ
126}
127
40838a72
AC
128/*
129INTERNAL_DEFINITION
130 struct bfd_iovec
93509525 131
40838a72 132DESCRIPTION
93509525 133
40838a72
AC
134 The <<struct bfd_iovec>> contains the internal file I/O class.
135 Each <<BFD>> has an instance of this class and all file I/O is
136 routed through it (it is assumed that the instance implements
137 all methods listed below).
138
139.struct bfd_iovec
140.{
141. {* To avoid problems with macros, a "b" rather than "f"
142. prefix is prepended to each method name. *}
143. {* Attempt to read/write NBYTES on ABFD's IOSTREAM storing/fetching
144. bytes starting at PTR. Return the number of bytes actually
145. transfered (a read past end-of-file returns less than NBYTES),
146. or -1 (setting <<bfd_error>>) if an error occurs. *}
147. file_ptr (*bread) (struct bfd *abfd, void *ptr, file_ptr nbytes);
148. file_ptr (*bwrite) (struct bfd *abfd, const void *ptr,
07d6d2b8 149. file_ptr nbytes);
40838a72
AC
150. {* Return the current IOSTREAM file offset, or -1 (setting <<bfd_error>>
151. if an error occurs. *}
152. file_ptr (*btell) (struct bfd *abfd);
153. {* For the following, on successful completion a value of 0 is returned.
07d6d2b8 154. Otherwise, a value of -1 is returned (and <<bfd_error>> is set). *}
40838a72 155. int (*bseek) (struct bfd *abfd, file_ptr offset, int whence);
405bf443 156. int (*bclose) (struct bfd *abfd);
40838a72
AC
157. int (*bflush) (struct bfd *abfd);
158. int (*bstat) (struct bfd *abfd, struct stat *sb);
4c95ab76
TG
159. {* Mmap a part of the files. ADDR, LEN, PROT, FLAGS and OFFSET are the usual
160. mmap parameter, except that LEN and OFFSET do not need to be page
161. aligned. Returns (void *)-1 on failure, mmapped address on success.
162. Also write in MAP_ADDR the address of the page aligned buffer and in
163. MAP_LEN the size mapped (a page multiple). Use unmap with MAP_ADDR and
164. MAP_LEN to unmap. *}
f07749bb 165. void *(*bmmap) (struct bfd *abfd, void *addr, bfd_size_type len,
07d6d2b8
AM
166. int prot, int flags, file_ptr offset,
167. void **map_addr, bfd_size_type *map_len);
40838a72 168.};
93509525 169
65077aa8
TG
170.extern const struct bfd_iovec _bfd_memory_iovec;
171
40838a72 172*/
93509525 173
93509525
KD
174
175/* Return value is amount read. */
176
177bfd_size_type
c58b9523 178bfd_bread (void *ptr, bfd_size_type size, bfd *abfd)
93509525 179{
5c4ce239
AM
180 file_ptr nread;
181 bfd *element_bfd = abfd;
182 ufile_ptr offset = 0;
183
184 while (abfd->my_archive != NULL
185 && !bfd_is_thin_archive (abfd->my_archive))
186 {
187 offset += abfd->origin;
188 abfd = abfd->my_archive;
189 }
93509525 190
1fb41da4
AM
191 /* If this is an archive element, don't read past the end of
192 this element. */
5c4ce239 193 if (element_bfd->arelt_data != NULL)
1fb41da4 194 {
5c4ce239 195 bfd_size_type maxbytes = arelt_size (element_bfd);
f1bb16f8 196
5c4ce239 197 if (abfd->where < offset || abfd->where - offset >= maxbytes)
07d6d2b8 198 {
5c4ce239
AM
199 bfd_set_error (bfd_error_invalid_operation);
200 return -1;
07d6d2b8 201 }
5c4ce239
AM
202 if (abfd->where - offset + size > maxbytes)
203 size = maxbytes - (abfd->where - offset);
1fb41da4
AM
204 }
205
5c4ce239
AM
206 if (abfd->iovec == NULL)
207 {
208 bfd_set_error (bfd_error_invalid_operation);
209 return -1;
210 }
211
212 nread = abfd->iovec->bread (abfd, ptr, size);
213 if (nread != -1)
93509525
KD
214 abfd->where += nread;
215
93509525
KD
216 return nread;
217}
218
219bfd_size_type
c58b9523 220bfd_bwrite (const void *ptr, bfd_size_type size, bfd *abfd)
93509525 221{
5c4ce239 222 file_ptr nwrote;
93509525 223
5c4ce239
AM
224 while (abfd->my_archive != NULL
225 && !bfd_is_thin_archive (abfd->my_archive))
226 abfd = abfd->my_archive;
69fd4758 227
5c4ce239
AM
228 if (abfd->iovec == NULL)
229 {
230 bfd_set_error (bfd_error_invalid_operation);
231 return -1;
232 }
233
234 nwrote = abfd->iovec->bwrite (abfd, ptr, size);
235 if (nwrote != -1)
93509525 236 abfd->where += nwrote;
5c4ce239 237 if ((bfd_size_type) nwrote != size)
93509525
KD
238 {
239#ifdef ENOSPC
240 errno = ENOSPC;
241#endif
242 bfd_set_error (bfd_error_system_call);
243 }
244 return nwrote;
245}
246
7c192733 247file_ptr
c58b9523 248bfd_tell (bfd *abfd)
93509525 249{
5c4ce239 250 ufile_ptr offset = 0;
93509525
KD
251 file_ptr ptr;
252
5c4ce239
AM
253 while (abfd->my_archive != NULL
254 && !bfd_is_thin_archive (abfd->my_archive))
69fd4758 255 {
5c4ce239
AM
256 offset += abfd->origin;
257 abfd = abfd->my_archive;
69fd4758 258 }
93509525 259
5c4ce239
AM
260 if (abfd->iovec == NULL)
261 return 0;
262
263 ptr = abfd->iovec->btell (abfd);
93509525 264 abfd->where = ptr;
5c4ce239 265 return ptr - offset;
93509525
KD
266}
267
268int
c58b9523 269bfd_flush (bfd *abfd)
93509525 270{
5c4ce239
AM
271 while (abfd->my_archive != NULL
272 && !bfd_is_thin_archive (abfd->my_archive))
273 abfd = abfd->my_archive;
274
275 if (abfd->iovec == NULL)
276 return 0;
277
278 return abfd->iovec->bflush (abfd);
93509525
KD
279}
280
281/* Returns 0 for success, negative value for failure (in which case
282 bfd_get_error can retrieve the error code). */
283int
c58b9523 284bfd_stat (bfd *abfd, struct stat *statbuf)
93509525 285{
93509525
KD
286 int result;
287
5c4ce239
AM
288 while (abfd->my_archive != NULL
289 && !bfd_is_thin_archive (abfd->my_archive))
290 abfd = abfd->my_archive;
69fd4758 291
5c4ce239
AM
292 if (abfd->iovec == NULL)
293 {
294 bfd_set_error (bfd_error_invalid_operation);
295 return -1;
296 }
297
298 result = abfd->iovec->bstat (abfd, statbuf);
93509525
KD
299 if (result < 0)
300 bfd_set_error (bfd_error_system_call);
301 return result;
302}
303
304/* Returns 0 for success, nonzero for failure (in which case bfd_get_error
305 can retrieve the error code). */
306
307int
c58b9523 308bfd_seek (bfd *abfd, file_ptr position, int direction)
93509525
KD
309{
310 int result;
5c4ce239 311 ufile_ptr offset = 0;
93509525 312
5c4ce239
AM
313 while (abfd->my_archive != NULL
314 && !bfd_is_thin_archive (abfd->my_archive))
93509525 315 {
5c4ce239
AM
316 offset += abfd->origin;
317 abfd = abfd->my_archive;
93509525 318 }
93509525 319
5c4ce239 320 if (abfd->iovec == NULL)
660722b0 321 {
5c4ce239
AM
322 bfd_set_error (bfd_error_invalid_operation);
323 return -1;
660722b0 324 }
93509525 325
5c4ce239
AM
326 /* For the time being, a BFD may not seek to it's end. The problem
327 is that we don't easily have a way to recognize the end of an
328 element in an archive. */
329 BFD_ASSERT (direction == SEEK_SET || direction == SEEK_CUR);
330
331 if (direction != SEEK_CUR)
332 position += offset;
69fd4758 333
ff91d2f0
AM
334 if ((direction == SEEK_CUR && position == 0)
335 || (direction == SEEK_SET && (ufile_ptr) position == abfd->where))
336 return 0;
337
5c4ce239 338 result = abfd->iovec->bseek (abfd, position, direction);
93509525
KD
339 if (result != 0)
340 {
93509525 341 /* An EINVAL error probably means that the file offset was
07d6d2b8 342 absurd. */
5c4ce239 343 if (errno == EINVAL)
93509525
KD
344 bfd_set_error (bfd_error_file_truncated);
345 else
5c4ce239 346 bfd_set_error (bfd_error_system_call);
93509525
KD
347 }
348 else
349 {
350 /* Adjust `where' field. */
5c4ce239 351 if (direction == SEEK_CUR)
93509525 352 abfd->where += position;
5c4ce239
AM
353 else
354 abfd->where = position;
93509525 355 }
5c4ce239 356
93509525
KD
357 return result;
358}
359
360/*
361FUNCTION
362 bfd_get_mtime
363
364SYNOPSIS
c58b9523 365 long bfd_get_mtime (bfd *abfd);
93509525
KD
366
367DESCRIPTION
368 Return the file modification time (as read from the file system, or
369 from the archive header for archive members).
370
371*/
372
373long
c58b9523 374bfd_get_mtime (bfd *abfd)
93509525 375{
93509525
KD
376 struct stat buf;
377
378 if (abfd->mtime_set)
379 return abfd->mtime;
380
5c4ce239 381 if (bfd_stat (abfd, &buf) != 0)
93509525
KD
382 return 0;
383
384 abfd->mtime = buf.st_mtime; /* Save value in case anyone wants it */
385 return buf.st_mtime;
386}
387
388/*
389FUNCTION
390 bfd_get_size
391
392SYNOPSIS
47fdcf63 393 ufile_ptr bfd_get_size (bfd *abfd);
93509525
KD
394
395DESCRIPTION
396 Return the file size (as read from file system) for the file
397 associated with BFD @var{abfd}.
398
399 The initial motivation for, and use of, this routine is not
400 so we can get the exact size of the object the BFD applies to, since
401 that might not be generally possible (archive members for example).
402 It would be ideal if someone could eventually modify
403 it so that such results were guaranteed.
404
405 Instead, we want to ask questions like "is this NNN byte sized
406 object I'm about to try read from file offset YYY reasonable?"
407 As as example of where we might do this, some object formats
408 use string tables for which the first <<sizeof (long)>> bytes of the
409 table contain the size of the table itself, including the size bytes.
410 If an application tries to read what it thinks is one of these
411 string tables, without some way to validate the size, and for
412 some reason the size is wrong (byte swapping error, wrong location
413 for the string table, etc.), the only clue is likely to be a read
414 error when it tries to read the table, or a "virtual memory
415 exhausted" error when it tries to allocate 15 bazillon bytes
416 of space for the 15 bazillon byte table it is about to read.
5c4491d3 417 This function at least allows us to answer the question, "is the
93509525 418 size reasonable?".
b03202e3
AM
419
420 A return value of zero indicates the file size is unknown.
93509525
KD
421*/
422
47fdcf63 423ufile_ptr
c58b9523 424bfd_get_size (bfd *abfd)
93509525 425{
b03202e3
AM
426 /* A size of 0 means we haven't yet called bfd_stat. A size of 1
427 means we have a cached value of 0, ie. unknown. */
428 if (abfd->size <= 1 || bfd_write_p (abfd))
429 {
430 struct stat buf;
93509525 431
b03202e3
AM
432 if (abfd->size == 1 && !bfd_write_p (abfd))
433 return 0;
93509525 434
b03202e3
AM
435 if (bfd_stat (abfd, &buf) != 0
436 || buf.st_size == 0
437 || buf.st_size - (ufile_ptr) buf.st_size != 0)
438 {
439 abfd->size = 1;
440 return 0;
441 }
442 abfd->size = buf.st_size;
443 }
444 return abfd->size;
93509525 445}
25b88f33 446
8e2f54bc
L
447/*
448FUNCTION
449 bfd_get_file_size
450
451SYNOPSIS
47fdcf63 452 ufile_ptr bfd_get_file_size (bfd *abfd);
8e2f54bc
L
453
454DESCRIPTION
455 Return the file size (as read from file system) for the file
456 associated with BFD @var{abfd}. It supports both normal files
457 and archive elements.
458
459*/
460
47fdcf63 461ufile_ptr
8e2f54bc
L
462bfd_get_file_size (bfd *abfd)
463{
b570b954
AM
464 ufile_ptr file_size, archive_size = (ufile_ptr) -1;
465
8e2f54bc
L
466 if (abfd->my_archive != NULL
467 && !bfd_is_thin_archive (abfd->my_archive))
b570b954
AM
468 {
469 struct areltdata *adata = (struct areltdata *) abfd->arelt_data;
470 archive_size = adata->parsed_size;
471 /* If the archive is compressed we can't compare against file size. */
472 if (memcmp (((struct ar_hdr *) adata->arch_header)->ar_fmag,
473 "Z\012", 2) == 0)
474 return archive_size;
475 abfd = abfd->my_archive;
476 }
8e2f54bc 477
b570b954
AM
478 file_size = bfd_get_size (abfd);
479 if (archive_size < file_size)
480 return archive_size;
481 return file_size;
8e2f54bc 482}
25b88f33
PP
483
484/*
485FUNCTION
486 bfd_mmap
487
488SYNOPSIS
489 void *bfd_mmap (bfd *abfd, void *addr, bfd_size_type len,
07d6d2b8
AM
490 int prot, int flags, file_ptr offset,
491 void **map_addr, bfd_size_type *map_len);
25b88f33
PP
492
493DESCRIPTION
494 Return mmap()ed region of the file, if possible and implemented.
07d6d2b8
AM
495 LEN and OFFSET do not need to be page aligned. The page aligned
496 address and length are written to MAP_ADDR and MAP_LEN.
25b88f33
PP
497
498*/
499
500void *
501bfd_mmap (bfd *abfd, void *addr, bfd_size_type len,
4c95ab76 502 int prot, int flags, file_ptr offset,
07d6d2b8 503 void **map_addr, bfd_size_type *map_len)
25b88f33 504{
5c4ce239
AM
505 while (abfd->my_archive != NULL
506 && !bfd_is_thin_archive (abfd->my_archive))
507 {
508 offset += abfd->origin;
509 abfd = abfd->my_archive;
510 }
25b88f33
PP
511
512 if (abfd->iovec == NULL)
5c4ce239
AM
513 {
514 bfd_set_error (bfd_error_invalid_operation);
515 return (void *) -1;
516 }
25b88f33 517
4c95ab76 518 return abfd->iovec->bmmap (abfd, addr, len, prot, flags, offset,
07d6d2b8 519 map_addr, map_len);
25b88f33 520}
65077aa8
TG
521
522/* Memory file I/O operations. */
523
524static file_ptr
525memory_bread (bfd *abfd, void *ptr, file_ptr size)
526{
527 struct bfd_in_memory *bim;
528 bfd_size_type get;
529
530 bim = (struct bfd_in_memory *) abfd->iostream;
531 get = size;
532 if (abfd->where + get > bim->size)
533 {
534 if (bim->size < (bfd_size_type) abfd->where)
07d6d2b8 535 get = 0;
65077aa8 536 else
07d6d2b8 537 get = bim->size - abfd->where;
65077aa8
TG
538 bfd_set_error (bfd_error_file_truncated);
539 }
540 memcpy (ptr, bim->buffer + abfd->where, (size_t) get);
541 return get;
542}
543
544static file_ptr
545memory_bwrite (bfd *abfd, const void *ptr, file_ptr size)
546{
547 struct bfd_in_memory *bim = (struct bfd_in_memory *) abfd->iostream;
548
549 if (abfd->where + size > bim->size)
550 {
551 bfd_size_type newsize, oldsize;
552
553 oldsize = (bim->size + 127) & ~(bfd_size_type) 127;
554 bim->size = abfd->where + size;
555 /* Round up to cut down on memory fragmentation */
556 newsize = (bim->size + 127) & ~(bfd_size_type) 127;
557 if (newsize > oldsize)
07d6d2b8
AM
558 {
559 bim->buffer = (bfd_byte *) bfd_realloc_or_free (bim->buffer, newsize);
560 if (bim->buffer == NULL)
561 {
562 bim->size = 0;
563 return 0;
564 }
565 if (newsize > bim->size)
566 memset (bim->buffer + bim->size, 0, newsize - bim->size);
567 }
65077aa8
TG
568 }
569 memcpy (bim->buffer + abfd->where, ptr, (size_t) size);
570 return size;
571}
572
573static file_ptr
574memory_btell (bfd *abfd)
575{
576 return abfd->where;
577}
578
579static int
580memory_bseek (bfd *abfd, file_ptr position, int direction)
581{
582 file_ptr nwhere;
583 struct bfd_in_memory *bim;
584
585 bim = (struct bfd_in_memory *) abfd->iostream;
586
587 if (direction == SEEK_SET)
588 nwhere = position;
589 else
590 nwhere = abfd->where + position;
591
592 if (nwhere < 0)
593 {
594 abfd->where = 0;
595 errno = EINVAL;
596 return -1;
597 }
598
599 if ((bfd_size_type)nwhere > bim->size)
600 {
601 if (abfd->direction == write_direction
07d6d2b8
AM
602 || abfd->direction == both_direction)
603 {
604 bfd_size_type newsize, oldsize;
605
606 oldsize = (bim->size + 127) & ~(bfd_size_type) 127;
607 bim->size = nwhere;
608 /* Round up to cut down on memory fragmentation */
609 newsize = (bim->size + 127) & ~(bfd_size_type) 127;
610 if (newsize > oldsize)
611 {
612 bim->buffer = (bfd_byte *) bfd_realloc_or_free (bim->buffer, newsize);
613 if (bim->buffer == NULL)
614 {
615 errno = EINVAL;
616 bim->size = 0;
617 return -1;
618 }
619 memset (bim->buffer + oldsize, 0, newsize - oldsize);
620 }
621 }
65077aa8 622 else
07d6d2b8
AM
623 {
624 abfd->where = bim->size;
625 errno = EINVAL;
626 bfd_set_error (bfd_error_file_truncated);
627 return -1;
628 }
65077aa8
TG
629 }
630 return 0;
631}
632
405bf443 633static int
65077aa8
TG
634memory_bclose (struct bfd *abfd)
635{
636 struct bfd_in_memory *bim = (struct bfd_in_memory *) abfd->iostream;
637
638 if (bim->buffer != NULL)
639 free (bim->buffer);
640 free (bim);
641 abfd->iostream = NULL;
642
405bf443 643 return 0;
65077aa8
TG
644}
645
646static int
647memory_bflush (bfd *abfd ATTRIBUTE_UNUSED)
648{
649 return 0;
650}
651
652static int
653memory_bstat (bfd *abfd, struct stat *statbuf)
654{
655 struct bfd_in_memory *bim = (struct bfd_in_memory *) abfd->iostream;
656
b5dee4ea 657 memset (statbuf, 0, sizeof (*statbuf));
65077aa8
TG
658 statbuf->st_size = bim->size;
659
660 return 0;
661}
662
663static void *
664memory_bmmap (bfd *abfd ATTRIBUTE_UNUSED, void *addr ATTRIBUTE_UNUSED,
07d6d2b8
AM
665 bfd_size_type len ATTRIBUTE_UNUSED, int prot ATTRIBUTE_UNUSED,
666 int flags ATTRIBUTE_UNUSED, file_ptr offset ATTRIBUTE_UNUSED,
667 void **map_addr ATTRIBUTE_UNUSED,
668 bfd_size_type *map_len ATTRIBUTE_UNUSED)
65077aa8
TG
669{
670 return (void *)-1;
671}
672
673const struct bfd_iovec _bfd_memory_iovec =
674{
675 &memory_bread, &memory_bwrite, &memory_btell, &memory_bseek,
676 &memory_bclose, &memory_bflush, &memory_bstat, &memory_bmmap
677};
This page took 0.847928 seconds and 4 git commands to generate.