| 1 | /* Support for memory-mapped windows into a BFD. |
| 2 | Copyright (C) 1995-2019 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 | && !bfd_is_thin_archive (abfd->my_archive)) |
| 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 */ |