diff --git a/docs/USAGE.md b/docs/USAGE.md index b03ef8eabf..c095a05bfa 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -170,6 +170,15 @@ Enable the emulation of x86 strong memory model. Available in WowBox64. * 3: All in 2, plus more memory barriers on a regular basis. * 4: Mimic x86 TSO similarly to QEMU's approach, for evaluation purposes. +### BOX64_DYNAREC_STRONGMEM_RANGE + +Apply BOX64_DYNAREC_STRONGMEM level 4 to selected code ranges only, leaving the rest of the program at the global level. + + * 0xXXXXXXXX-0xYYYYYYYY: Define the range of addresses (inclusive-exclusive) to apply it to. + * module:XXXX-YYYY: Same, but expressed as an hexadecimal RVA range inside a module, resolved when that module is mapped. Useful when the load address is not fixed. + +Several ranges can be given, separated by commas. + ### BOX64_DYNAREC_VOLATILE_METADATA Use volatile metadata parsed from PE files, only valid for 64bit Windows games. diff --git a/src/dynarec/dynarec_helper.h b/src/dynarec/dynarec_helper.h index 700f290ab5..94b2ef7f0f 100644 --- a/src/dynarec/dynarec_helper.h +++ b/src/dynarec/dynarec_helper.h @@ -38,7 +38,12 @@ #define STRONGMEM_SEQ_WRITE 3 // The level of a barrier at every third memory store will be put #define STRONGMEM_QEMU 4 // The level of a mimic to QEMU's strong memory model -#define STRONGMEM_LEVEL() ((box64_wine && VolatileRangesContains(ip)) ? 0 : BOX64DRENV(dynarec_strongmem)) +#define STRONGMEM_LEVEL() \ + (StrongMemRangesContains(ip) \ + ? STRONGMEM_QEMU \ + : ((box64_wine && VolatileRangesContains(ip)) \ + ? 0 \ + : BOX64DRENV(dynarec_strongmem))) #if STEP == 1 diff --git a/src/include/env.h b/src/include/env.h index 6da8dacb76..40da0533e3 100644 --- a/src/include/env.h +++ b/src/include/env.h @@ -62,6 +62,7 @@ extern char* ftrace_name; INTEGER(BOX64_DYNAREC_FASTROUND, dynarec_fastround, 1, 0, 2, 1, 2) \ INTEGER(BOX64_DYNAREC_FORWARD, dynarec_forward, 128, 0, 1024, 1, 3) \ STRING(BOX64_DYNAREC_GDBJIT, dynarec_gdbjit_str, 0, 0) \ + STRING(BOX64_DYNAREC_STRONGMEM_RANGE, dynarec_strongmem_range, 1, 0) \ INTEGER(BOX64_DYNAREC_LOG, dynarec_log, 0, 0, 3, 1, 0) \ INTEGER(BOX64_DYNAREC_MISSING, dynarec_missing, 0, 0, 2, 1, 0) \ BOOLEAN(BOX64_DYNAREC_NATIVEFLAGS, dynarec_nativeflags, 1, 1, 1) \ diff --git a/src/include/pe_tools.h b/src/include/pe_tools.h index 81f4a1e150..900332bb08 100644 --- a/src/include/pe_tools.h +++ b/src/include/pe_tools.h @@ -11,4 +11,11 @@ int VolatileRangesContains(uintptr_t addr); // Check if a given address is contained within the volatile opcode entries. int VolatileOpcodesHas(uintptr_t addr); +// Register strong memory ranges, from a comma separated spec of "-" (plain addresses) +// and/or ":-" (resolved when that module is mapped) entries. +void RegisterStrongMemRanges(const char* spec, const char* filename, void* addr); + +// Check if a given address is contained within the strong memory ranges. +int StrongMemRangesContains(uintptr_t addr); + #endif // __PE_TOOLS_H__ diff --git a/src/os/os_wine.c b/src/os/os_wine.c index 123f0574a5..540da18ac4 100644 --- a/src/os/os_wine.c +++ b/src/os/os_wine.c @@ -301,6 +301,15 @@ int VolatileOpcodesHas(uintptr_t addr) return 0; } +void RegisterStrongMemRanges(const char* spec, const char* filename, void* addr) +{ +} + +int StrongMemRangesContains(uintptr_t addr) +{ + return 0; +} + void PrintfFtrace(int prefix, const char* fmt, ...) { static char buf[1024] = { 0 }; diff --git a/src/tools/env.c b/src/tools/env.c index 37e3caa4f0..7802e96b30 100644 --- a/src/tools/env.c +++ b/src/tools/env.c @@ -867,6 +867,7 @@ void RecordEnvMappings(uintptr_t addr, size_t length, int fd) // First time we see this file if (box64_wine && BOX64ENV(unityplayer)) DetectUnityPlayer(lowercase_filename+1); if (box64_wine && !box64_is32bits && BOX64ENV(dynarec_volatile_metadata)) ParseVolatileMetadata(fullname, (void*)addr); + if (BOX64ENV(dynarec_strongmem_range)) RegisterStrongMemRanges(BOX64ENV(dynarec_strongmem_range), fullname, (void*)addr); #if defined(DYNAREC) && !defined(WIN32) int dynacache = box64env.dynacache; if(mapping->env && mapping->env->is_dynacache_overridden) diff --git a/src/tools/pe_tools.c b/src/tools/pe_tools.c index 048e9aca12..ea325a874b 100644 --- a/src/tools/pe_tools.c +++ b/src/tools/pe_tools.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -325,3 +326,62 @@ int VolatileOpcodesHas(uintptr_t addr) if (!volatileOpcodes) return 0; return kh_get(volatileopcode, volatileOpcodes, addr) != kh_end(volatileOpcodes); } + +static rbtree_t* strongMemRanges = NULL; // never freed + +void RegisterStrongMemRanges(const char* spec, const char* filename, void* addr) +{ + static int absolute_done = 0; + + if (!spec || !*spec) return; + + const char* baseName = NULL; + size_t baseLen = 0; + if (filename) { + baseName = strrchr(filename, '/'); + baseName = baseName ? baseName + 1 : filename; + baseLen = strlen(baseName); + } + + const char* p = spec; + while (*p) { + while (*p == ',' || *p == ' ') ++p; + if (!*p) break; + + const char* comma = strchr(p, ','); + const char* colon = strchr(p, ':'); + if (colon && comma && colon > comma) colon = NULL; // that colon belongs to a later entry + const char* rest = colon ? colon + 1 : p; + + unsigned long long s = 0, e = 0; + if (sscanf(rest, "%llx-%llx", &s, &e) == 2 && e > s) { + if (colon) { + // ":-": resolved when that module is mapped + size_t nameLen = (size_t)(colon - p); + if (baseName && nameLen == baseLen && !strncasecmp(baseName, p, nameLen)) { + if (!strongMemRanges) strongMemRanges = rbtree_init("strongMemRanges"); + rb_set(strongMemRanges, (uintptr_t)addr + (uintptr_t)s, (uintptr_t)addr + (uintptr_t)e, 1); + printf_log(LOG_INFO, "StrongMem range for %s: %p-%p (rva %llx-%llx)\n", + baseName, (void*)((uintptr_t)addr + (uintptr_t)s), + (void*)((uintptr_t)addr + (uintptr_t)e), s, e); + } + } else if (!absolute_done) { + // "-": plain addresses, module independent, registered once + if (!strongMemRanges) strongMemRanges = rbtree_init("strongMemRanges"); + rb_set(strongMemRanges, (uintptr_t)s, (uintptr_t)e, 1); + printf_log(LOG_INFO, "StrongMem range %p-%p\n", (void*)(uintptr_t)s, (void*)(uintptr_t)e); + } + } + + const char* next = strchr(rest, ','); + p = next ? next + 1 : rest + strlen(rest); + } + + absolute_done = 1; +} + +int StrongMemRangesContains(uintptr_t addr) +{ + if (!strongMemRanges) return 0; + return rb_get(strongMemRanges, addr) != 0; +}