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