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