(root)/
binutils-2.41/
bfd/
bfdwin.c
       1  /* Support for memory-mapped windows into a BFD.
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       3     Written by Cygnus Support.
       4  
       5     This file is part of BFD, the Binary File Descriptor library.
       6  
       7     This program is free software; you can redistribute it and/or modify
       8     it under the terms of the GNU General Public License as published by
       9     the Free Software Foundation; either version 3 of the License, or
      10     (at your option) any later version.
      11  
      12     This program is distributed in the hope that it will be useful,
      13     but WITHOUT ANY WARRANTY; without even the implied warranty of
      14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15     GNU General Public License for more details.
      16  
      17     You should have received a copy of the GNU General Public License
      18     along with this program; if not, write to the Free Software
      19     Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
      20     MA 02110-1301, USA.  */
      21  
      22  #include "sysdep.h"
      23  
      24  #include "bfd.h"
      25  #include "libbfd.h"
      26  
      27  /* Currently, if USE_MMAP is undefined, none of the window stuff is
      28     used.  Enabled by --with-mmap.  */
      29  
      30  #ifdef USE_MMAP
      31  
      32  #undef HAVE_MPROTECT /* code's not tested yet */
      33  
      34  #if HAVE_MMAP || HAVE_MPROTECT || HAVE_MADVISE
      35  #include <sys/mman.h>
      36  #endif
      37  
      38  #ifndef MAP_FILE
      39  #define MAP_FILE 0
      40  #endif
      41  
      42  static int debug_windows;
      43  
      44  /* The idea behind the next and refcount fields is that one mapped
      45     region can suffice for multiple read-only windows or multiple
      46     non-overlapping read-write windows.  It's not implemented yet
      47     though.  */
      48  
      49  /*
      50  INTERNAL
      51  .typedef struct _bfd_window_internal
      52  .{
      53  .  struct _bfd_window_internal *next;
      54  .  void *data;
      55  .  bfd_size_type size;
      56  .  int refcount : 31;		{* should be enough...  *}
      57  .  unsigned mapped : 1;		{* 1 = mmap, 0 = malloc *}
      58  .}
      59  .bfd_window_internal;
      60  .
      61  
      62  EXTERNAL
      63  .struct _bfd_window_internal;
      64  .
      65  .typedef struct _bfd_window
      66  .{
      67  .  {* What the user asked for.  *}
      68  .  void *data;
      69  .  bfd_size_type size;
      70  .  {* The actual window used by BFD.  Small user-requested read-only
      71  .     regions sharing a page may share a single window into the object
      72  .     file.  Read-write versions shouldn't until I've fixed things to
      73  .     keep track of which portions have been claimed by the
      74  .     application; don't want to give the same region back when the
      75  .     application wants two writable copies!  *}
      76  .  struct _bfd_window_internal *i;
      77  .}
      78  .bfd_window;
      79  .
      80  */
      81  
      82  /*
      83  FUNCTION
      84  	bfd_init_window
      85  
      86  SYNOPSIS
      87  	void bfd_init_window (bfd_window *);
      88  
      89  DESCRIPTION
      90  	Initialise mmap window.
      91  */
      92  
      93  void
      94  bfd_init_window (bfd_window *windowp)
      95  {
      96    windowp->data = 0;
      97    windowp->i = 0;
      98    windowp->size = 0;
      99  }
     100  
     101  /*
     102  FUNCTION
     103  	bfd_free_window
     104  
     105  SYNOPSIS
     106  	void bfd_free_window (bfd_window *);
     107  
     108  DESCRIPTION
     109  	Finalise mmap window struct.
     110  */
     111  
     112  void
     113  bfd_free_window (bfd_window *windowp)
     114  {
     115    bfd_window_internal *i = windowp->i;
     116    windowp->i = 0;
     117    windowp->data = 0;
     118    if (i == 0)
     119      return;
     120    i->refcount--;
     121    if (debug_windows)
     122      fprintf (stderr, "freeing window @%p<%p,%lx,%p>\n",
     123  	     windowp, windowp->data, (unsigned long) windowp->size, windowp->i);
     124    if (i->refcount != 0)
     125      return;
     126  
     127    if (i->mapped)
     128      {
     129  #ifdef HAVE_MMAP
     130        munmap (i->data, i->size);
     131        goto no_free;
     132  #else
     133        abort ();
     134  #endif
     135      }
     136  #ifdef HAVE_MPROTECT
     137    mprotect (i->data, i->size, PROT_READ | PROT_WRITE);
     138  #endif
     139    free (i->data);
     140  #ifdef HAVE_MMAP
     141   no_free:
     142  #endif
     143    i->data = 0;
     144    /* There should be no more references to i at this point.  */
     145    free (i);
     146  }
     147  
     148  /*
     149  FUNCTION
     150  	bfd_get_file_window
     151  
     152  SYNOPSIS
     153  	bool bfd_get_file_window
     154  	  (bfd *, file_ptr, bfd_size_type, bfd_window *, bool {*writable*});
     155  
     156  DESCRIPTION
     157  	mmap from a bfd's iostream.
     158  */
     159  
     160  bool
     161  bfd_get_file_window (bfd *abfd,
     162  		     file_ptr offset,
     163  		     bfd_size_type size,
     164  		     bfd_window *windowp,
     165  		     bool writable)
     166  {
     167    static int ok_to_map = 1;
     168    static size_t pagesize;
     169    bfd_window_internal *i = windowp->i;
     170    bfd_size_type size_to_alloc = size;
     171  
     172    if (debug_windows)
     173      fprintf (stderr, "bfd_get_file_window (%p, %6ld, %6ld, %p<%p,%lx,%p>, %d)",
     174  	     abfd, (long) offset, (long) size,
     175  	     windowp, windowp->data, (unsigned long) windowp->size,
     176  	     windowp->i, writable);
     177  
     178    /* Make sure we know the page size, so we can be friendly to mmap.  */
     179    if (pagesize == 0)
     180      pagesize = getpagesize ();
     181    if (pagesize == 0)
     182      abort ();
     183  
     184    if (i == NULL)
     185      {
     186        i = bfd_zmalloc (sizeof (bfd_window_internal));
     187        if (i == NULL)
     188  	return false;
     189        i->data = NULL;
     190      }
     191  #ifdef HAVE_MMAP
     192    if (ok_to_map
     193        && (i->data == NULL || i->mapped == 1)
     194        && (abfd->flags & BFD_IN_MEMORY) == 0)
     195      {
     196        file_ptr file_offset, offset2;
     197        size_t real_size;
     198        int fd;
     199  
     200        /* Find the real file and the real offset into it.  */
     201        while (abfd->my_archive != NULL
     202  	     && !bfd_is_thin_archive (abfd->my_archive))
     203  	{
     204  	  offset += abfd->origin;
     205  	  abfd = abfd->my_archive;
     206  	}
     207        offset += abfd->origin;
     208  
     209        /* Seek into the file, to ensure it is open if cacheable.  */
     210        if (abfd->iostream == NULL
     211  	  && (abfd->iovec == NULL
     212  	      || abfd->iovec->bseek (abfd, offset, SEEK_SET) != 0))
     213  	goto free_and_fail;
     214  
     215        fd = fileno ((FILE *) abfd->iostream);
     216        /* Compute offsets and size for mmap and for the user's data.  */
     217        offset2 = offset % pagesize;
     218        if (offset2 < 0)
     219  	abort ();
     220        file_offset = offset - offset2;
     221        real_size = offset + size - file_offset;
     222        real_size = real_size + pagesize - 1;
     223        real_size -= real_size % pagesize;
     224  
     225        /* If we're re-using a memory region, make sure it's big enough.  */
     226        if (i->data != NULL && i->size < size)
     227  	{
     228  	  munmap (i->data, i->size);
     229  	  i->data = NULL;
     230  	}
     231        i->data = mmap (i->data, real_size,
     232  		      writable ? PROT_WRITE | PROT_READ : PROT_READ,
     233  		      (writable
     234  		       ? MAP_FILE | MAP_PRIVATE
     235  		       : MAP_FILE | MAP_SHARED),
     236  		      fd, file_offset);
     237        if (i->data == (void *) -1)
     238  	{
     239  	  /* An error happened.  Report it, or try using malloc, or
     240  	     something.  */
     241  	  bfd_set_error (bfd_error_system_call);
     242  	  windowp->data = 0;
     243  	  if (debug_windows)
     244  	    fprintf (stderr, "\t\tmmap failed!\n");
     245  	  goto free_and_fail;
     246  	}
     247        if (debug_windows)
     248  	fprintf (stderr, "\n\tmapped %ld at %p, offset is %ld\n",
     249  		 (long) real_size, i->data, (long) offset2);
     250        i->size = real_size;
     251        windowp->data = (bfd_byte *) i->data + offset2;
     252        windowp->size = size;
     253        i->mapped = 1;
     254        i->refcount = 1;
     255        windowp->i = i;
     256        return true;
     257      }
     258    else if (debug_windows)
     259      {
     260        if (ok_to_map)
     261  	fprintf (stderr, _("not mapping: data=%lx mapped=%d\n"),
     262  		 (unsigned long) i->data, (int) i->mapped);
     263        else
     264  	fprintf (stderr, _("not mapping: env var not set\n"));
     265      }
     266  #else
     267    ok_to_map = 0;
     268  #endif
     269  
     270  #ifdef HAVE_MPROTECT
     271    if (!writable)
     272      {
     273        size_to_alloc += pagesize - 1;
     274        size_to_alloc -= size_to_alloc % pagesize;
     275      }
     276  #endif
     277    if (debug_windows)
     278      fprintf (stderr, "\n\t%s(%6ld)",
     279  	     i->data ? "realloc" : " malloc", (long) size_to_alloc);
     280    i->data = bfd_realloc_or_free (i->data, size_to_alloc);
     281    if (debug_windows)
     282      fprintf (stderr, "\t-> %p\n", i->data);
     283    if (i->data == NULL)
     284      {
     285        if (size_to_alloc == 0)
     286  	{
     287  	  windowp->i = i;
     288  	  return true;
     289  	}
     290        goto free_and_fail;
     291      }
     292    i->refcount = 1;
     293    if (bfd_seek (abfd, offset, SEEK_SET) != 0)
     294      goto free_and_fail;
     295    i->size = bfd_bread (i->data, size, abfd);
     296    if (i->size != size)
     297      goto free_and_fail;
     298    i->mapped = 0;
     299  #ifdef HAVE_MPROTECT
     300    if (!writable)
     301      {
     302        if (debug_windows)
     303  	fprintf (stderr, "\tmprotect (%p, %ld, PROT_READ)\n", i->data,
     304  		 (long) i->size);
     305        mprotect (i->data, i->size, PROT_READ);
     306      }
     307  #endif
     308    windowp->data = i->data;
     309    windowp->size = i->size;
     310    windowp->i = i;
     311    return true;
     312  
     313   free_and_fail:
     314    /* We have a bfd_window_internal, but an error occurred.  Free it. */
     315    free (i);
     316    return false;
     317  }
     318  
     319  #endif /* USE_MMAP */