Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it makes no sense to support module: syntax. Please take a look at USAGE.md; see how the RC file works.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personaly like the module syntax! It make sense to me, as many module can be oad at different addresses, and having a module+offset is a nice to have feature, that I would like to see on other settings that use address ranges

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we agree that this is a rarely used option, probably only in Cyberpunk 2077? If we agree, I would like

[Cyberpunk2077.exe]
BOX64_DYNAREC_STRONGMEM_RANGE=0x142000-0x144000

instead of

BOX64_DYNAREC_STRONGMEM_RANGE=Cyberpunk2077.exe:142000-144000

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can keep this as a general debug option, I am trying to look deeper into the Cyberpunk issue to find the root cause and what really caused it, it probably isn’t a good idea to hard code the address range since it can change

@ptitSeb ptitSeb Sep 4, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we agree that this is a rarely used option, probably only in Cyberpunk 2077? If we agree, I would like

[Cyberpunk2077.exe]
BOX64_DYNAREC_STRONGMEM_RANGE=0x142000-0x144000

instead of

BOX64_DYNAREC_STRONGMEM_RANGE=Cyberpunk2077.exe:142000-144000

To me, that's not the same things at all. The first range means absolute address (which, for this game, are probably not a avalid range as the main exe loads at 0x14000000000), while the second is a range of relative address, based on the load address of Cyberpunk2077.exe
The point is, we could also use a dll as the base address for a range. It can be really usefull when the exe or dll loads at a non-fixed address

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I see. Then we should only keep this form: BOX64_DYNAREC_STRONGMEM_RANGE=Cyberpunk2077.exe:142000-144000, not both.


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.
Expand Down
7 changes: 6 additions & 1 deletion src/dynarec/dynarec_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions src/include/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
7 changes: 7 additions & 0 deletions src/include/pe_tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<start>-<end>" (plain addresses)
// and/or "<module>:<rvaStart>-<rvaEnd>" (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__
9 changes: 9 additions & 0 deletions src/os/os_wine.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
1 change: 1 addition & 0 deletions src/tools/env.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
60 changes: 60 additions & 0 deletions src/tools/pe_tools.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <stddef.h>

Expand Down Expand Up @@ -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) {
// "<module>:<rvaStart>-<rvaEnd>": 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) {
// "<start>-<end>": 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;
}
Loading