xref: /dflybsd-src/contrib/gdb-7/bfd/bfdwin.c (revision ec70266467411565ead9166ad4c1dbb79ff7cd77)
15796c8dcSSimon Schubert /* Support for memory-mapped windows into a BFD.
2*a45ae5f8SJohn Marino    Copyright 1995, 1996, 2001, 2002, 2003, 2005, 2007, 2008, 2009, 2011
35796c8dcSSimon Schubert    Free Software Foundation, Inc.
45796c8dcSSimon Schubert    Written by Cygnus Support.
55796c8dcSSimon Schubert 
65796c8dcSSimon Schubert    This file is part of BFD, the Binary File Descriptor library.
75796c8dcSSimon Schubert 
85796c8dcSSimon Schubert    This program is free software; you can redistribute it and/or modify
95796c8dcSSimon Schubert    it under the terms of the GNU General Public License as published by
105796c8dcSSimon Schubert    the Free Software Foundation; either version 3 of the License, or
115796c8dcSSimon Schubert    (at your option) any later version.
125796c8dcSSimon Schubert 
135796c8dcSSimon Schubert    This program is distributed in the hope that it will be useful,
145796c8dcSSimon Schubert    but WITHOUT ANY WARRANTY; without even the implied warranty of
155796c8dcSSimon Schubert    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
165796c8dcSSimon Schubert    GNU General Public License for more details.
175796c8dcSSimon Schubert 
185796c8dcSSimon Schubert    You should have received a copy of the GNU General Public License
195796c8dcSSimon Schubert    along with this program; if not, write to the Free Software
205796c8dcSSimon Schubert    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
215796c8dcSSimon Schubert    MA 02110-1301, USA.  */
225796c8dcSSimon Schubert 
235796c8dcSSimon Schubert #include "sysdep.h"
245796c8dcSSimon Schubert 
255796c8dcSSimon Schubert #include "bfd.h"
265796c8dcSSimon Schubert #include "libbfd.h"
275796c8dcSSimon Schubert 
285796c8dcSSimon Schubert /* Currently, if USE_MMAP is undefined, none of the window stuff is
295796c8dcSSimon Schubert    used.  Enabled by --with-mmap.  */
305796c8dcSSimon Schubert 
315796c8dcSSimon Schubert #ifdef USE_MMAP
325796c8dcSSimon Schubert 
335796c8dcSSimon Schubert #undef HAVE_MPROTECT /* code's not tested yet */
345796c8dcSSimon Schubert 
355796c8dcSSimon Schubert #if HAVE_MMAP || HAVE_MPROTECT || HAVE_MADVISE
365796c8dcSSimon Schubert #include <sys/mman.h>
375796c8dcSSimon Schubert #endif
385796c8dcSSimon Schubert 
395796c8dcSSimon Schubert #ifndef MAP_FILE
405796c8dcSSimon Schubert #define MAP_FILE 0
415796c8dcSSimon Schubert #endif
425796c8dcSSimon Schubert 
435796c8dcSSimon Schubert static int debug_windows;
445796c8dcSSimon Schubert 
455796c8dcSSimon Schubert /* The idea behind the next and refcount fields is that one mapped
465796c8dcSSimon Schubert    region can suffice for multiple read-only windows or multiple
475796c8dcSSimon Schubert    non-overlapping read-write windows.  It's not implemented yet
485796c8dcSSimon Schubert    though.  */
495796c8dcSSimon Schubert 
505796c8dcSSimon Schubert /*
515796c8dcSSimon Schubert INTERNAL_DEFINITION
525796c8dcSSimon Schubert 
535796c8dcSSimon Schubert .struct _bfd_window_internal {
545796c8dcSSimon Schubert .  struct _bfd_window_internal *next;
555796c8dcSSimon Schubert .  void *data;
565796c8dcSSimon Schubert .  bfd_size_type size;
575796c8dcSSimon Schubert .  int refcount : 31;		{* should be enough...  *}
585796c8dcSSimon Schubert .  unsigned mapped : 1;		{* 1 = mmap, 0 = malloc *}
595796c8dcSSimon Schubert .};
605796c8dcSSimon Schubert */
615796c8dcSSimon Schubert 
625796c8dcSSimon Schubert void
bfd_init_window(bfd_window * windowp)635796c8dcSSimon Schubert bfd_init_window (bfd_window *windowp)
645796c8dcSSimon Schubert {
655796c8dcSSimon Schubert   windowp->data = 0;
665796c8dcSSimon Schubert   windowp->i = 0;
675796c8dcSSimon Schubert   windowp->size = 0;
685796c8dcSSimon Schubert }
695796c8dcSSimon Schubert 
705796c8dcSSimon Schubert void
bfd_free_window(bfd_window * windowp)715796c8dcSSimon Schubert bfd_free_window (bfd_window *windowp)
725796c8dcSSimon Schubert {
735796c8dcSSimon Schubert   bfd_window_internal *i = windowp->i;
745796c8dcSSimon Schubert   windowp->i = 0;
755796c8dcSSimon Schubert   windowp->data = 0;
765796c8dcSSimon Schubert   if (i == 0)
775796c8dcSSimon Schubert     return;
785796c8dcSSimon Schubert   i->refcount--;
795796c8dcSSimon Schubert   if (debug_windows)
805796c8dcSSimon Schubert     fprintf (stderr, "freeing window @%p<%p,%lx,%p>\n",
815796c8dcSSimon Schubert 	     windowp, windowp->data, (unsigned long) windowp->size, windowp->i);
825796c8dcSSimon Schubert   if (i->refcount != 0)
835796c8dcSSimon Schubert     return;
845796c8dcSSimon Schubert 
855796c8dcSSimon Schubert   if (i->mapped)
865796c8dcSSimon Schubert     {
875796c8dcSSimon Schubert #ifdef HAVE_MMAP
885796c8dcSSimon Schubert       munmap (i->data, i->size);
895796c8dcSSimon Schubert       goto no_free;
905796c8dcSSimon Schubert #else
915796c8dcSSimon Schubert       abort ();
925796c8dcSSimon Schubert #endif
935796c8dcSSimon Schubert     }
945796c8dcSSimon Schubert #ifdef HAVE_MPROTECT
955796c8dcSSimon Schubert   mprotect (i->data, i->size, PROT_READ | PROT_WRITE);
965796c8dcSSimon Schubert #endif
975796c8dcSSimon Schubert   free (i->data);
985796c8dcSSimon Schubert #ifdef HAVE_MMAP
995796c8dcSSimon Schubert  no_free:
1005796c8dcSSimon Schubert #endif
1015796c8dcSSimon Schubert   i->data = 0;
1025796c8dcSSimon Schubert   /* There should be no more references to i at this point.  */
1035796c8dcSSimon Schubert   free (i);
1045796c8dcSSimon Schubert }
1055796c8dcSSimon Schubert 
1065796c8dcSSimon Schubert static int ok_to_map = 1;
1075796c8dcSSimon Schubert 
1085796c8dcSSimon Schubert bfd_boolean
bfd_get_file_window(bfd * abfd,file_ptr offset,bfd_size_type size,bfd_window * windowp,bfd_boolean writable)1095796c8dcSSimon Schubert bfd_get_file_window (bfd *abfd,
1105796c8dcSSimon Schubert 		     file_ptr offset,
1115796c8dcSSimon Schubert 		     bfd_size_type size,
1125796c8dcSSimon Schubert 		     bfd_window *windowp,
1135796c8dcSSimon Schubert 		     bfd_boolean writable)
1145796c8dcSSimon Schubert {
1155796c8dcSSimon Schubert   static size_t pagesize;
1165796c8dcSSimon Schubert   bfd_window_internal *i = windowp->i;
1175796c8dcSSimon Schubert   bfd_size_type size_to_alloc = size;
1185796c8dcSSimon Schubert 
1195796c8dcSSimon Schubert   if (debug_windows)
1205796c8dcSSimon Schubert     fprintf (stderr, "bfd_get_file_window (%p, %6ld, %6ld, %p<%p,%lx,%p>, %d)",
1215796c8dcSSimon Schubert 	     abfd, (long) offset, (long) size,
1225796c8dcSSimon Schubert 	     windowp, windowp->data, (unsigned long) windowp->size,
1235796c8dcSSimon Schubert 	     windowp->i, writable);
1245796c8dcSSimon Schubert 
1255796c8dcSSimon Schubert   /* Make sure we know the page size, so we can be friendly to mmap.  */
1265796c8dcSSimon Schubert   if (pagesize == 0)
1275796c8dcSSimon Schubert     pagesize = getpagesize ();
1285796c8dcSSimon Schubert   if (pagesize == 0)
1295796c8dcSSimon Schubert     abort ();
1305796c8dcSSimon Schubert 
131*a45ae5f8SJohn Marino   if (i == NULL)
1325796c8dcSSimon Schubert     {
1335796c8dcSSimon Schubert       i = bfd_zmalloc (sizeof (bfd_window_internal));
134*a45ae5f8SJohn Marino       if (i == NULL)
1355796c8dcSSimon Schubert 	return FALSE;
136*a45ae5f8SJohn Marino       i->data = NULL;
1375796c8dcSSimon Schubert     }
1385796c8dcSSimon Schubert #ifdef HAVE_MMAP
1395796c8dcSSimon Schubert   if (ok_to_map
140*a45ae5f8SJohn Marino       && (i->data == NULL || i->mapped == 1)
1415796c8dcSSimon Schubert       && (abfd->flags & BFD_IN_MEMORY) == 0)
1425796c8dcSSimon Schubert     {
1435796c8dcSSimon Schubert       file_ptr file_offset, offset2;
1445796c8dcSSimon Schubert       size_t real_size;
1455796c8dcSSimon Schubert       int fd;
1465796c8dcSSimon Schubert 
1475796c8dcSSimon Schubert       /* Find the real file and the real offset into it.  */
1485796c8dcSSimon Schubert       while (abfd->my_archive != NULL)
1495796c8dcSSimon Schubert 	{
1505796c8dcSSimon Schubert 	  offset += abfd->origin;
1515796c8dcSSimon Schubert 	  abfd = abfd->my_archive;
1525796c8dcSSimon Schubert 	}
1535796c8dcSSimon Schubert 
1545796c8dcSSimon Schubert       /* Seek into the file, to ensure it is open if cacheable.  */
1555796c8dcSSimon Schubert       if (abfd->iostream == NULL
1565796c8dcSSimon Schubert 	  && (abfd->iovec == NULL
1575796c8dcSSimon Schubert 	      || abfd->iovec->bseek (abfd, offset, SEEK_SET) != 0))
158*a45ae5f8SJohn Marino 	goto free_and_fail;
1595796c8dcSSimon Schubert 
160*a45ae5f8SJohn Marino       fd = fileno ((FILE *) abfd->iostream);
1615796c8dcSSimon Schubert       /* Compute offsets and size for mmap and for the user's data.  */
1625796c8dcSSimon Schubert       offset2 = offset % pagesize;
1635796c8dcSSimon Schubert       if (offset2 < 0)
1645796c8dcSSimon Schubert 	abort ();
1655796c8dcSSimon Schubert       file_offset = offset - offset2;
1665796c8dcSSimon Schubert       real_size = offset + size - file_offset;
1675796c8dcSSimon Schubert       real_size = real_size + pagesize - 1;
1685796c8dcSSimon Schubert       real_size -= real_size % pagesize;
1695796c8dcSSimon Schubert 
1705796c8dcSSimon Schubert       /* If we're re-using a memory region, make sure it's big enough.  */
171*a45ae5f8SJohn Marino       if (i->data != NULL && i->size < size)
1725796c8dcSSimon Schubert 	{
1735796c8dcSSimon Schubert 	  munmap (i->data, i->size);
174*a45ae5f8SJohn Marino 	  i->data = NULL;
1755796c8dcSSimon Schubert 	}
1765796c8dcSSimon Schubert       i->data = mmap (i->data, real_size,
1775796c8dcSSimon Schubert 		      writable ? PROT_WRITE | PROT_READ : PROT_READ,
1785796c8dcSSimon Schubert 		      (writable
1795796c8dcSSimon Schubert 		       ? MAP_FILE | MAP_PRIVATE
1805796c8dcSSimon Schubert 		       : MAP_FILE | MAP_SHARED),
1815796c8dcSSimon Schubert 		      fd, file_offset);
1825796c8dcSSimon Schubert       if (i->data == (void *) -1)
1835796c8dcSSimon Schubert 	{
1845796c8dcSSimon Schubert 	  /* An error happened.  Report it, or try using malloc, or
1855796c8dcSSimon Schubert 	     something.  */
1865796c8dcSSimon Schubert 	  bfd_set_error (bfd_error_system_call);
1875796c8dcSSimon Schubert 	  windowp->data = 0;
1885796c8dcSSimon Schubert 	  if (debug_windows)
1895796c8dcSSimon Schubert 	    fprintf (stderr, "\t\tmmap failed!\n");
190*a45ae5f8SJohn Marino 	  goto free_and_fail;
1915796c8dcSSimon Schubert 	}
1925796c8dcSSimon Schubert       if (debug_windows)
1935796c8dcSSimon Schubert 	fprintf (stderr, "\n\tmapped %ld at %p, offset is %ld\n",
1945796c8dcSSimon Schubert 		 (long) real_size, i->data, (long) offset2);
1955796c8dcSSimon Schubert       i->size = real_size;
1965796c8dcSSimon Schubert       windowp->data = (bfd_byte *) i->data + offset2;
1975796c8dcSSimon Schubert       windowp->size = size;
1985796c8dcSSimon Schubert       i->mapped = 1;
199*a45ae5f8SJohn Marino       i->refcount = 1;
200*a45ae5f8SJohn Marino       windowp->i = i;
2015796c8dcSSimon Schubert       return TRUE;
2025796c8dcSSimon Schubert     }
2035796c8dcSSimon Schubert   else if (debug_windows)
2045796c8dcSSimon Schubert     {
2055796c8dcSSimon Schubert       if (ok_to_map)
2065796c8dcSSimon Schubert 	fprintf (stderr, _("not mapping: data=%lx mapped=%d\n"),
2075796c8dcSSimon Schubert 		 (unsigned long) i->data, (int) i->mapped);
2085796c8dcSSimon Schubert       else
2095796c8dcSSimon Schubert 	fprintf (stderr, _("not mapping: env var not set\n"));
2105796c8dcSSimon Schubert     }
2115796c8dcSSimon Schubert #else
2125796c8dcSSimon Schubert   ok_to_map = 0;
2135796c8dcSSimon Schubert #endif
2145796c8dcSSimon Schubert 
2155796c8dcSSimon Schubert #ifdef HAVE_MPROTECT
2165796c8dcSSimon Schubert   if (!writable)
2175796c8dcSSimon Schubert     {
2185796c8dcSSimon Schubert       size_to_alloc += pagesize - 1;
2195796c8dcSSimon Schubert       size_to_alloc -= size_to_alloc % pagesize;
2205796c8dcSSimon Schubert     }
2215796c8dcSSimon Schubert #endif
2225796c8dcSSimon Schubert   if (debug_windows)
2235796c8dcSSimon Schubert     fprintf (stderr, "\n\t%s(%6ld)",
2245796c8dcSSimon Schubert 	     i->data ? "realloc" : " malloc", (long) size_to_alloc);
2255796c8dcSSimon Schubert   i->data = bfd_realloc_or_free (i->data, size_to_alloc);
2265796c8dcSSimon Schubert   if (debug_windows)
2275796c8dcSSimon Schubert     fprintf (stderr, "\t-> %p\n", i->data);
2285796c8dcSSimon Schubert   if (i->data == NULL)
2295796c8dcSSimon Schubert     {
2305796c8dcSSimon Schubert       if (size_to_alloc == 0)
231*a45ae5f8SJohn Marino 	{
232*a45ae5f8SJohn Marino 	  windowp->i = i;
2335796c8dcSSimon Schubert 	  return TRUE;
234*a45ae5f8SJohn Marino 	}
235*a45ae5f8SJohn Marino       goto free_and_fail;
2365796c8dcSSimon Schubert     }
2375796c8dcSSimon Schubert   i->refcount = 1;
2385796c8dcSSimon Schubert   if (bfd_seek (abfd, offset, SEEK_SET) != 0)
239*a45ae5f8SJohn Marino     goto free_and_fail;
2405796c8dcSSimon Schubert   i->size = bfd_bread (i->data, size, abfd);
2415796c8dcSSimon Schubert   if (i->size != size)
242*a45ae5f8SJohn Marino     goto free_and_fail;
2435796c8dcSSimon Schubert   i->mapped = 0;
2445796c8dcSSimon Schubert #ifdef HAVE_MPROTECT
2455796c8dcSSimon Schubert   if (!writable)
2465796c8dcSSimon Schubert     {
2475796c8dcSSimon Schubert       if (debug_windows)
2485796c8dcSSimon Schubert 	fprintf (stderr, "\tmprotect (%p, %ld, PROT_READ)\n", i->data,
2495796c8dcSSimon Schubert 		 (long) i->size);
2505796c8dcSSimon Schubert       mprotect (i->data, i->size, PROT_READ);
2515796c8dcSSimon Schubert     }
2525796c8dcSSimon Schubert #endif
2535796c8dcSSimon Schubert   windowp->data = i->data;
2545796c8dcSSimon Schubert   windowp->size = i->size;
255*a45ae5f8SJohn Marino   windowp->i = i;
2565796c8dcSSimon Schubert   return TRUE;
257*a45ae5f8SJohn Marino 
258*a45ae5f8SJohn Marino  free_and_fail:
259*a45ae5f8SJohn Marino   /* We have a bfd_window_internal, but an error occurred.  Free it. */
260*a45ae5f8SJohn Marino   free (i);
261*a45ae5f8SJohn Marino   return FALSE;
2625796c8dcSSimon Schubert }
2635796c8dcSSimon Schubert 
2645796c8dcSSimon Schubert #endif /* USE_MMAP */
265