Home | History | Annotate | Download | only in Support
      1 //===- Space.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 <mcld/Support/Space.h>
     10 #include <mcld/Support/FileHandle.h>
     11 #include <mcld/Support/MsgHandling.h>
     12 #include <mcld/Support/SystemUtils.h>
     13 #include <cstdlib>
     14 #include <unistd.h>
     15 
     16 using namespace mcld;
     17 
     18 //===----------------------------------------------------------------------===//
     19 // constant data
     20 //===----------------------------------------------------------------------===//
     21 static const int PageSize = mcld::sys::GetPageSize();
     22 
     23 //===----------------------------------------------------------------------===//
     24 // Non-member functions
     25 //===----------------------------------------------------------------------===//
     26 //
     27 // low address      A page             high address
     28 // |--------------------|------------------|
     29 // ^ page_offset        ^ pFileOffset      ^ page_boundary
     30 //
     31 // Given a file offset, return the page offset.
     32 // return the first page boundary \b before pFileOffset
     33 inline static off_t page_offset(off_t pFileOffset)
     34 { return pFileOffset & ~ (PageSize - 1); }
     35 
     36 // page_boundary - Given a file size, return the size to read integral pages.
     37 // return the first page boundary \b after pFileOffset
     38 inline static off_t page_boundary(off_t pFileOffset)
     39 { return (pFileOffset + (PageSize - 1)) & ~ (PageSize - 1); }
     40 
     41 inline static Space::Type policy(off_t pOffset, size_t pLength)
     42 {
     43 #if defined(MCLD_ON_WIN32)
     44   return Space::ALLOCATED_ARRAY;
     45 #endif
     46   const size_t threshold = (PageSize*3)/4; // 3/4 page size in Linux
     47   if (pLength < threshold)
     48     return Space::ALLOCATED_ARRAY;
     49   else
     50     return Space::MMAPED;
     51 }
     52 
     53 //===----------------------------------------------------------------------===//
     54 // Space
     55 //===----------------------------------------------------------------------===//
     56 Space::Space()
     57   : m_Data(NULL), m_StartOffset(0), m_Size(0),
     58     m_RegionCount(0), m_Type(UNALLOCATED) {
     59 }
     60 
     61 Space::Space(Space::Type pType, void* pMemBuffer, size_t pSize)
     62   : m_Data(static_cast<Address>(pMemBuffer)), m_StartOffset(0), m_Size(pSize),
     63     m_RegionCount(0), m_Type(pType)
     64 {
     65 }
     66 
     67 Space::~Space()
     68 {
     69   // do nothing. m_Data is deleted by @ref releaseSpace
     70 }
     71 
     72 Space* Space::Create(void* pMemBuffer, size_t pSize)
     73 {
     74   Space* result = new Space(EXTERNAL, pMemBuffer, pSize);
     75   return result;
     76 }
     77 
     78 Space* Space::Create(FileHandle& pHandler, size_t pStart, size_t pSize)
     79 {
     80   Type type;
     81   void* memory = NULL;
     82   Space* result = NULL;
     83   size_t start = 0, size = 0, total_offset = 0;
     84   switch(type = policy(pStart, pSize)) {
     85     case ALLOCATED_ARRAY: {
     86       // adjust total_offset, start and size
     87       total_offset = pStart + pSize;
     88       start = pStart;
     89       if (total_offset > pHandler.size()) {
     90         if (pHandler.isWritable()) {
     91           size = pSize;
     92           pHandler.truncate(total_offset);
     93         }
     94         else if (pHandler.size() > start) {
     95           // not writable -> shrink the size
     96           size = pHandler.size() - start;
     97         }
     98         else {
     99           // create a space out of a read-only file.
    100           fatal(diag::err_cannot_read_small_file) << pHandler.path()
    101                                                   << pHandler.size()
    102                                                   << start << size;
    103         }
    104       }
    105       else {
    106         // within the space.
    107         size = pSize;
    108       }
    109 
    110       // malloc
    111       memory = (void*)malloc(size);
    112       if (!pHandler.read(memory, start, size))
    113         error(diag::err_cannot_read_file) << pHandler.path() << start << size;
    114 
    115       break;
    116     }
    117     case MMAPED: {
    118       // adjust total_offset, start and size
    119       total_offset = page_boundary(pStart + pSize);
    120       start = page_offset(pStart);
    121       if (total_offset > pHandler.size()) {
    122         if (pHandler.isWritable()) {
    123           size = page_boundary((pStart - start) + pSize);
    124           pHandler.truncate(total_offset);
    125         }
    126         else if (pHandler.size() > start)
    127           size = pHandler.size() - start;
    128         else {
    129           // create a space out of a read-only file.
    130           fatal(diag::err_cannot_read_small_file) << pHandler.path()
    131                                                   << pHandler.size()
    132                                                   << start << size;
    133         }
    134       }
    135       else
    136         size = page_boundary((pStart - start) + pSize);
    137 
    138       // mmap
    139       if (!pHandler.mmap(memory, start, size))
    140         error(diag::err_cannot_mmap_file) << pHandler.path() << start << size;
    141 
    142       break;
    143     }
    144     default:
    145       break;
    146   } // end of switch
    147 
    148   result = new Space(type, memory, size);
    149   result->setStart(start);
    150   return result;
    151 }
    152 
    153 void Space::Destroy(Space*& pSpace)
    154 {
    155   delete pSpace;
    156   pSpace = NULL;
    157 }
    158 
    159 void Space::Release(Space* pSpace, FileHandle& pHandler)
    160 {
    161   if (NULL == pSpace)
    162     return;
    163 
    164   switch(pSpace->type()) {
    165     case ALLOCATED_ARRAY:
    166       free(pSpace->memory());
    167       break;
    168     case MMAPED:
    169       if (!pHandler.munmap(pSpace->memory(), pSpace->size()))
    170         error(diag::err_cannot_munmap_file) << pHandler.path();
    171       break;
    172     default: // external and unallocated memory buffers
    173       break;
    174   } // end of switch
    175 }
    176 
    177 void Space::Sync(Space* pSpace, FileHandle& pHandler)
    178 {
    179   if (NULL == pSpace || !pHandler.isWritable())
    180     return;
    181 
    182   switch(pSpace->type()) {
    183     case Space::ALLOCATED_ARRAY: {
    184       if (!pHandler.write(pSpace->memory(),
    185                           pSpace->start(),
    186                           pSpace->size())) {
    187         error(diag::err_cannot_write_file) << pHandler.path()
    188                                            << pSpace->start()
    189                                            << pSpace->size();
    190       }
    191       return;
    192     }
    193     case Space::MMAPED:
    194     default: {
    195       // system will eventually write bakc the memory after
    196       // calling ::munmap
    197       return;
    198     }
    199   } // end of switch
    200 }
    201 
    202