|  | /* Copyright (C) 2021 Free Software Foundation, Inc. | 
|  | Contributed by Oracle. | 
|  |  | 
|  | This file is part of GNU Binutils. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; if not, write to the Free Software | 
|  | Foundation, 51 Franklin Street - Fifth Floor, Boston, | 
|  | MA 02110-1301, USA.  */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include <sys/types.h> | 
|  | #include <sys/mman.h> | 
|  | #include <fcntl.h> | 
|  | #include <unistd.h>     //  for close(); | 
|  |  | 
|  | #include "util.h" | 
|  | #include "Data_window.h" | 
|  | #include "debug.h" | 
|  |  | 
|  | enum | 
|  | { | 
|  | MINBUFSIZE    = 1 << 16, | 
|  | WIN_ALIGN     = 8 | 
|  | }; | 
|  |  | 
|  | Data_window::Data_window (char *file_name) | 
|  | { | 
|  | Dprintf (DEBUG_DATA_WINDOW, NTXT ("Data_window:%d %s\n"), (int) __LINE__, STR (file_name)); | 
|  | page_size = sysconf (_SC_PAGESIZE); | 
|  | need_swap_endian = false; | 
|  | opened = false; | 
|  | fsize = 0; | 
|  | base = NULL; | 
|  | woffset = 0; | 
|  | wsize = 0; | 
|  | basesize = 0; | 
|  | fname = dbe_strdup (file_name); | 
|  | mmap_on_file = false; | 
|  | use_mmap = false; | 
|  | #if DEBUG | 
|  | if (DBE_USE_MMAP) | 
|  | use_mmap = true; | 
|  | #endif /* DEBUG */ | 
|  | fd = open64 (fname, O_RDONLY); | 
|  | if (fd == -1) | 
|  | return; | 
|  | fsize = lseek (fd, 0, SEEK_END); | 
|  | if (fsize == 0) | 
|  | { | 
|  | close (fd); | 
|  | fd = -1; | 
|  | return; | 
|  | } | 
|  | opened = true; | 
|  | if (use_mmap) | 
|  | { | 
|  | if (fsize != -1) | 
|  | { | 
|  | base = (void*) mmap (NULL, (size_t) fsize, PROT_READ, MAP_PRIVATE, fd, 0); | 
|  | close (fd); | 
|  | fd = -1; | 
|  | if (base == MAP_FAILED) | 
|  | { | 
|  | base = NULL; | 
|  | use_mmap = false; | 
|  | return; | 
|  | } | 
|  | mmap_on_file = true; | 
|  | wsize = fsize; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void * | 
|  | Data_window::bind (int64_t file_offset, int64_t minSize) | 
|  | { | 
|  | Span span; | 
|  | span.length = fsize - file_offset; | 
|  | span.offset = file_offset; | 
|  | return bind (&span, minSize); | 
|  | } | 
|  |  | 
|  | void * | 
|  | Data_window::bind (Span *span, int64_t minSize) | 
|  | { | 
|  | // Do any necessary mapping to access the desired span of data | 
|  | // and return a pointer to the first byte. | 
|  | Dprintf (DEBUG_DATA_WINDOW, NTXT ("Data_window:bind:%d offset=%llx:%lld minSize=%lld \n"), | 
|  | (int) __LINE__, (long long) span->offset, (long long) span->length, (long long) minSize); | 
|  | if (minSize == 0 || span->length < minSize) | 
|  | return NULL; | 
|  |  | 
|  | if (span->offset < woffset || span->offset + minSize > woffset + wsize) | 
|  | { | 
|  | // Remap the window | 
|  | if (span->offset + minSize > fsize) | 
|  | return NULL; | 
|  | int myfd = fd; | 
|  | if (myfd == -1) | 
|  | { | 
|  | if (fname) | 
|  | myfd = open64 (fname, O_RDONLY, 0); | 
|  | if (myfd == -1) | 
|  | return NULL; | 
|  | } | 
|  | bool remap_failed = true; | 
|  | if (use_mmap) | 
|  | { | 
|  | if (base) | 
|  | { | 
|  | munmap ((caddr_t) base, (size_t) wsize); | 
|  | base = NULL; | 
|  | } | 
|  | woffset = span->offset & ~(page_size - 1); | 
|  | wsize = page_size * ((MINBUFSIZE + page_size - 1) / page_size); | 
|  | if (span->offset + minSize > woffset + wsize) | 
|  | // Extend a window | 
|  | wsize += page_size * ((span->offset + minSize - | 
|  | woffset - wsize + page_size - 1) / page_size); | 
|  | base = (void *) mmap (0, (size_t) wsize, PROT_READ, MAP_SHARED, fd, woffset); | 
|  | if (base == MAP_FAILED) | 
|  | { | 
|  | base = NULL; | 
|  | use_mmap = false; | 
|  | } | 
|  | remap_failed = (base == NULL); | 
|  | } | 
|  | if (remap_failed) | 
|  | { | 
|  | remap_failed = false; | 
|  | woffset = span->offset & ~(WIN_ALIGN - 1); | 
|  | wsize = minSize + (span->offset % WIN_ALIGN); | 
|  | if (wsize < MINBUFSIZE) | 
|  | wsize = MINBUFSIZE; | 
|  | if (wsize > fsize) | 
|  | wsize = fsize; | 
|  | if (basesize < wsize) | 
|  | { // Need to realloc 'base' | 
|  | free (base); | 
|  | basesize = wsize; | 
|  | base = (void *) malloc (basesize); | 
|  | Dprintf (DEBUG_DATA_WINDOW, | 
|  | NTXT ("Data_window:bind:%d realloc basesize=%llx woffset=%lld \n"), | 
|  | (int) __LINE__, (long long) basesize, (long long) woffset); | 
|  | if (base == NULL) | 
|  | { | 
|  | basesize = 0; | 
|  | remap_failed = true; | 
|  | } | 
|  | } | 
|  | if (wsize > fsize - woffset) | 
|  | wsize = fsize - woffset; | 
|  | off_t woff = (off_t) woffset; | 
|  | if (base == NULL || woff != lseek (myfd, woff, SEEK_SET) | 
|  | || wsize != read_from_file (myfd, base, wsize)) | 
|  | remap_failed = true; | 
|  | } | 
|  | if (fd == -1) | 
|  | close (myfd); | 
|  | if (remap_failed) | 
|  | { | 
|  | woffset = 0; | 
|  | wsize = 0; | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  | return (void *) ((char*) base + span->offset - woffset); | 
|  | } | 
|  |  | 
|  | void * | 
|  | Data_window::get_data (int64_t offset, int64_t size, void *datap) | 
|  | { | 
|  | if (size <= 0) | 
|  | return NULL; | 
|  | void *buf = bind (offset, size); | 
|  | if (buf == NULL) | 
|  | return NULL; | 
|  | if (datap == NULL && !mmap_on_file) | 
|  | // Can be remmaped or reallocated. Need to make a copy | 
|  | datap = (void *) malloc (size); | 
|  | if (datap) | 
|  | { | 
|  | memcpy (datap, buf, (size_t) size); | 
|  | return datap; | 
|  | } | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | Data_window::~Data_window () | 
|  | { | 
|  | free (fname); | 
|  | if (fd != -1) | 
|  | close (fd); | 
|  | if (base) | 
|  | { | 
|  | if (use_mmap) | 
|  | munmap ((caddr_t) base, (size_t) wsize); | 
|  | else | 
|  | free (base); | 
|  | } | 
|  | } | 
|  |  | 
|  | int64_t | 
|  | Data_window::get_buf_size () | 
|  | { | 
|  | int64_t sz = MINBUFSIZE; | 
|  | if (sz < basesize) | 
|  | sz = basesize; | 
|  | if (sz > fsize) | 
|  | sz = fsize; | 
|  | return sz; | 
|  | } | 
|  |  | 
|  | int64_t | 
|  | Data_window::copy_to_file (int f, int64_t offset, int64_t size) | 
|  | { | 
|  | long long bsz = get_buf_size (); | 
|  | for (long long n = 0; n < size;) | 
|  | { | 
|  | long long sz = (bsz <= (size - n)) ? bsz : (size - n); | 
|  | void *b = bind (offset + n, sz); | 
|  | if (b == NULL) | 
|  | return n; | 
|  | long long len = write (f, b, sz); | 
|  | if (len <= 0) | 
|  | return n; | 
|  | n += len; | 
|  | } | 
|  | return size; | 
|  | } |