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