1 /* Support for memory-mapped windows into a BFD. 2 Copyright (C) 1995-2014 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_DEFINITION 51 52 .struct _bfd_window_internal { 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 */ 60 61 void 62 bfd_init_window (bfd_window *windowp) 63 { 64 windowp->data = 0; 65 windowp->i = 0; 66 windowp->size = 0; 67 } 68 69 void 70 bfd_free_window (bfd_window *windowp) 71 { 72 bfd_window_internal *i = windowp->i; 73 windowp->i = 0; 74 windowp->data = 0; 75 if (i == 0) 76 return; 77 i->refcount--; 78 if (debug_windows) 79 fprintf (stderr, "freeing window @%p<%p,%lx,%p>\n", 80 windowp, windowp->data, (unsigned long) windowp->size, windowp->i); 81 if (i->refcount != 0) 82 return; 83 84 if (i->mapped) 85 { 86 #ifdef HAVE_MMAP 87 munmap (i->data, i->size); 88 goto no_free; 89 #else 90 abort (); 91 #endif 92 } 93 #ifdef HAVE_MPROTECT 94 mprotect (i->data, i->size, PROT_READ | PROT_WRITE); 95 #endif 96 free (i->data); 97 #ifdef HAVE_MMAP 98 no_free: 99 #endif 100 i->data = 0; 101 /* There should be no more references to i at this point. */ 102 free (i); 103 } 104 105 static int ok_to_map = 1; 106 107 bfd_boolean 108 bfd_get_file_window (bfd *abfd, 109 file_ptr offset, 110 bfd_size_type size, 111 bfd_window *windowp, 112 bfd_boolean writable) 113 { 114 static size_t pagesize; 115 bfd_window_internal *i = windowp->i; 116 bfd_size_type size_to_alloc = size; 117 118 if (debug_windows) 119 fprintf (stderr, "bfd_get_file_window (%p, %6ld, %6ld, %p<%p,%lx,%p>, %d)", 120 abfd, (long) offset, (long) size, 121 windowp, windowp->data, (unsigned long) windowp->size, 122 windowp->i, writable); 123 124 /* Make sure we know the page size, so we can be friendly to mmap. */ 125 if (pagesize == 0) 126 pagesize = getpagesize (); 127 if (pagesize == 0) 128 abort (); 129 130 if (i == NULL) 131 { 132 i = bfd_zmalloc (sizeof (bfd_window_internal)); 133 if (i == NULL) 134 return FALSE; 135 i->data = NULL; 136 } 137 #ifdef HAVE_MMAP 138 if (ok_to_map 139 && (i->data == NULL || i->mapped == 1) 140 && (abfd->flags & BFD_IN_MEMORY) == 0) 141 { 142 file_ptr file_offset, offset2; 143 size_t real_size; 144 int fd; 145 146 /* Find the real file and the real offset into it. */ 147 while (abfd->my_archive != NULL) 148 { 149 offset += abfd->origin; 150 abfd = abfd->my_archive; 151 } 152 153 /* Seek into the file, to ensure it is open if cacheable. */ 154 if (abfd->iostream == NULL 155 && (abfd->iovec == NULL 156 || abfd->iovec->bseek (abfd, offset, SEEK_SET) != 0)) 157 goto free_and_fail; 158 159 fd = fileno ((FILE *) abfd->iostream); 160 /* Compute offsets and size for mmap and for the user's data. */ 161 offset2 = offset % pagesize; 162 if (offset2 < 0) 163 abort (); 164 file_offset = offset - offset2; 165 real_size = offset + size - file_offset; 166 real_size = real_size + pagesize - 1; 167 real_size -= real_size % pagesize; 168 169 /* If we're re-using a memory region, make sure it's big enough. */ 170 if (i->data != NULL && i->size < size) 171 { 172 munmap (i->data, i->size); 173 i->data = NULL; 174 } 175 i->data = mmap (i->data, real_size, 176 writable ? PROT_WRITE | PROT_READ : PROT_READ, 177 (writable 178 ? MAP_FILE | MAP_PRIVATE 179 : MAP_FILE | MAP_SHARED), 180 fd, file_offset); 181 if (i->data == (void *) -1) 182 { 183 /* An error happened. Report it, or try using malloc, or 184 something. */ 185 bfd_set_error (bfd_error_system_call); 186 windowp->data = 0; 187 if (debug_windows) 188 fprintf (stderr, "\t\tmmap failed!\n"); 189 goto free_and_fail; 190 } 191 if (debug_windows) 192 fprintf (stderr, "\n\tmapped %ld at %p, offset is %ld\n", 193 (long) real_size, i->data, (long) offset2); 194 i->size = real_size; 195 windowp->data = (bfd_byte *) i->data + offset2; 196 windowp->size = size; 197 i->mapped = 1; 198 i->refcount = 1; 199 windowp->i = i; 200 return TRUE; 201 } 202 else if (debug_windows) 203 { 204 if (ok_to_map) 205 fprintf (stderr, _("not mapping: data=%lx mapped=%d\n"), 206 (unsigned long) i->data, (int) i->mapped); 207 else 208 fprintf (stderr, _("not mapping: env var not set\n")); 209 } 210 #else 211 ok_to_map = 0; 212 #endif 213 214 #ifdef HAVE_MPROTECT 215 if (!writable) 216 { 217 size_to_alloc += pagesize - 1; 218 size_to_alloc -= size_to_alloc % pagesize; 219 } 220 #endif 221 if (debug_windows) 222 fprintf (stderr, "\n\t%s(%6ld)", 223 i->data ? "realloc" : " malloc", (long) size_to_alloc); 224 i->data = bfd_realloc_or_free (i->data, size_to_alloc); 225 if (debug_windows) 226 fprintf (stderr, "\t-> %p\n", i->data); 227 if (i->data == NULL) 228 { 229 if (size_to_alloc == 0) 230 { 231 windowp->i = i; 232 return TRUE; 233 } 234 goto free_and_fail; 235 } 236 i->refcount = 1; 237 if (bfd_seek (abfd, offset, SEEK_SET) != 0) 238 goto free_and_fail; 239 i->size = bfd_bread (i->data, size, abfd); 240 if (i->size != size) 241 goto free_and_fail; 242 i->mapped = 0; 243 #ifdef HAVE_MPROTECT 244 if (!writable) 245 { 246 if (debug_windows) 247 fprintf (stderr, "\tmprotect (%p, %ld, PROT_READ)\n", i->data, 248 (long) i->size); 249 mprotect (i->data, i->size, PROT_READ); 250 } 251 #endif 252 windowp->data = i->data; 253 windowp->size = i->size; 254 windowp->i = i; 255 return TRUE; 256 257 free_and_fail: 258 /* We have a bfd_window_internal, but an error occurred. Free it. */ 259 free (i); 260 return FALSE; 261 } 262 263 #endif /* USE_MMAP */ 264