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
page_offset(off_t 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
page_boundary(off_t pFileOffset)36 inline static off_t page_boundary(off_t pFileOffset)
37 { return (pFileOffset + (PageSize - 1)) & ~ (PageSize - 1); }
38
policy(off_t pOffset,size_t pLength)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 //===----------------------------------------------------------------------===//
Space()51 Space::Space()
52 : m_Data(NULL), m_StartOffset(0), m_Size(0),
53 m_RegionCount(0), m_Type(UNALLOCATED) {
54 }
55
Space(Space::Type pType,void * pMemBuffer,size_t pSize)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
~Space()62 Space::~Space()
63 {
64 // do nothing. m_Data is deleted by @ref releaseSpace
65 }
66
Create(void * pMemBuffer,size_t pSize)67 Space* Space::Create(void* pMemBuffer, size_t pSize)
68 {
69 Space* result = new Space(EXTERNAL, pMemBuffer, pSize);
70 return result;
71 }
72
Create(FileHandle & pHandler,size_t pStart,size_t pSize)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
Destroy(Space * & pSpace)144 void Space::Destroy(Space*& pSpace)
145 {
146 delete pSpace;
147 pSpace = NULL;
148 }
149
Release(Space * pSpace,FileHandle & pHandler)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
Sync(Space * pSpace,FileHandle & pHandler)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