|  | //===-- DataBufferMemoryMap.cpp ---------------------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | // C Includes | 
|  | #include <fcntl.h> | 
|  | #include <sys/stat.h> | 
|  | #ifdef _WIN32 | 
|  | #include "lldb/Host/windows/windows.h" | 
|  | #else | 
|  | #include <sys/mman.h> | 
|  |  | 
|  | #define MAP_EXTRA_HOST_READ_FLAGS 0 | 
|  |  | 
|  | #if defined(__APPLE__) | 
|  | //---------------------------------------------------------------------- | 
|  | // Newer versions of MacOSX have a flag that will allow us to read from | 
|  | // binaries whose code signature is invalid without crashing by using | 
|  | // the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media | 
|  | // is mapped we can avoid crashing and return zeroes to any pages we try | 
|  | // to read if the media becomes unavailable by using the | 
|  | // MAP_RESILIENT_MEDIA flag. | 
|  | //---------------------------------------------------------------------- | 
|  | #if defined(MAP_RESILIENT_CODESIGN) | 
|  | #undef MAP_EXTRA_HOST_READ_FLAGS | 
|  | #if defined(MAP_RESILIENT_MEDIA) | 
|  | #define MAP_EXTRA_HOST_READ_FLAGS MAP_RESILIENT_CODESIGN | MAP_RESILIENT_MEDIA | 
|  | #else | 
|  | #define MAP_EXTRA_HOST_READ_FLAGS MAP_RESILIENT_CODESIGN | 
|  | #endif | 
|  | #endif // #if defined(MAP_RESILIENT_CODESIGN) | 
|  | #endif // #if defined (__APPLE__) | 
|  |  | 
|  | #endif // #else #ifdef _WIN32 | 
|  | // C++ Includes | 
|  | #include <cerrno> | 
|  | #include <climits> | 
|  |  | 
|  | // Other libraries and framework includes | 
|  | #include "llvm/Support/MathExtras.h" | 
|  |  | 
|  | // Project includes | 
|  | #include "lldb/Core/DataBufferMemoryMap.h" | 
|  | #include "lldb/Core/Error.h" | 
|  | #include "lldb/Core/Log.h" | 
|  | #include "lldb/Host/File.h" | 
|  | #include "lldb/Host/FileSpec.h" | 
|  | #include "lldb/Host/HostInfo.h" | 
|  |  | 
|  | using namespace lldb; | 
|  | using namespace lldb_private; | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Default Constructor | 
|  | //---------------------------------------------------------------------- | 
|  | DataBufferMemoryMap::DataBufferMemoryMap() | 
|  | : m_mmap_addr(nullptr), m_mmap_size(0), m_data(nullptr), m_size(0) {} | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Virtual destructor since this class inherits from a pure virtual | 
|  | // base class. | 
|  | //---------------------------------------------------------------------- | 
|  | DataBufferMemoryMap::~DataBufferMemoryMap() { Clear(); } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Return a pointer to the bytes owned by this object, or nullptr if | 
|  | // the object contains no bytes. | 
|  | //---------------------------------------------------------------------- | 
|  | uint8_t *DataBufferMemoryMap::GetBytes() { return m_data; } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Return a const pointer to the bytes owned by this object, or nullptr | 
|  | // if the object contains no bytes. | 
|  | //---------------------------------------------------------------------- | 
|  | const uint8_t *DataBufferMemoryMap::GetBytes() const { return m_data; } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Return the number of bytes this object currently contains. | 
|  | //---------------------------------------------------------------------- | 
|  | uint64_t DataBufferMemoryMap::GetByteSize() const { return m_size; } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Reverts this object to an empty state by unmapping any memory | 
|  | // that is currently owned. | 
|  | //---------------------------------------------------------------------- | 
|  | void DataBufferMemoryMap::Clear() { | 
|  | if (m_mmap_addr != nullptr) { | 
|  | Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MMAP)); | 
|  | if (log) | 
|  | log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size " | 
|  | "= %" PRIu64 "", | 
|  | (void *)m_mmap_addr, (uint64_t)m_mmap_size); | 
|  | #ifdef _WIN32 | 
|  | UnmapViewOfFile(m_mmap_addr); | 
|  | #else | 
|  | ::munmap((void *)m_mmap_addr, m_mmap_size); | 
|  | #endif | 
|  | m_mmap_addr = nullptr; | 
|  | m_mmap_size = 0; | 
|  | m_data = nullptr; | 
|  | m_size = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Memory map "length" bytes from "file" starting "offset" | 
|  | // bytes into the file. If "length" is set to SIZE_MAX, then | 
|  | // map as many bytes as possible. | 
|  | // | 
|  | // Returns the number of bytes mapped starting from the requested | 
|  | // offset. | 
|  | //---------------------------------------------------------------------- | 
|  | size_t DataBufferMemoryMap::MemoryMapFromFileSpec(const FileSpec *filespec, | 
|  | lldb::offset_t offset, | 
|  | size_t length, | 
|  | bool writeable) { | 
|  | if (filespec != nullptr) { | 
|  | Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MMAP)); | 
|  | if (log) { | 
|  | log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(file=\"%s\", " | 
|  | "offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i", | 
|  | filespec->GetPath().c_str(), offset, (uint64_t)length, | 
|  | writeable); | 
|  | } | 
|  | char path[PATH_MAX]; | 
|  | if (filespec->GetPath(path, sizeof(path))) { | 
|  | uint32_t options = File::eOpenOptionRead; | 
|  | if (writeable) | 
|  | options |= File::eOpenOptionWrite; | 
|  |  | 
|  | File file; | 
|  | Error error(file.Open(path, options)); | 
|  | if (error.Success()) { | 
|  | const bool fd_is_file = true; | 
|  | return MemoryMapFromFileDescriptor(file.GetDescriptor(), offset, length, | 
|  | writeable, fd_is_file); | 
|  | } | 
|  | } | 
|  | } | 
|  | // We should only get here if there was an error | 
|  | Clear(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef _WIN32 | 
|  | static size_t win32memmapalignment = 0; | 
|  | void LoadWin32MemMapAlignment() { | 
|  | SYSTEM_INFO data; | 
|  | GetSystemInfo(&data); | 
|  | win32memmapalignment = data.dwAllocationGranularity; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // The file descriptor FD is assumed to already be opened as read only | 
|  | // and the STAT structure is assumed to a valid pointer and already | 
|  | // containing valid data from a call to stat(). | 
|  | // | 
|  | // Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into | 
|  | // the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes | 
|  | // as possible. | 
|  | // | 
|  | // RETURNS | 
|  | //  Number of bytes mapped starting from the requested offset. | 
|  | //---------------------------------------------------------------------- | 
|  | size_t DataBufferMemoryMap::MemoryMapFromFileDescriptor(int fd, | 
|  | lldb::offset_t offset, | 
|  | size_t length, | 
|  | bool writeable, | 
|  | bool fd_is_file) { | 
|  | Clear(); | 
|  | if (fd >= 0) { | 
|  | Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MMAP | | 
|  | LIBLLDB_LOG_VERBOSE)); | 
|  | if (log) { | 
|  | log->Printf("DataBufferMemoryMap::MemoryMapFromFileDescriptor(fd=%i, " | 
|  | "offset=0x%" PRIx64 ", length=0x%" PRIx64 | 
|  | ", writeable=%i, fd_is_file=%i)", | 
|  | fd, offset, (uint64_t)length, writeable, fd_is_file); | 
|  | } | 
|  | #ifdef _WIN32 | 
|  | HANDLE handle = (HANDLE)_get_osfhandle(fd); | 
|  | DWORD file_size_low, file_size_high; | 
|  | file_size_low = GetFileSize(handle, &file_size_high); | 
|  | const lldb::offset_t file_size = | 
|  | llvm::Make_64(file_size_high, file_size_low); | 
|  | const lldb::offset_t max_bytes_available = file_size - offset; | 
|  | const size_t max_bytes_mappable = | 
|  | (size_t)std::min<lldb::offset_t>(SIZE_MAX, max_bytes_available); | 
|  | if (length == SIZE_MAX || length > max_bytes_mappable) { | 
|  | // Cap the length if too much data was requested | 
|  | length = max_bytes_mappable; | 
|  | } | 
|  |  | 
|  | if (length > 0) { | 
|  | HANDLE fileMapping = CreateFileMapping( | 
|  | handle, nullptr, writeable ? PAGE_READWRITE : PAGE_READONLY, | 
|  | file_size_high, file_size_low, nullptr); | 
|  | if (fileMapping != nullptr) { | 
|  | if (win32memmapalignment == 0) | 
|  | LoadWin32MemMapAlignment(); | 
|  | lldb::offset_t realoffset = offset; | 
|  | lldb::offset_t delta = 0; | 
|  | if (realoffset % win32memmapalignment != 0) { | 
|  | realoffset = realoffset / win32memmapalignment * win32memmapalignment; | 
|  | delta = offset - realoffset; | 
|  | } | 
|  |  | 
|  | LPVOID data = MapViewOfFile(fileMapping, | 
|  | writeable ? FILE_MAP_WRITE : FILE_MAP_READ, | 
|  | 0, realoffset, length + delta); | 
|  | m_mmap_addr = (uint8_t *)data; | 
|  | if (!data) { | 
|  | Error error; | 
|  | error.SetErrorToErrno(); | 
|  | } else { | 
|  | m_data = m_mmap_addr + delta; | 
|  | m_size = length; | 
|  | } | 
|  | CloseHandle(fileMapping); | 
|  | } | 
|  | } | 
|  | #else | 
|  | struct stat stat; | 
|  | if (::fstat(fd, &stat) == 0) { | 
|  | if (S_ISREG(stat.st_mode) && | 
|  | (stat.st_size > static_cast<off_t>(offset))) { | 
|  | const size_t max_bytes_available = stat.st_size - offset; | 
|  | if (length == SIZE_MAX) { | 
|  | length = max_bytes_available; | 
|  | } else if (length > max_bytes_available) { | 
|  | // Cap the length if too much data was requested | 
|  | length = max_bytes_available; | 
|  | } | 
|  |  | 
|  | if (length > 0) { | 
|  | int prot = PROT_READ; | 
|  | int flags = MAP_PRIVATE; | 
|  | if (writeable) | 
|  | prot |= PROT_WRITE; | 
|  | else | 
|  | flags |= MAP_EXTRA_HOST_READ_FLAGS; | 
|  |  | 
|  | if (fd_is_file) | 
|  | flags |= MAP_FILE; | 
|  |  | 
|  | m_mmap_addr = | 
|  | (uint8_t *)::mmap(nullptr, length, prot, flags, fd, offset); | 
|  | Error error; | 
|  |  | 
|  | if (m_mmap_addr == (void *)-1) { | 
|  | error.SetErrorToErrno(); | 
|  | if (error.GetError() == EINVAL) { | 
|  | // We may still have a shot at memory mapping if we align things | 
|  | // correctly | 
|  | size_t page_offset = offset % HostInfo::GetPageSize(); | 
|  | if (page_offset != 0) { | 
|  | m_mmap_addr = | 
|  | (uint8_t *)::mmap(nullptr, length + page_offset, prot, | 
|  | flags, fd, offset - page_offset); | 
|  | if (m_mmap_addr == (void *)-1) { | 
|  | // Failed to map file | 
|  | m_mmap_addr = nullptr; | 
|  | } else if (m_mmap_addr != nullptr) { | 
|  | // We recovered and were able to memory map | 
|  | // after we aligned things to page boundaries | 
|  |  | 
|  | // Save the actual mmap'ed size | 
|  | m_mmap_size = length + page_offset; | 
|  | // Our data is at an offset into the mapped data | 
|  | m_data = m_mmap_addr + page_offset; | 
|  | // Our pretend size is the size that was requested | 
|  | m_size = length; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (error.GetError() == ENOMEM) { | 
|  | error.SetErrorStringWithFormat("could not allocate %" PRId64 | 
|  | " bytes of memory to mmap in file", | 
|  | (uint64_t)length); | 
|  | } | 
|  | } else { | 
|  | // We were able to map the requested data in one chunk | 
|  | // where our mmap and actual data are the same. | 
|  | m_mmap_size = length; | 
|  | m_data = m_mmap_addr; | 
|  | m_size = length; | 
|  | } | 
|  |  | 
|  | if (log) { | 
|  | log->Printf( | 
|  | "DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = " | 
|  | "%p, m_mmap_size = %" PRIu64 ", error = %s", | 
|  | (void *)m_mmap_addr, (uint64_t)m_mmap_size, error.AsCString()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  | } | 
|  | return GetByteSize(); | 
|  | } |