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
25using namespace lldb;
26using namespace lldb_private;
27
28//----------------------------------------------------------------------
29// Default Constructor
30//----------------------------------------------------------------------
31DataBufferMemoryMap::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//----------------------------------------------------------------------
43DataBufferMemoryMap::~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//----------------------------------------------------------------------
52uint8_t *
53DataBufferMemoryMap::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//----------------------------------------------------------------------
62const uint8_t *
63DataBufferMemoryMap::GetBytes() const
64{
65    return m_data;
66}
67
68//----------------------------------------------------------------------
69// Return the number of bytes this object currently contains.
70//----------------------------------------------------------------------
71uint64_t
72DataBufferMemoryMap::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//----------------------------------------------------------------------
81void
82DataBufferMemoryMap::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//----------------------------------------------------------------------
105size_t
106DataBufferMemoryMap::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//----------------------------------------------------------------------
156size_t
157DataBufferMemoryMap::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