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 //----------------------------------------------------------------------
DataBufferMemoryMap()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 //----------------------------------------------------------------------
~DataBufferMemoryMap()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 *
GetBytes()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 *
GetBytes() const63 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
GetByteSize() const72 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
Clear()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
MemoryMapFromFileSpec(const FileSpec * filespec,lldb::offset_t offset,lldb::offset_t length,bool writeable)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
MemoryMapFromFileDescriptor(int fd,lldb::offset_t offset,lldb::offset_t length,bool writeable,bool fd_is_file)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