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