| /* Copyright (C) 2021-2024 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; | |
| } |