| // SwiftShader Software Renderer | |
| // | |
| // Copyright(c) 2005-2011 TransGaming Inc. | |
| // | |
| // All rights reserved. No part of this software may be copied, distributed, transmitted, | |
| // transcribed, stored in a retrieval system, translated into any human or computer | |
| // language by any means, or disclosed to third parties without the explicit written | |
| // agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express | |
| // or implied, including but not limited to any patent rights, are granted to you. | |
| // | |
| #include "Dll.hpp" | |
| #include <time.h> | |
| #ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | |
| #define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 | |
| #endif | |
| #ifndef IMAGE_DLLCHARACTERISTICS_NX_COMPAT | |
| #define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 | |
| #endif | |
| namespace sw | |
| { | |
| #ifdef _M_AMD64 | |
| const bool AMD64 = true; | |
| #else | |
| const bool AMD64 = false; | |
| #endif | |
| DLL::DLL(const char *name, const void *constants, int constSize) : constants(constants), constSize(constSize) | |
| { | |
| dllName = new char[strlen(name) + 1]; | |
| strcpy(dllName, name); | |
| codeSize = 0; | |
| } | |
| DLL::~DLL() | |
| { | |
| delete[] dllName; | |
| for(FunctionList::iterator i = functionList.begin(); i != functionList.end(); i++) | |
| { | |
| delete i->second; | |
| } | |
| } | |
| void DLL::addFunction(const void *function, const void *entry, int size) | |
| { | |
| functionOrder.push_back(function); | |
| functionList[function] = new Function(codeSize, function, entry, size); | |
| codeSize += size; | |
| } | |
| void DLL::addRelocation(const void *function, const void *address, bool ripRelative) | |
| { | |
| globalRelocations[function].push_back(Relocation((unsigned int)((unsigned char*)address - (unsigned char*)function), ripRelative)); | |
| } | |
| void DLL::emit() | |
| { | |
| if(codeSize == 0) | |
| { | |
| return; | |
| } | |
| for(GlobalRelocations::iterator i = globalRelocations.begin(); i != globalRelocations.end(); i++) | |
| { | |
| const unsigned char *function = (const unsigned char*)i->first; | |
| const std::vector<Relocation> &functionRelocations = i->second; | |
| unsigned int location = functionList[function]->location; | |
| for(unsigned int j = 0; j < functionRelocations.size(); j++) | |
| { | |
| unsigned int address = location + functionRelocations[j].offset; | |
| unsigned int page = address / 0x1000; | |
| unsigned short reloc = address - page * 0x1000; | |
| unsigned int relocType = AMD64 ? IMAGE_REL_BASED_DIR64 : IMAGE_REL_BASED_HIGHLOW; | |
| pageRelocations[page].push_back((relocType << 12) | reloc); | |
| } | |
| } | |
| if(pageRelocations.empty()) | |
| { | |
| pageRelocations[0]; // Initialize an emtpy list | |
| } | |
| int relocSize = 0; | |
| for(PageRelocations::iterator i = pageRelocations.begin(); i != pageRelocations.end(); i++) | |
| { | |
| if(i->second.size() % 2) // Pad to align to DWORD | |
| { | |
| i->second.push_back(0); | |
| } | |
| relocSize += (int)sizeof(IMAGE_BASE_RELOCATION) + (int)i->second.size() * (int)sizeof(unsigned short); | |
| } | |
| unsigned long timeDateStamp = (unsigned long)time(0); | |
| memset(&DOSheader, 0, sizeof(DOSheader)); | |
| DOSheader.e_magic = IMAGE_DOS_SIGNATURE; // "MZ" | |
| DOSheader.e_lfanew = sizeof(DOSheader); | |
| int base = 0x10000000; | |
| int codePage = pageAlign(sizeof(DOSheader) + AMD64 ? sizeof(COFFheader64) : sizeof(COFFheader32)); | |
| int exportsPage = codePage + pageAlign(codeSize); | |
| int exportsSize = (int)(sizeof(IMAGE_EXPORT_DIRECTORY) + functionList.size() * sizeof(void*) + (strlen(dllName) + 1)); | |
| int relocPage = exportsPage + pageAlign(exportsSize); | |
| int constPage = relocPage + pageAlign(relocSize); | |
| if(!AMD64) | |
| { | |
| memset(&COFFheader32, 0, sizeof(COFFheader32)); | |
| COFFheader32.Signature = IMAGE_NT_SIGNATURE; // "PE" | |
| COFFheader32.FileHeader.Machine = IMAGE_FILE_MACHINE_I386; | |
| COFFheader32.FileHeader.NumberOfSections = 4; | |
| COFFheader32.FileHeader.TimeDateStamp = timeDateStamp; | |
| COFFheader32.FileHeader.PointerToSymbolTable = 0; // Deprecated COFF symbol table | |
| COFFheader32.FileHeader.NumberOfSymbols = 0; | |
| COFFheader32.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32); | |
| COFFheader32.FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | | |
| IMAGE_FILE_32BIT_MACHINE | | |
| IMAGE_FILE_DLL; | |
| COFFheader32.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC; | |
| COFFheader32.OptionalHeader.MajorLinkerVersion = 8; | |
| COFFheader32.OptionalHeader.MinorLinkerVersion = 0; | |
| COFFheader32.OptionalHeader.SizeOfCode = fileAlign(codeSize); | |
| COFFheader32.OptionalHeader.SizeOfInitializedData = fileAlign(exportsSize) + fileAlign(relocSize) + fileAlign(constSize); | |
| COFFheader32.OptionalHeader.SizeOfUninitializedData = 0; | |
| COFFheader32.OptionalHeader.AddressOfEntryPoint = 0; | |
| COFFheader32.OptionalHeader.BaseOfCode = codePage; | |
| COFFheader32.OptionalHeader.BaseOfData = exportsPage; | |
| COFFheader32.OptionalHeader.ImageBase = base; | |
| COFFheader32.OptionalHeader.SectionAlignment = 0x1000; | |
| COFFheader32.OptionalHeader.FileAlignment = 0x200; | |
| COFFheader32.OptionalHeader.MajorOperatingSystemVersion = 4; | |
| COFFheader32.OptionalHeader.MinorOperatingSystemVersion = 0; | |
| COFFheader32.OptionalHeader.MajorImageVersion = 0; | |
| COFFheader32.OptionalHeader.MinorImageVersion = 0; | |
| COFFheader32.OptionalHeader.MajorSubsystemVersion = 4; | |
| COFFheader32.OptionalHeader.MinorSubsystemVersion = 0; | |
| COFFheader32.OptionalHeader.Win32VersionValue = 0; | |
| COFFheader32.OptionalHeader.SizeOfImage = constPage + pageAlign(constSize); | |
| COFFheader32.OptionalHeader.SizeOfHeaders = fileAlign(sizeof(DOSheader) + sizeof(COFFheader32)); | |
| COFFheader32.OptionalHeader.CheckSum = 0; | |
| COFFheader32.OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; | |
| COFFheader32.OptionalHeader.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_NO_SEH | | |
| IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | // Base address randomization | |
| IMAGE_DLLCHARACTERISTICS_NX_COMPAT; // Data Execution Prevention compatible | |
| COFFheader32.OptionalHeader.SizeOfStackReserve = 1024 * 1024; | |
| COFFheader32.OptionalHeader.SizeOfStackCommit = 4 * 1024; | |
| COFFheader32.OptionalHeader.SizeOfHeapReserve = 1024 * 1024; | |
| COFFheader32.OptionalHeader.SizeOfHeapCommit = 4 * 1024; | |
| COFFheader32.OptionalHeader.LoaderFlags = 0; | |
| COFFheader32.OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; | |
| COFFheader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = exportsPage; | |
| COFFheader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = exportsSize; | |
| COFFheader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = relocPage; | |
| COFFheader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = relocSize; | |
| } | |
| else | |
| { | |
| memset(&COFFheader64, 0, sizeof(COFFheader64)); | |
| COFFheader64.Signature = IMAGE_NT_SIGNATURE; // "PE" | |
| COFFheader64.FileHeader.Machine = IMAGE_FILE_MACHINE_AMD64; | |
| COFFheader64.FileHeader.NumberOfSections = 4; | |
| COFFheader64.FileHeader.TimeDateStamp = timeDateStamp; | |
| COFFheader64.FileHeader.PointerToSymbolTable = 0; // Deprecated COFF symbol table | |
| COFFheader64.FileHeader.NumberOfSymbols = 0; | |
| COFFheader64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64); | |
| COFFheader64.FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | | |
| IMAGE_FILE_LARGE_ADDRESS_AWARE | | |
| IMAGE_FILE_DLL; | |
| COFFheader64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; | |
| COFFheader64.OptionalHeader.MajorLinkerVersion = 8; | |
| COFFheader64.OptionalHeader.MinorLinkerVersion = 0; | |
| COFFheader64.OptionalHeader.SizeOfCode = fileAlign(codeSize); | |
| COFFheader64.OptionalHeader.SizeOfInitializedData = fileAlign(exportsSize) + fileAlign(relocSize) + fileAlign(constSize); | |
| COFFheader64.OptionalHeader.SizeOfUninitializedData = 0; | |
| COFFheader64.OptionalHeader.AddressOfEntryPoint = 0; | |
| COFFheader64.OptionalHeader.BaseOfCode = codePage; | |
| COFFheader64.OptionalHeader.ImageBase = base; | |
| COFFheader64.OptionalHeader.SectionAlignment = 0x1000; | |
| COFFheader64.OptionalHeader.FileAlignment = 0x200; | |
| COFFheader64.OptionalHeader.MajorOperatingSystemVersion = 4; | |
| COFFheader64.OptionalHeader.MinorOperatingSystemVersion = 0; | |
| COFFheader64.OptionalHeader.MajorImageVersion = 0; | |
| COFFheader64.OptionalHeader.MinorImageVersion = 0; | |
| COFFheader64.OptionalHeader.MajorSubsystemVersion = 4; | |
| COFFheader64.OptionalHeader.MinorSubsystemVersion = 0; | |
| COFFheader64.OptionalHeader.Win32VersionValue = 0; | |
| COFFheader64.OptionalHeader.SizeOfImage = constPage + pageAlign(constSize); | |
| COFFheader64.OptionalHeader.SizeOfHeaders = fileAlign(sizeof(DOSheader) + sizeof(COFFheader64)); | |
| COFFheader64.OptionalHeader.CheckSum = 0; | |
| COFFheader64.OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; | |
| COFFheader64.OptionalHeader.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_NO_SEH | | |
| IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | // Base address randomization | |
| IMAGE_DLLCHARACTERISTICS_NX_COMPAT; // Data Execution Prevention compatible | |
| COFFheader64.OptionalHeader.SizeOfStackReserve = 1024 * 1024; | |
| COFFheader64.OptionalHeader.SizeOfStackCommit = 4 * 1024; | |
| COFFheader64.OptionalHeader.SizeOfHeapReserve = 1024 * 1024; | |
| COFFheader64.OptionalHeader.SizeOfHeapCommit = 4 * 1024; | |
| COFFheader64.OptionalHeader.LoaderFlags = 0; | |
| COFFheader64.OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; | |
| COFFheader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = exportsPage; | |
| COFFheader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = exportsSize; | |
| COFFheader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = relocPage; | |
| COFFheader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = relocSize; | |
| } | |
| memset(&textSection, 0, sizeof(textSection)); | |
| strcpy((char*)&textSection.Name, ".text"); | |
| textSection.Misc.VirtualSize = pageAlign(codeSize); | |
| textSection.VirtualAddress = codePage; | |
| textSection.SizeOfRawData = fileAlign(codeSize); | |
| textSection.PointerToRawData = fileAlign(sizeof(DOSheader) + AMD64 ? sizeof(COFFheader64) : sizeof(COFFheader32)); | |
| textSection.PointerToRelocations = 0; | |
| textSection.PointerToLinenumbers = 0; | |
| textSection.NumberOfRelocations = 0; | |
| textSection.NumberOfLinenumbers = 0; | |
| textSection.Characteristics = IMAGE_SCN_CNT_CODE | | |
| IMAGE_SCN_MEM_EXECUTE | | |
| IMAGE_SCN_MEM_READ; | |
| memset(&exportsSection, 0, sizeof(exportsSection)); | |
| strcpy((char*)&exportsSection.Name, ".edata"); | |
| exportsSection.Misc.VirtualSize = pageAlign(exportsSize); | |
| exportsSection.VirtualAddress = exportsPage; | |
| exportsSection.SizeOfRawData = fileAlign(exportsSize); | |
| exportsSection.PointerToRawData = textSection.PointerToRawData + fileAlign(codeSize); | |
| exportsSection.PointerToRelocations = 0; | |
| exportsSection.PointerToLinenumbers = 0; | |
| exportsSection.NumberOfRelocations = 0; | |
| exportsSection.NumberOfLinenumbers = 0; | |
| exportsSection.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | | |
| IMAGE_SCN_MEM_READ; | |
| memset(&relocSection, 0, sizeof(relocSection)); | |
| strcpy((char*)&relocSection.Name, ".reloc"); | |
| relocSection.Misc.VirtualSize = pageAlign(relocSize); | |
| relocSection.VirtualAddress = relocPage; | |
| relocSection.SizeOfRawData = fileAlign(relocSize); | |
| relocSection.PointerToRawData = exportsSection.PointerToRawData + fileAlign(exportsSize); | |
| relocSection.PointerToRelocations = 0; | |
| relocSection.PointerToLinenumbers = 0; | |
| relocSection.NumberOfRelocations = 0; | |
| relocSection.NumberOfLinenumbers = 0; | |
| relocSection.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | | |
| IMAGE_SCN_MEM_DISCARDABLE | | |
| IMAGE_SCN_MEM_READ; | |
| memset(&constSection, 0, sizeof(constSection)); | |
| strcpy((char*)&constSection.Name, ".rdata"); | |
| constSection.Misc.VirtualSize = pageAlign(constSize); | |
| constSection.VirtualAddress = constPage; | |
| constSection.SizeOfRawData = fileAlign(constSize); | |
| constSection.PointerToRawData = relocSection.PointerToRawData + fileAlign(relocSize); | |
| constSection.PointerToRelocations = 0; | |
| constSection.PointerToLinenumbers = 0; | |
| constSection.NumberOfRelocations = 0; | |
| constSection.NumberOfLinenumbers = 0; | |
| constSection.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | | |
| IMAGE_SCN_MEM_READ; | |
| memset(&exportDirectory, 0, sizeof(exportDirectory)); | |
| exportDirectory.Characteristics = 0; | |
| exportDirectory.TimeDateStamp = timeDateStamp; | |
| exportDirectory.MajorVersion = 0; | |
| exportDirectory.MinorVersion = 0; | |
| exportDirectory.Name = (unsigned long)(exportsPage + sizeof(IMAGE_EXPORT_DIRECTORY) + functionList.size() * sizeof(void*)); | |
| exportDirectory.Base = 1; | |
| exportDirectory.NumberOfFunctions = (unsigned long)functionList.size(); | |
| exportDirectory.NumberOfNames = 0; | |
| exportDirectory.AddressOfFunctions = exportsPage + sizeof(IMAGE_EXPORT_DIRECTORY); | |
| exportDirectory.AddressOfNames = 0; | |
| exportDirectory.AddressOfNameOrdinals = 0; | |
| FILE *file = fopen(dllName, "wb"); | |
| if(file) | |
| { | |
| fwrite(&DOSheader, 1, sizeof(DOSheader), file); | |
| if(AMD64) | |
| { | |
| fwrite(&COFFheader64, 1, sizeof(COFFheader64), file); | |
| } | |
| else | |
| { | |
| fwrite(&COFFheader32, 1, sizeof(COFFheader32), file); | |
| } | |
| fwrite(&textSection, 1, sizeof(textSection), file); | |
| fwrite(&exportsSection, 1, sizeof(textSection), file); | |
| fwrite(&relocSection, 1, sizeof(relocSection), file); | |
| fwrite(&constSection, 1, sizeof(constSection), file); | |
| for(FunctionList::iterator i = functionList.begin(); i != functionList.end(); i++) | |
| { | |
| const void *function = i->first; | |
| unsigned int location = i->second->location; | |
| const std::vector<Relocation> &functionRelocations = globalRelocations[function]; | |
| for(unsigned int j = 0; j < functionRelocations.size(); j++) | |
| { | |
| unsigned int *address = (unsigned int*)((unsigned char*)i->second->buffer + functionRelocations[j].offset); | |
| if(functionRelocations[j].ripRelative) | |
| { | |
| *address = base + codePage + location + (*address - (unsigned int)(size_t)function); | |
| } | |
| else | |
| { | |
| *address = base + constPage + (*address - (unsigned int)(size_t)constants); | |
| } | |
| } | |
| fseek(file, textSection.PointerToRawData + location, SEEK_SET); | |
| fwrite(i->second->buffer, 1, i->second->size, file); | |
| } | |
| fseek(file, exportsSection.PointerToRawData, SEEK_SET); | |
| fwrite(&exportDirectory, 1, sizeof(exportDirectory), file); | |
| for(unsigned int i = 0; i < functionOrder.size(); i++) | |
| { | |
| const void *buffer = functionOrder[i]; | |
| Function *function = functionList[buffer]; | |
| unsigned int functionAddress = codePage + function->location; | |
| unsigned int functionEntry = functionAddress + (int)((size_t)function->entry - (size_t)buffer); | |
| fwrite(&functionEntry, 1, sizeof(functionEntry), file); | |
| } | |
| fwrite(dllName, 1, strlen(dllName) + 1, file); | |
| fseek(file, relocSection.PointerToRawData, SEEK_SET); | |
| for(PageRelocations::iterator i = pageRelocations.begin(); i != pageRelocations.end(); i++) | |
| { | |
| IMAGE_BASE_RELOCATION relocationBlock; | |
| relocationBlock.VirtualAddress = codePage + i->first * 0x1000; | |
| relocationBlock.SizeOfBlock = (unsigned long)(sizeof(IMAGE_BASE_RELOCATION) + i->second.size() * sizeof(unsigned short)); | |
| fwrite(&relocationBlock, 1, sizeof(IMAGE_BASE_RELOCATION), file); | |
| if(i->second.size() > 0) | |
| { | |
| fwrite(&i->second[0], 1, i->second.size() * sizeof(unsigned short), file); | |
| } | |
| } | |
| fseek(file, constSection.PointerToRawData, SEEK_SET); | |
| fwrite(constants, 1, constSize, file); | |
| char *padding = new char[fileAlign(constSize) - constSize]; | |
| fwrite(padding, 1, fileAlign(constSize) - constSize, file); | |
| delete[] padding; | |
| fclose(file); | |
| } | |
| } | |
| } |