| 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 | |
| 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 2 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., 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 | |
| 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, 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 == 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 */ |