Merge pull request #5164 from bylaws/gmeo

ImageTracker: Track loaded PE images for LookupExecutableFileSection
This commit is contained in:
Ryan Houdek authored and GitHub committed 2025-12-28 12:08:07 -08:00
commit 212a3f45f8
7 files changed
+95 -15

No files matched your search

@@ -11,7 +11,6 @@
#include <Interface/IR/PassManager.h>
#include <FEXCore/Core/Thunks.h>
#include <FEXCore/HLE/SourcecodeResolver.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXHeaderUtils/Filesystem.h>
@@ -30,7 +29,6 @@ ExecutableFileInfo::ExecutableFileInfo(fextl::unique_ptr<HLE::SourcecodeMap> Map
, FileId(FileId)
, Filename(Filename) {}
#endif
ExecutableFileInfo::~ExecutableFileInfo() = default;
fextl::string CodeMap::GetBaseFilename(const ExecutableFileInfo& MainExecutable, bool AddNombSuffix) {
auto FileId = MainExecutable.FileId;
+1 -2
View File
@@ -8,6 +8,7 @@
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/vector.h>
#include <FEXCore/fextl/unordered_map.h>
#include <FEXCore/HLE/SourcecodeResolver.h>
#include <atomic>
#include <cstdint>
@@ -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<HLE::SourcecodeMap>, uint64_t, fextl::string);
+1 -1
View File
@@ -522,7 +522,7 @@ public:
}
std::optional<FEXCore::ExecutableFileSectionInfo> 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 {
+15 -4
View File
@@ -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};
+60 -4
View File
@@ -2,8 +2,10 @@
#include <array>
#include <mutex>
#include <filesystem>
#include <optional>
#include <string_view>
#include <cctype>
#include <FEXCore/Core/Context.h>
#include <FEXCore/Debug/InternalThreadState.h>
@@ -13,13 +15,31 @@
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/vector.h>
#include "Common/Config.h"
#include <fcntl.h>
#include <io.h>
#include <winternl.h>
#include <xxhash.h>
#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<uint64_t>(SizeOfImage) << 32 | TimeDateStamp);
}
static void LoadImageVolatileMetadata(fextl::set<uint64_t>& VolatileInstructions, FEXCore::IntervalList<uint64_t>& 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<uint32_t, FEXCore::GuestRelocationType> 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<HMODULE>(Address);
auto* Nt = reinterpret_cast<ArchImageNtHeaders*>(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<uint32_t, FEXCore::GuestRelocationType> {}));
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<uint64_t> VolatileInstructions {};
FEXCore::IntervalList<uint64_t> 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<FEXCore::ExecutableFileSectionInfo> 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
+17 -1
View File
@@ -9,6 +9,7 @@
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CodeCache.h>
#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<FEXCore::ExecutableFileSectionInfo> 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<uint32_t, FEXCore::GuestRelocationType> Relocations);
};
FEXCore::Context::Context& CTX;
FEX_CONFIG_OPT(ExtendedVolatileMetadataConfig, EXTENDEDVOLATILEMETADATA);
fextl::unordered_map<fextl::string, FEX::VolatileMetadata::ExtendedVolatileMetadata> ExtendedMetaData;
std::shared_mutex ImagesLock;
std::map<uint64_t, MappedImageInfo> MappedImages;
};
} // namespace FEX::Windows
+1 -1
View File
@@ -455,7 +455,7 @@ public:
}
std::optional<FEXCore::ExecutableFileSectionInfo> 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 {