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