1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
|
/* Low-level I/O routines for BFDs.
Copyright (C) 1990-2024 Free Software Foundation, Inc.
Written by Cygnus Support.
This file is part of BFD, the Binary File Descriptor library.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
#include "sysdep.h"
#include <limits.h>
#include "bfd.h"
#include "libbfd.h"
#include "aout/ar.h"
#if defined (_WIN32)
#include <windows.h>
#include <locale.h>
#endif
#ifndef S_IXUSR
#define S_IXUSR 0100 /* Execute by owner. */
#endif
#ifndef S_IXGRP
#define S_IXGRP 0010 /* Execute by group. */
#endif
#ifndef S_IXOTH
#define S_IXOTH 0001 /* Execute by others. */
#endif
#ifndef FD_CLOEXEC
#define FD_CLOEXEC 1
#endif
file_ptr
_bfd_real_ftell (FILE *file)
{
#if defined (HAVE_FTELLO64)
return ftello64 (file);
#elif defined (HAVE_FTELLO)
return ftello (file);
#else
return ftell (file);
#endif
}
int
_bfd_real_fseek (FILE *file, file_ptr offset, int whence)
{
#if defined (HAVE_FSEEKO64)
return fseeko64 (file, offset, whence);
#elif defined (HAVE_FSEEKO)
return fseeko (file, offset, whence);
#else
return fseek (file, offset, whence);
#endif
}
/* Mark FILE as close-on-exec. Return FILE. FILE may be NULL, in
which case nothing is done. */
static FILE *
close_on_exec (FILE *file)
{
#if defined (HAVE_FILENO) && defined (F_GETFD)
if (file)
{
int fd = fileno (file);
int old = fcntl (fd, F_GETFD, 0);
if (old >= 0)
fcntl (fd, F_SETFD, old | FD_CLOEXEC);
}
#endif
return file;
}
FILE *
_bfd_real_fopen (const char *filename, const char *modes)
{
#ifdef VMS
char *vms_attr;
/* On VMS, fopen allows file attributes as optional arguments.
We need to use them but we'd better to use the common prototype.
In fopen-vms.h, they are separated from the mode with a comma.
Split here. */
vms_attr = strchr (modes, ',');
if (vms_attr != NULL)
{
/* Attributes found. Split. */
size_t modes_len = strlen (modes) + 1;
char attrs[modes_len + 1];
char *at[3];
int i;
memcpy (attrs, modes, modes_len);
at[0] = attrs;
for (i = 0; i < 2; i++)
{
at[i + 1] = strchr (at[i], ',');
BFD_ASSERT (at[i + 1] != NULL);
*(at[i + 1]++) = 0; /* Replace ',' with a nul, and skip it. */
}
return close_on_exec (fopen (filename, at[0], at[1], at[2]));
}
#elif defined (_WIN32)
/* PR 25713: Handle extra long path names possibly containing '..' and '.'. */
wchar_t ** lpFilePart = {NULL};
const wchar_t prefixDOS[] = L"\\\\?\\";
const wchar_t prefixUNC[] = L"\\\\?\\UNC\\";
const wchar_t prefixNone[] = L"";
const size_t partPathLen = strlen (filename) + 1;
const wchar_t * prefix;
size_t sizeof_prefix;
bool strip_network_prefix = false;
/* PR 31527: In order to not hit limits in the maximum file path, all paths
need converting to Universal Naming Convention (UNC) syntax. The following
forms may be provided to this function and are converted accordingly.
1. UNC paths (start with \\?\), these are unconverted;
2. Network paths (start with \\ or // but not \\?\), these are given the
\\?UNC\ prefix, and have the incoming \\ or // removed;
3. DOS drive paths (a letter followed by a colon), these are given the
\\?\ prefix;
4. Paths relative to CWD, the current working directory is tested for the
above conditions, and otherwise are assumed to be DOS paths.
For more information see:
https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
*/
if (startswith (filename, "\\\\?\\"))
{
prefix = prefixNone;
sizeof_prefix = sizeof (prefixNone);
}
else if (startswith (filename, "\\\\") || startswith (filename, "//"))
{
prefix = prefixUNC;
sizeof_prefix = sizeof (prefixUNC);
strip_network_prefix = true;
}
else if (strlen (filename) > 2 && filename[1] == ':')
{
prefix = prefixDOS;
sizeof_prefix = sizeof (prefixDOS);
}
else
{
/* The partial path is relative to the current working directory, use this
to determine the prefix.
1) Get the length: Calling with lpBuffer set to null returns the length.
2) Resolve the path. */
size_t pwdWSize = GetCurrentDirectoryW (0, NULL);
wchar_t * pwdPath = calloc (pwdWSize, sizeof(wchar_t));
GetCurrentDirectoryW (pwdWSize, pwdPath);
if (wcsncmp (pwdPath, L"\\\\?\\", 6) == 0)
{
prefix = prefixNone;
sizeof_prefix = sizeof (prefixNone);
}
else if (wcsncmp (pwdPath, L"\\\\", 2) == 0
|| wcsncmp (pwdPath, L"//", 2) == 0)
{
prefix = prefixUNC;
sizeof_prefix = sizeof (prefixUNC);
strip_network_prefix = true;
}
else
{
prefix = prefixDOS;
sizeof_prefix = sizeof (prefixDOS);
}
free (pwdPath);
}
#ifdef __MINGW32__
#if !HAVE_DECL____LC_CODEPAGE_FUNC
/* This prototype was added to locale.h in version 9.0 of MinGW-w64. */
_CRTIMP unsigned int __cdecl ___lc_codepage_func (void);
#endif
const unsigned int cp = ___lc_codepage_func ();
#else
const unsigned int cp = CP_UTF8;
#endif
/* Converting the partial path from ascii to unicode.
1) Get the length: Calling with lpWideCharStr set to null returns the length.
2) Convert the string: Calling with cbMultiByte set to -1 includes the terminating null. */
size_t partPathWSize = MultiByteToWideChar (cp, 0, filename, -1, NULL, 0);
wchar_t * partPath = calloc (partPathWSize, sizeof(wchar_t));
size_t ix;
MultiByteToWideChar (cp, 0, filename, -1, partPath, partPathWSize);
/* Convert any UNIX style path separators into the DOS i.e. backslash separator. */
for (ix = 0; ix < partPathLen; ix++)
if (IS_UNIX_DIR_SEPARATOR(filename[ix]))
partPath[ix] = '\\';
/* Getting the full path from the provided partial path.
1) Get the length.
2) Resolve the path. */
long fullPathWSize = GetFullPathNameW (partPath, 0, NULL, lpFilePart);
wchar_t * fullPath = calloc (fullPathWSize + sizeof_prefix + 1, sizeof(wchar_t));
wcscpy (fullPath, prefix);
int prefixLen = sizeof_prefix / sizeof(wchar_t);
/* Do not add a prefix to the null device. */
if (stricmp (filename, "nul") == 0)
prefixLen = 1;
wchar_t * fullPathOffset = fullPath + prefixLen - 1;
GetFullPathNameW (partPath, fullPathWSize, fullPathOffset, lpFilePart);
if (strip_network_prefix)
{
/* Remove begining of the beginning two backslash characters (\\). */
wchar_t *_fullPath = calloc (fullPathWSize + sizeof_prefix + 1, sizeof(wchar_t));
GetFullPathNameW (fullPath, fullPathWSize + sizeof_prefix + 1, _fullPath, lpFilePart);
free (fullPath);
fullPath = _fullPath;
}
free (partPath);
/* It is non-standard for modes to exceed 16 characters. */
wchar_t modesW[16];
MultiByteToWideChar (cp, 0, modes, -1, modesW, sizeof(modesW));
FILE * file = _wfopen (fullPath, modesW);
free (fullPath);
return close_on_exec (file);
#elif defined (HAVE_FOPEN64)
return close_on_exec (fopen64 (filename, modes));
#else
return close_on_exec (fopen (filename, modes));
#endif
}
/*
INTERNAL_DEFINITION
struct bfd_iovec
DESCRIPTION
The <<struct bfd_iovec>> contains the internal file I/O class.
Each <<BFD>> has an instance of this class and all file I/O is
routed through it (it is assumed that the instance implements
all methods listed below).
.struct bfd_iovec
.{
. {* To avoid problems with macros, a "b" rather than "f"
. prefix is prepended to each method name. *}
. {* Attempt to read/write NBYTES on ABFD's IOSTREAM storing/fetching
. bytes starting at PTR. Return the number of bytes actually
. transfered (a read past end-of-file returns less than NBYTES),
. or -1 (setting <<bfd_error>>) if an error occurs. *}
. file_ptr (*bread) (struct bfd *abfd, void *ptr, file_ptr nbytes);
. file_ptr (*bwrite) (struct bfd *abfd, const void *ptr,
. file_ptr nbytes);
. {* Return the current IOSTREAM file offset, or -1 (setting <<bfd_error>>
. if an error occurs. *}
. file_ptr (*btell) (struct bfd *abfd);
. {* For the following, on successful completion a value of 0 is returned.
. Otherwise, a value of -1 is returned (and <<bfd_error>> is set). *}
. int (*bseek) (struct bfd *abfd, file_ptr offset, int whence);
. int (*bclose) (struct bfd *abfd);
. int (*bflush) (struct bfd *abfd);
. int (*bstat) (struct bfd *abfd, struct stat *sb);
. {* Mmap a part of the files. ADDR, LEN, PROT, FLAGS and OFFSET are the usual
. mmap parameter, except that LEN and OFFSET do not need to be page
. aligned. Returns (void *)-1 on failure, mmapped address on success.
. Also write in MAP_ADDR the address of the page aligned buffer and in
. MAP_LEN the size mapped (a page multiple). Use unmap with MAP_ADDR and
. MAP_LEN to unmap. *}
. void *(*bmmap) (struct bfd *abfd, void *addr, size_t len,
. int prot, int flags, file_ptr offset,
. void **map_addr, size_t *map_len);
.};
.extern const struct bfd_iovec _bfd_memory_iovec;
.
*/
/*
FUNCTION
bfd_read
SYNOPSIS
bfd_size_type bfd_read (void *, bfd_size_type, bfd *)
ATTRIBUTE_WARN_UNUSED_RESULT;
DESCRIPTION
Attempt to read SIZE bytes from ABFD's iostream to PTR.
Return the amount read.
*/
bfd_size_type
bfd_read (void *ptr, bfd_size_type size, bfd *abfd)
{
file_ptr nread;
bfd *element_bfd = abfd;
ufile_ptr offset = 0;
while (abfd->my_archive != NULL
&& !bfd_is_thin_archive (abfd->my_archive))
{
offset += abfd->origin;
abfd = abfd->my_archive;
}
offset += abfd->origin;
/* If this is a non-thin archive element, don't read past the end of
this element. */
if (element_bfd->arelt_data != NULL
&& element_bfd->my_archive != NULL
&& !bfd_is_thin_archive (element_bfd->my_archive))
{
bfd_size_type maxbytes = arelt_size (element_bfd);
if (abfd->where < offset || abfd->where - offset >= maxbytes)
{
bfd_set_error (bfd_error_invalid_operation);
return -1;
}
if (abfd->where - offset + size > maxbytes)
size = maxbytes - (abfd->where - offset);
}
if (abfd->iovec == NULL)
{
bfd_set_error (bfd_error_invalid_operation);
return -1;
}
if (abfd->last_io == bfd_io_write)
{
abfd->last_io = bfd_io_force;
if (bfd_seek (abfd, 0, SEEK_CUR) != 0)
return -1;
}
abfd->last_io = bfd_io_read;
nread = abfd->iovec->bread (abfd, ptr, size);
if (nread != -1)
abfd->where += nread;
return nread;
}
/*
FUNCTION
bfd_write
SYNOPSIS
bfd_size_type bfd_write (const void *, bfd_size_type, bfd *)
ATTRIBUTE_WARN_UNUSED_RESULT;
DESCRIPTION
Attempt to write SIZE bytes to ABFD's iostream from PTR.
Return the amount written.
*/
bfd_size_type
bfd_write (const void *ptr, bfd_size_type size, bfd *abfd)
{
file_ptr nwrote;
while (abfd->my_archive != NULL
&& !bfd_is_thin_archive (abfd->my_archive))
abfd = abfd->my_archive;
if (abfd->iovec == NULL)
{
bfd_set_error (bfd_error_invalid_operation);
return -1;
}
if (abfd->last_io == bfd_io_read)
{
abfd->last_io = bfd_io_force;
if (bfd_seek (abfd, 0, SEEK_CUR) != 0)
return -1;
}
abfd->last_io = bfd_io_write;
nwrote = abfd->iovec->bwrite (abfd, ptr, size);
if (nwrote != -1)
abfd->where += nwrote;
if ((bfd_size_type) nwrote != size)
{
#ifdef ENOSPC
errno = ENOSPC;
#endif
bfd_set_error (bfd_error_system_call);
}
return nwrote;
}
/*
FUNCTION
bfd_tell
SYNOPSIS
file_ptr bfd_tell (bfd *) ATTRIBUTE_WARN_UNUSED_RESULT;
DESCRIPTION
Return ABFD's iostream file position.
*/
file_ptr
bfd_tell (bfd *abfd)
{
ufile_ptr offset = 0;
file_ptr ptr;
while (abfd->my_archive != NULL
&& !bfd_is_thin_archive (abfd->my_archive))
{
offset += abfd->origin;
abfd = abfd->my_archive;
}
offset += abfd->origin;
if (abfd->iovec == NULL)
return 0;
ptr = abfd->iovec->btell (abfd);
abfd->where = ptr;
return ptr - offset;
}
/*
FUNCTION
bfd_flush
SYNOPSIS
int bfd_flush (bfd *);
DESCRIPTION
Flush ABFD's iostream pending IO.
*/
int
bfd_flush (bfd *abfd)
{
while (abfd->my_archive != NULL
&& !bfd_is_thin_archive (abfd->my_archive))
abfd = abfd->my_archive;
if (abfd->iovec == NULL)
return 0;
return abfd->iovec->bflush (abfd);
}
/*
FUNCTION
bfd_stat
SYNOPSIS
int bfd_stat (bfd *, struct stat *) ATTRIBUTE_WARN_UNUSED_RESULT;
DESCRIPTION
Call fstat on ABFD's iostream. Return 0 on success, and a
negative value on failure.
*/
int
bfd_stat (bfd *abfd, struct stat *statbuf)
{
int result;
while (abfd->my_archive != NULL
&& !bfd_is_thin_archive (abfd->my_archive))
abfd = abfd->my_archive;
if (abfd->iovec == NULL)
{
bfd_set_error (bfd_error_invalid_operation);
return -1;
}
result = abfd->iovec->bstat (abfd, statbuf);
if (result < 0)
bfd_set_error (bfd_error_system_call);
return result;
}
/*
FUNCTION
bfd_seek
SYNOPSIS
int bfd_seek (bfd *, file_ptr, int) ATTRIBUTE_WARN_UNUSED_RESULT;
DESCRIPTION
Call fseek on ABFD's iostream. Return 0 on success, and a
negative value on failure.
*/
int
bfd_seek (bfd *abfd, file_ptr position, int direction)
{
int result;
ufile_ptr offset = 0;
while (abfd->my_archive != NULL
&& !bfd_is_thin_archive (abfd->my_archive))
{
offset += abfd->origin;
abfd = abfd->my_archive;
}
offset += abfd->origin;
if (abfd->iovec == NULL)
{
bfd_set_error (bfd_error_invalid_operation);
return -1;
}
/* For the time being, a BFD may not seek to it's end. The problem
is that we don't easily have a way to recognize the end of an
element in an archive. */
BFD_ASSERT (direction == SEEK_SET || direction == SEEK_CUR);
if (direction != SEEK_CUR)
position += offset;
if (((direction == SEEK_CUR && position == 0)
|| (direction == SEEK_SET && (ufile_ptr) position == abfd->where))
&& abfd->last_io != bfd_io_force)
return 0;
abfd->last_io = bfd_io_seek;
result = abfd->iovec->bseek (abfd, position, direction);
if (result != 0)
{
/* An EINVAL error probably means that the file offset was
absurd. */
if (errno == EINVAL)
bfd_set_error (bfd_error_file_truncated);
else
bfd_set_error (bfd_error_system_call);
}
else
{
/* Adjust `where' field. */
if (direction == SEEK_CUR)
abfd->where += position;
else
abfd->where = position;
}
return result;
}
/*
FUNCTION
bfd_get_mtime
SYNOPSIS
long bfd_get_mtime (bfd *abfd);
DESCRIPTION
Return the file modification time (as read from the file system, or
from the archive header for archive members).
*/
long
bfd_get_mtime (bfd *abfd)
{
struct stat buf;
if (abfd->mtime_set)
return abfd->mtime;
if (bfd_stat (abfd, &buf) != 0)
return 0;
abfd->mtime = buf.st_mtime; /* Save value in case anyone wants it */
return buf.st_mtime;
}
/*
FUNCTION
bfd_get_size
SYNOPSIS
ufile_ptr bfd_get_size (bfd *abfd);
DESCRIPTION
Return the file size (as read from file system) for the file
associated with BFD @var{abfd}.
The initial motivation for, and use of, this routine is not
so we can get the exact size of the object the BFD applies to, since
that might not be generally possible (archive members for example).
It would be ideal if someone could eventually modify
it so that such results were guaranteed.
Instead, we want to ask questions like "is this NNN byte sized
object I'm about to try read from file offset YYY reasonable?"
As as example of where we might do this, some object formats
use string tables for which the first <<sizeof (long)>> bytes of the
table contain the size of the table itself, including the size bytes.
If an application tries to read what it thinks is one of these
string tables, without some way to validate the size, and for
some reason the size is wrong (byte swapping error, wrong location
for the string table, etc.), the only clue is likely to be a read
error when it tries to read the table, or a "virtual memory
exhausted" error when it tries to allocate 15 bazillon bytes
of space for the 15 bazillon byte table it is about to read.
This function at least allows us to answer the question, "is the
size reasonable?".
A return value of zero indicates the file size is unknown.
*/
ufile_ptr
bfd_get_size (bfd *abfd)
{
/* A size of 0 means we haven't yet called bfd_stat. A size of 1
means we have a cached value of 0, ie. unknown. */
if (abfd->size <= 1 || bfd_write_p (abfd))
{
struct stat buf;
if (abfd->size == 1 && !bfd_write_p (abfd))
return 0;
if (bfd_stat (abfd, &buf) != 0
|| buf.st_size == 0
|| buf.st_size - (ufile_ptr) buf.st_size != 0)
{
abfd->size = 1;
return 0;
}
abfd->size = buf.st_size;
}
return abfd->size;
}
/*
FUNCTION
bfd_get_file_size
SYNOPSIS
ufile_ptr bfd_get_file_size (bfd *abfd);
DESCRIPTION
Return the file size (as read from file system) for the file
associated with BFD @var{abfd}. It supports both normal files
and archive elements.
*/
ufile_ptr
bfd_get_file_size (bfd *abfd)
{
ufile_ptr file_size, archive_size = (ufile_ptr) -1;
unsigned int compression_p2 = 0;
if (abfd->my_archive != NULL
&& !bfd_is_thin_archive (abfd->my_archive))
{
struct areltdata *adata = (struct areltdata *) abfd->arelt_data;
if (adata != NULL)
{
archive_size = adata->parsed_size;
/* If the archive is compressed, assume an element won't
expand more than eight times file size. */
if (adata->arch_header != NULL
&& memcmp (((struct ar_hdr *) adata->arch_header)->ar_fmag,
"Z\012", 2) == 0)
compression_p2 = 3;
abfd = abfd->my_archive;
}
}
file_size = bfd_get_size (abfd) << compression_p2;
if (archive_size < file_size)
return archive_size;
return file_size;
}
/*
FUNCTION
bfd_mmap
SYNOPSIS
void *bfd_mmap (bfd *abfd, void *addr, size_t len,
int prot, int flags, file_ptr offset,
void **map_addr, size_t *map_len)
ATTRIBUTE_WARN_UNUSED_RESULT;
DESCRIPTION
Return mmap()ed region of the file, if possible and implemented.
LEN and OFFSET do not need to be page aligned. The page aligned
address and length are written to MAP_ADDR and MAP_LEN.
*/
void *
bfd_mmap (bfd *abfd, void *addr, size_t len,
int prot, int flags, file_ptr offset,
void **map_addr, size_t *map_len)
{
while (abfd->my_archive != NULL
&& !bfd_is_thin_archive (abfd->my_archive))
{
offset += abfd->origin;
abfd = abfd->my_archive;
}
offset += abfd->origin;
if (abfd->iovec == NULL)
{
bfd_set_error (bfd_error_invalid_operation);
return MAP_FAILED;
}
return abfd->iovec->bmmap (abfd, addr, len, prot, flags, offset,
map_addr, map_len);
}
/* Memory file I/O operations. */
static file_ptr
memory_bread (bfd *abfd, void *ptr, file_ptr size)
{
struct bfd_in_memory *bim;
bfd_size_type get;
bim = (struct bfd_in_memory *) abfd->iostream;
get = size;
if (abfd->where + get > bim->size)
{
if (bim->size < (bfd_size_type) abfd->where)
get = 0;
else
get = bim->size - abfd->where;
bfd_set_error (bfd_error_file_truncated);
}
memcpy (ptr, bim->buffer + abfd->where, (size_t) get);
return get;
}
static file_ptr
memory_bwrite (bfd *abfd, const void *ptr, file_ptr size)
{
struct bfd_in_memory *bim = (struct bfd_in_memory *) abfd->iostream;
if (abfd->where + size > bim->size)
{
bfd_size_type newsize, oldsize;
oldsize = (bim->size + 127) & ~(bfd_size_type) 127;
bim->size = abfd->where + size;
/* Round up to cut down on memory fragmentation */
newsize = (bim->size + 127) & ~(bfd_size_type) 127;
if (newsize > oldsize)
{
bim->buffer = (bfd_byte *) bfd_realloc_or_free (bim->buffer, newsize);
if (bim->buffer == NULL)
{
bim->size = 0;
return 0;
}
if (newsize > bim->size)
memset (bim->buffer + bim->size, 0, newsize - bim->size);
}
}
memcpy (bim->buffer + abfd->where, ptr, (size_t) size);
return size;
}
static file_ptr
memory_btell (bfd *abfd)
{
return abfd->where;
}
static int
memory_bseek (bfd *abfd, file_ptr position, int direction)
{
file_ptr nwhere;
struct bfd_in_memory *bim;
bim = (struct bfd_in_memory *) abfd->iostream;
if (direction == SEEK_SET)
nwhere = position;
else
nwhere = abfd->where + position;
if (nwhere < 0)
{
abfd->where = 0;
errno = EINVAL;
return -1;
}
if ((bfd_size_type)nwhere > bim->size)
{
if (abfd->direction == write_direction
|| abfd->direction == both_direction)
{
bfd_size_type newsize, oldsize;
oldsize = (bim->size + 127) & ~(bfd_size_type) 127;
bim->size = nwhere;
/* Round up to cut down on memory fragmentation */
newsize = (bim->size + 127) & ~(bfd_size_type) 127;
if (newsize > oldsize)
{
bim->buffer = (bfd_byte *) bfd_realloc_or_free (bim->buffer, newsize);
if (bim->buffer == NULL)
{
errno = EINVAL;
bim->size = 0;
return -1;
}
memset (bim->buffer + oldsize, 0, newsize - oldsize);
}
}
else
{
abfd->where = bim->size;
errno = EINVAL;
bfd_set_error (bfd_error_file_truncated);
return -1;
}
}
return 0;
}
static int
memory_bclose (struct bfd *abfd)
{
struct bfd_in_memory *bim = (struct bfd_in_memory *) abfd->iostream;
free (bim->buffer);
free (bim);
abfd->iostream = NULL;
return 0;
}
static int
memory_bflush (bfd *abfd ATTRIBUTE_UNUSED)
{
return 0;
}
static int
memory_bstat (bfd *abfd, struct stat *statbuf)
{
struct bfd_in_memory *bim = (struct bfd_in_memory *) abfd->iostream;
memset (statbuf, 0, sizeof (*statbuf));
statbuf->st_size = bim->size;
return 0;
}
static void *
memory_bmmap (bfd *abfd ATTRIBUTE_UNUSED, void *addr ATTRIBUTE_UNUSED,
size_t len ATTRIBUTE_UNUSED, int prot ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED, file_ptr offset ATTRIBUTE_UNUSED,
void **map_addr ATTRIBUTE_UNUSED,
size_t *map_len ATTRIBUTE_UNUSED)
{
return (void *)-1;
}
const struct bfd_iovec _bfd_memory_iovec =
{
&memory_bread, &memory_bwrite, &memory_btell, &memory_bseek,
&memory_bclose, &memory_bflush, &memory_bstat, &memory_bmmap
};
/*
FUNCTION
bfd_get_current_time
SYNOPSIS
time_t bfd_get_current_time (time_t now);
DESCRIPTION
Returns the current time.
If the environment variable SOURCE_DATE_EPOCH is defined
then this is parsed and its value is returned. Otherwise
if the paramter NOW is non-zero, then that is returned.
Otherwise the result of the system call "time(NULL)" is
returned.
*/
time_t
bfd_get_current_time (time_t now)
{
char *source_date_epoch;
unsigned long long epoch;
/* FIXME: We could probably cache this lookup,
and the parsing of its value below. */
source_date_epoch = getenv ("SOURCE_DATE_EPOCH");
if (source_date_epoch == NULL)
{
if (now)
return now;
return time (NULL);
}
epoch = strtoull (source_date_epoch, NULL, 0);
/* If epoch == 0 then something is wrong with SOURCE_DATE_EPOCH,
but we do not have an easy way to report it. Since the presence
of the environment variable implies that the user wants
deterministic behaviour we just accept the 0 value. */
return (time_t) epoch;
}
|