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