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