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:
Tony Wasserka committed 2026-07-09 17:12:45 +02:00
1 parent dddad1c2ca
commit 54236df6e0
5 files changed
+47 -27

No files matched your search

+11 -5
View File
@@ -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)});
}
+6 -3
View File
@@ -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);
+11 -11
View File
@@ -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;
+14 -7
View File
@@ -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;
}
+5 -1
View File
@@ -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));
}
}