//===-- DataBufferMemoryMap.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include #include #include "lldb/Core/DataBufferMemoryMap.h" #include "lldb/Core/Error.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Core/Log.h" #include "lldb/lldb-private-log.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // Default Constructor //---------------------------------------------------------------------- DataBufferMemoryMap::DataBufferMemoryMap() : m_mmap_addr(NULL), m_mmap_size(0), m_data(NULL), 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 NULL 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 NULL // 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 != NULL) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); if (log) log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %zu", m_mmap_addr, m_mmap_size); ::munmap((void *)m_mmap_addr, m_mmap_size); m_mmap_addr = NULL; m_mmap_size = 0; m_data = NULL; 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, lldb::offset_t length, bool writeable) { if (filespec != NULL) { 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, 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; } //---------------------------------------------------------------------- // 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, lldb::offset_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::MemoryMapFromFileSpec(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)", fd, offset, length, writeable, fd_is_file); } struct stat stat; if (::fstat(fd, &stat) == 0) { if (S_ISREG(stat.st_mode) && (stat.st_size > 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; if (writeable) prot |= PROT_WRITE; int flags = MAP_PRIVATE; if (fd_is_file) flags |= MAP_FILE; m_mmap_addr = (uint8_t *)::mmap(NULL, 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 % Host::GetPageSize(); if (page_offset != 0) { m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset); if (m_mmap_addr == (void*)-1) { // Failed to map file m_mmap_addr = NULL; } else if (m_mmap_addr != NULL) { // 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 the mapped data m_data = m_mmap_addr + page_offset; // Our pretend size is the size that was requestd 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 = %zu, error = %s", m_mmap_addr, m_mmap_size, error.AsCString()); } } } } } return GetByteSize (); }