1130561Sobrien/* Support for memory-mapped windows into a BFD. 2218822Sdim Copyright 1995, 1996, 2001, 2002, 2003, 2005 3218822Sdim Free Software Foundation, Inc. 4130561Sobrien Written by Cygnus Support. 5130561Sobrien 6130561SobrienThis file is part of BFD, the Binary File Descriptor library. 7130561Sobrien 8130561SobrienThis program is free software; you can redistribute it and/or modify 9130561Sobrienit under the terms of the GNU General Public License as published by 10130561Sobrienthe Free Software Foundation; either version 2 of the License, or 11130561Sobrien(at your option) any later version. 12130561Sobrien 13130561SobrienThis program is distributed in the hope that it will be useful, 14130561Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of 15130561SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16130561SobrienGNU General Public License for more details. 17130561Sobrien 18130561SobrienYou should have received a copy of the GNU General Public License 19130561Sobrienalong with this program; if not, write to the Free Software 20218822SdimFoundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ 21130561Sobrien 22130561Sobrien#include "sysdep.h" 23130561Sobrien 24130561Sobrien#include "bfd.h" 25130561Sobrien#include "libbfd.h" 26130561Sobrien 27130561Sobrien/* Currently, if USE_MMAP is undefined, none if the window stuff is 28130561Sobrien used. Okay, so it's mis-named. At least the command-line option 29130561Sobrien "--without-mmap" is more obvious than "--without-windows" or some 30130561Sobrien such. */ 31130561Sobrien 32130561Sobrien#ifdef USE_MMAP 33130561Sobrien 34130561Sobrien#undef HAVE_MPROTECT /* code's not tested yet */ 35130561Sobrien 36130561Sobrien#if HAVE_MMAP || HAVE_MPROTECT || HAVE_MADVISE 37130561Sobrien#include <sys/mman.h> 38130561Sobrien#endif 39130561Sobrien 40130561Sobrien#ifndef MAP_FILE 41130561Sobrien#define MAP_FILE 0 42130561Sobrien#endif 43130561Sobrien 44130561Sobrienstatic int debug_windows; 45130561Sobrien 46130561Sobrien/* The idea behind the next and refcount fields is that one mapped 47130561Sobrien region can suffice for multiple read-only windows or multiple 48130561Sobrien non-overlapping read-write windows. It's not implemented yet 49130561Sobrien though. */ 50130561Sobrien 51130561Sobrien/* 52130561SobrienINTERNAL_DEFINITION 53130561Sobrien 54130561Sobrien.struct _bfd_window_internal { 55130561Sobrien. struct _bfd_window_internal *next; 56130561Sobrien. void *data; 57130561Sobrien. bfd_size_type size; 58130561Sobrien. int refcount : 31; {* should be enough... *} 59130561Sobrien. unsigned mapped : 1; {* 1 = mmap, 0 = malloc *} 60130561Sobrien.}; 61130561Sobrien*/ 62130561Sobrien 63130561Sobrienvoid 64130561Sobrienbfd_init_window (bfd_window *windowp) 65130561Sobrien{ 66130561Sobrien windowp->data = 0; 67130561Sobrien windowp->i = 0; 68130561Sobrien windowp->size = 0; 69130561Sobrien} 70130561Sobrien 71130561Sobrienvoid 72130561Sobrienbfd_free_window (bfd_window *windowp) 73130561Sobrien{ 74130561Sobrien bfd_window_internal *i = windowp->i; 75130561Sobrien windowp->i = 0; 76130561Sobrien windowp->data = 0; 77130561Sobrien if (i == 0) 78130561Sobrien return; 79130561Sobrien i->refcount--; 80130561Sobrien if (debug_windows) 81130561Sobrien fprintf (stderr, "freeing window @%p<%p,%lx,%p>\n", 82130561Sobrien windowp, windowp->data, windowp->size, windowp->i); 83130561Sobrien if (i->refcount != 0) 84130561Sobrien return; 85130561Sobrien 86130561Sobrien if (i->mapped) 87130561Sobrien { 88130561Sobrien#ifdef HAVE_MMAP 89130561Sobrien munmap (i->data, i->size); 90130561Sobrien goto no_free; 91130561Sobrien#else 92130561Sobrien abort (); 93130561Sobrien#endif 94130561Sobrien } 95130561Sobrien#ifdef HAVE_MPROTECT 96130561Sobrien mprotect (i->data, i->size, PROT_READ | PROT_WRITE); 97130561Sobrien#endif 98130561Sobrien free (i->data); 99130561Sobrien#ifdef HAVE_MMAP 100130561Sobrien no_free: 101130561Sobrien#endif 102130561Sobrien i->data = 0; 103130561Sobrien /* There should be no more references to i at this point. */ 104130561Sobrien free (i); 105130561Sobrien} 106130561Sobrien 107130561Sobrienstatic int ok_to_map = 1; 108130561Sobrien 109130561Sobrienbfd_boolean 110130561Sobrienbfd_get_file_window (bfd *abfd, 111130561Sobrien file_ptr offset, 112130561Sobrien bfd_size_type size, 113130561Sobrien bfd_window *windowp, 114130561Sobrien bfd_boolean writable) 115130561Sobrien{ 116130561Sobrien static size_t pagesize; 117130561Sobrien bfd_window_internal *i = windowp->i; 118130561Sobrien bfd_size_type size_to_alloc = size; 119130561Sobrien 120130561Sobrien if (debug_windows) 121130561Sobrien fprintf (stderr, "bfd_get_file_window (%p, %6ld, %6ld, %p<%p,%lx,%p>, %d)", 122130561Sobrien abfd, (long) offset, (long) size, 123130561Sobrien windowp, windowp->data, (unsigned long) windowp->size, 124130561Sobrien windowp->i, writable); 125130561Sobrien 126130561Sobrien /* Make sure we know the page size, so we can be friendly to mmap. */ 127130561Sobrien if (pagesize == 0) 128130561Sobrien pagesize = getpagesize (); 129130561Sobrien if (pagesize == 0) 130130561Sobrien abort (); 131130561Sobrien 132130561Sobrien if (i == 0) 133130561Sobrien { 134130561Sobrien i = bfd_zmalloc (sizeof (bfd_window_internal)); 135130561Sobrien windowp->i = i; 136130561Sobrien if (i == 0) 137130561Sobrien return FALSE; 138130561Sobrien i->data = 0; 139130561Sobrien } 140130561Sobrien#ifdef HAVE_MMAP 141130561Sobrien if (ok_to_map 142130561Sobrien && (i->data == 0 || i->mapped == 1) 143130561Sobrien && (abfd->flags & BFD_IN_MEMORY) == 0) 144130561Sobrien { 145130561Sobrien file_ptr file_offset, offset2; 146130561Sobrien size_t real_size; 147130561Sobrien int fd; 148130561Sobrien 149130561Sobrien /* Find the real file and the real offset into it. */ 150130561Sobrien while (abfd->my_archive != NULL) 151130561Sobrien { 152130561Sobrien offset += abfd->origin; 153130561Sobrien abfd = abfd->my_archive; 154130561Sobrien } 155130561Sobrien 156218822Sdim /* Seek into the file, to ensure it is open if cacheable. */ 157218822Sdim if (abfd->iostream == NULL 158218822Sdim && (abfd->iovec == NULL 159218822Sdim || abfd->iovec->bseek (abfd, offset, SEEK_SET) != 0)) 160218822Sdim return FALSE; 161218822Sdim fd = fileno ((FILE *) abfd->iostream); 162218822Sdim 163130561Sobrien /* Compute offsets and size for mmap and for the user's data. */ 164130561Sobrien offset2 = offset % pagesize; 165130561Sobrien if (offset2 < 0) 166130561Sobrien abort (); 167130561Sobrien file_offset = offset - offset2; 168130561Sobrien real_size = offset + size - file_offset; 169130561Sobrien real_size = real_size + pagesize - 1; 170130561Sobrien real_size -= real_size % pagesize; 171130561Sobrien 172130561Sobrien /* If we're re-using a memory region, make sure it's big enough. */ 173130561Sobrien if (i->data && i->size < size) 174130561Sobrien { 175130561Sobrien munmap (i->data, i->size); 176130561Sobrien i->data = 0; 177130561Sobrien } 178130561Sobrien i->data = mmap (i->data, real_size, 179130561Sobrien writable ? PROT_WRITE | PROT_READ : PROT_READ, 180130561Sobrien (writable 181130561Sobrien ? MAP_FILE | MAP_PRIVATE 182130561Sobrien : MAP_FILE | MAP_SHARED), 183130561Sobrien fd, file_offset); 184130561Sobrien if (i->data == (void *) -1) 185130561Sobrien { 186130561Sobrien /* An error happened. Report it, or try using malloc, or 187130561Sobrien something. */ 188130561Sobrien bfd_set_error (bfd_error_system_call); 189130561Sobrien i->data = 0; 190130561Sobrien windowp->data = 0; 191130561Sobrien if (debug_windows) 192130561Sobrien fprintf (stderr, "\t\tmmap failed!\n"); 193130561Sobrien return FALSE; 194130561Sobrien } 195130561Sobrien if (debug_windows) 196130561Sobrien fprintf (stderr, "\n\tmapped %ld at %p, offset is %ld\n", 197130561Sobrien (long) real_size, i->data, (long) offset2); 198130561Sobrien i->size = real_size; 199130561Sobrien windowp->data = (bfd_byte *) i->data + offset2; 200130561Sobrien windowp->size = size; 201130561Sobrien i->mapped = 1; 202130561Sobrien return TRUE; 203130561Sobrien } 204130561Sobrien else if (debug_windows) 205130561Sobrien { 206130561Sobrien if (ok_to_map) 207130561Sobrien fprintf (stderr, _("not mapping: data=%lx mapped=%d\n"), 208130561Sobrien (unsigned long) i->data, (int) i->mapped); 209130561Sobrien else 210130561Sobrien fprintf (stderr, _("not mapping: env var not set\n")); 211130561Sobrien } 212130561Sobrien#else 213130561Sobrien ok_to_map = 0; 214130561Sobrien#endif 215130561Sobrien 216130561Sobrien#ifdef HAVE_MPROTECT 217130561Sobrien if (!writable) 218130561Sobrien { 219130561Sobrien size_to_alloc += pagesize - 1; 220130561Sobrien size_to_alloc -= size_to_alloc % pagesize; 221130561Sobrien } 222130561Sobrien#endif 223130561Sobrien if (debug_windows) 224130561Sobrien fprintf (stderr, "\n\t%s(%6ld)", 225130561Sobrien i->data ? "realloc" : " malloc", (long) size_to_alloc); 226130561Sobrien i->data = bfd_realloc (i->data, size_to_alloc); 227130561Sobrien if (debug_windows) 228130561Sobrien fprintf (stderr, "\t-> %p\n", i->data); 229130561Sobrien i->refcount = 1; 230130561Sobrien if (i->data == NULL) 231130561Sobrien { 232130561Sobrien if (size_to_alloc == 0) 233130561Sobrien return TRUE; 234130561Sobrien return FALSE; 235130561Sobrien } 236130561Sobrien if (bfd_seek (abfd, offset, SEEK_SET) != 0) 237130561Sobrien return FALSE; 238130561Sobrien i->size = bfd_bread (i->data, size, abfd); 239130561Sobrien if (i->size != size) 240130561Sobrien return FALSE; 241130561Sobrien i->mapped = 0; 242130561Sobrien#ifdef HAVE_MPROTECT 243130561Sobrien if (!writable) 244130561Sobrien { 245130561Sobrien if (debug_windows) 246130561Sobrien fprintf (stderr, "\tmprotect (%p, %ld, PROT_READ)\n", i->data, 247130561Sobrien (long) i->size); 248130561Sobrien mprotect (i->data, i->size, PROT_READ); 249130561Sobrien } 250130561Sobrien#endif 251130561Sobrien windowp->data = i->data; 252130561Sobrien windowp->size = i->size; 253130561Sobrien return TRUE; 254130561Sobrien} 255130561Sobrien 256130561Sobrien#endif /* USE_MMAP */ 257