mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 09:00:17 +02:00
CodeCache: Record main executable bitness in code map
Code maps already contain the main executable they were recorded from, so it's convenient to capture the executable's bitness along the way.
This commit is contained in:
1 parent
dddad1c2ca
commit
54236df6e0
5 files changed
+47
-27
No files matched your search
@@ -107,16 +107,22 @@ fextl::map<CodeMapFileId, CodeMap::ParsedContents> CodeMap::ParseCodeMap(std::if
|
||||
break;
|
||||
}
|
||||
Ret[Info.ExternalFileId].Filename = std::move(Filename);
|
||||
} else if (Entry.FileId == SetExecutableFileId {}.Marker.FileId && Entry.BlockOffset == SetExecutableFileId {}.Marker.BlockOffset) {
|
||||
} else if ((Entry.FileId == SetExecutableFileId::Marker32.FileId && Entry.BlockOffset == SetExecutableFileId::Marker32.BlockOffset) ||
|
||||
(Entry.FileId == SetExecutableFileId::Marker64.FileId && Entry.BlockOffset == SetExecutableFileId::Marker64.BlockOffset)) {
|
||||
CodeMapFileId ExecutableFileId;
|
||||
File.read(reinterpret_cast<char*>(&ExecutableFileId), sizeof(ExecutableFileId));
|
||||
if (!File) {
|
||||
break;
|
||||
}
|
||||
Ret[ExecutableFileId].IsExecutable = true;
|
||||
Ret[ExecutableFileId].ExecutableBitness =
|
||||
(Entry.FileId == SetExecutableFileId::Marker32.FileId && Entry.BlockOffset == SetExecutableFileId::Marker32.BlockOffset) ? 32 : 64;
|
||||
} else {
|
||||
if (!Ret.contains(Entry.FileId)) {
|
||||
LogMan::Msg::EFmt("Code map referenced unknown file id {:016x}", Entry.FileId);
|
||||
if (Entry.FileId == 0xffff'ffff'ffff'ffff) {
|
||||
ERROR_AND_DIE_FMT("Malformed code map");
|
||||
} else {
|
||||
LogMan::Msg::EFmt("Code map referenced unknown file id {:016x}", Entry.FileId);
|
||||
}
|
||||
} else {
|
||||
Ret[Entry.FileId].Blocks.insert(Entry.BlockOffset);
|
||||
}
|
||||
@@ -222,8 +228,8 @@ void CodeMapWriter::AppendLibraryLoad(const FEXCore::ExecutableFileInfo& FileInf
|
||||
AppendData(std::as_bytes(std::span {Data, TotalSize}));
|
||||
}
|
||||
|
||||
void CodeMapWriter::AppendSetMainExecutable(const FEXCore::ExecutableFileInfo& FileInfo) {
|
||||
CodeMap::SetExecutableFileId Data {.ExecutableFileId = FileInfo.FileId};
|
||||
void CodeMapWriter::AppendSetMainExecutable(const FEXCore::ExecutableFileInfo& FileInfo, bool Is64Bit) {
|
||||
CodeMap::SetExecutableFileId Data {Is64Bit ? CodeMap::SetExecutableFileId::Marker64 : CodeMap::SetExecutableFileId::Marker32, FileInfo.FileId};
|
||||
AppendData(std::span {reinterpret_cast<const std::byte*>(&Data), sizeof(Data)});
|
||||
}
|
||||
|
||||
|
||||
@@ -103,14 +103,17 @@ struct CodeMap {
|
||||
static constexpr Entry LoadExternalLibrary = {0xffff'ffff'ffff'ffff, 0xffff'ffff};
|
||||
|
||||
struct FEX_PACKED SetExecutableFileId {
|
||||
Entry Marker = {0xffff'ffff'ffff'ffff, 0xffff'fffe};
|
||||
static constexpr Entry Marker32 = {0xffff'ffff'ffff'ffff, 0xffff'ffee};
|
||||
static constexpr Entry Marker64 = {0xffff'ffff'ffff'ffff, 0xffff'ffef};
|
||||
Entry Marker;
|
||||
CodeMapFileId ExecutableFileId;
|
||||
};
|
||||
|
||||
struct ParsedContents {
|
||||
fextl::string Filename;
|
||||
fextl::set<uint64_t> Blocks;
|
||||
bool IsExecutable = false;
|
||||
// 32/64 for executables, nullopt for non-executables (libraries)
|
||||
std::optional<int> ExecutableBitness;
|
||||
};
|
||||
|
||||
// Follows scheme fileid[-nomb]
|
||||
@@ -152,7 +155,7 @@ public:
|
||||
|
||||
void AppendBlock(const FEXCore::ExecutableFileSectionInfo&, uint64_t Entry);
|
||||
void AppendLibraryLoad(const FEXCore::ExecutableFileInfo&);
|
||||
void AppendSetMainExecutable(const FEXCore::ExecutableFileInfo&);
|
||||
void AppendSetMainExecutable(const FEXCore::ExecutableFileInfo&, bool Is64Bit);
|
||||
|
||||
// Thread-safely commit any pending data to disk
|
||||
void Flush(size_t Offset);
|
||||
|
||||
@@ -572,7 +572,7 @@ static int GenerateCache(int argc, const char** argv) {
|
||||
}
|
||||
|
||||
for (auto& [FileId, Contents] : Parsed) {
|
||||
if (!ExplicitFileId && (Contents.IsExecutable || Parsed.size() == 1)) {
|
||||
if (!ExplicitFileId && (Contents.ExecutableBitness || Parsed.size() == 1)) {
|
||||
ProgramName.FileId = FileId;
|
||||
ProgramName.Filename = Contents.Filename;
|
||||
}
|
||||
@@ -621,7 +621,7 @@ static int GenerateCache(int argc, const char** argv) {
|
||||
* Writes aggregated code map data into a single code map file that is ready to be used for cache generation
|
||||
*/
|
||||
static void WriteNewCodeMap(const FEXCore::ExecutableFileInfo& File, const std::string& OutputName, const fextl::set<uintptr_t>& Blocks,
|
||||
bool IsExecutable, const std::set<FEXCore::ExecutableFileInfo>& Dependencies) {
|
||||
std::optional<int> ExecutableBitness, const std::set<FEXCore::ExecutableFileInfo>& Dependencies) {
|
||||
fmt::print("Writing {} blocks to {}\n", Blocks.size(), OutputName);
|
||||
|
||||
struct CodeMapOpener : FEXCore::CodeMapOpener {
|
||||
@@ -638,9 +638,9 @@ static void WriteNewCodeMap(const FEXCore::ExecutableFileInfo& File, const std::
|
||||
|
||||
CodeMapOpener CodeMapOpener(OutputName);
|
||||
FEXCore::CodeMapWriter OutputCodeMap(CodeMapOpener, true);
|
||||
if (IsExecutable) {
|
||||
if (ExecutableBitness) {
|
||||
// List the main executable and all used libraries
|
||||
OutputCodeMap.AppendSetMainExecutable(File);
|
||||
OutputCodeMap.AppendSetMainExecutable(File, ExecutableBitness == 64);
|
||||
|
||||
for (auto& Dependency : Dependencies) {
|
||||
OutputCodeMap.AppendLibraryLoad(Dependency);
|
||||
@@ -658,7 +658,7 @@ static void WriteNewCodeMap(const FEXCore::ExecutableFileInfo& File, const std::
|
||||
struct ParsedContentsAndDependencies {
|
||||
fextl::string Filename;
|
||||
fextl::set<uint64_t> Blocks;
|
||||
bool IsExecutable = false;
|
||||
std::optional<int> ExecutableBitness;
|
||||
std::set<FEXCore::CodeMapFileId> Dependencies;
|
||||
};
|
||||
|
||||
@@ -688,13 +688,13 @@ static std::map<FEXCore::CodeMapFileId, ParsedContentsAndDependencies> ImportPen
|
||||
std::set<FEXCore::CodeMapFileId> Dependencies;
|
||||
std::optional<FEXCore::CodeMapFileId> ExecutableFileId;
|
||||
for (auto& [FileId, Contents] : FEXCore::CodeMap::ParseCodeMap(Incoming)) {
|
||||
auto& [Filename, Blocks, IsExecutable, _] =
|
||||
auto& [Filename, Blocks, ExecutableBitness, _] =
|
||||
Result.emplace(std::piecewise_construct, std::forward_as_tuple(FileId), std::tuple {}).first->second;
|
||||
Filename = std::move(Contents.Filename);
|
||||
Blocks.merge(std::move(Contents.Blocks));
|
||||
IsExecutable = Contents.IsExecutable;
|
||||
if (IsExecutable) {
|
||||
LOGMAN_THROW_A_FMT(!ExecutableFileId, "Expected a unique executable identifiers per code map");
|
||||
ExecutableBitness = Contents.ExecutableBitness;
|
||||
if (ExecutableBitness) {
|
||||
LOGMAN_THROW_A_FMT(!ExecutableFileId, "Expected a unique executable identifier per code map");
|
||||
ExecutableFileId = FileId;
|
||||
} else {
|
||||
Dependencies.insert(FileId);
|
||||
@@ -745,7 +745,7 @@ static void AggregateCodeMaps(const std::string& NewCodeMapDirectory, const std:
|
||||
for (auto& Dependency : Contents.Dependencies) {
|
||||
Dependencies.emplace(nullptr, Dependency, IncomingCodeMap.at(Dependency).Filename);
|
||||
}
|
||||
WriteNewCodeMap(File, OutputName, Contents.Blocks, Contents.IsExecutable, Dependencies);
|
||||
WriteNewCodeMap(File, OutputName, Contents.Blocks, Contents.ExecutableBitness, Dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,7 +767,7 @@ static int ProcessAll() {
|
||||
for (auto& Entry : std::filesystem::directory_iterator(ReadyCodeMapDirectory)) {
|
||||
std::ifstream CodeMap(Entry.path(), std::ios_base::binary);
|
||||
auto Parsed = FEXCore::CodeMap::ParseCodeMap(CodeMap);
|
||||
auto ExecutableIt = std::ranges::find_if(Parsed, [](const auto& Entry) { return Entry.second.IsExecutable; });
|
||||
auto ExecutableIt = std::ranges::find_if(Parsed, [](const auto& Entry) { return Entry.second.ExecutableBitness.has_value(); });
|
||||
if (ExecutableIt == Parsed.end()) {
|
||||
// Skip libraries; they're only processed as dependencies of a main executable
|
||||
continue;
|
||||
|
||||
@@ -307,7 +307,7 @@ void SendFDSuccessPacket(fasio::tcp_socket& Socket, int FD) {
|
||||
|
||||
// Discovers any pending code maps, parses their contents into a runtime data structure, and deletes them
|
||||
static std::map<FEXCore::ExecutableFileInfo, fextl::set<uintptr_t>>
|
||||
ImportPendingCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, bool HasMultiblock) {
|
||||
ImportPendingCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, bool HasMultiblock, std::optional<int>& ExecutableBitness) {
|
||||
// Detect code maps by checking file name suffixes by counting up an index.
|
||||
// Code maps that are ready for reading must be non-empty and flock(FLOCK_EX) must succeed:
|
||||
// - If empty, we tried generating the cache before the client could even lock it
|
||||
@@ -334,7 +334,7 @@ ImportPendingCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, bool HasMul
|
||||
fmt::print("Found code map {}, queuing for merge\n", CodeMap);
|
||||
}
|
||||
close(FD);
|
||||
CodeMaps.push_back(CodeMap);
|
||||
CodeMaps.push_back(std::move(CodeMap));
|
||||
}
|
||||
|
||||
// Update merged code map
|
||||
@@ -348,6 +348,11 @@ ImportPendingCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, bool HasMul
|
||||
for (auto& [FileId, Contents] : NewBlocks) {
|
||||
ImportedCodeMaps.emplace(std::piecewise_construct, std::forward_as_tuple(nullptr, FileId, std::move(Contents.Filename)),
|
||||
std::forward_as_tuple(std::move(Contents.Blocks)));
|
||||
if (FileId == MainFileId.FileId) {
|
||||
LOGMAN_THROW_A_FMT(Contents.ExecutableBitness && (!ExecutableBitness.has_value() || ExecutableBitness == Contents.ExecutableBitness),
|
||||
"Inconsistent executable bitness");
|
||||
ExecutableBitness = Contents.ExecutableBitness;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,7 +370,7 @@ ImportPendingCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, bool HasMul
|
||||
* Writes aggregated code map data into a single code map file that is ready to be used for cache generation
|
||||
*/
|
||||
static void WriteNewCodeMap(const FEXCore::ExecutableFileInfo& File, const std::string& OutputName, const fextl::set<uintptr_t>& Blocks,
|
||||
bool IsMainFile, const auto& Dependencies) {
|
||||
std::optional<int> ExecutableBitness, const auto& Dependencies) {
|
||||
fmt::print("Writing {} blocks to {}\n", Blocks.size(), OutputName);
|
||||
|
||||
struct CodeMapOpener : FEXCore::CodeMapOpener {
|
||||
@@ -382,9 +387,9 @@ static void WriteNewCodeMap(const FEXCore::ExecutableFileInfo& File, const std::
|
||||
|
||||
CodeMapOpener CodeMapOpener(OutputName);
|
||||
FEXCore::CodeMapWriter OutputCodeMap(CodeMapOpener, true);
|
||||
if (IsMainFile) {
|
||||
if (ExecutableBitness) {
|
||||
// List the main executable and all used libraries
|
||||
OutputCodeMap.AppendSetMainExecutable(File);
|
||||
OutputCodeMap.AppendSetMainExecutable(File, ExecutableBitness == 64);
|
||||
|
||||
for (auto& [Dependency, _] : Dependencies) {
|
||||
OutputCodeMap.AppendLibraryLoad(Dependency);
|
||||
@@ -426,11 +431,13 @@ static std::map<FEXCore::ExecutableFileInfo, NeedsCacheRefresh> AggregateCodeMap
|
||||
}
|
||||
|
||||
// Accumulate information from new code maps
|
||||
auto IncomingCodeMap = ImportPendingCodeMaps(MainFileId, HasMultiblock);
|
||||
std::optional<int> ExecutableBitness;
|
||||
auto IncomingCodeMap = ImportPendingCodeMaps(MainFileId, HasMultiblock, ExecutableBitness);
|
||||
for (auto& [File, _] : IncomingCodeMap) {
|
||||
Result.emplace(std::piecewise_construct, std::forward_as_tuple(nullptr, File.FileId, File.Filename),
|
||||
std::forward_as_tuple(NeedsCacheRefresh::No));
|
||||
}
|
||||
LOGMAN_THROW_A_FMT(ExecutableBitness, "New code maps did not contain executable marker");
|
||||
|
||||
// For each referenced library, add referenced offsets to that library's reference code map
|
||||
for (auto& [File, Blocks] : IncomingCodeMap) {
|
||||
@@ -452,7 +459,7 @@ static std::map<FEXCore::ExecutableFileInfo, NeedsCacheRefresh> AggregateCodeMap
|
||||
|
||||
// Update code map and queue for cache generation
|
||||
std::map<FEXCore::ExecutableFileInfo, NeedsCacheRefresh> Empty;
|
||||
WriteNewCodeMap(File, OutputName, Blocks, true, File.FileId == MainFileId.FileId ? Result : Empty);
|
||||
WriteNewCodeMap(File, OutputName, Blocks, ExecutableBitness, File.FileId == MainFileId.FileId ? Result : Empty);
|
||||
Result.at(File) = NeedsCacheRefresh::Yes;
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,11 @@ FEXCore::ExecutableFileSectionInfo ImageTracker::HandleImageMap(std::string_view
|
||||
ActiveCodeMapPath = fmt::format("{}{}.{}.bin", CodeMapDir, ID, Time.QuadPart);
|
||||
|
||||
auto Writer = fextl::make_unique<FEXCore::CodeMapWriter>(*this, false);
|
||||
Writer->AppendSetMainExecutable(ImageInfo->Info);
|
||||
#ifdef _M_ARM64EC
|
||||
Writer->AppendSetMainExecutable(ImageInfo->Info, true);
|
||||
#else
|
||||
Writer->AppendSetMainExecutable(ImageInfo->Info, false);
|
||||
#endif
|
||||
CTX.SetCodeMapWriter(std::move(Writer));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user