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