bfdwin.c revision 130561
1/* Support for memory-mapped windows into a BFD. 2 Copyright 1995, 1996, 2001, 2002, 2003 Free Software Foundation, Inc. 3 Written by Cygnus Support. 4 5This file is part of BFD, the Binary File Descriptor library. 6 7This program is free software; you can redistribute it and/or modify 8it under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 2 of the License, or 10(at your option) any later version. 11 12This program is distributed in the hope that it will be useful, 13but WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15GNU General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with this program; if not, write to the Free Software 19Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 20 21#include "sysdep.h" 22 23#include "bfd.h" 24#include "libbfd.h" 25 26/* Currently, if USE_MMAP is undefined, none if the window stuff is 27 used. Okay, so it's mis-named. At least the command-line option 28 "--without-mmap" is more obvious than "--without-windows" or some 29 such. */ 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 43static 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/* 51INTERNAL_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 62void 63bfd_init_window (bfd_window *windowp) 64{ 65 windowp->data = 0; 66 windowp->i = 0; 67 windowp->size = 0; 68} 69 70void 71bfd_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, 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 106static int ok_to_map = 1; 107 108bfd_boolean 109bfd_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 == 0) 132 { 133 i = bfd_zmalloc (sizeof (bfd_window_internal)); 134 windowp->i = i; 135 if (i == 0) 136 return FALSE; 137 i->data = 0; 138 } 139#ifdef HAVE_MMAP 140 if (ok_to_map 141 && (i->data == 0 || i->mapped == 1) 142 && (abfd->flags & BFD_IN_MEMORY) == 0) 143 { 144 file_ptr file_offset, offset2; 145 size_t real_size; 146 int fd; 147 FILE *f; 148 149 /* Find the real file and the real offset into it. */ 150 while (abfd->my_archive != NULL) 151 { 152 offset += abfd->origin; 153 abfd = abfd->my_archive; 154 } 155 f = bfd_cache_lookup (abfd); 156 fd = fileno (f); 157 158 /* Compute offsets and size for mmap and for the user's data. */ 159 offset2 = offset % pagesize; 160 if (offset2 < 0) 161 abort (); 162 file_offset = offset - offset2; 163 real_size = offset + size - file_offset; 164 real_size = real_size + pagesize - 1; 165 real_size -= real_size % pagesize; 166 167 /* If we're re-using a memory region, make sure it's big enough. */ 168 if (i->data && i->size < size) 169 { 170 munmap (i->data, i->size); 171 i->data = 0; 172 } 173 i->data = mmap (i->data, real_size, 174 writable ? PROT_WRITE | PROT_READ : PROT_READ, 175 (writable 176 ? MAP_FILE | MAP_PRIVATE 177 : MAP_FILE | MAP_SHARED), 178 fd, file_offset); 179 if (i->data == (void *) -1) 180 { 181 /* An error happened. Report it, or try using malloc, or 182 something. */ 183 bfd_set_error (bfd_error_system_call); 184 i->data = 0; 185 windowp->data = 0; 186 if (debug_windows) 187 fprintf (stderr, "\t\tmmap failed!\n"); 188 return FALSE; 189 } 190 if (debug_windows) 191 fprintf (stderr, "\n\tmapped %ld at %p, offset is %ld\n", 192 (long) real_size, i->data, (long) offset2); 193 i->size = real_size; 194 windowp->data = (bfd_byte *) i->data + offset2; 195 windowp->size = size; 196 i->mapped = 1; 197 return TRUE; 198 } 199 else if (debug_windows) 200 { 201 if (ok_to_map) 202 fprintf (stderr, _("not mapping: data=%lx mapped=%d\n"), 203 (unsigned long) i->data, (int) i->mapped); 204 else 205 fprintf (stderr, _("not mapping: env var not set\n")); 206 } 207#else 208 ok_to_map = 0; 209#endif 210 211#ifdef HAVE_MPROTECT 212 if (!writable) 213 { 214 size_to_alloc += pagesize - 1; 215 size_to_alloc -= size_to_alloc % pagesize; 216 } 217#endif 218 if (debug_windows) 219 fprintf (stderr, "\n\t%s(%6ld)", 220 i->data ? "realloc" : " malloc", (long) size_to_alloc); 221 i->data = bfd_realloc (i->data, size_to_alloc); 222 if (debug_windows) 223 fprintf (stderr, "\t-> %p\n", i->data); 224 i->refcount = 1; 225 if (i->data == NULL) 226 { 227 if (size_to_alloc == 0) 228 return TRUE; 229 return FALSE; 230 } 231 if (bfd_seek (abfd, offset, SEEK_SET) != 0) 232 return FALSE; 233 i->size = bfd_bread (i->data, size, abfd); 234 if (i->size != size) 235 return FALSE; 236 i->mapped = 0; 237#ifdef HAVE_MPROTECT 238 if (!writable) 239 { 240 if (debug_windows) 241 fprintf (stderr, "\tmprotect (%p, %ld, PROT_READ)\n", i->data, 242 (long) i->size); 243 mprotect (i->data, i->size, PROT_READ); 244 } 245#endif 246 windowp->data = i->data; 247 windowp->size = i->size; 248 return TRUE; 249} 250 251#endif /* USE_MMAP */ 252