blob: 729b6401f593f3b125e86ff00f146e4974a38137 [file] [log] [blame]
// 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);
}
}
}