• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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