forked from OSchip/llvm-project
308 lines
12 KiB
C++
308 lines
12 KiB
C++
//===-- 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();
|
|
}
|