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 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 34 MemoryArea::~MemoryArea() 35 { 36 // truncate the file to real size 37 if (isWritable()) 38 truncate(m_FileSize); 39 40 unmap(); 41 } 42 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 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 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 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 117 bool MemoryArea::isMapped() const 118 { 119 return (-1 != m_FileDescriptor); 120 } 121 122 bool MemoryArea::isGood() const 123 { 124 return 0x0 == (m_State & (BadBit | FailBit)); 125 } 126 127 bool MemoryArea::isBad() const 128 { 129 return 0x0 != (m_State & BadBit); 130 } 131 132 bool MemoryArea::isFailed() const 133 { 134 return 0x0 != (m_State & FailBit); 135 } 136 137 bool MemoryArea::isEOF() const 138 { 139 return 0x0 != (m_State & EOFBit); 140 } 141 142 bool MemoryArea::isReadable() const 143 { 144 return (((m_AccessFlags & AccessMask) == ReadOnly) || 145 ((m_AccessFlags & AccessMask) == ReadWrite)); 146 } 147 148 bool MemoryArea::isWritable() const 149 { 150 return (((m_AccessFlags & AccessMask) == WriteOnly) || 151 ((m_AccessFlags & AccessMask) == ReadWrite)); 152 } 153 154 int MemoryArea::rdstate() const 155 { 156 return m_State; 157 } 158 159 void MemoryArea::setState(MemoryArea::IOState pState) 160 { 161 m_State |= pState; 162 } 163 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 // 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 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 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 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 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 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 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 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 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 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