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
page_offset(off_t 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
page_boundary(off_t pFileOffset)38 inline static off_t page_boundary(off_t pFileOffset)
39 { return (pFileOffset + (PageSize - 1)) & ~ (PageSize - 1); }
40
policy(off_t pOffset,size_t pLength)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 //===----------------------------------------------------------------------===//
Space()56 Space::Space()
57 : m_Data(NULL), m_StartOffset(0), m_Size(0),
58 m_RegionCount(0), m_Type(UNALLOCATED) {
59 }
60
Space(Space::Type pType,void * pMemBuffer,size_t pSize)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
~Space()67 Space::~Space()
68 {
69 // do nothing. m_Data is deleted by @ref releaseSpace
70 }
71
Create(void * pMemBuffer,size_t pSize)72 Space* Space::Create(void* pMemBuffer, size_t pSize)
73 {
74 Space* result = new Space(EXTERNAL, pMemBuffer, pSize);
75 return result;
76 }
77
Create(FileHandle & pHandler,size_t pStart,size_t pSize)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
Destroy(Space * & pSpace)153 void Space::Destroy(Space*& pSpace)
154 {
155 delete pSpace;
156 pSpace = NULL;
157 }
158
Release(Space * pSpace,FileHandle & pHandler)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
Sync(Space * pSpace,FileHandle & pHandler)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