1d2201f2fSdrahn /* Low-level I/O routines for BFDs.
2cf2f2c56Smiod
3cf2f2c56Smiod Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
4cf2f2c56Smiod 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
5cf2f2c56Smiod
6d2201f2fSdrahn Written by Cygnus Support.
7d2201f2fSdrahn
8d2201f2fSdrahn This file is part of BFD, the Binary File Descriptor library.
9d2201f2fSdrahn
10d2201f2fSdrahn This program is free software; you can redistribute it and/or modify
11d2201f2fSdrahn it under the terms of the GNU General Public License as published by
12d2201f2fSdrahn the Free Software Foundation; either version 2 of the License, or
13d2201f2fSdrahn (at your option) any later version.
14d2201f2fSdrahn
15d2201f2fSdrahn This program is distributed in the hope that it will be useful,
16d2201f2fSdrahn but WITHOUT ANY WARRANTY; without even the implied warranty of
17d2201f2fSdrahn MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18d2201f2fSdrahn GNU General Public License for more details.
19d2201f2fSdrahn
20d2201f2fSdrahn You should have received a copy of the GNU General Public License
21d2201f2fSdrahn along with this program; if not, write to the Free Software
22d2201f2fSdrahn Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23d2201f2fSdrahn
24d2201f2fSdrahn #include "sysdep.h"
25d2201f2fSdrahn
26d2201f2fSdrahn #include "bfd.h"
27d2201f2fSdrahn #include "libbfd.h"
28d2201f2fSdrahn
29d2201f2fSdrahn #include <limits.h>
30d2201f2fSdrahn
31d2201f2fSdrahn #ifndef S_IXUSR
32d2201f2fSdrahn #define S_IXUSR 0100 /* Execute by owner. */
33d2201f2fSdrahn #endif
34d2201f2fSdrahn #ifndef S_IXGRP
35d2201f2fSdrahn #define S_IXGRP 0010 /* Execute by group. */
36d2201f2fSdrahn #endif
37d2201f2fSdrahn #ifndef S_IXOTH
38d2201f2fSdrahn #define S_IXOTH 0001 /* Execute by others. */
39d2201f2fSdrahn #endif
40d2201f2fSdrahn
41cf2f2c56Smiod file_ptr
real_ftell(FILE * file)42cf2f2c56Smiod real_ftell (FILE *file)
43cf2f2c56Smiod {
44cf2f2c56Smiod #if defined (HAVE_FTELLO64)
45cf2f2c56Smiod return ftello64 (file);
46cf2f2c56Smiod #elif defined (HAVE_FTELLO)
47cf2f2c56Smiod return ftello (file);
48cf2f2c56Smiod #else
49cf2f2c56Smiod return ftell (file);
50cf2f2c56Smiod #endif
51cf2f2c56Smiod }
52cf2f2c56Smiod
53cf2f2c56Smiod int
real_fseek(FILE * file,file_ptr offset,int whence)54cf2f2c56Smiod real_fseek (FILE *file, file_ptr offset, int whence)
55cf2f2c56Smiod {
56cf2f2c56Smiod #if defined (HAVE_FSEEKO64)
57cf2f2c56Smiod return fseeko64 (file, offset, whence);
58cf2f2c56Smiod #elif defined (HAVE_FSEEKO)
59cf2f2c56Smiod return fseeko (file, offset, whence);
60cf2f2c56Smiod #else
61cf2f2c56Smiod return fseek (file, offset, whence);
62cf2f2c56Smiod #endif
63cf2f2c56Smiod }
64cf2f2c56Smiod
65d2201f2fSdrahn /* Note that archive entries don't have streams; they share their parent's.
66d2201f2fSdrahn This allows someone to play with the iostream behind BFD's back.
67d2201f2fSdrahn
68d2201f2fSdrahn Also, note that the origin pointer points to the beginning of a file's
69d2201f2fSdrahn contents (0 for non-archive elements). For archive entries this is the
70d2201f2fSdrahn first octet in the file, NOT the beginning of the archive header. */
71d2201f2fSdrahn
72d2201f2fSdrahn static size_t
real_read(void * where,size_t a,size_t b,FILE * file)73cf2f2c56Smiod real_read (void *where, size_t a, size_t b, FILE *file)
74d2201f2fSdrahn {
75d2201f2fSdrahn /* FIXME - this looks like an optimization, but it's really to cover
76d2201f2fSdrahn up for a feature of some OSs (not solaris - sigh) that
77d2201f2fSdrahn ld/pe-dll.c takes advantage of (apparently) when it creates BFDs
78d2201f2fSdrahn internally and tries to link against them. BFD seems to be smart
79d2201f2fSdrahn enough to realize there are no symbol records in the "file" that
80d2201f2fSdrahn doesn't exist but attempts to read them anyway. On Solaris,
81d2201f2fSdrahn attempting to read zero bytes from a NULL file results in a core
82d2201f2fSdrahn dump, but on other platforms it just returns zero bytes read.
83d2201f2fSdrahn This makes it to something reasonable. - DJ */
84d2201f2fSdrahn if (a == 0 || b == 0)
85d2201f2fSdrahn return 0;
86d2201f2fSdrahn
87d2201f2fSdrahn
88d2201f2fSdrahn #if defined (__VAX) && defined (VMS)
89d2201f2fSdrahn /* Apparently fread on Vax VMS does not keep the record length
90d2201f2fSdrahn information. */
91d2201f2fSdrahn return read (fileno (file), where, a * b);
92d2201f2fSdrahn #else
93d2201f2fSdrahn return fread (where, a, b, file);
94d2201f2fSdrahn #endif
95d2201f2fSdrahn }
96d2201f2fSdrahn
97d2201f2fSdrahn /* Return value is amount read. */
98d2201f2fSdrahn
99d2201f2fSdrahn bfd_size_type
bfd_bread(void * ptr,bfd_size_type size,bfd * abfd)100cf2f2c56Smiod bfd_bread (void *ptr, bfd_size_type size, bfd *abfd)
101d2201f2fSdrahn {
102d2201f2fSdrahn size_t nread;
103d2201f2fSdrahn
104d2201f2fSdrahn if ((abfd->flags & BFD_IN_MEMORY) != 0)
105d2201f2fSdrahn {
106d2201f2fSdrahn struct bfd_in_memory *bim;
107d2201f2fSdrahn bfd_size_type get;
108d2201f2fSdrahn
109cf2f2c56Smiod bim = abfd->iostream;
110d2201f2fSdrahn get = size;
111d2201f2fSdrahn if (abfd->where + get > bim->size)
112d2201f2fSdrahn {
113d2201f2fSdrahn if (bim->size < (bfd_size_type) abfd->where)
114d2201f2fSdrahn get = 0;
115d2201f2fSdrahn else
116d2201f2fSdrahn get = bim->size - abfd->where;
117d2201f2fSdrahn bfd_set_error (bfd_error_file_truncated);
118d2201f2fSdrahn }
119d2201f2fSdrahn memcpy (ptr, bim->buffer + abfd->where, (size_t) get);
120d2201f2fSdrahn abfd->where += get;
121d2201f2fSdrahn return get;
122d2201f2fSdrahn }
123d2201f2fSdrahn
124d2201f2fSdrahn nread = real_read (ptr, 1, (size_t) size, bfd_cache_lookup (abfd));
125d2201f2fSdrahn if (nread != (size_t) -1)
126d2201f2fSdrahn abfd->where += nread;
127d2201f2fSdrahn
128d2201f2fSdrahn /* Set bfd_error if we did not read as much data as we expected.
129d2201f2fSdrahn
130d2201f2fSdrahn If the read failed due to an error set the bfd_error_system_call,
131d2201f2fSdrahn else set bfd_error_file_truncated.
132d2201f2fSdrahn
133d2201f2fSdrahn A BFD backend may wish to override bfd_error_file_truncated to
134d2201f2fSdrahn provide something more useful (eg. no_symbols or wrong_format). */
135d2201f2fSdrahn if (nread != size)
136d2201f2fSdrahn {
137d2201f2fSdrahn if (ferror (bfd_cache_lookup (abfd)))
138d2201f2fSdrahn bfd_set_error (bfd_error_system_call);
139d2201f2fSdrahn else
140d2201f2fSdrahn bfd_set_error (bfd_error_file_truncated);
141d2201f2fSdrahn }
142d2201f2fSdrahn
143d2201f2fSdrahn return nread;
144d2201f2fSdrahn }
145d2201f2fSdrahn
146d2201f2fSdrahn bfd_size_type
bfd_bwrite(const void * ptr,bfd_size_type size,bfd * abfd)147cf2f2c56Smiod bfd_bwrite (const void *ptr, bfd_size_type size, bfd *abfd)
148d2201f2fSdrahn {
149d2201f2fSdrahn size_t nwrote;
150d2201f2fSdrahn
151d2201f2fSdrahn if ((abfd->flags & BFD_IN_MEMORY) != 0)
152d2201f2fSdrahn {
153cf2f2c56Smiod struct bfd_in_memory *bim = abfd->iostream;
154d2201f2fSdrahn size = (size_t) size;
155d2201f2fSdrahn if (abfd->where + size > bim->size)
156d2201f2fSdrahn {
157d2201f2fSdrahn bfd_size_type newsize, oldsize;
158d2201f2fSdrahn
159d2201f2fSdrahn oldsize = (bim->size + 127) & ~(bfd_size_type) 127;
160d2201f2fSdrahn bim->size = abfd->where + size;
161d2201f2fSdrahn /* Round up to cut down on memory fragmentation */
162d2201f2fSdrahn newsize = (bim->size + 127) & ~(bfd_size_type) 127;
163d2201f2fSdrahn if (newsize > oldsize)
164d2201f2fSdrahn {
165cf2f2c56Smiod bim->buffer = bfd_realloc (bim->buffer, newsize);
166d2201f2fSdrahn if (bim->buffer == 0)
167d2201f2fSdrahn {
168d2201f2fSdrahn bim->size = 0;
169d2201f2fSdrahn return 0;
170d2201f2fSdrahn }
171d2201f2fSdrahn }
172d2201f2fSdrahn }
173d2201f2fSdrahn memcpy (bim->buffer + abfd->where, ptr, (size_t) size);
174d2201f2fSdrahn abfd->where += size;
175d2201f2fSdrahn return size;
176d2201f2fSdrahn }
177d2201f2fSdrahn
178d2201f2fSdrahn nwrote = fwrite (ptr, 1, (size_t) size, bfd_cache_lookup (abfd));
179d2201f2fSdrahn if (nwrote != (size_t) -1)
180d2201f2fSdrahn abfd->where += nwrote;
181d2201f2fSdrahn if (nwrote != size)
182d2201f2fSdrahn {
183d2201f2fSdrahn #ifdef ENOSPC
184d2201f2fSdrahn errno = ENOSPC;
185d2201f2fSdrahn #endif
186d2201f2fSdrahn bfd_set_error (bfd_error_system_call);
187d2201f2fSdrahn }
188d2201f2fSdrahn return nwrote;
189d2201f2fSdrahn }
190d2201f2fSdrahn
191cf2f2c56Smiod file_ptr
bfd_tell(bfd * abfd)192cf2f2c56Smiod bfd_tell (bfd *abfd)
193d2201f2fSdrahn {
194d2201f2fSdrahn file_ptr ptr;
195d2201f2fSdrahn
196d2201f2fSdrahn if ((abfd->flags & BFD_IN_MEMORY) != 0)
197d2201f2fSdrahn return abfd->where;
198d2201f2fSdrahn
199cf2f2c56Smiod ptr = real_ftell (bfd_cache_lookup (abfd));
200d2201f2fSdrahn
201d2201f2fSdrahn if (abfd->my_archive)
202d2201f2fSdrahn ptr -= abfd->origin;
203d2201f2fSdrahn abfd->where = ptr;
204d2201f2fSdrahn return ptr;
205d2201f2fSdrahn }
206d2201f2fSdrahn
207d2201f2fSdrahn int
bfd_flush(bfd * abfd)208cf2f2c56Smiod bfd_flush (bfd *abfd)
209d2201f2fSdrahn {
210d2201f2fSdrahn if ((abfd->flags & BFD_IN_MEMORY) != 0)
211d2201f2fSdrahn return 0;
212d2201f2fSdrahn return fflush (bfd_cache_lookup(abfd));
213d2201f2fSdrahn }
214d2201f2fSdrahn
215d2201f2fSdrahn /* Returns 0 for success, negative value for failure (in which case
216d2201f2fSdrahn bfd_get_error can retrieve the error code). */
217d2201f2fSdrahn int
bfd_stat(bfd * abfd,struct stat * statbuf)218cf2f2c56Smiod bfd_stat (bfd *abfd, struct stat *statbuf)
219d2201f2fSdrahn {
220d2201f2fSdrahn FILE *f;
221d2201f2fSdrahn int result;
222d2201f2fSdrahn
223d2201f2fSdrahn if ((abfd->flags & BFD_IN_MEMORY) != 0)
224d2201f2fSdrahn abort ();
225d2201f2fSdrahn
226d2201f2fSdrahn f = bfd_cache_lookup (abfd);
227d2201f2fSdrahn if (f == NULL)
228d2201f2fSdrahn {
229d2201f2fSdrahn bfd_set_error (bfd_error_system_call);
230d2201f2fSdrahn return -1;
231d2201f2fSdrahn }
232d2201f2fSdrahn result = fstat (fileno (f), statbuf);
233d2201f2fSdrahn if (result < 0)
234d2201f2fSdrahn bfd_set_error (bfd_error_system_call);
235d2201f2fSdrahn return result;
236d2201f2fSdrahn }
237d2201f2fSdrahn
238d2201f2fSdrahn /* Returns 0 for success, nonzero for failure (in which case bfd_get_error
239d2201f2fSdrahn can retrieve the error code). */
240d2201f2fSdrahn
241d2201f2fSdrahn int
bfd_seek(bfd * abfd,file_ptr position,int direction)242cf2f2c56Smiod bfd_seek (bfd *abfd, file_ptr position, int direction)
243d2201f2fSdrahn {
244d2201f2fSdrahn int result;
245d2201f2fSdrahn FILE *f;
246cf2f2c56Smiod file_ptr file_position;
247d2201f2fSdrahn /* For the time being, a BFD may not seek to it's end. The problem
248d2201f2fSdrahn is that we don't easily have a way to recognize the end of an
249d2201f2fSdrahn element in an archive. */
250d2201f2fSdrahn
251d2201f2fSdrahn BFD_ASSERT (direction == SEEK_SET || direction == SEEK_CUR);
252d2201f2fSdrahn
253d2201f2fSdrahn if (direction == SEEK_CUR && position == 0)
254d2201f2fSdrahn return 0;
255d2201f2fSdrahn
256d2201f2fSdrahn if ((abfd->flags & BFD_IN_MEMORY) != 0)
257d2201f2fSdrahn {
258d2201f2fSdrahn struct bfd_in_memory *bim;
259d2201f2fSdrahn
260cf2f2c56Smiod bim = abfd->iostream;
261d2201f2fSdrahn
262d2201f2fSdrahn if (direction == SEEK_SET)
263d2201f2fSdrahn abfd->where = position;
264d2201f2fSdrahn else
265d2201f2fSdrahn abfd->where += position;
266d2201f2fSdrahn
267d2201f2fSdrahn if (abfd->where > bim->size)
268d2201f2fSdrahn {
269d2201f2fSdrahn if ((abfd->direction == write_direction) ||
270d2201f2fSdrahn (abfd->direction == both_direction))
271d2201f2fSdrahn {
272d2201f2fSdrahn bfd_size_type newsize, oldsize;
273d2201f2fSdrahn oldsize = (bim->size + 127) & ~(bfd_size_type) 127;
274d2201f2fSdrahn bim->size = abfd->where;
275d2201f2fSdrahn /* Round up to cut down on memory fragmentation */
276d2201f2fSdrahn newsize = (bim->size + 127) & ~(bfd_size_type) 127;
277d2201f2fSdrahn if (newsize > oldsize)
278d2201f2fSdrahn {
279cf2f2c56Smiod bim->buffer = bfd_realloc (bim->buffer, newsize);
280d2201f2fSdrahn if (bim->buffer == 0)
281d2201f2fSdrahn {
282d2201f2fSdrahn bim->size = 0;
283d2201f2fSdrahn return -1;
284d2201f2fSdrahn }
285d2201f2fSdrahn }
286d2201f2fSdrahn }
287d2201f2fSdrahn else
288d2201f2fSdrahn {
289d2201f2fSdrahn abfd->where = bim->size;
290d2201f2fSdrahn bfd_set_error (bfd_error_file_truncated);
291d2201f2fSdrahn return -1;
292d2201f2fSdrahn }
293d2201f2fSdrahn }
294d2201f2fSdrahn return 0;
295d2201f2fSdrahn }
296d2201f2fSdrahn
297d2201f2fSdrahn if (abfd->format != bfd_archive && abfd->my_archive == 0)
298d2201f2fSdrahn {
299d2201f2fSdrahn #if 0
300d2201f2fSdrahn /* Explanation for this code: I'm only about 95+% sure that the above
301d2201f2fSdrahn conditions are sufficient and that all i/o calls are properly
302d2201f2fSdrahn adjusting the `where' field. So this is sort of an `assert'
303d2201f2fSdrahn that the `where' field is correct. If we can go a while without
304d2201f2fSdrahn tripping the abort, we can probably safely disable this code,
305d2201f2fSdrahn so that the real optimizations happen. */
306d2201f2fSdrahn file_ptr where_am_i_now;
307cf2f2c56Smiod where_am_i_now = real_ftell (bfd_cache_lookup (abfd));
308d2201f2fSdrahn if (abfd->my_archive)
309d2201f2fSdrahn where_am_i_now -= abfd->origin;
310d2201f2fSdrahn if (where_am_i_now != abfd->where)
311d2201f2fSdrahn abort ();
312d2201f2fSdrahn #endif
313d2201f2fSdrahn if (direction == SEEK_SET && (bfd_vma) position == abfd->where)
314d2201f2fSdrahn return 0;
315d2201f2fSdrahn }
316d2201f2fSdrahn else
317d2201f2fSdrahn {
318d2201f2fSdrahn /* We need something smarter to optimize access to archives.
319d2201f2fSdrahn Currently, anything inside an archive is read via the file
320d2201f2fSdrahn handle for the archive. Which means that a bfd_seek on one
321d2201f2fSdrahn component affects the `current position' in the archive, as
322d2201f2fSdrahn well as in any other component.
323d2201f2fSdrahn
324d2201f2fSdrahn It might be sufficient to put a spike through the cache
325d2201f2fSdrahn abstraction, and look to the archive for the file position,
326d2201f2fSdrahn but I think we should try for something cleaner.
327d2201f2fSdrahn
328d2201f2fSdrahn In the meantime, no optimization for archives. */
329d2201f2fSdrahn }
330d2201f2fSdrahn
331d2201f2fSdrahn f = bfd_cache_lookup (abfd);
332d2201f2fSdrahn file_position = position;
333d2201f2fSdrahn if (direction == SEEK_SET && abfd->my_archive != NULL)
334d2201f2fSdrahn file_position += abfd->origin;
335d2201f2fSdrahn
336cf2f2c56Smiod result = real_fseek (f, file_position, direction);
337d2201f2fSdrahn if (result != 0)
338d2201f2fSdrahn {
339d2201f2fSdrahn int hold_errno = errno;
340d2201f2fSdrahn
341d2201f2fSdrahn /* Force redetermination of `where' field. */
342d2201f2fSdrahn bfd_tell (abfd);
343d2201f2fSdrahn
344d2201f2fSdrahn /* An EINVAL error probably means that the file offset was
345d2201f2fSdrahn absurd. */
346d2201f2fSdrahn if (hold_errno == EINVAL)
347d2201f2fSdrahn bfd_set_error (bfd_error_file_truncated);
348d2201f2fSdrahn else
349d2201f2fSdrahn {
350d2201f2fSdrahn bfd_set_error (bfd_error_system_call);
351d2201f2fSdrahn errno = hold_errno;
352d2201f2fSdrahn }
353d2201f2fSdrahn }
354d2201f2fSdrahn else
355d2201f2fSdrahn {
356d2201f2fSdrahn /* Adjust `where' field. */
357d2201f2fSdrahn if (direction == SEEK_SET)
358d2201f2fSdrahn abfd->where = position;
359d2201f2fSdrahn else
360d2201f2fSdrahn abfd->where += position;
361d2201f2fSdrahn }
362d2201f2fSdrahn return result;
363d2201f2fSdrahn }
364d2201f2fSdrahn
365d2201f2fSdrahn /*
366d2201f2fSdrahn FUNCTION
367d2201f2fSdrahn bfd_get_mtime
368d2201f2fSdrahn
369d2201f2fSdrahn SYNOPSIS
370*d04417c8Smiod time_t bfd_get_mtime (bfd *abfd);
371d2201f2fSdrahn
372d2201f2fSdrahn DESCRIPTION
373d2201f2fSdrahn Return the file modification time (as read from the file system, or
374d2201f2fSdrahn from the archive header for archive members).
375d2201f2fSdrahn
376d2201f2fSdrahn */
377d2201f2fSdrahn
378*d04417c8Smiod time_t
bfd_get_mtime(bfd * abfd)379cf2f2c56Smiod bfd_get_mtime (bfd *abfd)
380d2201f2fSdrahn {
381d2201f2fSdrahn FILE *fp;
382d2201f2fSdrahn struct stat buf;
383d2201f2fSdrahn
384d2201f2fSdrahn if (abfd->mtime_set)
385d2201f2fSdrahn return abfd->mtime;
386d2201f2fSdrahn
387d2201f2fSdrahn fp = bfd_cache_lookup (abfd);
388d2201f2fSdrahn if (0 != fstat (fileno (fp), &buf))
389d2201f2fSdrahn return 0;
390d2201f2fSdrahn
391d2201f2fSdrahn abfd->mtime = buf.st_mtime; /* Save value in case anyone wants it */
392d2201f2fSdrahn return buf.st_mtime;
393d2201f2fSdrahn }
394d2201f2fSdrahn
395d2201f2fSdrahn /*
396d2201f2fSdrahn FUNCTION
397d2201f2fSdrahn bfd_get_size
398d2201f2fSdrahn
399d2201f2fSdrahn SYNOPSIS
400d2201f2fSdrahn long bfd_get_size (bfd *abfd);
401d2201f2fSdrahn
402d2201f2fSdrahn DESCRIPTION
403d2201f2fSdrahn Return the file size (as read from file system) for the file
404d2201f2fSdrahn associated with BFD @var{abfd}.
405d2201f2fSdrahn
406d2201f2fSdrahn The initial motivation for, and use of, this routine is not
407d2201f2fSdrahn so we can get the exact size of the object the BFD applies to, since
408d2201f2fSdrahn that might not be generally possible (archive members for example).
409d2201f2fSdrahn It would be ideal if someone could eventually modify
410d2201f2fSdrahn it so that such results were guaranteed.
411d2201f2fSdrahn
412d2201f2fSdrahn Instead, we want to ask questions like "is this NNN byte sized
413d2201f2fSdrahn object I'm about to try read from file offset YYY reasonable?"
414d2201f2fSdrahn As as example of where we might do this, some object formats
415d2201f2fSdrahn use string tables for which the first <<sizeof (long)>> bytes of the
416d2201f2fSdrahn table contain the size of the table itself, including the size bytes.
417d2201f2fSdrahn If an application tries to read what it thinks is one of these
418d2201f2fSdrahn string tables, without some way to validate the size, and for
419d2201f2fSdrahn some reason the size is wrong (byte swapping error, wrong location
420d2201f2fSdrahn for the string table, etc.), the only clue is likely to be a read
421d2201f2fSdrahn error when it tries to read the table, or a "virtual memory
422d2201f2fSdrahn exhausted" error when it tries to allocate 15 bazillon bytes
423d2201f2fSdrahn of space for the 15 bazillon byte table it is about to read.
424cf2f2c56Smiod This function at least allows us to answer the question, "is the
425d2201f2fSdrahn size reasonable?".
426d2201f2fSdrahn */
427d2201f2fSdrahn
428d2201f2fSdrahn long
bfd_get_size(bfd * abfd)429cf2f2c56Smiod bfd_get_size (bfd *abfd)
430d2201f2fSdrahn {
431d2201f2fSdrahn FILE *fp;
432d2201f2fSdrahn struct stat buf;
433d2201f2fSdrahn
434d2201f2fSdrahn if ((abfd->flags & BFD_IN_MEMORY) != 0)
435d2201f2fSdrahn return ((struct bfd_in_memory *) abfd->iostream)->size;
436d2201f2fSdrahn
437d2201f2fSdrahn fp = bfd_cache_lookup (abfd);
438d2201f2fSdrahn if (0 != fstat (fileno (fp), & buf))
439d2201f2fSdrahn return 0;
440d2201f2fSdrahn
441d2201f2fSdrahn return buf.st_size;
442d2201f2fSdrahn }
443