bfdwin.c revision 302408
1/* Support for memory-mapped windows into a BFD.
2   Copyright 1995, 1996, 2001, 2002, 2003, 2005
3   Free Software Foundation, Inc.
4   Written by Cygnus Support.
5
6This file is part of BFD, the Binary File Descriptor library.
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
21
22#include "sysdep.h"
23
24#include "bfd.h"
25#include "libbfd.h"
26
27/* Currently, if USE_MMAP is undefined, none if the window stuff is
28   used.  Okay, so it's mis-named.  At least the command-line option
29   "--without-mmap" is more obvious than "--without-windows" or some
30   such.  */
31
32#ifdef USE_MMAP
33
34#undef HAVE_MPROTECT /* code's not tested yet */
35
36#if HAVE_MMAP || HAVE_MPROTECT || HAVE_MADVISE
37#include <sys/mman.h>
38#endif
39
40#ifndef MAP_FILE
41#define MAP_FILE 0
42#endif
43
44static int debug_windows;
45
46/* The idea behind the next and refcount fields is that one mapped
47   region can suffice for multiple read-only windows or multiple
48   non-overlapping read-write windows.  It's not implemented yet
49   though.  */
50
51/*
52INTERNAL_DEFINITION
53
54.struct _bfd_window_internal {
55.  struct _bfd_window_internal *next;
56.  void *data;
57.  bfd_size_type size;
58.  int refcount : 31;		{* should be enough...  *}
59.  unsigned mapped : 1;		{* 1 = mmap, 0 = malloc *}
60.};
61*/
62
63void
64bfd_init_window (bfd_window *windowp)
65{
66  windowp->data = 0;
67  windowp->i = 0;
68  windowp->size = 0;
69}
70
71void
72bfd_free_window (bfd_window *windowp)
73{
74  bfd_window_internal *i = windowp->i;
75  windowp->i = 0;
76  windowp->data = 0;
77  if (i == 0)
78    return;
79  i->refcount--;
80  if (debug_windows)
81    fprintf (stderr, "freeing window @%p<%p,%lx,%p>\n",
82	     windowp, windowp->data, windowp->size, windowp->i);
83  if (i->refcount != 0)
84    return;
85
86  if (i->mapped)
87    {
88#ifdef HAVE_MMAP
89      munmap (i->data, i->size);
90      goto no_free;
91#else
92      abort ();
93#endif
94    }
95#ifdef HAVE_MPROTECT
96  mprotect (i->data, i->size, PROT_READ | PROT_WRITE);
97#endif
98  free (i->data);
99#ifdef HAVE_MMAP
100 no_free:
101#endif
102  i->data = 0;
103  /* There should be no more references to i at this point.  */
104  free (i);
105}
106
107static int ok_to_map = 1;
108
109bfd_boolean
110bfd_get_file_window (bfd *abfd,
111		     file_ptr offset,
112		     bfd_size_type size,
113		     bfd_window *windowp,
114		     bfd_boolean writable)
115{
116  static size_t pagesize;
117  bfd_window_internal *i = windowp->i;
118  bfd_size_type size_to_alloc = size;
119
120  if (debug_windows)
121    fprintf (stderr, "bfd_get_file_window (%p, %6ld, %6ld, %p<%p,%lx,%p>, %d)",
122	     abfd, (long) offset, (long) size,
123	     windowp, windowp->data, (unsigned long) windowp->size,
124	     windowp->i, writable);
125
126  /* Make sure we know the page size, so we can be friendly to mmap.  */
127  if (pagesize == 0)
128    pagesize = getpagesize ();
129  if (pagesize == 0)
130    abort ();
131
132  if (i == 0)
133    {
134      i = bfd_zmalloc (sizeof (bfd_window_internal));
135      windowp->i = i;
136      if (i == 0)
137	return FALSE;
138      i->data = 0;
139    }
140#ifdef HAVE_MMAP
141  if (ok_to_map
142      && (i->data == 0 || i->mapped == 1)
143      && (abfd->flags & BFD_IN_MEMORY) == 0)
144    {
145      file_ptr file_offset, offset2;
146      size_t real_size;
147      int fd;
148
149      /* Find the real file and the real offset into it.  */
150      while (abfd->my_archive != NULL)
151	{
152	  offset += abfd->origin;
153	  abfd = abfd->my_archive;
154	}
155
156      /* Seek into the file, to ensure it is open if cacheable.  */
157      if (abfd->iostream == NULL
158	  && (abfd->iovec == NULL
159	      || abfd->iovec->bseek (abfd, offset, SEEK_SET) != 0))
160	return FALSE;
161      fd = fileno ((FILE *) abfd->iostream);
162
163      /* Compute offsets and size for mmap and for the user's data.  */
164      offset2 = offset % pagesize;
165      if (offset2 < 0)
166	abort ();
167      file_offset = offset - offset2;
168      real_size = offset + size - file_offset;
169      real_size = real_size + pagesize - 1;
170      real_size -= real_size % pagesize;
171
172      /* If we're re-using a memory region, make sure it's big enough.  */
173      if (i->data && i->size < size)
174	{
175	  munmap (i->data, i->size);
176	  i->data = 0;
177	}
178      i->data = mmap (i->data, real_size,
179		      writable ? PROT_WRITE | PROT_READ : PROT_READ,
180		      (writable
181		       ? MAP_FILE | MAP_PRIVATE
182		       : MAP_FILE | MAP_SHARED),
183		      fd, file_offset);
184      if (i->data == (void *) -1)
185	{
186	  /* An error happened.  Report it, or try using malloc, or
187	     something.  */
188	  bfd_set_error (bfd_error_system_call);
189	  i->data = 0;
190	  windowp->data = 0;
191	  if (debug_windows)
192	    fprintf (stderr, "\t\tmmap failed!\n");
193	  return FALSE;
194	}
195      if (debug_windows)
196	fprintf (stderr, "\n\tmapped %ld at %p, offset is %ld\n",
197		 (long) real_size, i->data, (long) offset2);
198      i->size = real_size;
199      windowp->data = (bfd_byte *) i->data + offset2;
200      windowp->size = size;
201      i->mapped = 1;
202      return TRUE;
203    }
204  else if (debug_windows)
205    {
206      if (ok_to_map)
207	fprintf (stderr, _("not mapping: data=%lx mapped=%d\n"),
208		 (unsigned long) i->data, (int) i->mapped);
209      else
210	fprintf (stderr, _("not mapping: env var not set\n"));
211    }
212#else
213  ok_to_map = 0;
214#endif
215
216#ifdef HAVE_MPROTECT
217  if (!writable)
218    {
219      size_to_alloc += pagesize - 1;
220      size_to_alloc -= size_to_alloc % pagesize;
221    }
222#endif
223  if (debug_windows)
224    fprintf (stderr, "\n\t%s(%6ld)",
225	     i->data ? "realloc" : " malloc", (long) size_to_alloc);
226  i->data = bfd_realloc (i->data, size_to_alloc);
227  if (debug_windows)
228    fprintf (stderr, "\t-> %p\n", i->data);
229  i->refcount = 1;
230  if (i->data == NULL)
231    {
232      if (size_to_alloc == 0)
233	return TRUE;
234      return FALSE;
235    }
236  if (bfd_seek (abfd, offset, SEEK_SET) != 0)
237    return FALSE;
238  i->size = bfd_bread (i->data, size, abfd);
239  if (i->size != size)
240    return FALSE;
241  i->mapped = 0;
242#ifdef HAVE_MPROTECT
243  if (!writable)
244    {
245      if (debug_windows)
246	fprintf (stderr, "\tmprotect (%p, %ld, PROT_READ)\n", i->data,
247		 (long) i->size);
248      mprotect (i->data, i->size, PROT_READ);
249    }
250#endif
251  windowp->data = i->data;
252  windowp->size = i->size;
253  return TRUE;
254}
255
256#endif /* USE_MMAP */
257