MemoryArea.cpp revision 5460a1f25d9ddecb5c70667267d66d51af177a99
1//===- MemoryArea.cpp -----------------------------------------------------===//
2//
3//                     The MCLinker Project
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include <llvm/Support/ErrorHandling.h>
10#include <llvm/ADT/Twine.h>
11
12#include <mcld/Support/RegionFactory.h>
13#include <mcld/Support/MemoryArea.h>
14#include <mcld/Support/MemoryRegion.h>
15#include <mcld/Support/FileSystem.h>
16
17#include <cerrno>
18#include <fcntl.h>
19#include <sys/mman.h>
20#include <sys/stat.h>
21
22using namespace mcld;
23
24//===--------------------------------------------------------------------===//
25// MemoryArea
26MemoryArea::MemoryArea(RegionFactory& pRegionFactory)
27  : m_RegionFactory(pRegionFactory),
28    m_FileDescriptor(-1),
29    m_FileSize(0),
30    m_AccessFlags(ReadOnly),
31    m_State(BadBit) {
32}
33
34MemoryArea::~MemoryArea()
35{
36  // truncate the file to real size
37  if (isWritable())
38    truncate(m_FileSize);
39
40  unmap();
41}
42
43void MemoryArea::truncate(size_t pLength)
44{
45  if (!isWritable())
46    return;
47
48  if (-1 == ::ftruncate(m_FileDescriptor, static_cast<off_t>(pLength))) {
49    llvm::report_fatal_error(llvm::Twine("Cannot truncate `") +
50                             m_FilePath.native() +
51                             llvm::Twine("' to size: ") +
52                             llvm::Twine(pLength) +
53                             llvm::Twine(".\n"));
54  }
55}
56
57void MemoryArea::map(const sys::fs::Path& pPath, int pFlags)
58{
59  m_AccessFlags = pFlags;
60  m_FilePath = pPath;
61  m_FileDescriptor = ::open(m_FilePath.c_str(), m_AccessFlags);
62
63  if (-1 == m_FileDescriptor) {
64    m_State |= FailBit;
65  }
66  else {
67    struct stat st;
68    int stat_result = ::stat(m_FilePath.native().c_str(), &st);
69    if (0x0 == stat_result) {
70      m_FileSize = static_cast<size_t>(st.st_size);
71      m_State = GoodBit;
72    }
73    else {
74      m_FileSize = 0x0;
75      m_State |= FailBit;
76      m_State |= BadBit;
77    }
78  }
79}
80
81void MemoryArea::map(const sys::fs::Path& pPath, int pFlags, int pMode)
82{
83  m_AccessFlags = pFlags;
84  m_FilePath = pPath;
85  m_FileDescriptor = ::open(m_FilePath.c_str(), m_AccessFlags, pMode);
86
87  if (-1 == m_FileDescriptor) {
88    m_State |= FailBit;
89  }
90  else {
91    struct stat st;
92    int stat_result = ::stat(m_FilePath.native().c_str(), &st);
93    if (0x0 == stat_result) {
94      m_FileSize = static_cast<size_t>(st.st_size);
95      m_State = GoodBit;
96    }
97    else {
98      m_FileSize = 0x0;
99      m_State |= FailBit;
100      m_State |= BadBit;
101    }
102  }
103}
104
105void MemoryArea::unmap()
106{
107  if (isMapped()) {
108    if (-1 == ::close(m_FileDescriptor))
109      m_State |= FailBit;
110    else {
111      m_FileDescriptor = -1;
112      m_AccessFlags = ReadOnly;
113    }
114  }
115}
116
117bool MemoryArea::isMapped() const
118{
119  return (-1 != m_FileDescriptor);
120}
121
122bool MemoryArea::isGood() const
123{
124  return 0x0 == (m_State & (BadBit | FailBit));
125}
126
127bool MemoryArea::isBad() const
128{
129  return 0x0 != (m_State & BadBit);
130}
131
132bool MemoryArea::isFailed() const
133{
134  return 0x0 != (m_State & FailBit);
135}
136
137bool MemoryArea::isEOF() const
138{
139  return 0x0 != (m_State & EOFBit);
140}
141
142bool MemoryArea::isReadable() const
143{
144  return (((m_AccessFlags & AccessMask) == ReadOnly) ||
145         ((m_AccessFlags & AccessMask) == ReadWrite));
146}
147
148bool MemoryArea::isWritable() const
149{
150  return (((m_AccessFlags & AccessMask) == WriteOnly) ||
151         ((m_AccessFlags & AccessMask) == ReadWrite));
152}
153
154int MemoryArea::rdstate() const
155{
156  return m_State;
157}
158
159void MemoryArea::setState(MemoryArea::IOState pState)
160{
161  m_State |= pState;
162}
163
164void MemoryArea::clear(MemoryArea::IOState pState)
165{
166  m_State = pState;
167}
168
169// The layout of MemorySpace in the virtual memory space
170//
171// |  : page boundary
172// [,]: MemoryRegion
173// -  : fillment
174// =  : data
175//
176// |---[=|====|====|==]--|
177// ^   ^              ^  ^
178// |   |              |  |
179// | r_start      +r_len |
180// space.data      +space.size
181//
182// space.file_offset is the offset of the mapped file segment from the start of
183// the file. if the MemorySpace's type is ALLOCATED_ARRAY, the distances of
184// (space.data, r_start) and (r_len, space.size) are zero.
185//
186MemoryRegion* MemoryArea::request(size_t pOffset, size_t pLength)
187{
188  if (!isMapped() || !isGood())
189    return NULL;
190
191  if (0x0 == pLength)
192    return NULL;
193
194  if (!isWritable() && (pOffset + pLength) > m_FileSize)
195    return NULL;
196
197  if (isWritable() && (pOffset + pLength) > m_FileSize) {
198    // If the memory area is writable, user can expand the size of file by
199    // request a region larger than the file.
200    // MemoryArea should enlarge the file if the requested region is larger
201    // than the file.
202    m_FileSize = page_boundary(pOffset + pLength + 1);
203    truncate(m_FileSize);
204  }
205
206  Space* space = find(pOffset, pLength);
207  MemoryArea::Address r_start = 0;
208  if (NULL == space) {
209    // the space does not exist, create a new space.
210    space = new Space(this, pOffset, pLength);
211    m_SpaceList.push_back(space);
212    switch(space->type = policy(pOffset, pLength)) {
213      case Space::MMAPED: {
214        int mm_prot, mm_flag;
215        if (isWritable()) {
216          mm_prot = PROT_READ | PROT_WRITE;
217          mm_flag = MAP_FILE | MAP_SHARED;
218        }
219        else {
220          mm_prot = PROT_READ;
221          mm_flag = MAP_FILE | MAP_PRIVATE;
222        }
223
224        space->file_offset = page_offset(pOffset);
225
226        // The space's size may be larger than filesize.
227        space->size = page_boundary(pLength + pOffset + 1 - space->file_offset);
228        space->data = (Address) ::mmap(NULL,
229                                       space->size,
230                                       mm_prot, mm_flag,
231                                       m_FileDescriptor,
232                                       space->file_offset);
233
234        if (space->data == MAP_FAILED) {
235          llvm::report_fatal_error(llvm::Twine("cannot open memory map file :") +
236                                   m_FilePath.native() +
237                                   llvm::Twine(" (") +
238                                   sys::fs::detail::strerror(errno) +
239                                   llvm::Twine(").\n"));
240        }
241
242        r_start = space->data + (pOffset - space->file_offset);
243        break;
244      }
245      case Space::ALLOCATED_ARRAY: {
246        // space->offset and space->size are set in constructor. We only need
247        // to set up data.
248        space->data = new unsigned char[pLength];
249        r_start = space->data;
250        if ((m_AccessFlags & AccessMask) != WriteOnly) {
251          // Read data from the backend file.
252          if (!read(*space)) {
253            llvm::report_fatal_error(llvm::Twine("Failed to read data from ") +
254                                     m_FilePath.native() +
255                                     llvm::Twine(" (") +
256                                     sys::fs::detail::strerror(errno) +
257                                     llvm::Twine(") at offset ") +
258                                     llvm::Twine(pOffset) +
259                                     llvm::Twine(" lenght ") +
260                                     llvm::Twine(pLength) + llvm::Twine(".\n"));
261          }
262        }
263        break;
264      } // case
265      default: {
266        llvm::report_fatal_error("unhandled space type\n");
267      }
268    } // switch
269  }
270  else { // found
271    off_t distance = pOffset - space->file_offset;
272    r_start = space->data + distance;
273  }
274
275  // now, we have a legal space to hold the new MemoryRegion
276  return m_RegionFactory.produce(space, r_start, pLength);
277}
278
279// release - release a MemoryRegion
280void MemoryArea::release(MemoryRegion* pRegion)
281{
282  if (!isMapped() || !isGood())
283    return;
284
285  Space *space = pRegion->parent();
286  m_RegionFactory.destruct(pRegion);
287
288  if (0 == space->region_num) {
289    write(*space);
290    m_SpaceList.remove(*space);
291    release(space);
292  }
293}
294
295void MemoryArea::clean()
296{
297  m_RegionFactory.clear();
298
299  SpaceList::iterator sIter, sEnd = m_SpaceList.end();
300  for (sIter = m_SpaceList.begin(); sIter!=sEnd; ++sIter) {
301    write(*sIter);
302    release(sIter);
303  }
304  m_SpaceList.clear();
305}
306
307void MemoryArea::sync()
308{
309  SpaceList::iterator sIter, sEnd = m_SpaceList.end();
310  for (sIter = m_SpaceList.begin(); sIter!=sEnd; ++sIter) {
311    write(*sIter);
312  }
313}
314
315MemoryArea::Space* MemoryArea::find(size_t pOffset, size_t pLength)
316{
317  SpaceList::iterator sIter, sEnd = m_SpaceList.end();
318  for (sIter = m_SpaceList.begin(); sIter!=sEnd; ++sIter) {
319    if (sIter->file_offset <= pOffset &&
320       (pOffset+pLength) <= (sIter->file_offset+sIter->size) ) { // within
321      return sIter;
322    }
323  }
324  return NULL;
325}
326
327void MemoryArea::release(MemoryArea::Space* pSpace)
328{
329  switch (pSpace->type) {
330    case Space::ALLOCATED_ARRAY: {
331      delete [] pSpace->data;
332      break;
333    }
334    case Space::MMAPED: {
335      ::munmap(pSpace->data, pSpace->size);
336      break;
337    }
338    default:
339      break;
340  }
341}
342
343MemoryArea::Space::Type MemoryArea::policy(off_t pOffset, size_t pLength)
344{
345  const size_t threshold = (PageSize*3)/4; // 3/4 page size in Linux
346  if (pLength < threshold)
347    return Space::ALLOCATED_ARRAY;
348  else
349    return Space::MMAPED;
350}
351
352ssize_t MemoryArea::readToBuffer(sys::fs::detail::Address pBuf,
353                                 size_t pSize, size_t pOffset) {
354  assert(((m_AccessFlags & AccessMask) != WriteOnly) &&
355         "Write-only file cannot be read!");
356
357  ssize_t read_bytes = sys::fs::detail::pread(m_FileDescriptor, pBuf,
358                                              pSize, pOffset);
359  if (static_cast<size_t>(read_bytes) != pSize) {
360    // Some error occurred during pread().
361    if (read_bytes < 0) {
362      m_State |= FailBit;
363    }
364    else if (static_cast<size_t>(read_bytes) < pSize) {
365      m_State |= EOFBit;
366      if ((m_AccessFlags & AccessMask) != ReadWrite) {
367        // Files which is not read-write are not allowed read beyonds the EOF
368        // marker.
369        m_State |= BadBit;
370      }
371    }
372    else {
373      m_State |= BadBit;
374    }
375  }
376  return read_bytes;
377}
378
379bool MemoryArea::read(Space& pSpace) {
380  if (!isGood() || !isReadable())
381    return false;
382
383  if (pSpace.type == Space::ALLOCATED_ARRAY) {
384    readToBuffer(pSpace.data, pSpace.size, pSpace.file_offset);
385    return isGood();
386  }
387  else {
388    // Data associated with mmap()'ed space is already at the position the
389    // pSpace points to.
390    assert((pSpace.type == Space::MMAPED) && "Unknown type of Space!");
391    return true;
392  }
393}
394
395
396void MemoryArea::write(const Space& pSpace)
397{
398  if (!isMapped() || !isGood() || !isWritable())
399    return;
400
401  switch(pSpace.type) {
402    case Space::MMAPED: {
403      if(-1 == ::msync(pSpace.data, pSpace.size, MS_SYNC))
404        m_State |= FailBit;
405      return;
406    }
407    case Space::ALLOCATED_ARRAY: {
408      ssize_t write_bytes = sys::fs::detail::pwrite(m_FileDescriptor,
409                                                    pSpace.data,
410                                                    pSpace.size,
411                                                    pSpace.file_offset);
412      if (0 > write_bytes) {
413        m_State |= FailBit;
414        return;
415      }
416      if (0 == write_bytes && 0 != pSpace.size)
417        m_State |= BadBit;
418      if ( pSpace.size > static_cast<size_t>(write_bytes) )
419        m_State |= EOFBit;
420      return;
421    }
422    default:
423      return;
424  }
425}
426
427