//===- Space.cpp ----------------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/Support/Space.h>
#include <mcld/Support/FileHandle.h>
#include <mcld/Support/MsgHandling.h>
#include <cstdlib>
#include <unistd.h>
using namespace mcld;
//===----------------------------------------------------------------------===//
// constant data
static const off_t PageSize = getpagesize();
//===----------------------------------------------------------------------===//
// Non-member functions
//===----------------------------------------------------------------------===//
//
// low address A page high address
// |--------------------|------------------|
// ^ page_offset ^ pFileOffset ^ page_boundary
//
// Given a file offset, return the page offset.
// return the first page boundary \b before pFileOffset
inline static off_t page_offset(off_t pFileOffset)
{ return pFileOffset & ~ (PageSize - 1); }
// page_boundary - Given a file size, return the size to read integral pages.
// return the first page boundary \b after pFileOffset
inline static off_t page_boundary(off_t pFileOffset)
{ return (pFileOffset + (PageSize - 1)) & ~ (PageSize - 1); }
inline static Space::Type policy(off_t pOffset, size_t pLength)
{
const size_t threshold = (PageSize*3)/4; // 3/4 page size in Linux
if (pLength < threshold)
return Space::ALLOCATED_ARRAY;
else
return Space::MMAPED;
}
//===----------------------------------------------------------------------===//
// Space
//===----------------------------------------------------------------------===//
Space::Space()
: m_Data(NULL), m_StartOffset(0), m_Size(0),
m_RegionCount(0), m_Type(UNALLOCATED) {
}
Space::Space(Space::Type pType, void* pMemBuffer, size_t pSize)
: m_Data(static_cast<Address>(pMemBuffer)), m_StartOffset(0), m_Size(pSize),
m_RegionCount(0), m_Type(pType)
{
}
Space::~Space()
{
// do nothing. m_Data is deleted by @ref releaseSpace
}
Space* Space::Create(void* pMemBuffer, size_t pSize)
{
Space* result = new Space(EXTERNAL, pMemBuffer, pSize);
return result;
}
Space* Space::Create(FileHandle& pHandler, size_t pStart, size_t pSize)
{
Type type;
void* memory = NULL;
Space* result = NULL;
size_t start = 0, size = 0, total_offset;
switch(type = policy(pStart, pSize)) {
case ALLOCATED_ARRAY: {
// adjust total_offset, start and size
total_offset = pStart + pSize;
start = pStart;
if (total_offset > pHandler.size()) {
if (pHandler.isWritable()) {
size = pSize;
pHandler.truncate(total_offset);
}
else if (pHandler.size() > start)
size = pHandler.size() - start;
else {
// create a space out of a read-only file.
fatal(diag::err_cannot_read_small_file) << pHandler.path()
<< pHandler.size()
<< start << size;
}
}
else
size = pSize;
// malloc
memory = (void*)malloc(size);
if (!pHandler.read(memory, start, size))
error(diag::err_cannot_read_file) << pHandler.path() << start << size;
break;
}
case MMAPED: {
// adjust total_offset, start and size
total_offset = page_boundary(pStart + pSize);
start = page_offset(pStart);
if (total_offset > pHandler.size()) {
if (pHandler.isWritable()) {
size = page_boundary((pStart - start) + pSize);
pHandler.truncate(total_offset);
}
else if (pHandler.size() > start)
size = pHandler.size() - start;
else {
// create a space out of a read-only file.
fatal(diag::err_cannot_read_small_file) << pHandler.path()
<< pHandler.size()
<< start << size;
}
}
else
size = page_boundary((pStart - start) + pSize);
// mmap
if (!pHandler.mmap(memory, start, size))
error(diag::err_cannot_mmap_file) << pHandler.path() << start << size;
break;
}
default:
break;
} // end of switch
result = new Space(type, memory, size);
result->setStart(start);
return result;
}
void Space::Destroy(Space*& pSpace)
{
delete pSpace;
pSpace = NULL;
}
void Space::Release(Space* pSpace, FileHandle& pHandler)
{
if (NULL == pSpace)
return;
switch(pSpace->type()) {
case ALLOCATED_ARRAY:
free(pSpace->memory());
break;
case MMAPED:
if (!pHandler.munmap(pSpace->memory(), pSpace->size()))
error(diag::err_cannot_munmap_file) << pHandler.path();
break;
default: // external and unallocated memory buffers
break;
} // end of switch
}
void Space::Sync(Space* pSpace, FileHandle& pHandler)
{
if (NULL == pSpace || !pHandler.isWritable())
return;
switch(pSpace->type()) {
case Space::ALLOCATED_ARRAY: {
if (!pHandler.write(pSpace->memory(),
pSpace->start(),
pSpace->size())) {
error(diag::err_cannot_write_file) << pHandler.path()
<< pSpace->start()
<< pSpace->size();
}
return;
}
case Space::MMAPED:
default: {
// system will eventually write bakc the memory after
// calling ::munmap
return;
}
} // end of switch
}