1//===- Space.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 <mcld/Support/Space.h>
10#include <mcld/Support/FileHandle.h>
11#include <mcld/Support/MsgHandling.h>
12#include <cstdlib>
13#include <unistd.h>
14
15using namespace mcld;
16
17//===----------------------------------------------------------------------===//
18// constant data
19static const off_t PageSize = getpagesize();
20
21//===----------------------------------------------------------------------===//
22// Non-member functions
23//
24// low address      A page             high address
25// |--------------------|------------------|
26// ^ page_offset        ^ pFileOffset      ^ page_boundary
27
28// Given a file offset, return the page offset.
29// return the first page boundary \b before pFileOffset
30inline static off_t page_offset(off_t pFileOffset)
31{ return pFileOffset & ~ (PageSize - 1); }
32
33// page_boundary - Given a file size, return the size to read integral pages.
34// return the first page boundary \b after pFileOffset
35inline static off_t page_boundary(off_t pFileOffset)
36{ return (pFileOffset + (PageSize - 1)) & ~ (PageSize - 1); }
37
38inline static Space::Type policy(off_t pOffset, size_t pLength)
39{
40  const size_t threshold = (PageSize*3)/4; // 3/4 page size in Linux
41  if (pLength < threshold)
42    return Space::ALLOCATED_ARRAY;
43  else
44    return Space::MMAPED;
45}
46
47//===----------------------------------------------------------------------===//
48// Space
49Space::Space()
50  : m_Data(NULL), m_StartOffset(0), m_Size(0),
51    m_RegionCount(0), m_Type(UNALLOCATED) {
52}
53
54Space::Space(Space::Type pType, void* pMemBuffer, size_t pSize)
55  : m_Data(static_cast<Address>(pMemBuffer)), m_StartOffset(0), m_Size(pSize),
56    m_RegionCount(0), m_Type(pType)
57{
58}
59
60Space::~Space()
61{
62  // do nothing. m_Data is deleted by @ref releaseSpace
63}
64
65Space* Space::createSpace(FileHandle& pHandler,
66                          size_t pStart, size_t pSize)
67{
68  Type type;
69  void* memory;
70  Space* result = NULL;
71  size_t start, size = 0, total_offset;
72  switch(type = policy(pStart, pSize)) {
73    case ALLOCATED_ARRAY: {
74      // adjust total_offset, start and size
75      total_offset = pStart + pSize;
76      start = pStart;
77      if (total_offset > pHandler.size()) {
78        if (pHandler.isWritable()) {
79          size = pSize;
80          pHandler.truncate(total_offset);
81        }
82        else if (pHandler.size() > start)
83          size = pHandler.size() - start;
84        else {
85          // create a space out of a read-only file.
86          fatal(diag::err_cannot_read_small_file) << pHandler.path()
87                                                  << pHandler.size()
88                                                  << start << size;
89        }
90      }
91      else
92        size = pSize;
93
94      // malloc
95      memory = (void*)malloc(size);
96      if (!pHandler.read(memory, start, size))
97        error(diag::err_cannot_read_file) << pHandler.path() << start << size;
98
99      break;
100    }
101    case MMAPED: {
102      // adjust total_offset, start and size
103      total_offset = page_boundary(pStart + pSize);
104      start = page_offset(pStart);
105      if (total_offset > pHandler.size()) {
106        if (pHandler.isWritable()) {
107          size = page_boundary((pStart - start) + pSize);
108          pHandler.truncate(total_offset);
109        }
110        else if (pHandler.size() > start)
111          size = pHandler.size() - start;
112        else {
113          // create a space out of a read-only file.
114          fatal(diag::err_cannot_read_small_file) << pHandler.path()
115                                                  << pHandler.size()
116                                                  << start << size;
117        }
118      }
119      else
120        size = page_boundary((pStart - start) + pSize);
121
122      // mmap
123      if (!pHandler.mmap(memory, start, size))
124        error(diag::err_cannot_mmap_file) << pHandler.path() << start << size;
125
126      break;
127    }
128    default:
129      break;
130  } // end of switch
131
132  result = new Space(type, memory, size);
133  result->setStart(start);
134  return result;
135}
136
137void Space::releaseSpace(Space* pSpace, FileHandle& pHandler)
138{
139  if (NULL == pSpace)
140    return;
141
142  switch(pSpace->type()) {
143    case ALLOCATED_ARRAY:
144      free(pSpace->memory());
145      break;
146    case MMAPED:
147      if (!pHandler.munmap(pSpace->memory(), pSpace->size()))
148        error(diag::err_cannot_munmap_file) << pHandler.path();
149      break;
150    default: // external and unallocated memory buffers
151      break;
152  } // end of switch
153}
154
155void Space::syncSpace(Space* pSpace, FileHandle& pHandler)
156{
157  if (NULL == pSpace || !pHandler.isWritable())
158    return;
159
160  switch(pSpace->type()) {
161    case Space::ALLOCATED_ARRAY: {
162      if (!pHandler.write(pSpace->memory(),
163                          pSpace->start(),
164                          pSpace->size())) {
165        error(diag::err_cannot_write_file) << pHandler.path()
166                                           << pSpace->start()
167                                           << pSpace->size();
168      }
169      return;
170    }
171    case Space::MMAPED:
172    default: {
173      // system will eventually write bakc the memory after
174      // calling ::munmap
175      return;
176    }
177  } // end of switch
178}
179
180