1/* Copyright (C) 2021 Free Software Foundation, Inc. 2 Contributed by Oracle. 3 4 This file is part of GNU Binutils. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, 51 Franklin Street - Fifth Floor, Boston, 19 MA 02110-1301, USA. */ 20 21#include "config.h" 22#include <sys/types.h> 23#include <sys/mman.h> 24#include <fcntl.h> 25#include <unistd.h> // for close(); 26 27#include "util.h" 28#include "Data_window.h" 29#include "debug.h" 30 31enum 32{ 33 MINBUFSIZE = 1 << 16, 34 WIN_ALIGN = 8 35}; 36 37Data_window::Data_window (char *file_name) 38{ 39 Dprintf (DEBUG_DATA_WINDOW, NTXT ("Data_window:%d %s\n"), (int) __LINE__, STR (file_name)); 40 page_size = sysconf (_SC_PAGESIZE); 41 need_swap_endian = false; 42 opened = false; 43 fsize = 0; 44 base = NULL; 45 woffset = 0; 46 wsize = 0; 47 basesize = 0; 48 fname = dbe_strdup (file_name); 49 mmap_on_file = false; 50 use_mmap = false; 51#if DEBUG 52 if (DBE_USE_MMAP) 53 use_mmap = true; 54#endif /* DEBUG */ 55 fd = open64 (fname, O_RDONLY); 56 if (fd == -1) 57 return; 58 fsize = lseek (fd, 0, SEEK_END); 59 if (fsize == 0) 60 { 61 close (fd); 62 fd = -1; 63 return; 64 } 65 opened = true; 66 if (use_mmap) 67 { 68 if (fsize != -1) 69 { 70 base = (void*) mmap (NULL, (size_t) fsize, PROT_READ, MAP_PRIVATE, fd, 0); 71 close (fd); 72 fd = -1; 73 if (base == MAP_FAILED) 74 { 75 base = NULL; 76 use_mmap = false; 77 return; 78 } 79 mmap_on_file = true; 80 wsize = fsize; 81 } 82 } 83} 84 85void * 86Data_window::bind (int64_t file_offset, int64_t minSize) 87{ 88 Span span; 89 span.length = fsize - file_offset; 90 span.offset = file_offset; 91 return bind (&span, minSize); 92} 93 94void * 95Data_window::bind (Span *span, int64_t minSize) 96{ 97 // Do any necessary mapping to access the desired span of data 98 // and return a pointer to the first byte. 99 Dprintf (DEBUG_DATA_WINDOW, NTXT ("Data_window:bind:%d offset=%llx:%lld minSize=%lld \n"), 100 (int) __LINE__, (long long) span->offset, (long long) span->length, (long long) minSize); 101 if (minSize == 0 || span->length < minSize) 102 return NULL; 103 104 if (span->offset < woffset || span->offset + minSize > woffset + wsize) 105 { 106 // Remap the window 107 if (span->offset + minSize > fsize) 108 return NULL; 109 int myfd = fd; 110 if (myfd == -1) 111 { 112 if (fname) 113 myfd = open64 (fname, O_RDONLY, 0); 114 if (myfd == -1) 115 return NULL; 116 } 117 bool remap_failed = true; 118 if (use_mmap) 119 { 120 if (base) 121 { 122 munmap ((caddr_t) base, (size_t) wsize); 123 base = NULL; 124 } 125 woffset = span->offset & ~(page_size - 1); 126 wsize = page_size * ((MINBUFSIZE + page_size - 1) / page_size); 127 if (span->offset + minSize > woffset + wsize) 128 // Extend a window 129 wsize += page_size * ((span->offset + minSize - 130 woffset - wsize + page_size - 1) / page_size); 131 base = (void *) mmap (0, (size_t) wsize, PROT_READ, MAP_SHARED, fd, woffset); 132 if (base == MAP_FAILED) 133 { 134 base = NULL; 135 use_mmap = false; 136 } 137 remap_failed = (base == NULL); 138 } 139 if (remap_failed) 140 { 141 remap_failed = false; 142 woffset = span->offset & ~(WIN_ALIGN - 1); 143 wsize = minSize + (span->offset % WIN_ALIGN); 144 if (wsize < MINBUFSIZE) 145 wsize = MINBUFSIZE; 146 if (wsize > fsize) 147 wsize = fsize; 148 if (basesize < wsize) 149 { // Need to realloc 'base' 150 free (base); 151 basesize = wsize; 152 base = (void *) malloc (basesize); 153 Dprintf (DEBUG_DATA_WINDOW, 154 NTXT ("Data_window:bind:%d realloc basesize=%llx woffset=%lld \n"), 155 (int) __LINE__, (long long) basesize, (long long) woffset); 156 if (base == NULL) 157 { 158 basesize = 0; 159 remap_failed = true; 160 } 161 } 162 if (wsize > fsize - woffset) 163 wsize = fsize - woffset; 164 off_t woff = (off_t) woffset; 165 if (base == NULL || woff != lseek (myfd, woff, SEEK_SET) 166 || wsize != read_from_file (myfd, base, wsize)) 167 remap_failed = true; 168 } 169 if (fd == -1) 170 close (myfd); 171 if (remap_failed) 172 { 173 woffset = 0; 174 wsize = 0; 175 return NULL; 176 } 177 } 178 return (void *) ((char*) base + span->offset - woffset); 179} 180 181void * 182Data_window::get_data (int64_t offset, int64_t size, void *datap) 183{ 184 if (size <= 0) 185 return NULL; 186 void *buf = bind (offset, size); 187 if (buf == NULL) 188 return NULL; 189 if (datap == NULL && !mmap_on_file) 190 // Can be remmaped or reallocated. Need to make a copy 191 datap = (void *) malloc (size); 192 if (datap) 193 { 194 memcpy (datap, buf, (size_t) size); 195 return datap; 196 } 197 return buf; 198} 199 200Data_window::~Data_window () 201{ 202 free (fname); 203 if (fd != -1) 204 close (fd); 205 if (base) 206 { 207 if (use_mmap) 208 munmap ((caddr_t) base, (size_t) wsize); 209 else 210 free (base); 211 } 212} 213 214int64_t 215Data_window::get_buf_size () 216{ 217 int64_t sz = MINBUFSIZE; 218 if (sz < basesize) 219 sz = basesize; 220 if (sz > fsize) 221 sz = fsize; 222 return sz; 223} 224 225int64_t 226Data_window::copy_to_file (int f, int64_t offset, int64_t size) 227{ 228 long long bsz = get_buf_size (); 229 for (long long n = 0; n < size;) 230 { 231 long long sz = (bsz <= (size - n)) ? bsz : (size - n); 232 void *b = bind (offset + n, sz); 233 if (b == NULL) 234 return n; 235 long long len = write (f, b, sz); 236 if (len <= 0) 237 return n; 238 n += len; 239 } 240 return size; 241} 242