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