bfdwin.c revision 130561
1130561Sobrien/* Support for memory-mapped windows into a BFD.
2130561Sobrien   Copyright 1995, 1996, 2001, 2002, 2003 Free Software Foundation, Inc.
3130561Sobrien   Written by Cygnus Support.
4130561Sobrien
5130561SobrienThis file is part of BFD, the Binary File Descriptor library.
6130561Sobrien
7130561SobrienThis program is free software; you can redistribute it and/or modify
8130561Sobrienit under the terms of the GNU General Public License as published by
9130561Sobrienthe Free Software Foundation; either version 2 of the License, or
10130561Sobrien(at your option) any later version.
11130561Sobrien
12130561SobrienThis program is distributed in the hope that it will be useful,
13130561Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of
14130561SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15130561SobrienGNU General Public License for more details.
16130561Sobrien
17130561SobrienYou should have received a copy of the GNU General Public License
18130561Sobrienalong with this program; if not, write to the Free Software
19130561SobrienFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20130561Sobrien
21130561Sobrien#include "sysdep.h"
22130561Sobrien
23130561Sobrien#include "bfd.h"
24130561Sobrien#include "libbfd.h"
25130561Sobrien
26130561Sobrien/* Currently, if USE_MMAP is undefined, none if the window stuff is
27130561Sobrien   used.  Okay, so it's mis-named.  At least the command-line option
28130561Sobrien   "--without-mmap" is more obvious than "--without-windows" or some
29130561Sobrien   such.  */
30130561Sobrien
31130561Sobrien#ifdef USE_MMAP
32130561Sobrien
33130561Sobrien#undef HAVE_MPROTECT /* code's not tested yet */
34130561Sobrien
35130561Sobrien#if HAVE_MMAP || HAVE_MPROTECT || HAVE_MADVISE
36130561Sobrien#include <sys/mman.h>
37130561Sobrien#endif
38130561Sobrien
39130561Sobrien#ifndef MAP_FILE
40130561Sobrien#define MAP_FILE 0
41130561Sobrien#endif
42130561Sobrien
43130561Sobrienstatic int debug_windows;
44130561Sobrien
45130561Sobrien/* The idea behind the next and refcount fields is that one mapped
46130561Sobrien   region can suffice for multiple read-only windows or multiple
47130561Sobrien   non-overlapping read-write windows.  It's not implemented yet
48130561Sobrien   though.  */
49130561Sobrien
50130561Sobrien/*
51130561SobrienINTERNAL_DEFINITION
52130561Sobrien
53130561Sobrien.struct _bfd_window_internal {
54130561Sobrien.  struct _bfd_window_internal *next;
55130561Sobrien.  void *data;
56130561Sobrien.  bfd_size_type size;
57130561Sobrien.  int refcount : 31;		{* should be enough...  *}
58130561Sobrien.  unsigned mapped : 1;		{* 1 = mmap, 0 = malloc *}
59130561Sobrien.};
60130561Sobrien*/
61130561Sobrien
62130561Sobrienvoid
63130561Sobrienbfd_init_window (bfd_window *windowp)
64130561Sobrien{
65130561Sobrien  windowp->data = 0;
66130561Sobrien  windowp->i = 0;
67130561Sobrien  windowp->size = 0;
68130561Sobrien}
69130561Sobrien
70130561Sobrienvoid
71130561Sobrienbfd_free_window (bfd_window *windowp)
72130561Sobrien{
73130561Sobrien  bfd_window_internal *i = windowp->i;
74130561Sobrien  windowp->i = 0;
75130561Sobrien  windowp->data = 0;
76130561Sobrien  if (i == 0)
77130561Sobrien    return;
78130561Sobrien  i->refcount--;
79130561Sobrien  if (debug_windows)
80130561Sobrien    fprintf (stderr, "freeing window @%p<%p,%lx,%p>\n",
81130561Sobrien	     windowp, windowp->data, windowp->size, windowp->i);
82130561Sobrien  if (i->refcount != 0)
83130561Sobrien    return;
84130561Sobrien
85130561Sobrien  if (i->mapped)
86130561Sobrien    {
87130561Sobrien#ifdef HAVE_MMAP
88130561Sobrien      munmap (i->data, i->size);
89130561Sobrien      goto no_free;
90130561Sobrien#else
91130561Sobrien      abort ();
92130561Sobrien#endif
93130561Sobrien    }
94130561Sobrien#ifdef HAVE_MPROTECT
95130561Sobrien  mprotect (i->data, i->size, PROT_READ | PROT_WRITE);
96130561Sobrien#endif
97130561Sobrien  free (i->data);
98130561Sobrien#ifdef HAVE_MMAP
99130561Sobrien no_free:
100130561Sobrien#endif
101130561Sobrien  i->data = 0;
102130561Sobrien  /* There should be no more references to i at this point.  */
103130561Sobrien  free (i);
104130561Sobrien}
105130561Sobrien
106130561Sobrienstatic int ok_to_map = 1;
107130561Sobrien
108130561Sobrienbfd_boolean
109130561Sobrienbfd_get_file_window (bfd *abfd,
110130561Sobrien		     file_ptr offset,
111130561Sobrien		     bfd_size_type size,
112130561Sobrien		     bfd_window *windowp,
113130561Sobrien		     bfd_boolean writable)
114130561Sobrien{
115130561Sobrien  static size_t pagesize;
116130561Sobrien  bfd_window_internal *i = windowp->i;
117130561Sobrien  bfd_size_type size_to_alloc = size;
118130561Sobrien
119130561Sobrien  if (debug_windows)
120130561Sobrien    fprintf (stderr, "bfd_get_file_window (%p, %6ld, %6ld, %p<%p,%lx,%p>, %d)",
121130561Sobrien	     abfd, (long) offset, (long) size,
122130561Sobrien	     windowp, windowp->data, (unsigned long) windowp->size,
123130561Sobrien	     windowp->i, writable);
124130561Sobrien
125130561Sobrien  /* Make sure we know the page size, so we can be friendly to mmap.  */
126130561Sobrien  if (pagesize == 0)
127130561Sobrien    pagesize = getpagesize ();
128130561Sobrien  if (pagesize == 0)
129130561Sobrien    abort ();
130130561Sobrien
131130561Sobrien  if (i == 0)
132130561Sobrien    {
133130561Sobrien      i = bfd_zmalloc (sizeof (bfd_window_internal));
134130561Sobrien      windowp->i = i;
135130561Sobrien      if (i == 0)
136130561Sobrien	return FALSE;
137130561Sobrien      i->data = 0;
138130561Sobrien    }
139130561Sobrien#ifdef HAVE_MMAP
140130561Sobrien  if (ok_to_map
141130561Sobrien      && (i->data == 0 || i->mapped == 1)
142130561Sobrien      && (abfd->flags & BFD_IN_MEMORY) == 0)
143130561Sobrien    {
144130561Sobrien      file_ptr file_offset, offset2;
145130561Sobrien      size_t real_size;
146130561Sobrien      int fd;
147130561Sobrien      FILE *f;
148130561Sobrien
149130561Sobrien      /* Find the real file and the real offset into it.  */
150130561Sobrien      while (abfd->my_archive != NULL)
151130561Sobrien	{
152130561Sobrien	  offset += abfd->origin;
153130561Sobrien	  abfd = abfd->my_archive;
154130561Sobrien	}
155130561Sobrien      f = bfd_cache_lookup (abfd);
156130561Sobrien      fd = fileno (f);
157130561Sobrien
158130561Sobrien      /* Compute offsets and size for mmap and for the user's data.  */
159130561Sobrien      offset2 = offset % pagesize;
160130561Sobrien      if (offset2 < 0)
161130561Sobrien	abort ();
162130561Sobrien      file_offset = offset - offset2;
163130561Sobrien      real_size = offset + size - file_offset;
164130561Sobrien      real_size = real_size + pagesize - 1;
165130561Sobrien      real_size -= real_size % pagesize;
166130561Sobrien
167130561Sobrien      /* If we're re-using a memory region, make sure it's big enough.  */
168130561Sobrien      if (i->data && i->size < size)
169130561Sobrien	{
170130561Sobrien	  munmap (i->data, i->size);
171130561Sobrien	  i->data = 0;
172130561Sobrien	}
173130561Sobrien      i->data = mmap (i->data, real_size,
174130561Sobrien		      writable ? PROT_WRITE | PROT_READ : PROT_READ,
175130561Sobrien		      (writable
176130561Sobrien		       ? MAP_FILE | MAP_PRIVATE
177130561Sobrien		       : MAP_FILE | MAP_SHARED),
178130561Sobrien		      fd, file_offset);
179130561Sobrien      if (i->data == (void *) -1)
180130561Sobrien	{
181130561Sobrien	  /* An error happened.  Report it, or try using malloc, or
182130561Sobrien	     something.  */
183130561Sobrien	  bfd_set_error (bfd_error_system_call);
184130561Sobrien	  i->data = 0;
185130561Sobrien	  windowp->data = 0;
186130561Sobrien	  if (debug_windows)
187130561Sobrien	    fprintf (stderr, "\t\tmmap failed!\n");
188130561Sobrien	  return FALSE;
189130561Sobrien	}
190130561Sobrien      if (debug_windows)
191130561Sobrien	fprintf (stderr, "\n\tmapped %ld at %p, offset is %ld\n",
192130561Sobrien		 (long) real_size, i->data, (long) offset2);
193130561Sobrien      i->size = real_size;
194130561Sobrien      windowp->data = (bfd_byte *) i->data + offset2;
195130561Sobrien      windowp->size = size;
196130561Sobrien      i->mapped = 1;
197130561Sobrien      return TRUE;
198130561Sobrien    }
199130561Sobrien  else if (debug_windows)
200130561Sobrien    {
201130561Sobrien      if (ok_to_map)
202130561Sobrien	fprintf (stderr, _("not mapping: data=%lx mapped=%d\n"),
203130561Sobrien		 (unsigned long) i->data, (int) i->mapped);
204130561Sobrien      else
205130561Sobrien	fprintf (stderr, _("not mapping: env var not set\n"));
206130561Sobrien    }
207130561Sobrien#else
208130561Sobrien  ok_to_map = 0;
209130561Sobrien#endif
210130561Sobrien
211130561Sobrien#ifdef HAVE_MPROTECT
212130561Sobrien  if (!writable)
213130561Sobrien    {
214130561Sobrien      size_to_alloc += pagesize - 1;
215130561Sobrien      size_to_alloc -= size_to_alloc % pagesize;
216130561Sobrien    }
217130561Sobrien#endif
218130561Sobrien  if (debug_windows)
219130561Sobrien    fprintf (stderr, "\n\t%s(%6ld)",
220130561Sobrien	     i->data ? "realloc" : " malloc", (long) size_to_alloc);
221130561Sobrien  i->data = bfd_realloc (i->data, size_to_alloc);
222130561Sobrien  if (debug_windows)
223130561Sobrien    fprintf (stderr, "\t-> %p\n", i->data);
224130561Sobrien  i->refcount = 1;
225130561Sobrien  if (i->data == NULL)
226130561Sobrien    {
227130561Sobrien      if (size_to_alloc == 0)
228130561Sobrien	return TRUE;
229130561Sobrien      return FALSE;
230130561Sobrien    }
231130561Sobrien  if (bfd_seek (abfd, offset, SEEK_SET) != 0)
232130561Sobrien    return FALSE;
233130561Sobrien  i->size = bfd_bread (i->data, size, abfd);
234130561Sobrien  if (i->size != size)
235130561Sobrien    return FALSE;
236130561Sobrien  i->mapped = 0;
237130561Sobrien#ifdef HAVE_MPROTECT
238130561Sobrien  if (!writable)
239130561Sobrien    {
240130561Sobrien      if (debug_windows)
241130561Sobrien	fprintf (stderr, "\tmprotect (%p, %ld, PROT_READ)\n", i->data,
242130561Sobrien		 (long) i->size);
243130561Sobrien      mprotect (i->data, i->size, PROT_READ);
244130561Sobrien    }
245130561Sobrien#endif
246130561Sobrien  windowp->data = i->data;
247130561Sobrien  windowp->size = i->size;
248130561Sobrien  return TRUE;
249130561Sobrien}
250130561Sobrien
251130561Sobrien#endif /* USE_MMAP */
252