Home | History | Annotate | Download | only in Core
      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 #include <sys/mman.h>
     16 
     17 #include "lldb/Core/DataBufferMemoryMap.h"
     18 #include "lldb/Core/Error.h"
     19 #include "lldb/Host/File.h"
     20 #include "lldb/Host/FileSpec.h"
     21 #include "lldb/Host/Host.h"
     22 #include "lldb/Core/Log.h"
     23 #include "lldb/lldb-private-log.h"
     24 
     25 using namespace lldb;
     26 using namespace lldb_private;
     27 
     28 //----------------------------------------------------------------------
     29 // Default Constructor
     30 //----------------------------------------------------------------------
     31 DataBufferMemoryMap::DataBufferMemoryMap() :
     32     m_mmap_addr(NULL),
     33     m_mmap_size(0),
     34     m_data(NULL),
     35     m_size(0)
     36 {
     37 }
     38 
     39 //----------------------------------------------------------------------
     40 // Virtual destructor since this class inherits from a pure virtual
     41 // base class.
     42 //----------------------------------------------------------------------
     43 DataBufferMemoryMap::~DataBufferMemoryMap()
     44 {
     45     Clear();
     46 }
     47 
     48 //----------------------------------------------------------------------
     49 // Return a pointer to the bytes owned by this object, or NULL if
     50 // the object contains no bytes.
     51 //----------------------------------------------------------------------
     52 uint8_t *
     53 DataBufferMemoryMap::GetBytes()
     54 {
     55     return m_data;
     56 }
     57 
     58 //----------------------------------------------------------------------
     59 // Return a const pointer to the bytes owned by this object, or NULL
     60 // if the object contains no bytes.
     61 //----------------------------------------------------------------------
     62 const uint8_t *
     63 DataBufferMemoryMap::GetBytes() const
     64 {
     65     return m_data;
     66 }
     67 
     68 //----------------------------------------------------------------------
     69 // Return the number of bytes this object currently contains.
     70 //----------------------------------------------------------------------
     71 uint64_t
     72 DataBufferMemoryMap::GetByteSize() const
     73 {
     74     return m_size;
     75 }
     76 
     77 //----------------------------------------------------------------------
     78 // Reverts this object to an empty state by unmapping any memory
     79 // that is currently owned.
     80 //----------------------------------------------------------------------
     81 void
     82 DataBufferMemoryMap::Clear()
     83 {
     84     if (m_mmap_addr != NULL)
     85     {
     86         Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP));
     87         if (log)
     88             log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %zu", m_mmap_addr, m_mmap_size);
     89         ::munmap((void *)m_mmap_addr, m_mmap_size);
     90         m_mmap_addr = NULL;
     91         m_mmap_size = 0;
     92         m_data = NULL;
     93         m_size = 0;
     94     }
     95 }
     96 
     97 //----------------------------------------------------------------------
     98 // Memory map "length" bytes from "file" starting "offset"
     99 // bytes into the file. If "length" is set to SIZE_MAX, then
    100 // map as many bytes as possible.
    101 //
    102 // Returns the number of bytes mapped starting from the requested
    103 // offset.
    104 //----------------------------------------------------------------------
    105 size_t
    106 DataBufferMemoryMap::MemoryMapFromFileSpec (const FileSpec* filespec,
    107                                             lldb::offset_t offset,
    108                                             lldb::offset_t length,
    109                                             bool writeable)
    110 {
    111     if (filespec != NULL)
    112     {
    113         Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP));
    114         if (log)
    115         {
    116             log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(file=\"%s\", offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i",
    117                         filespec->GetPath().c_str(),
    118                         offset,
    119                         length,
    120                         writeable);
    121         }
    122         char path[PATH_MAX];
    123         if (filespec->GetPath(path, sizeof(path)))
    124         {
    125             uint32_t options = File::eOpenOptionRead;
    126             if (writeable)
    127                 options |= File::eOpenOptionWrite;
    128 
    129             File file;
    130             Error error (file.Open(path, options));
    131             if (error.Success())
    132             {
    133                 const bool fd_is_file = true;
    134                 return MemoryMapFromFileDescriptor (file.GetDescriptor(), offset, length, writeable, fd_is_file);
    135             }
    136         }
    137     }
    138     // We should only get here if there was an error
    139     Clear();
    140     return 0;
    141 }
    142 
    143 
    144 //----------------------------------------------------------------------
    145 // The file descriptor FD is assumed to already be opened as read only
    146 // and the STAT structure is assumed to a valid pointer and already
    147 // containing valid data from a call to stat().
    148 //
    149 // Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into
    150 // the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes
    151 // as possible.
    152 //
    153 // RETURNS
    154 //  Number of bytes mapped starting from the requested offset.
    155 //----------------------------------------------------------------------
    156 size_t
    157 DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd,
    158                                                   lldb::offset_t offset,
    159                                                   lldb::offset_t length,
    160                                                   bool writeable,
    161                                                   bool fd_is_file)
    162 {
    163     Clear();
    164     if (fd >= 0)
    165     {
    166         Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP|LIBLLDB_LOG_VERBOSE));
    167         if (log)
    168         {
    169             log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)",
    170                         fd,
    171                         offset,
    172                         length,
    173                         writeable,
    174                         fd_is_file);
    175         }
    176         struct stat stat;
    177         if (::fstat(fd, &stat) == 0)
    178         {
    179             if (S_ISREG(stat.st_mode) && (stat.st_size > offset))
    180             {
    181                 const size_t max_bytes_available = stat.st_size - offset;
    182                 if (length == SIZE_MAX)
    183                 {
    184                     length = max_bytes_available;
    185                 }
    186                 else if (length > max_bytes_available)
    187                 {
    188                     // Cap the length if too much data was requested
    189                     length = max_bytes_available;
    190                 }
    191 
    192                 if (length > 0)
    193                 {
    194                     int prot = PROT_READ;
    195                     if (writeable)
    196                         prot |= PROT_WRITE;
    197 
    198                     int flags = MAP_PRIVATE;
    199                     if (fd_is_file)
    200                         flags |= MAP_FILE;
    201 
    202                     m_mmap_addr = (uint8_t *)::mmap(NULL, length, prot, flags, fd, offset);
    203                     Error error;
    204 
    205                     if (m_mmap_addr == (void*)-1)
    206                     {
    207                         error.SetErrorToErrno ();
    208                         if (error.GetError() == EINVAL)
    209                         {
    210                             // We may still have a shot at memory mapping if we align things correctly
    211                             size_t page_offset = offset % Host::GetPageSize();
    212                             if (page_offset != 0)
    213                             {
    214                                 m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset);
    215                                 if (m_mmap_addr == (void*)-1)
    216                                 {
    217                                     // Failed to map file
    218                                     m_mmap_addr = NULL;
    219                                 }
    220                                 else if (m_mmap_addr != NULL)
    221                                 {
    222                                     // We recovered and were able to memory map
    223                                     // after we aligned things to page boundaries
    224 
    225                                     // Save the actual mmap'ed size
    226                                     m_mmap_size = length + page_offset;
    227                                     // Our data is at an offset into the the mapped data
    228                                     m_data = m_mmap_addr + page_offset;
    229                                     // Our pretend size is the size that was requestd
    230                                     m_size = length;
    231                                 }
    232                             }
    233                         }
    234                         if (error.GetError() == ENOMEM)
    235                         {
    236                            error.SetErrorStringWithFormat("could not allocate %" PRId64 " bytes of memory to mmap in file", (uint64_t) length);
    237                         }
    238                     }
    239                     else
    240                     {
    241                         // We were able to map the requested data in one chunk
    242                         // where our mmap and actual data are the same.
    243                         m_mmap_size = length;
    244                         m_data = m_mmap_addr;
    245                         m_size = length;
    246                     }
    247 
    248                     if (log)
    249                     {
    250                         log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %zu, error = %s",
    251                                     m_mmap_addr, m_mmap_size, error.AsCString());
    252                     }
    253                 }
    254             }
    255         }
    256     }
    257     return GetByteSize ();
    258 }
    259