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