2024-05-13 20:56:57 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "UbaObjectFile.h"
|
2024-05-19 23:12:02 -04:00
|
|
|
#include "UbaBinaryReaderWriter.h"
|
2024-08-16 18:47:18 -04:00
|
|
|
#include "UbaCompressedObjFileHeader.h"
|
2024-05-13 20:56:57 -04:00
|
|
|
#include "UbaFileAccessor.h"
|
2024-05-15 13:14:32 -04:00
|
|
|
#include "UbaObjectFileCoff.h"
|
|
|
|
|
#include "UbaObjectFileElf.h"
|
2024-08-16 18:47:18 -04:00
|
|
|
#include "UbaObjectFileImportLib.h"
|
2024-08-06 20:59:37 -04:00
|
|
|
#include "UbaObjectFileLLVMIR.h"
|
2024-08-16 18:47:18 -04:00
|
|
|
#include <oodle2.h>
|
2024-05-13 20:56:57 -04:00
|
|
|
|
|
|
|
|
namespace uba
|
|
|
|
|
{
|
2024-05-22 15:22:17 -04:00
|
|
|
u8 SymbolFileVersion = 1;
|
|
|
|
|
|
2024-05-19 23:12:02 -04:00
|
|
|
ObjectFile* ObjectFile::OpenAndParse(Logger& logger, const tchar* filename)
|
2024-05-13 20:56:57 -04:00
|
|
|
{
|
2024-05-15 13:14:32 -04:00
|
|
|
auto file = new FileAccessor(logger, filename);
|
|
|
|
|
auto fileGuard = MakeGuard([&]() { delete file; });
|
2024-05-13 20:56:57 -04:00
|
|
|
|
2024-05-15 13:14:32 -04:00
|
|
|
if (!file->OpenMemoryRead())
|
|
|
|
|
return nullptr;
|
2024-05-13 20:56:57 -04:00
|
|
|
|
2024-05-19 23:12:02 -04:00
|
|
|
ObjectFile* objectFile = Parse(logger, file->GetData(), file->GetSize(), filename);
|
|
|
|
|
if (!objectFile)
|
|
|
|
|
return nullptr;
|
2024-05-13 20:56:57 -04:00
|
|
|
|
2024-05-19 23:12:02 -04:00
|
|
|
fileGuard.Cancel();
|
|
|
|
|
objectFile->m_file = file;
|
|
|
|
|
return objectFile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ObjectFile* ObjectFile::Parse(Logger& logger, u8* data, u64 dataSize, const tchar* hint)
|
|
|
|
|
{
|
2024-05-15 13:14:32 -04:00
|
|
|
ObjectFile* objectFile = nullptr;
|
2024-05-13 20:56:57 -04:00
|
|
|
|
2024-08-16 18:47:18 -04:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-06 20:59:37 -04:00
|
|
|
if (IsElfFile(data, dataSize))
|
2024-05-15 13:14:32 -04:00
|
|
|
objectFile = new ObjectFileElf();
|
2024-08-06 20:59:37 -04:00
|
|
|
else if (IsLLVMIRFile(data, dataSize))
|
|
|
|
|
objectFile = new ObjectFileLLVMIR();
|
|
|
|
|
else if (IsCoffFile(data, dataSize))
|
2024-05-15 13:14:32 -04:00
|
|
|
objectFile = new ObjectFileCoff();
|
2024-08-16 18:47:18 -04:00
|
|
|
else if (IsImportLib(data, dataSize))
|
|
|
|
|
objectFile = new ObjectFileImportLib();
|
2024-08-06 20:59:37 -04:00
|
|
|
else
|
|
|
|
|
{
|
2024-08-16 18:47:18 -04:00
|
|
|
if (ownsData)
|
|
|
|
|
free(data);
|
2024-08-06 20:59:37 -04:00
|
|
|
logger.Error(TC("Unknown object file format. Maybe msvc FE IL? (%s)"), hint);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2024-05-13 20:56:57 -04:00
|
|
|
|
2024-05-15 13:14:32 -04:00
|
|
|
objectFile->m_data = data;
|
|
|
|
|
objectFile->m_dataSize = dataSize;
|
2024-08-16 18:47:18 -04:00
|
|
|
objectFile->m_ownsData = ownsData;
|
2024-05-13 20:56:57 -04:00
|
|
|
|
2024-05-19 23:12:02 -04:00
|
|
|
if (objectFile->Parse(logger, hint))
|
2024-05-15 13:14:32 -04:00
|
|
|
return objectFile;
|
2024-05-13 20:56:57 -04:00
|
|
|
|
2024-08-16 18:47:18 -04:00
|
|
|
if (ownsData)
|
|
|
|
|
free(data);
|
2024-05-15 13:14:32 -04:00
|
|
|
delete objectFile;
|
|
|
|
|
return nullptr;
|
2024-05-13 20:56:57 -04:00
|
|
|
}
|
|
|
|
|
|
2024-05-19 23:12:02 -04:00
|
|
|
bool ObjectFile::CopyMemoryAndClose()
|
|
|
|
|
{
|
|
|
|
|
u8* data = (u8*)malloc(m_dataSize);
|
|
|
|
|
memcpy(data, m_data, m_dataSize);
|
2024-08-16 18:47:18 -04:00
|
|
|
if (m_ownsData)
|
|
|
|
|
free(m_data);
|
2024-05-19 23:12:02 -04:00
|
|
|
m_data = data;
|
|
|
|
|
m_ownsData = true;
|
|
|
|
|
delete m_file;
|
|
|
|
|
m_file = nullptr;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ObjectFile::StripExports(Logger& logger)
|
|
|
|
|
{
|
2024-08-16 14:05:39 -04:00
|
|
|
return StripExports(logger, m_data, {});
|
2024-05-19 23:12:02 -04:00
|
|
|
}
|
|
|
|
|
|
2024-05-22 15:22:17 -04:00
|
|
|
bool ObjectFile::WriteImportsAndExports(Logger& logger, MemoryBlock& memoryBlock)
|
2024-05-19 23:12:02 -04:00
|
|
|
{
|
2024-08-16 14:05:39 -04:00
|
|
|
auto write = [&](const void* data, u64 dataSize) { memcpy(memoryBlock.Allocate(dataSize, 1, TC("ObjectFile::WriteImportsAndExports")), data, dataSize); };
|
2024-05-19 23:12:02 -04:00
|
|
|
|
2024-05-22 15:22:17 -04:00
|
|
|
write(&SymbolFileVersion, 1);
|
|
|
|
|
write(&m_type, 1);
|
|
|
|
|
|
2024-05-19 23:12:02 -04:00
|
|
|
// 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());
|
2024-08-16 14:05:39 -04:00
|
|
|
write(kv.second.extra.c_str(), kv.second.extra.size());
|
2024-05-19 23:12:02 -04:00
|
|
|
write("", 1);
|
|
|
|
|
}
|
|
|
|
|
write("", 1);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-22 15:22:17 -04:00
|
|
|
bool ObjectFile::WriteImportsAndExports(Logger& logger, const tchar* exportsFilename)
|
2024-05-19 23:12:02 -04:00
|
|
|
{
|
|
|
|
|
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());
|
2024-08-16 14:05:39 -04:00
|
|
|
write(kv.second.extra.c_str(), kv.second.extra.size());
|
2024-05-19 23:12:02 -04:00
|
|
|
write("", 1);
|
|
|
|
|
}
|
|
|
|
|
write("", 1);
|
|
|
|
|
|
|
|
|
|
flush();
|
|
|
|
|
|
|
|
|
|
return exportsFile.Close();
|
|
|
|
|
}
|
2024-05-13 20:56:57 -04:00
|
|
|
|
2024-08-16 18:47:18 -04:00
|
|
|
const char* ObjectFile::GetLibName()
|
|
|
|
|
{
|
|
|
|
|
UBA_ASSERT(false);
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-13 20:56:57 -04:00
|
|
|
ObjectFile::~ObjectFile()
|
|
|
|
|
{
|
2024-05-19 23:12:02 -04:00
|
|
|
if (m_ownsData)
|
|
|
|
|
free(m_data);
|
2024-05-13 20:56:57 -04:00
|
|
|
delete m_file;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-16 18:47:18 -04:00
|
|
|
void ObjectFile::RemoveExportedSymbol(const char* symbol)
|
|
|
|
|
{
|
|
|
|
|
m_exports.erase(symbol);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-13 20:56:57 -04:00
|
|
|
const tchar* ObjectFile::GetFileName() const
|
|
|
|
|
{
|
|
|
|
|
return m_file->GetFileName();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UnorderedSymbols& ObjectFile::GetImports() const
|
|
|
|
|
{
|
|
|
|
|
return m_imports;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-19 23:12:02 -04:00
|
|
|
const UnorderedExports& ObjectFile::GetExports() const
|
2024-05-13 20:56:57 -04:00
|
|
|
{
|
|
|
|
|
return m_exports;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-15 01:57:03 -04:00
|
|
|
const UnorderedSymbols& ObjectFile::GetPotentialDuplicates() const
|
|
|
|
|
{
|
|
|
|
|
return m_potentialDuplicates;
|
|
|
|
|
}
|
2024-05-19 23:12:02 -04:00
|
|
|
|
2024-08-27 16:28:54 -04:00
|
|
|
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)
|
2024-05-19 23:12:02 -04:00
|
|
|
{
|
2024-05-22 15:22:17 -04:00
|
|
|
ObjectFileCoff objectFileCoff;
|
|
|
|
|
ObjectFileElf objectFileElf;
|
|
|
|
|
|
2024-05-19 23:12:02 -04:00
|
|
|
MemoryBlock memoryBlock(16*1024*1024);
|
|
|
|
|
|
2024-08-21 23:19:52 -04:00
|
|
|
bool res;
|
|
|
|
|
if (platform.Equals(TC("win64")) || platform.Equals(TC("wingdk")) || platform.Equals(TC("xb1")) || platform.Equals(TC("xsx")))
|
2024-08-26 02:00:38 -04:00
|
|
|
res = ObjectFileCoff::CreateExtraFile(logger, platform, memoryBlock, allExternalImports, allInternalImports, allExports, includeExportsInFile);
|
2024-08-27 17:17:30 -04:00
|
|
|
else if (extraObjFilename.EndsWith(TC("dynlist")))
|
2024-08-26 02:00:38 -04:00
|
|
|
res = CreateDynamicListFile(logger, memoryBlock, allExternalImports, allInternalImports, allExports, includeExportsInFile);
|
2024-08-27 16:28:54 -04:00
|
|
|
else if (extraObjFilename.EndsWith(TC("emd")))
|
|
|
|
|
res = CreateEmdFile(logger, memoryBlock, moduleName, allExternalImports, allInternalImports, allExports, includeExportsInFile);
|
2024-08-21 23:19:52 -04:00
|
|
|
else
|
2024-08-26 02:00:38 -04:00
|
|
|
res = ObjectFileElf::CreateExtraFile(logger, platform, memoryBlock, allExternalImports, allInternalImports, allExports, includeExportsInFile);
|
2024-08-21 23:19:52 -04:00
|
|
|
|
|
|
|
|
if (!res)
|
2024-05-19 23:12:02 -04:00
|
|
|
return false;
|
|
|
|
|
|
2024-08-21 23:19:52 -04:00
|
|
|
FileAccessor extraFile(logger, extraObjFilename.data);
|
2024-05-19 23:12:02 -04:00
|
|
|
if (!extraFile.CreateWrite())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!extraFile.Write(memoryBlock.memory, memoryBlock.writtenSize))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return extraFile.Close();
|
|
|
|
|
}
|
2024-05-22 15:22:17 -04:00
|
|
|
|
|
|
|
|
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);
|
2024-08-16 23:35:00 -04:00
|
|
|
ExportInfo info;
|
2024-05-22 15:22:17 -04:00
|
|
|
if (const char* comma = strchr(readPos, ','))
|
|
|
|
|
{
|
|
|
|
|
strEnd = comma - readPos;
|
2024-08-16 23:35:00 -04:00
|
|
|
info.extra = comma;
|
2024-05-22 15:22:17 -04:00
|
|
|
}
|
2024-08-16 23:35:00 -04:00
|
|
|
exports.emplace(std::string(readPos, readPos + strEnd), info);
|
2024-05-22 15:22:17 -04:00
|
|
|
readPos = readPos + strEnd + 1;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2024-08-26 02:00:38 -04:00
|
|
|
|
|
|
|
|
bool ObjectFile::CreateDynamicListFile(Logger& logger, MemoryBlock& memoryBlock, const UnorderedSymbols& allExternalImports, const UnorderedSymbols& allInternalImports, const UnorderedExports& allExports, bool includeExportsInFile)
|
|
|
|
|
{
|
2024-08-27 16:28:54 -04:00
|
|
|
auto WriteString = [&](const char* str, u64 strLen) { memcpy(memoryBlock.Allocate(strLen, 1, TC("")), str, strLen); };
|
2024-08-26 02:00:38 -04:00
|
|
|
|
|
|
|
|
//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;
|
|
|
|
|
}
|
2024-08-27 16:28:54 -04:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2024-05-13 20:56:57 -04:00
|
|
|
}
|