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 29using namespace lldb; 30using namespace lldb_private; 31 32//---------------------------------------------------------------------- 33// Default Constructor 34//---------------------------------------------------------------------- 35DataBufferMemoryMap::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//---------------------------------------------------------------------- 47DataBufferMemoryMap::~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//---------------------------------------------------------------------- 56uint8_t * 57DataBufferMemoryMap::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//---------------------------------------------------------------------- 66const uint8_t * 67DataBufferMemoryMap::GetBytes() const 68{ 69 return m_data; 70} 71 72//---------------------------------------------------------------------- 73// Return the number of bytes this object currently contains. 74//---------------------------------------------------------------------- 75uint64_t 76DataBufferMemoryMap::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//---------------------------------------------------------------------- 85void 86DataBufferMemoryMap::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//---------------------------------------------------------------------- 113size_t 114DataBufferMemoryMap::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 153static size_t win32memmapalignment = 0; 154void 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//---------------------------------------------------------------------- 174size_t 175DataBufferMemoryMap::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