Home | History | Annotate | Download | only in Support
      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