1 /* Support for memory-mapped windows into a BFD. 2 Copyright 1995, 1996, 2001, 2002, 2003, 2005, 2007, 2008 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 if the window stuff is 29 used. Okay, so it's mis-named. At least the command-line option 30 "--without-mmap" is more obvious than "--without-windows" or some 31 such. */ 32 33 #ifdef USE_MMAP 34 35 #undef HAVE_MPROTECT /* code's not tested yet */ 36 37 #if HAVE_MMAP || HAVE_MPROTECT || HAVE_MADVISE 38 #include <sys/mman.h> 39 #endif 40 41 #ifndef MAP_FILE 42 #define MAP_FILE 0 43 #endif 44 45 static int debug_windows; 46 47 /* The idea behind the next and refcount fields is that one mapped 48 region can suffice for multiple read-only windows or multiple 49 non-overlapping read-write windows. It's not implemented yet 50 though. */ 51 52 /* 53 INTERNAL_DEFINITION 54 55 .struct _bfd_window_internal { 56 . struct _bfd_window_internal *next; 57 . void *data; 58 . bfd_size_type size; 59 . int refcount : 31; {* should be enough... *} 60 . unsigned mapped : 1; {* 1 = mmap, 0 = malloc *} 61 .}; 62 */ 63 64 void 65 bfd_init_window (bfd_window *windowp) 66 { 67 windowp->data = 0; 68 windowp->i = 0; 69 windowp->size = 0; 70 } 71 72 void 73 bfd_free_window (bfd_window *windowp) 74 { 75 bfd_window_internal *i = windowp->i; 76 windowp->i = 0; 77 windowp->data = 0; 78 if (i == 0) 79 return; 80 i->refcount--; 81 if (debug_windows) 82 fprintf (stderr, "freeing window @%p<%p,%lx,%p>\n", 83 windowp, windowp->data, windowp->size, windowp->i); 84 if (i->refcount != 0) 85 return; 86 87 if (i->mapped) 88 { 89 #ifdef HAVE_MMAP 90 munmap (i->data, i->size); 91 goto no_free; 92 #else 93 abort (); 94 #endif 95 } 96 #ifdef HAVE_MPROTECT 97 mprotect (i->data, i->size, PROT_READ | PROT_WRITE); 98 #endif 99 free (i->data); 100 #ifdef HAVE_MMAP 101 no_free: 102 #endif 103 i->data = 0; 104 /* There should be no more references to i at this point. */ 105 free (i); 106 } 107 108 static int ok_to_map = 1; 109 110 bfd_boolean 111 bfd_get_file_window (bfd *abfd, 112 file_ptr offset, 113 bfd_size_type size, 114 bfd_window *windowp, 115 bfd_boolean writable) 116 { 117 static size_t pagesize; 118 bfd_window_internal *i = windowp->i; 119 bfd_size_type size_to_alloc = size; 120 121 if (debug_windows) 122 fprintf (stderr, "bfd_get_file_window (%p, %6ld, %6ld, %p<%p,%lx,%p>, %d)", 123 abfd, (long) offset, (long) size, 124 windowp, windowp->data, (unsigned long) windowp->size, 125 windowp->i, writable); 126 127 /* Make sure we know the page size, so we can be friendly to mmap. */ 128 if (pagesize == 0) 129 pagesize = getpagesize (); 130 if (pagesize == 0) 131 abort (); 132 133 if (i == 0) 134 { 135 i = bfd_zmalloc (sizeof (bfd_window_internal)); 136 windowp->i = i; 137 if (i == 0) 138 return FALSE; 139 i->data = 0; 140 } 141 #ifdef HAVE_MMAP 142 if (ok_to_map 143 && (i->data == 0 || i->mapped == 1) 144 && (abfd->flags & BFD_IN_MEMORY) == 0) 145 { 146 file_ptr file_offset, offset2; 147 size_t real_size; 148 int fd; 149 150 /* Find the real file and the real offset into it. */ 151 while (abfd->my_archive != NULL) 152 { 153 offset += abfd->origin; 154 abfd = abfd->my_archive; 155 } 156 157 /* Seek into the file, to ensure it is open if cacheable. */ 158 if (abfd->iostream == NULL 159 && (abfd->iovec == NULL 160 || abfd->iovec->bseek (abfd, offset, SEEK_SET) != 0)) 161 return FALSE; 162 fd = fileno ((FILE *) abfd->iostream); 163 164 /* Compute offsets and size for mmap and for the user's data. */ 165 offset2 = offset % pagesize; 166 if (offset2 < 0) 167 abort (); 168 file_offset = offset - offset2; 169 real_size = offset + size - file_offset; 170 real_size = real_size + pagesize - 1; 171 real_size -= real_size % pagesize; 172 173 /* If we're re-using a memory region, make sure it's big enough. */ 174 if (i->data && i->size < size) 175 { 176 munmap (i->data, i->size); 177 i->data = 0; 178 } 179 i->data = mmap (i->data, real_size, 180 writable ? PROT_WRITE | PROT_READ : PROT_READ, 181 (writable 182 ? MAP_FILE | MAP_PRIVATE 183 : MAP_FILE | MAP_SHARED), 184 fd, file_offset); 185 if (i->data == (void *) -1) 186 { 187 /* An error happened. Report it, or try using malloc, or 188 something. */ 189 bfd_set_error (bfd_error_system_call); 190 i->data = 0; 191 windowp->data = 0; 192 if (debug_windows) 193 fprintf (stderr, "\t\tmmap failed!\n"); 194 return FALSE; 195 } 196 if (debug_windows) 197 fprintf (stderr, "\n\tmapped %ld at %p, offset is %ld\n", 198 (long) real_size, i->data, (long) offset2); 199 i->size = real_size; 200 windowp->data = (bfd_byte *) i->data + offset2; 201 windowp->size = size; 202 i->mapped = 1; 203 return TRUE; 204 } 205 else if (debug_windows) 206 { 207 if (ok_to_map) 208 fprintf (stderr, _("not mapping: data=%lx mapped=%d\n"), 209 (unsigned long) i->data, (int) i->mapped); 210 else 211 fprintf (stderr, _("not mapping: env var not set\n")); 212 } 213 #else 214 ok_to_map = 0; 215 #endif 216 217 #ifdef HAVE_MPROTECT 218 if (!writable) 219 { 220 size_to_alloc += pagesize - 1; 221 size_to_alloc -= size_to_alloc % pagesize; 222 } 223 #endif 224 if (debug_windows) 225 fprintf (stderr, "\n\t%s(%6ld)", 226 i->data ? "realloc" : " malloc", (long) size_to_alloc); 227 i->data = bfd_realloc_or_free (i->data, size_to_alloc); 228 if (debug_windows) 229 fprintf (stderr, "\t-> %p\n", i->data); 230 if (i->data == NULL) 231 { 232 if (size_to_alloc == 0) 233 return TRUE; 234 return FALSE; 235 } 236 i->refcount = 1; 237 if (bfd_seek (abfd, offset, SEEK_SET) != 0) 238 return FALSE; 239 i->size = bfd_bread (i->data, size, abfd); 240 if (i->size != size) 241 return FALSE; 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 return TRUE; 255 } 256 257 #endif /* USE_MMAP */ 258