xref: /openbsd-src/gnu/usr.bin/binutils/bfd/bfdwin.c (revision cf2f2c5620d6d9a4fd01930983c4b9a1f76d7aa3)
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