diff --git a/FEXCore/Source/Interface/Core/CodeCache.cpp b/FEXCore/Source/Interface/Core/CodeCache.cpp index 876fc088b..b0a0cf700 100644 --- a/FEXCore/Source/Interface/Core/CodeCache.cpp +++ b/FEXCore/Source/Interface/Core/CodeCache.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -30,7 +29,6 @@ ExecutableFileInfo::ExecutableFileInfo(fextl::unique_ptr Map , FileId(FileId) , Filename(Filename) {} #endif -ExecutableFileInfo::~ExecutableFileInfo() = default; fextl::string CodeMap::GetBaseFilename(const ExecutableFileInfo& MainExecutable, bool AddNombSuffix) { auto FileId = MainExecutable.FileId; diff --git a/FEXCore/include/FEXCore/Core/CodeCache.h b/FEXCore/include/FEXCore/Core/CodeCache.h index fff72094c..c92d4a878 100644 --- a/FEXCore/include/FEXCore/Core/CodeCache.h +++ b/FEXCore/include/FEXCore/Core/CodeCache.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -31,8 +32,6 @@ enum class GuestRelocationType : uint32_t { Rel32, Rel64 }; // Generic information associated with an executable file. struct ExecutableFileInfo { - ~ExecutableFileInfo(); - #if __clang_major__ < 16 // Workaround for broken aggregate-initialization with std::piecewise_construct ExecutableFileInfo(fextl::unique_ptr, uint64_t, fextl::string); diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index c32ad7d7a..c26a5d116 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -522,7 +522,7 @@ public: } std::optional LookupExecutableFileSection(FEXCore::Core::InternalThreadState*, uint64_t Address) override { - return std::nullopt; + return ImageTracker->LookupExecutableFileSection(Address); } void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override { diff --git a/Source/Windows/Common/Handle.h b/Source/Windows/Common/Handle.h index 1f856ad3e..8424c5692 100644 --- a/Source/Windows/Common/Handle.h +++ b/Source/Windows/Common/Handle.h @@ -6,6 +6,8 @@ namespace FEX::Windows { class ScopedHandle final { public: + ScopedHandle() = default; + explicit ScopedHandle(HANDLE Handle) : Handle(Handle) {} @@ -23,15 +25,24 @@ public: } } - HANDLE operator*() const { + const HANDLE& operator*() const { return Handle; } + + HANDLE& operator*() { + return Handle; + } + + operator bool() const { + return Handle != INVALID_HANDLE_VALUE; + } + private: - HANDLE Handle; + HANDLE Handle {INVALID_HANDLE_VALUE}; }; -bool ValidateHandleAccess(HANDLE Handle, ACCESS_MASK Access) { +inline bool ValidateHandleAccess(HANDLE Handle, ACCESS_MASK Access) { OBJECT_BASIC_INFORMATION Info; if (NtQueryObject(Handle, ObjectBasicInformation, &Info, sizeof(Info), nullptr)) { @@ -41,7 +52,7 @@ bool ValidateHandleAccess(HANDLE Handle, ACCESS_MASK Access) { return (Info.GrantedAccess & Access) == Access; } -ScopedHandle DupHandle(HANDLE Handle, ACCESS_MASK Access) { +inline ScopedHandle DupHandle(HANDLE Handle, ACCESS_MASK Access) { HANDLE Duplicated = INVALID_HANDLE_VALUE; NtDuplicateObject(NtCurrentProcess(), Handle, NtCurrentProcess(), &Duplicated, Access, 0, 0); return ScopedHandle {Duplicated}; diff --git a/Source/Windows/Common/ImageTracker.cpp b/Source/Windows/Common/ImageTracker.cpp index 39598a67d..35f9eaaf8 100644 --- a/Source/Windows/Common/ImageTracker.cpp +++ b/Source/Windows/Common/ImageTracker.cpp @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include #include @@ -13,13 +15,31 @@ #include #include #include +#include "Common/Config.h" +#include +#include #include +#include +#include "Handle.h" #include "Module.h" +#include "Priv.h" #include "ImageTracker.h" namespace FEX::Windows { +static fextl::string ToLower(std::string_view String) { + fextl::string Res; + Res.resize(String.size()); + std::transform(String.begin(), String.end(), Res.begin(), [](unsigned char c) { return std::tolower(c); }); + return Res; +} + +FEXCore::CodeMapFileId ComputeCodeMapId(std::string_view FileName, uint32_t TimeDateStamp, uint32_t SizeOfImage) { + const auto Norm {ToLower(FileName)}; + return XXH3_64bits(Norm.data(), Norm.size()) ^ (static_cast(SizeOfImage) << 32 | TimeDateStamp); +} + static void LoadImageVolatileMetadata(fextl::set& VolatileInstructions, FEXCore::IntervalList& VolatileValidRanges, HMODULE Module, ArchImageNtHeaders* Nt, uint64_t Address, uint64_t EndAddress) { ULONG Size; @@ -59,12 +79,35 @@ ImageTracker::ImageTracker(FEXCore::Context::Context& CTX) : CTX {CTX} , ExtendedMetaData {FEX::VolatileMetadata::ParseExtendedVolatileMetadata(ExtendedVolatileMetadataConfig())} {} -void ImageTracker::HandleImageMap(std::string_view Path, uint64_t Address, bool MainImage) { - fextl::string ModuleName {BaseName(Path)}; - LogMan::Msg::DFmt("Load module {}: {:X}", ModuleName, Address); +ImageTracker::MappedImageInfo::MappedImageInfo(std::string_view Path, uint64_t Address, ArchImageNtHeaders* Nt, + fextl::unordered_map Relocations) + : Info {.FileId = ComputeCodeMapId(BaseName(Path), Nt->FileHeader.TimeDateStamp, Nt->OptionalHeader.SizeOfImage), + .Filename = ToLower(Path), // Normalize path case as Windows paths are case-insensitive + .Relocations = std::move(Relocations)} + , SectionInfo {.FileInfo = Info, .FileStartVA = Address, .BeginVA = Address, .EndVA = Address + Nt->OptionalHeader.SizeOfImage} {} +FEXCore::ExecutableFileSectionInfo ImageTracker::HandleImageMap(std::string_view Path, uint64_t Address, bool MainImage) { + std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); + const fextl::string ModuleName {BaseName(Path)}; const auto Module = reinterpret_cast(Address); auto* Nt = reinterpret_cast(RtlImageNtHeader(Module)); + MappedImageInfo* ImageInfo = nullptr; + { + std::unique_lock Lk {ImagesLock}; + auto [It, Inserted] = + MappedImages.emplace(std::piecewise_construct, std::forward_as_tuple(Address), + std::forward_as_tuple(Path, Address, Nt, fextl::unordered_map {})); + + if (!Inserted) { + return It->second.SectionInfo; + } + + ImageInfo = &It->second; + } + + auto ID = FEXCore::CodeMap::GetBaseFilename(ImageInfo->Info, false); + LogMan::Msg::DFmt("Load module {} ({}): {:X}", ModuleName, ID, Address); + uint64_t EndAddress = Address + Nt->OptionalHeader.SizeOfImage; fextl::set VolatileInstructions {}; FEXCore::IntervalList VolatileValidRanges {}; @@ -75,13 +118,26 @@ void ImageTracker::HandleImageMap(std::string_view Path, uint64_t Address, bool if (!VolatileInstructions.empty() || !VolatileValidRanges.Empty()) { LogMan::Msg::DFmt("Loaded volatile metadata for {:X}: {} entries", Address, VolatileInstructions.size()); - std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); CTX.AddForceTSOInformation(VolatileValidRanges, std::move(VolatileInstructions)); } + + return ImageInfo->SectionInfo; } void ImageTracker::HandleImageUnmap(uint64_t Address, uint64_t Size) { std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); CTX.RemoveForceTSOInformation(Address, Size); + + std::unique_lock Lk {ImagesLock}; + MappedImages.erase(Address); +} + +std::optional ImageTracker::LookupExecutableFileSection(uint64_t Address) { + std::shared_lock Lk {ImagesLock}; + auto It = MappedImages.upper_bound(Address); + if (It == MappedImages.begin() || std::prev(It)->second.SectionInfo.EndVA <= Address) { + return {}; + } + return std::prev(It)->second.SectionInfo; } } // namespace FEX::Windows diff --git a/Source/Windows/Common/ImageTracker.h b/Source/Windows/Common/ImageTracker.h index 110f4084e..f54efbe1a 100644 --- a/Source/Windows/Common/ImageTracker.h +++ b/Source/Windows/Common/ImageTracker.h @@ -9,6 +9,7 @@ #include #include +#include #include "Common/VolatileMetadata.h" #include "Module.h" @@ -30,20 +31,35 @@ using ArchImageNtHeaders = IMAGE_NT_HEADERS32; using ArchImageLoadConfigDirectory = _IMAGE_LOAD_CONFIG_DIRECTORY32; #endif +FEXCore::CodeMapFileId ComputeCodeMapId(std::string_view FileName, uint32_t TimeDateStamp, uint32_t SizeOfImage); + /** * @brief Tracks mapped PE code images and handles their volatile metadata */ class ImageTracker { public: ImageTracker(FEXCore::Context::Context& CTX); - void HandleImageMap(std::string_view Path, uint64_t Address, bool MainImage); + FEXCore::ExecutableFileSectionInfo HandleImageMap(std::string_view Path, uint64_t Address, bool MainImage); void HandleImageUnmap(uint64_t Address, uint64_t Size); + std::optional LookupExecutableFileSection(uint64_t Address); + private: + struct MappedImageInfo { + FEXCore::ExecutableFileInfo Info; + FEXCore::ExecutableFileSectionInfo SectionInfo; + + MappedImageInfo(std::string_view Path, uint64_t Address, ArchImageNtHeaders* Nt, + fextl::unordered_map Relocations); + }; + FEXCore::Context::Context& CTX; FEX_CONFIG_OPT(ExtendedVolatileMetadataConfig, EXTENDEDVOLATILEMETADATA); fextl::unordered_map ExtendedMetaData; + + std::shared_mutex ImagesLock; + std::map MappedImages; }; } // namespace FEX::Windows diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index 752b771aa..5be957143 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -455,7 +455,7 @@ public: } std::optional LookupExecutableFileSection(FEXCore::Core::InternalThreadState*, uint64_t Address) override { - return std::nullopt; + return ImageTracker->LookupExecutableFileSection(Address); } void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override {