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