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