1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "Dll.hpp"
16 
17 #include <time.h>
18 
19 #ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
20 #define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
21 #endif
22 
23 #ifndef IMAGE_DLLCHARACTERISTICS_NX_COMPAT
24 #define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100
25 #endif
26 
27 namespace sw
28 {
29 	#ifdef _M_AMD64
30 		const bool AMD64 = true;
31 	#else
32 		const bool AMD64 = false;
33 	#endif
34 
DLL(const char * name,const void * constants,int constSize)35 	DLL::DLL(const char *name, const void *constants, int constSize) : constants(constants), constSize(constSize)
36 	{
37 		dllName = new char[strlen(name) + 1];
38 		strcpy(dllName, name);
39 
40 		codeSize = 0;
41 	}
42 
~DLL()43 	DLL::~DLL()
44 	{
45 		delete[] dllName;
46 
47 		for(FunctionList::iterator i = functionList.begin(); i != functionList.end(); i++)
48 		{
49 			delete i->second;
50 		}
51 	}
52 
addFunction(const void * function,const void * entry,int size)53 	void DLL::addFunction(const void *function, const void *entry, int size)
54 	{
55 		functionOrder.push_back(function);
56 		functionList[function] = new Function(codeSize, function, entry, size);
57 
58 		codeSize += size;
59 	}
60 
addRelocation(const void * function,const void * address,bool ripRelative)61 	void DLL::addRelocation(const void *function, const void *address, bool ripRelative)
62 	{
63 		globalRelocations[function].push_back(Relocation((unsigned int)((unsigned char*)address - (unsigned char*)function), ripRelative));
64 	}
65 
emit()66 	void DLL::emit()
67 	{
68 		if(codeSize == 0)
69 		{
70 			return;
71 		}
72 
73 		for(GlobalRelocations::iterator i = globalRelocations.begin(); i != globalRelocations.end(); i++)
74 		{
75 			const unsigned char *function = (const unsigned char*)i->first;
76 			const std::vector<Relocation> &functionRelocations = i->second;
77 			unsigned int location = functionList[function]->location;
78 
79 			for(unsigned int j = 0; j < functionRelocations.size(); j++)
80 			{
81 				unsigned int address = location + functionRelocations[j].offset;
82 				unsigned int page = address / 0x1000;
83 				unsigned short reloc = address - page * 0x1000;
84 
85 				unsigned int relocType = AMD64 ? IMAGE_REL_BASED_DIR64 : IMAGE_REL_BASED_HIGHLOW;
86 				pageRelocations[page].push_back((relocType << 12) | reloc);
87 			}
88 		}
89 
90 		if(pageRelocations.empty())
91 		{
92 			pageRelocations[0];   // Initialize an emtpy list
93 		}
94 
95 		int relocSize = 0;
96 
97 		for(PageRelocations::iterator i = pageRelocations.begin(); i != pageRelocations.end(); i++)
98 		{
99 			if(i->second.size() % 2)   // Pad to align to DWORD
100 			{
101 				i->second.push_back(0);
102 			}
103 
104 			relocSize += (int)sizeof(IMAGE_BASE_RELOCATION) + (int)i->second.size() * (int)sizeof(unsigned short);
105 		}
106 
107 		unsigned long timeDateStamp = (unsigned long)time(0);
108 
109 		memset(&DOSheader, 0, sizeof(DOSheader));
110 		DOSheader.e_magic = IMAGE_DOS_SIGNATURE;   // "MZ"
111 		DOSheader.e_lfanew = sizeof(DOSheader);
112 
113 		int base = 0x10000000;
114 		int codePage = pageAlign(sizeof(DOSheader) + (AMD64 ? sizeof(COFFheader64) : sizeof(COFFheader32)));
115 		int exportsPage = codePage + pageAlign(codeSize);
116 		int exportsSize = (int)(sizeof(IMAGE_EXPORT_DIRECTORY) + functionList.size() * sizeof(void*) + (strlen(dllName) + 1));
117 		int relocPage = exportsPage + pageAlign(exportsSize);
118 		int constPage = relocPage + pageAlign(relocSize);
119 
120 		if(!AMD64)
121 		{
122 			memset(&COFFheader32, 0, sizeof(COFFheader32));
123 			COFFheader32.Signature = IMAGE_NT_SIGNATURE;   // "PE"
124 			COFFheader32.FileHeader.Machine = IMAGE_FILE_MACHINE_I386;
125 			COFFheader32.FileHeader.NumberOfSections = 4;
126 			COFFheader32.FileHeader.TimeDateStamp = timeDateStamp;
127 			COFFheader32.FileHeader.PointerToSymbolTable = 0;   // Deprecated COFF symbol table
128 			COFFheader32.FileHeader.NumberOfSymbols = 0;
129 			COFFheader32.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
130 			COFFheader32.FileHeader.Characteristics =	IMAGE_FILE_EXECUTABLE_IMAGE |
131 														IMAGE_FILE_32BIT_MACHINE |
132 														IMAGE_FILE_DLL;
133 
134 			COFFheader32.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC;
135 			COFFheader32.OptionalHeader.MajorLinkerVersion = 8;
136 			COFFheader32.OptionalHeader.MinorLinkerVersion = 0;
137 			COFFheader32.OptionalHeader.SizeOfCode = fileAlign(codeSize);
138 			COFFheader32.OptionalHeader.SizeOfInitializedData = fileAlign(exportsSize) + fileAlign(relocSize) + fileAlign(constSize);
139 			COFFheader32.OptionalHeader.SizeOfUninitializedData = 0;
140 			COFFheader32.OptionalHeader.AddressOfEntryPoint = 0;
141 			COFFheader32.OptionalHeader.BaseOfCode = codePage;
142 			COFFheader32.OptionalHeader.BaseOfData = exportsPage;
143 
144 			COFFheader32.OptionalHeader.ImageBase = base;
145 			COFFheader32.OptionalHeader.SectionAlignment = 0x1000;
146 			COFFheader32.OptionalHeader.FileAlignment = 0x200;
147 			COFFheader32.OptionalHeader.MajorOperatingSystemVersion = 4;
148 			COFFheader32.OptionalHeader.MinorOperatingSystemVersion = 0;
149 			COFFheader32.OptionalHeader.MajorImageVersion = 0;
150 			COFFheader32.OptionalHeader.MinorImageVersion = 0;
151 			COFFheader32.OptionalHeader.MajorSubsystemVersion = 4;
152 			COFFheader32.OptionalHeader.MinorSubsystemVersion = 0;
153 			COFFheader32.OptionalHeader.Win32VersionValue = 0;
154 			COFFheader32.OptionalHeader.SizeOfImage = constPage + pageAlign(constSize);
155 			COFFheader32.OptionalHeader.SizeOfHeaders = fileAlign(sizeof(DOSheader) + sizeof(COFFheader32));
156 			COFFheader32.OptionalHeader.CheckSum = 0;
157 			COFFheader32.OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
158 			COFFheader32.OptionalHeader.DllCharacteristics =	IMAGE_DLLCHARACTERISTICS_NO_SEH |
159 															IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE |   // Base address randomization
160 															IMAGE_DLLCHARACTERISTICS_NX_COMPAT;       // Data Execution Prevention compatible
161 			COFFheader32.OptionalHeader.SizeOfStackReserve = 1024 * 1024;
162 			COFFheader32.OptionalHeader.SizeOfStackCommit = 4 * 1024;
163 			COFFheader32.OptionalHeader.SizeOfHeapReserve = 1024 * 1024;
164 			COFFheader32.OptionalHeader.SizeOfHeapCommit = 4 * 1024;
165 			COFFheader32.OptionalHeader.LoaderFlags = 0;
166 			COFFheader32.OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES;
167 
168 			COFFheader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = exportsPage;
169 			COFFheader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = exportsSize;
170 
171 			COFFheader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = relocPage;
172 			COFFheader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = relocSize;
173 		}
174 		else
175 		{
176 			memset(&COFFheader64, 0, sizeof(COFFheader64));
177 			COFFheader64.Signature = IMAGE_NT_SIGNATURE;   // "PE"
178 			COFFheader64.FileHeader.Machine = IMAGE_FILE_MACHINE_AMD64;
179 			COFFheader64.FileHeader.NumberOfSections = 4;
180 			COFFheader64.FileHeader.TimeDateStamp = timeDateStamp;
181 			COFFheader64.FileHeader.PointerToSymbolTable = 0;   // Deprecated COFF symbol table
182 			COFFheader64.FileHeader.NumberOfSymbols = 0;
183 			COFFheader64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
184 			COFFheader64.FileHeader.Characteristics =	IMAGE_FILE_EXECUTABLE_IMAGE |
185 														IMAGE_FILE_LARGE_ADDRESS_AWARE |
186 														IMAGE_FILE_DLL;
187 
188 			COFFheader64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
189 			COFFheader64.OptionalHeader.MajorLinkerVersion = 8;
190 			COFFheader64.OptionalHeader.MinorLinkerVersion = 0;
191 			COFFheader64.OptionalHeader.SizeOfCode = fileAlign(codeSize);
192 			COFFheader64.OptionalHeader.SizeOfInitializedData = fileAlign(exportsSize) + fileAlign(relocSize) + fileAlign(constSize);
193 			COFFheader64.OptionalHeader.SizeOfUninitializedData = 0;
194 			COFFheader64.OptionalHeader.AddressOfEntryPoint = 0;
195 			COFFheader64.OptionalHeader.BaseOfCode = codePage;
196 
197 			COFFheader64.OptionalHeader.ImageBase = base;
198 			COFFheader64.OptionalHeader.SectionAlignment = 0x1000;
199 			COFFheader64.OptionalHeader.FileAlignment = 0x200;
200 			COFFheader64.OptionalHeader.MajorOperatingSystemVersion = 4;
201 			COFFheader64.OptionalHeader.MinorOperatingSystemVersion = 0;
202 			COFFheader64.OptionalHeader.MajorImageVersion = 0;
203 			COFFheader64.OptionalHeader.MinorImageVersion = 0;
204 			COFFheader64.OptionalHeader.MajorSubsystemVersion = 4;
205 			COFFheader64.OptionalHeader.MinorSubsystemVersion = 0;
206 			COFFheader64.OptionalHeader.Win32VersionValue = 0;
207 			COFFheader64.OptionalHeader.SizeOfImage = constPage + pageAlign(constSize);
208 			COFFheader64.OptionalHeader.SizeOfHeaders = fileAlign(sizeof(DOSheader) + sizeof(COFFheader64));
209 			COFFheader64.OptionalHeader.CheckSum = 0;
210 			COFFheader64.OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
211 			COFFheader64.OptionalHeader.DllCharacteristics =	IMAGE_DLLCHARACTERISTICS_NO_SEH |
212 															IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE |   // Base address randomization
213 															IMAGE_DLLCHARACTERISTICS_NX_COMPAT;       // Data Execution Prevention compatible
214 			COFFheader64.OptionalHeader.SizeOfStackReserve = 1024 * 1024;
215 			COFFheader64.OptionalHeader.SizeOfStackCommit = 4 * 1024;
216 			COFFheader64.OptionalHeader.SizeOfHeapReserve = 1024 * 1024;
217 			COFFheader64.OptionalHeader.SizeOfHeapCommit = 4 * 1024;
218 			COFFheader64.OptionalHeader.LoaderFlags = 0;
219 			COFFheader64.OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES;
220 
221 			COFFheader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = exportsPage;
222 			COFFheader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = exportsSize;
223 
224 			COFFheader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = relocPage;
225 			COFFheader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = relocSize;
226 		}
227 
228 		memset(&textSection, 0, sizeof(textSection));
229 		strcpy((char*)&textSection.Name, ".text");
230 		textSection.Misc.VirtualSize = pageAlign(codeSize);
231 		textSection.VirtualAddress = codePage;
232 		textSection.SizeOfRawData = fileAlign(codeSize);
233 		textSection.PointerToRawData = fileAlign(sizeof(DOSheader) + (AMD64 ? sizeof(COFFheader64) : sizeof(COFFheader32)));
234 		textSection.PointerToRelocations = 0;
235 		textSection.PointerToLinenumbers = 0;
236 		textSection.NumberOfRelocations = 0;
237 		textSection.NumberOfLinenumbers = 0;
238 		textSection.Characteristics = IMAGE_SCN_CNT_CODE |
239 		                              IMAGE_SCN_MEM_EXECUTE |
240 		                              IMAGE_SCN_MEM_READ;
241 
242 		memset(&exportsSection, 0, sizeof(exportsSection));
243 		strcpy((char*)&exportsSection.Name, ".edata");
244 		exportsSection.Misc.VirtualSize = pageAlign(exportsSize);
245 		exportsSection.VirtualAddress = exportsPage;
246 		exportsSection.SizeOfRawData = fileAlign(exportsSize);
247 		exportsSection.PointerToRawData = textSection.PointerToRawData + fileAlign(codeSize);
248 		exportsSection.PointerToRelocations = 0;
249 		exportsSection.PointerToLinenumbers = 0;
250 		exportsSection.NumberOfRelocations = 0;
251 		exportsSection.NumberOfLinenumbers = 0;
252 		exportsSection.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA |
253 		                                 IMAGE_SCN_MEM_READ;
254 
255 		memset(&relocSection, 0, sizeof(relocSection));
256 		strcpy((char*)&relocSection.Name, ".reloc");
257 		relocSection.Misc.VirtualSize = pageAlign(relocSize);
258 		relocSection.VirtualAddress = relocPage;
259 		relocSection.SizeOfRawData = fileAlign(relocSize);
260 		relocSection.PointerToRawData = exportsSection.PointerToRawData + fileAlign(exportsSize);
261 		relocSection.PointerToRelocations = 0;
262 		relocSection.PointerToLinenumbers = 0;
263 		relocSection.NumberOfRelocations = 0;
264 		relocSection.NumberOfLinenumbers = 0;
265 		relocSection.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA |
266 		                               IMAGE_SCN_MEM_DISCARDABLE |
267 		                               IMAGE_SCN_MEM_READ;
268 
269 		memset(&constSection, 0, sizeof(constSection));
270 		strcpy((char*)&constSection.Name, ".rdata");
271 		constSection.Misc.VirtualSize = pageAlign(constSize);
272 		constSection.VirtualAddress = constPage;
273 		constSection.SizeOfRawData = fileAlign(constSize);
274 		constSection.PointerToRawData = relocSection.PointerToRawData + fileAlign(relocSize);
275 		constSection.PointerToRelocations = 0;
276 		constSection.PointerToLinenumbers = 0;
277 		constSection.NumberOfRelocations = 0;
278 		constSection.NumberOfLinenumbers = 0;
279 		constSection.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA |
280 		                               IMAGE_SCN_MEM_READ;
281 
282 		memset(&exportDirectory, 0, sizeof(exportDirectory));
283 		exportDirectory.Characteristics = 0;
284 		exportDirectory.TimeDateStamp = timeDateStamp;
285 		exportDirectory.MajorVersion = 0;
286 		exportDirectory.MinorVersion = 0;
287 		exportDirectory.Name = (unsigned long)(exportsPage + sizeof(IMAGE_EXPORT_DIRECTORY) + functionList.size() * sizeof(void*));
288 		exportDirectory.Base = 1;
289 		exportDirectory.NumberOfFunctions = (unsigned long)functionList.size();
290 		exportDirectory.NumberOfNames = 0;
291 		exportDirectory.AddressOfFunctions = exportsPage + sizeof(IMAGE_EXPORT_DIRECTORY);
292 		exportDirectory.AddressOfNames = 0;
293 		exportDirectory.AddressOfNameOrdinals = 0;
294 
295 		FILE *file = fopen(dllName, "wb");
296 
297 		if(file)
298 		{
299 			fwrite(&DOSheader, 1, sizeof(DOSheader), file);
300 
301 			if(AMD64)
302 			{
303 				fwrite(&COFFheader64, 1, sizeof(COFFheader64), file);
304 			}
305 			else
306 			{
307 				fwrite(&COFFheader32, 1, sizeof(COFFheader32), file);
308 			}
309 
310 			fwrite(&textSection, 1, sizeof(textSection), file);
311 			fwrite(&exportsSection, 1, sizeof(textSection), file);
312 			fwrite(&relocSection, 1, sizeof(relocSection), file);
313 			fwrite(&constSection, 1, sizeof(constSection), file);
314 
315 			for(FunctionList::iterator i = functionList.begin(); i != functionList.end(); i++)
316 			{
317 				const void *function = i->first;
318 				unsigned int location = i->second->location;
319 				const std::vector<Relocation> &functionRelocations = globalRelocations[function];
320 
321 				for(unsigned int j = 0; j < functionRelocations.size(); j++)
322 				{
323 					unsigned int *address = (unsigned int*)((unsigned char*)i->second->buffer + functionRelocations[j].offset);
324 
325 					if(functionRelocations[j].ripRelative)
326 					{
327 						*address = base + codePage + location + (*address - (unsigned int)(size_t)function);
328 					}
329 					else
330 					{
331 						*address = base + constPage + (*address - (unsigned int)(size_t)constants);
332 					}
333 				}
334 
335 				fseek(file, textSection.PointerToRawData + location, SEEK_SET);
336 				fwrite(i->second->buffer, 1, i->second->size, file);
337 			}
338 
339 			fseek(file, exportsSection.PointerToRawData, SEEK_SET);
340 			fwrite(&exportDirectory, 1, sizeof(exportDirectory), file);
341 
342 			for(unsigned int i = 0; i < functionOrder.size(); i++)
343 			{
344 				const void *buffer = functionOrder[i];
345 				Function *function = functionList[buffer];
346 
347 				unsigned int functionAddress = codePage + function->location;
348 				unsigned int functionEntry = functionAddress + (int)((size_t)function->entry - (size_t)buffer);
349 				fwrite(&functionEntry, 1, sizeof(functionEntry), file);
350 			}
351 
352 			fwrite(dllName, 1, strlen(dllName) + 1, file);
353 
354 			fseek(file, relocSection.PointerToRawData, SEEK_SET);
355 
356 			for(PageRelocations::iterator i = pageRelocations.begin(); i != pageRelocations.end(); i++)
357 			{
358 				IMAGE_BASE_RELOCATION relocationBlock;
359 
360 				relocationBlock.VirtualAddress = codePage + i->first * 0x1000;
361 				relocationBlock.SizeOfBlock = (unsigned long)(sizeof(IMAGE_BASE_RELOCATION) + i->second.size() * sizeof(unsigned short));
362 
363 				fwrite(&relocationBlock, 1, sizeof(IMAGE_BASE_RELOCATION), file);
364 
365 				if(i->second.size() > 0)
366 				{
367 					fwrite(&i->second[0], 1, i->second.size() * sizeof(unsigned short), file);
368 				}
369 			}
370 
371 			fseek(file, constSection.PointerToRawData, SEEK_SET);
372 			fwrite(constants, 1, constSize, file);
373 
374 			char *padding = new char[fileAlign(constSize) - constSize];
375 			fwrite(padding, 1, fileAlign(constSize) - constSize, file);
376 			delete[] padding;
377 
378 			fclose(file);
379 		}
380 	}
381 }
382