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