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
22 using namespace mcld;
23
24 //===--------------------------------------------------------------------===//
25 // MemoryArea
MemoryArea(RegionFactory & pRegionFactory)26 MemoryArea::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
~MemoryArea()34 MemoryArea::~MemoryArea()
35 {
36 // truncate the file to real size
37 if (isWritable())
38 truncate(m_FileSize);
39
40 unmap();
41 }
42
truncate(size_t pLength)43 void 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
map(const sys::fs::Path & pPath,int pFlags)57 void 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
map(const sys::fs::Path & pPath,int pFlags,int pMode)81 void 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
unmap()105 void 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
isMapped() const117 bool MemoryArea::isMapped() const
118 {
119 return (-1 != m_FileDescriptor);
120 }
121
isGood() const122 bool MemoryArea::isGood() const
123 {
124 return 0x0 == (m_State & (BadBit | FailBit));
125 }
126
isBad() const127 bool MemoryArea::isBad() const
128 {
129 return 0x0 != (m_State & BadBit);
130 }
131
isFailed() const132 bool MemoryArea::isFailed() const
133 {
134 return 0x0 != (m_State & FailBit);
135 }
136
isEOF() const137 bool MemoryArea::isEOF() const
138 {
139 return 0x0 != (m_State & EOFBit);
140 }
141
isReadable() const142 bool MemoryArea::isReadable() const
143 {
144 return (((m_AccessFlags & AccessMask) == ReadOnly) ||
145 ((m_AccessFlags & AccessMask) == ReadWrite));
146 }
147
isWritable() const148 bool MemoryArea::isWritable() const
149 {
150 return (((m_AccessFlags & AccessMask) == WriteOnly) ||
151 ((m_AccessFlags & AccessMask) == ReadWrite));
152 }
153
rdstate() const154 int MemoryArea::rdstate() const
155 {
156 return m_State;
157 }
158
setState(MemoryArea::IOState pState)159 void MemoryArea::setState(MemoryArea::IOState pState)
160 {
161 m_State |= pState;
162 }
163
clear(MemoryArea::IOState pState)164 void 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 //
request(size_t pOffset,size_t pLength)186 MemoryRegion* 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
release(MemoryRegion * pRegion)280 void 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
clean()295 void 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
sync()307 void 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
find(size_t pOffset,size_t pLength)315 MemoryArea::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
release(MemoryArea::Space * pSpace)327 void 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
policy(off_t pOffset,size_t pLength)343 MemoryArea::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
readToBuffer(sys::fs::detail::Address pBuf,size_t pSize,size_t pOffset)352 ssize_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
read(Space & pSpace)379 bool 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
write(const Space & pSpace)396 void 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