1 //===-- DataBufferMemoryMap.cpp ---------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <limits.h>
14 #include <sys/stat.h>
15 #ifdef _WIN32
16 #include "lldb/Host/windows/windows.h"
17 #else
18 #include <sys/mman.h>
19 #endif
20
21 #include "lldb/Core/DataBufferMemoryMap.h"
22 #include "lldb/Core/Error.h"
23 #include "lldb/Host/File.h"
24 #include "lldb/Host/FileSpec.h"
25 #include "lldb/Host/Host.h"
26 #include "lldb/Core/Log.h"
27 #include "lldb/lldb-private-log.h"
28
29 using namespace lldb;
30 using namespace lldb_private;
31
32 //----------------------------------------------------------------------
33 // Default Constructor
34 //----------------------------------------------------------------------
DataBufferMemoryMap()35 DataBufferMemoryMap::DataBufferMemoryMap() :
36 m_mmap_addr(NULL),
37 m_mmap_size(0),
38 m_data(NULL),
39 m_size(0)
40 {
41 }
42
43 //----------------------------------------------------------------------
44 // Virtual destructor since this class inherits from a pure virtual
45 // base class.
46 //----------------------------------------------------------------------
~DataBufferMemoryMap()47 DataBufferMemoryMap::~DataBufferMemoryMap()
48 {
49 Clear();
50 }
51
52 //----------------------------------------------------------------------
53 // Return a pointer to the bytes owned by this object, or NULL if
54 // the object contains no bytes.
55 //----------------------------------------------------------------------
56 uint8_t *
GetBytes()57 DataBufferMemoryMap::GetBytes()
58 {
59 return m_data;
60 }
61
62 //----------------------------------------------------------------------
63 // Return a const pointer to the bytes owned by this object, or NULL
64 // if the object contains no bytes.
65 //----------------------------------------------------------------------
66 const uint8_t *
GetBytes() const67 DataBufferMemoryMap::GetBytes() const
68 {
69 return m_data;
70 }
71
72 //----------------------------------------------------------------------
73 // Return the number of bytes this object currently contains.
74 //----------------------------------------------------------------------
75 uint64_t
GetByteSize() const76 DataBufferMemoryMap::GetByteSize() const
77 {
78 return m_size;
79 }
80
81 //----------------------------------------------------------------------
82 // Reverts this object to an empty state by unmapping any memory
83 // that is currently owned.
84 //----------------------------------------------------------------------
85 void
Clear()86 DataBufferMemoryMap::Clear()
87 {
88 if (m_mmap_addr != NULL)
89 {
90 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP));
91 if (log)
92 log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %zu", m_mmap_addr, m_mmap_size);
93 #ifdef _WIN32
94 UnmapViewOfFile(m_mmap_addr);
95 #else
96 ::munmap((void *)m_mmap_addr, m_mmap_size);
97 #endif
98 m_mmap_addr = NULL;
99 m_mmap_size = 0;
100 m_data = NULL;
101 m_size = 0;
102 }
103 }
104
105 //----------------------------------------------------------------------
106 // Memory map "length" bytes from "file" starting "offset"
107 // bytes into the file. If "length" is set to SIZE_MAX, then
108 // map as many bytes as possible.
109 //
110 // Returns the number of bytes mapped starting from the requested
111 // offset.
112 //----------------------------------------------------------------------
113 size_t
MemoryMapFromFileSpec(const FileSpec * filespec,lldb::offset_t offset,lldb::offset_t length,bool writeable)114 DataBufferMemoryMap::MemoryMapFromFileSpec (const FileSpec* filespec,
115 lldb::offset_t offset,
116 lldb::offset_t length,
117 bool writeable)
118 {
119 if (filespec != NULL)
120 {
121 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP));
122 if (log)
123 {
124 log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(file=\"%s\", offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i",
125 filespec->GetPath().c_str(),
126 offset,
127 length,
128 writeable);
129 }
130 char path[PATH_MAX];
131 if (filespec->GetPath(path, sizeof(path)))
132 {
133 uint32_t options = File::eOpenOptionRead;
134 if (writeable)
135 options |= File::eOpenOptionWrite;
136
137 File file;
138 Error error (file.Open(path, options));
139 if (error.Success())
140 {
141 const bool fd_is_file = true;
142 return MemoryMapFromFileDescriptor (file.GetDescriptor(), offset, length, writeable, fd_is_file);
143 }
144 }
145 }
146 // We should only get here if there was an error
147 Clear();
148 return 0;
149 }
150
151
152 #ifdef _WIN32
153 static size_t win32memmapalignment = 0;
LoadWin32MemMapAlignment()154 void LoadWin32MemMapAlignment ()
155 {
156 SYSTEM_INFO data;
157 GetSystemInfo(&data);
158 win32memmapalignment = data.dwAllocationGranularity;
159 }
160 #endif
161
162 //----------------------------------------------------------------------
163 // The file descriptor FD is assumed to already be opened as read only
164 // and the STAT structure is assumed to a valid pointer and already
165 // containing valid data from a call to stat().
166 //
167 // Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into
168 // the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes
169 // as possible.
170 //
171 // RETURNS
172 // Number of bytes mapped starting from the requested offset.
173 //----------------------------------------------------------------------
174 size_t
MemoryMapFromFileDescriptor(int fd,lldb::offset_t offset,lldb::offset_t length,bool writeable,bool fd_is_file)175 DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd,
176 lldb::offset_t offset,
177 lldb::offset_t length,
178 bool writeable,
179 bool fd_is_file)
180 {
181 Clear();
182 if (fd >= 0)
183 {
184 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP|LIBLLDB_LOG_VERBOSE));
185 if (log)
186 {
187 #ifdef _WIN32
188 log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%p, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)",
189 #else
190 log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)",
191 #endif
192 fd,
193 offset,
194 length,
195 writeable,
196 fd_is_file);
197 }
198 #ifdef _WIN32
199 HANDLE handle = (HANDLE)_get_osfhandle(fd);
200 DWORD file_size_low, file_size_high;
201 file_size_low = GetFileSize(handle, &file_size_high);
202 const size_t file_size = (file_size_high << 32) | file_size_low;
203 const size_t max_bytes_available = file_size - offset;
204 if (length == SIZE_MAX)
205 {
206 length = max_bytes_available;
207 }
208 else if (length > max_bytes_available)
209 {
210 // Cap the length if too much data was requested
211 length = max_bytes_available;
212 }
213
214 if (length > 0)
215 {
216 HANDLE fileMapping = CreateFileMapping(handle, NULL, writeable ? PAGE_READWRITE : PAGE_READONLY, file_size_high, file_size_low, NULL);
217 if (fileMapping != NULL)
218 {
219 if (win32memmapalignment == 0) LoadWin32MemMapAlignment();
220 lldb::offset_t realoffset = offset;
221 lldb::offset_t delta = 0;
222 if (realoffset % win32memmapalignment != 0) {
223 realoffset = realoffset / win32memmapalignment * win32memmapalignment;
224 delta = offset - realoffset;
225 }
226
227 LPVOID data = MapViewOfFile(fileMapping, writeable ? FILE_MAP_WRITE : FILE_MAP_READ, 0, realoffset, length + delta);
228 m_mmap_addr = (uint8_t *)data;
229 if (!data) {
230 Error error;
231 error.SetErrorToErrno ();
232 } else {
233 m_data = m_mmap_addr + delta;
234 m_size = length;
235 }
236 CloseHandle(fileMapping);
237 }
238 }
239 #else
240 struct stat stat;
241 if (::fstat(fd, &stat) == 0)
242 {
243 if (S_ISREG(stat.st_mode) && (stat.st_size > offset))
244 {
245 const size_t max_bytes_available = stat.st_size - offset;
246 if (length == SIZE_MAX)
247 {
248 length = max_bytes_available;
249 }
250 else if (length > max_bytes_available)
251 {
252 // Cap the length if too much data was requested
253 length = max_bytes_available;
254 }
255
256 if (length > 0)
257 {
258 int prot = PROT_READ;
259 if (writeable)
260 prot |= PROT_WRITE;
261
262 int flags = MAP_PRIVATE;
263 if (fd_is_file)
264 flags |= MAP_FILE;
265
266 m_mmap_addr = (uint8_t *)::mmap(NULL, length, prot, flags, fd, offset);
267 Error error;
268
269 if (m_mmap_addr == (void*)-1)
270 {
271 error.SetErrorToErrno ();
272 if (error.GetError() == EINVAL)
273 {
274 // We may still have a shot at memory mapping if we align things correctly
275 size_t page_offset = offset % Host::GetPageSize();
276 if (page_offset != 0)
277 {
278 m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset);
279 if (m_mmap_addr == (void*)-1)
280 {
281 // Failed to map file
282 m_mmap_addr = NULL;
283 }
284 else if (m_mmap_addr != NULL)
285 {
286 // We recovered and were able to memory map
287 // after we aligned things to page boundaries
288
289 // Save the actual mmap'ed size
290 m_mmap_size = length + page_offset;
291 // Our data is at an offset into the the mapped data
292 m_data = m_mmap_addr + page_offset;
293 // Our pretend size is the size that was requestd
294 m_size = length;
295 }
296 }
297 }
298 if (error.GetError() == ENOMEM)
299 {
300 error.SetErrorStringWithFormat("could not allocate %" PRId64 " bytes of memory to mmap in file", (uint64_t) length);
301 }
302 }
303 else
304 {
305 // We were able to map the requested data in one chunk
306 // where our mmap and actual data are the same.
307 m_mmap_size = length;
308 m_data = m_mmap_addr;
309 m_size = length;
310 }
311
312 if (log)
313 {
314 log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %zu, error = %s",
315 m_mmap_addr, m_mmap_size, error.AsCString());
316 }
317 }
318 }
319 }
320 #endif
321 }
322 return GetByteSize ();
323 }
324