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