Files
henrik karlsson ff474d539c [UBA]
* Renamed extension for dynamic-list to dynlist from ldscript

[CL 35838707 by henrik karlsson in ue5-main branch]
2024-08-27 17:17:30 -04:00

341 lines
9.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "UbaObjectFile.h"
#include "UbaBinaryReaderWriter.h"
#include "UbaCompressedObjFileHeader.h"
#include "UbaFileAccessor.h"
#include "UbaObjectFileCoff.h"
#include "UbaObjectFileElf.h"
#include "UbaObjectFileImportLib.h"
#include "UbaObjectFileLLVMIR.h"
#include <oodle2.h>
namespace uba
{
u8 SymbolFileVersion = 1;
ObjectFile* ObjectFile::OpenAndParse(Logger& logger, const tchar* filename)
{
auto file = new FileAccessor(logger, filename);
auto fileGuard = MakeGuard([&]() { delete file; });
if (!file->OpenMemoryRead())
return nullptr;
ObjectFile* objectFile = Parse(logger, file->GetData(), file->GetSize(), filename);
if (!objectFile)
return nullptr;
fileGuard.Cancel();
objectFile->m_file = file;
return objectFile;
}
ObjectFile* ObjectFile::Parse(Logger& logger, u8* data, u64 dataSize, const tchar* hint)
{
ObjectFile* objectFile = nullptr;
bool ownsData = false;
if (dataSize >= sizeof(CompressedObjFileHeader) && ((CompressedObjFileHeader*)data)->IsValid())
{
u64 decompressedSize = *(u64*)(data + sizeof(CompressedObjFileHeader));
u8* readPos = data + sizeof(CompressedObjFileHeader) + 8;
u8* decompressedData = (u8*)malloc(decompressedSize);
u8* writePos = decompressedData;
OO_SINTa decoredMemSize = OodleLZDecoder_MemorySizeNeeded(OodleLZ_Compressor_Kraken);
void* decoderMem = malloc(decoredMemSize);
auto mg = MakeGuard([decoderMem]() { free(decoderMem); });
u64 left = decompressedSize;
while (left)
{
u32 compressedBlockSize = *(u32*)readPos;
readPos += 4;
u32 decompressedBlockSize = *(u32*)readPos;
readPos += 4;
OO_SINTa decompLen = OodleLZ_Decompress(readPos, (OO_SINTa)compressedBlockSize, writePos, (OO_SINTa)decompressedBlockSize,
OodleLZ_FuzzSafe_Yes, OodleLZ_CheckCRC_No, OodleLZ_Verbosity_None, NULL, 0, NULL, NULL, decoderMem, decoredMemSize);
if (decompLen != decompressedBlockSize)
{
logger.Error(TC("Failed to decompress file %s"), hint);
return nullptr;
}
readPos += compressedBlockSize;
writePos += decompressedBlockSize;
left -= decompressedBlockSize;
}
data = decompressedData;
dataSize = decompressedSize;
ownsData = true;
}
if (IsElfFile(data, dataSize))
objectFile = new ObjectFileElf();
else if (IsLLVMIRFile(data, dataSize))
objectFile = new ObjectFileLLVMIR();
else if (IsCoffFile(data, dataSize))
objectFile = new ObjectFileCoff();
else if (IsImportLib(data, dataSize))
objectFile = new ObjectFileImportLib();
else
{
if (ownsData)
free(data);
logger.Error(TC("Unknown object file format. Maybe msvc FE IL? (%s)"), hint);
return nullptr;
}
objectFile->m_data = data;
objectFile->m_dataSize = dataSize;
objectFile->m_ownsData = ownsData;
if (objectFile->Parse(logger, hint))
return objectFile;
if (ownsData)
free(data);
delete objectFile;
return nullptr;
}
bool ObjectFile::CopyMemoryAndClose()
{
u8* data = (u8*)malloc(m_dataSize);
memcpy(data, m_data, m_dataSize);
if (m_ownsData)
free(m_data);
m_data = data;
m_ownsData = true;
delete m_file;
m_file = nullptr;
return true;
}
bool ObjectFile::StripExports(Logger& logger)
{
return StripExports(logger, m_data, {});
}
bool ObjectFile::WriteImportsAndExports(Logger& logger, MemoryBlock& memoryBlock)
{
auto write = [&](const void* data, u64 dataSize) { memcpy(memoryBlock.Allocate(dataSize, 1, TC("ObjectFile::WriteImportsAndExports")), data, dataSize); };
write(&SymbolFileVersion, 1);
write(&m_type, 1);
// Write all imports
for (auto& symbol : m_imports)
{
write(symbol.c_str(), symbol.size());
write("", 1);
}
write("", 1);
// Write all exports
for (auto& kv : m_exports)
{
write(kv.first.c_str(), kv.first.size());
write(kv.second.extra.c_str(), kv.second.extra.size());
write("", 1);
}
write("", 1);
return true;
}
bool ObjectFile::WriteImportsAndExports(Logger& logger, const tchar* exportsFilename)
{
FileAccessor exportsFile(logger, exportsFilename);
if (!exportsFile.CreateWrite())
return false;
char buffer[256*1024];
u64 bufferPos = 0;
auto flush = [&]() { exportsFile.Write(buffer, bufferPos); bufferPos = 0; };
auto write = [&](const void* data, u64 dataSize) { if (bufferPos + dataSize > sizeof(buffer)) flush(); memcpy(buffer + bufferPos, data, dataSize); bufferPos += dataSize; };
// Write all imports
for (auto& symbol : m_imports)
{
write(symbol.c_str(), symbol.size());
write("", 1);
}
write("", 1);
// Write all exports
for (auto& kv : m_exports)
{
write(kv.first.c_str(), kv.first.size());
write(kv.second.extra.c_str(), kv.second.extra.size());
write("", 1);
}
write("", 1);
flush();
return exportsFile.Close();
}
const char* ObjectFile::GetLibName()
{
UBA_ASSERT(false);
return "";
}
ObjectFile::~ObjectFile()
{
if (m_ownsData)
free(m_data);
delete m_file;
}
void ObjectFile::RemoveExportedSymbol(const char* symbol)
{
m_exports.erase(symbol);
}
const tchar* ObjectFile::GetFileName() const
{
return m_file->GetFileName();
}
const UnorderedSymbols& ObjectFile::GetImports() const
{
return m_imports;
}
const UnorderedExports& ObjectFile::GetExports() const
{
return m_exports;
}
const UnorderedSymbols& ObjectFile::GetPotentialDuplicates() const
{
return m_potentialDuplicates;
}
bool ObjectFile::CreateExtraFile(Logger& logger, const StringView& extraObjFilename, const StringView& moduleName, const StringView& platform, const UnorderedSymbols& allExternalImports, const UnorderedSymbols& allInternalImports, const UnorderedExports& allExports, bool includeExportsInFile)
{
ObjectFileCoff objectFileCoff;
ObjectFileElf objectFileElf;
MemoryBlock memoryBlock(16*1024*1024);
bool res;
if (platform.Equals(TC("win64")) || platform.Equals(TC("wingdk")) || platform.Equals(TC("xb1")) || platform.Equals(TC("xsx")))
res = ObjectFileCoff::CreateExtraFile(logger, platform, memoryBlock, allExternalImports, allInternalImports, allExports, includeExportsInFile);
else if (extraObjFilename.EndsWith(TC("dynlist")))
res = CreateDynamicListFile(logger, memoryBlock, allExternalImports, allInternalImports, allExports, includeExportsInFile);
else if (extraObjFilename.EndsWith(TC("emd")))
res = CreateEmdFile(logger, memoryBlock, moduleName, allExternalImports, allInternalImports, allExports, includeExportsInFile);
else
res = ObjectFileElf::CreateExtraFile(logger, platform, memoryBlock, allExternalImports, allInternalImports, allExports, includeExportsInFile);
if (!res)
return false;
FileAccessor extraFile(logger, extraObjFilename.data);
if (!extraFile.CreateWrite())
return false;
if (!extraFile.Write(memoryBlock.memory, memoryBlock.writtenSize))
return false;
return extraFile.Close();
}
bool SymbolFile::ParseFile(Logger& logger, const tchar* filename)
{
FileAccessor symFile(logger, filename);
if (!symFile.OpenMemoryRead())
return false;
auto readPos = (const char*)symFile.GetData();
u8 version = *(u8*)readPos++;
if (SymbolFileVersion != version)
return logger.Error(TC("%s - Import/export file version mismatch"), filename);
type = *(const ObjectFileType*)readPos++;
while (*readPos)
{
auto strEnd = strlen(readPos);
imports.insert(std::string(readPos, readPos + strEnd));
readPos = readPos + strEnd + 1;
}
++readPos;
while (*readPos)
{
auto strEnd = strlen(readPos);
ExportInfo info;
if (const char* comma = strchr(readPos, ','))
{
strEnd = comma - readPos;
info.extra = comma;
}
exports.emplace(std::string(readPos, readPos + strEnd), info);
readPos = readPos + strEnd + 1;
}
return true;
}
bool ObjectFile::CreateDynamicListFile(Logger& logger, MemoryBlock& memoryBlock, const UnorderedSymbols& allExternalImports, const UnorderedSymbols& allInternalImports, const UnorderedExports& allExports, bool includeExportsInFile)
{
auto WriteString = [&](const char* str, u64 strLen) { memcpy(memoryBlock.Allocate(strLen, 1, TC("")), str, strLen); };
//WriteString("VERSION ", 8);
WriteString("{", 1);
bool isFirst = true;
for (auto& symbol : allExports)
{
//if (strncmp(symbol.first.c_str(), "_ZTV", 4) != 0)
// continue;
if (allExternalImports.find(symbol.first) == allExternalImports.end())
continue;
if (isFirst)
WriteString("global: ", 8);
WriteString(symbol.first.c_str(), symbol.first.size());
WriteString(";", 1);
isFirst = false;
}
//WriteString("local: *;", 9);
WriteString("};", 2);
return true;
}
bool ObjectFile::CreateEmdFile(Logger& logger, MemoryBlock& memoryBlock, const StringView& moduleName, const UnorderedSymbols& allExternalImports, const UnorderedSymbols& allInternalImports, const UnorderedExports& allExports, bool includeExportsInFile)
{
auto WriteString = [&](const char* str, u64 strLen) { memcpy(memoryBlock.Allocate(strLen, 1, TC("")), str, strLen); };
char moduleName2[256];
u32 moduleNameLen = StringBuffer<>(moduleName.data).Parse(moduleName2, 256) - 1;
WriteString("Library: ", 9);
WriteString(moduleName2, moduleNameLen);
WriteString(" { export: {\r\n", 14);
bool symbolAdded = false;
for (auto& symbol : allExports)
if (allExternalImports.find(symbol.first) != allExternalImports.end())
{
WriteString(symbol.first.c_str(), symbol.first.size());
WriteString("\r\n", 2);
symbolAdded = true;
}
if (!symbolAdded)
WriteString("ThisIsAnUnrealEngineModule\r\n", 28); // Workaround for tool not liking empty lists
WriteString("}}", 2);
return true;
}
}