From 75aa4e22d6e84711efbb81be39d2cdebf2fda51a Mon Sep 17 00:00:00 2001 From: Ericky14 Date: Wed, 2 Sep 2026 19:26:00 -0400 Subject: [PATCH 1/3] [DYNAREC] Add BOX64_DYNAREC_LDACQ to apply STRONGMEM=4 to selected ranges Co-Authored-By: Claude Opus 5 (1M context) --- docs/USAGE.md | 6 +++++ src/dynarec/dynarec_helper.h | 7 +++++- src/include/env.h | 1 + src/include/pe_tools.h | 6 +++++ src/os/os_wine.c | 9 +++++++ src/tools/env.c | 1 + src/tools/pe_tools.c | 46 ++++++++++++++++++++++++++++++++++++ 7 files changed, 75 insertions(+), 1 deletion(-) diff --git a/docs/USAGE.md b/docs/USAGE.md index b03ef8eabf..38ed16d110 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -170,6 +170,12 @@ 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_LDACQ + +Apply BOX64_DYNAREC_STRONGMEM level 4 to selected code ranges only, leaving the rest of the program at the global level. + + * module:XXXX-YYYY[,...]: Basename of a module and the hexadecimal RVA range (inclusive-exclusive) to apply it to. + ### 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..d8551061f6 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() \ + (LdAcqRangesContains(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..e3efa7c475 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_LDACQ, dynarec_ldacq, 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..5b85399d29 100644 --- a/src/include/pe_tools.h +++ b/src/include/pe_tools.h @@ -11,4 +11,10 @@ int VolatileRangesContains(uintptr_t addr); // Check if a given address is contained within the volatile opcode entries. int VolatileOpcodesHas(uintptr_t addr); +// Register load-acquire ranges for a module, from a ":-[,...]" spec. +void RegisterLdAcqRanges(const char* spec, const char* filename, void* addr); + +// Check if a given address is contained within the load-acquire ranges. +int LdAcqRangesContains(uintptr_t addr); + #endif // __PE_TOOLS_H__ diff --git a/src/os/os_wine.c b/src/os/os_wine.c index 123f0574a5..6133021053 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 RegisterLdAcqRanges(const char* spec, const char* filename, void* addr) +{ +} + +int LdAcqRangesContains(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..8e0d4d24c8 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_ldacq)) RegisterLdAcqRanges(BOX64ENV(dynarec_ldacq), 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..29ad869cc8 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,48 @@ int VolatileOpcodesHas(uintptr_t addr) if (!volatileOpcodes) return 0; return kh_get(volatileopcode, volatileOpcodes, addr) != kh_end(volatileOpcodes); } + +static rbtree_t* ldacqRanges = NULL; // never freed + +void RegisterLdAcqRanges(const char* spec, const char* filename, void* addr) +{ + if (!spec || !*spec || !filename) return; + + const char* baseName = strrchr(filename, '/'); + baseName = baseName ? baseName + 1 : filename; + size_t baseLen = strlen(baseName); + + const char* p = spec; + while (*p) { + while (*p == ',' || *p == ' ') ++p; + if (!*p) break; + + const char* colon = strchr(p, ':'); + const char* comma = strchr(p, ','); + if (!colon || (comma && colon > comma)) { + p = comma ? comma + 1 : p + strlen(p); + continue; + } + size_t nameLen = (size_t)(colon - p); + const char* rest = colon + 1; + + unsigned long long s = 0, e = 0; + if (sscanf(rest, "%llx-%llx", &s, &e) == 2 && e > s + && nameLen == baseLen && !strncasecmp(baseName, p, nameLen)) { + if (!ldacqRanges) ldacqRanges = rbtree_init("ldacqRanges"); + rb_set(ldacqRanges, (uintptr_t)addr + (uintptr_t)s, (uintptr_t)addr + (uintptr_t)e, 1); + printf_log(LOG_INFO, "LDACQ 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); + } + + const char* next = strchr(rest, ','); + p = next ? next + 1 : rest + strlen(rest); + } +} + +int LdAcqRangesContains(uintptr_t addr) +{ + if (!ldacqRanges) return 0; + return rb_get(ldacqRanges, addr) != 0; +} From 798e98ca25640347345fc27de0dc94fdd92b189c Mon Sep 17 00:00:00 2001 From: Ericky14 Date: Thu, 3 Sep 2026 03:05:16 -0400 Subject: [PATCH 2/3] chore: rename BOX64_DYNAREC_LDACQ to BOX64_DYNAREC_STRONGMEM_RANGE --- docs/USAGE.md | 2 +- src/include/env.h | 2 +- src/tools/env.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/USAGE.md b/docs/USAGE.md index 38ed16d110..cf3b49c96c 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -170,7 +170,7 @@ 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_LDACQ +### 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. diff --git a/src/include/env.h b/src/include/env.h index e3efa7c475..40da0533e3 100644 --- a/src/include/env.h +++ b/src/include/env.h @@ -62,7 +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_LDACQ, dynarec_ldacq, 1, 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/tools/env.c b/src/tools/env.c index 8e0d4d24c8..2b3ad830e2 100644 --- a/src/tools/env.c +++ b/src/tools/env.c @@ -867,7 +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_ldacq)) RegisterLdAcqRanges(BOX64ENV(dynarec_ldacq), fullname, (void*)addr); + if (BOX64ENV(dynarec_strongmem_range)) RegisterLdAcqRanges(BOX64ENV(dynarec_strongmem_range), fullname, (void*)addr); #if defined(DYNAREC) && !defined(WIN32) int dynacache = box64env.dynacache; if(mapping->env && mapping->env->is_dynacache_overridden) From f5bf954b3f604a2e759b19822299cb35312c8b15 Mon Sep 17 00:00:00 2001 From: Ericky14 Date: Thu, 3 Sep 2026 16:28:44 -0400 Subject: [PATCH 3/3] [DYNAREC] Accept a plain address range in BOX64_DYNAREC_STRONGMEM_RANGE Co-Authored-By: Claude Opus 5 --- docs/USAGE.md | 5 ++- src/dynarec/dynarec_helper.h | 2 +- src/include/pe_tools.h | 9 +++--- src/os/os_wine.c | 4 +-- src/tools/env.c | 2 +- src/tools/pe_tools.c | 60 ++++++++++++++++++++++-------------- 6 files changed, 50 insertions(+), 32 deletions(-) diff --git a/docs/USAGE.md b/docs/USAGE.md index cf3b49c96c..c095a05bfa 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -174,7 +174,10 @@ Enable the emulation of x86 strong memory model. Available in WowBox64. Apply BOX64_DYNAREC_STRONGMEM level 4 to selected code ranges only, leaving the rest of the program at the global level. - * module:XXXX-YYYY[,...]: Basename of a module and the hexadecimal RVA range (inclusive-exclusive) to apply it to. + * 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 diff --git a/src/dynarec/dynarec_helper.h b/src/dynarec/dynarec_helper.h index d8551061f6..94b2ef7f0f 100644 --- a/src/dynarec/dynarec_helper.h +++ b/src/dynarec/dynarec_helper.h @@ -39,7 +39,7 @@ #define STRONGMEM_QEMU 4 // The level of a mimic to QEMU's strong memory model #define STRONGMEM_LEVEL() \ - (LdAcqRangesContains(ip) \ + (StrongMemRangesContains(ip) \ ? STRONGMEM_QEMU \ : ((box64_wine && VolatileRangesContains(ip)) \ ? 0 \ diff --git a/src/include/pe_tools.h b/src/include/pe_tools.h index 5b85399d29..900332bb08 100644 --- a/src/include/pe_tools.h +++ b/src/include/pe_tools.h @@ -11,10 +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 load-acquire ranges for a module, from a ":-[,...]" spec. -void RegisterLdAcqRanges(const char* spec, const char* filename, void* 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 load-acquire ranges. -int LdAcqRangesContains(uintptr_t 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 6133021053..540da18ac4 100644 --- a/src/os/os_wine.c +++ b/src/os/os_wine.c @@ -301,11 +301,11 @@ int VolatileOpcodesHas(uintptr_t addr) return 0; } -void RegisterLdAcqRanges(const char* spec, const char* filename, void* addr) +void RegisterStrongMemRanges(const char* spec, const char* filename, void* addr) { } -int LdAcqRangesContains(uintptr_t addr) +int StrongMemRangesContains(uintptr_t addr) { return 0; } diff --git a/src/tools/env.c b/src/tools/env.c index 2b3ad830e2..7802e96b30 100644 --- a/src/tools/env.c +++ b/src/tools/env.c @@ -867,7 +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)) RegisterLdAcqRanges(BOX64ENV(dynarec_strongmem_range), 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 29ad869cc8..ea325a874b 100644 --- a/src/tools/pe_tools.c +++ b/src/tools/pe_tools.c @@ -327,47 +327,61 @@ int VolatileOpcodesHas(uintptr_t addr) return kh_get(volatileopcode, volatileOpcodes, addr) != kh_end(volatileOpcodes); } -static rbtree_t* ldacqRanges = NULL; // never freed +static rbtree_t* strongMemRanges = NULL; // never freed -void RegisterLdAcqRanges(const char* spec, const char* filename, void* addr) +void RegisterStrongMemRanges(const char* spec, const char* filename, void* addr) { - if (!spec || !*spec || !filename) return; + static int absolute_done = 0; - const char* baseName = strrchr(filename, '/'); - baseName = baseName ? baseName + 1 : filename; - size_t baseLen = strlen(baseName); + 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* colon = strchr(p, ':'); const char* comma = strchr(p, ','); - if (!colon || (comma && colon > comma)) { - p = comma ? comma + 1 : p + strlen(p); - continue; - } - size_t nameLen = (size_t)(colon - p); - const char* rest = colon + 1; + 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 - && nameLen == baseLen && !strncasecmp(baseName, p, nameLen)) { - if (!ldacqRanges) ldacqRanges = rbtree_init("ldacqRanges"); - rb_set(ldacqRanges, (uintptr_t)addr + (uintptr_t)s, (uintptr_t)addr + (uintptr_t)e, 1); - printf_log(LOG_INFO, "LDACQ 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); + 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 LdAcqRangesContains(uintptr_t addr) +int StrongMemRangesContains(uintptr_t addr) { - if (!ldacqRanges) return 0; - return rb_get(ldacqRanges, addr) != 0; + if (!strongMemRanges) return 0; + return rb_get(strongMemRanges, addr) != 0; }