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