// 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); | |
} | |
} | |
} |