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