Skip to content

[Diablo II: Resurrected]: [fails to start] #5932

Description

@EvilToasterDBU

This post create with Claude support, I hope understandings, I'm not a professional.

[ARM64EC] Diablo II: Resurrected fails to start — code executed through RX alias of a section written via a separate RW mapping


Diablo II: Resurrected (D2R.exe) fails to start under Wine ARM64EC + FEX (libarm64ecfex.dll). The game's anti-tamper wrapper D2R_loader.dll crashes during DLL initialization with EXCEPTION_ACCESS_VIOLATION (execute fault) at an address that Wine has mapped as executable.

The protector maps a single section object twice into the same process — once with SECTION_MAP_EXECUTE and once with SECTION_MAP_WRITE — writes generated code through the RW view, and jumps into the RX view. Execution at the RX alias faults even though page permissions are correct from Wine's point of view.

The process never reaches a window; it aborts roughly one second after launch.

Environment

Kernel Linux 7.2.0 #1 SMP PREEMPT Wed Aug 26 15:03:37 CEST 2026 aarch64
Proton cachyos-11.0-20260703-slr (also reproduced on GE-Proton11-5)
Emulator libarm64ecfex.dll (bundled with Proton)
Game Diablo II: Resurrected, Steam build, launched from Steam
Proton options gamedrive, forcelgadd, pipewire

Symptom

6657.665:0140:0144:warn:seh:dispatch_exception backtrace: --- Exception 0xc0000005.
6657.665:0140:0144:trace:seh:dispatch_exception code=c0000005 (EXCEPTION_ACCESS_VIOLATION) flags=0 addr=0000000001243B69
6657.665:0140:0144:trace:seh:dispatch_exception  info[0]=0000000000000008
6657.665:0140:0144:trace:seh:dispatch_exception  info[1]=0000000001243B69
6657.665:0140:0144:trace:seh:dispatch_exception rip=0000000001243b69 rsp=000000000041eb58
6657.665:0140:0144:warn:seh:virtual_unwind backtrace: 0000000001243B69: unknown module.
6657.665:0140:0144:warn:seh:virtual_unwind backtrace: 00006FFFFA8663CB: L"D2R_loader.dll" + 0000000001E163CB.
6657.665:0140:0144:warn:seh:virtual_unwind backtrace: 00006FFFFA866312: L"D2R_loader.dll" + 0000000001E16312.
6657.665:0140:0144:warn:seh:virtual_unwind backtrace: 00006FFFFA85BE8C: L"D2R_loader.dll" + 0000000001E0BE8C.
6657.665:0140:0144:warn:seh:virtual_unwind backtrace: 00006FFFFA838675: L"D2R_loader.dll" + 0000000001DE8675.
6657.665:0140:0144:warn:seh:virtual_unwind backtrace: 00006FFFFA833F35: L"D2R_loader.dll" + 0000000001DE3F35.
6657.665:0140:0144:warn:seh:virtual_unwind backtrace: 00006FFFFA836EF2: L"D2R_loader.dll" + 0000000001DE6EF2.
6657.665:0140:0144:warn:seh:virtual_unwind backtrace: 00006FFFFA837127: L"D2R_loader.dll" + 0000000001DE7127.
6657.665:0140:0144:err:module:loader_init "D2R_loader.dll" failed to initialize, aborting
6657.665:0140:0144:err:module:loader_init Initializing dlls for L"D:\Games\Diablo II Resurrected\D2R.exe" failed, status c0000005

info[0] = 8 indicates an execute fault rather than a read/write fault. The faulting address is reported as unknown module because it lies in anonymous memory, not in any loaded image.

The faulting address varies slightly between runs (0x1243A06, 0x1243A60, 0x1243A6B, 0x1243B69) — the protector lays out its generated code at a slightly different offset each time. The D2R_loader.dll return addresses are byte-identical across every run and across both Proton builds.

Root cause

A WINEDEBUG=+virtual trace shows what happens 145 ms before the crash. The protector creates one section (handle 0x80) and maps it twice into the same process with different access:

5509.489:0140:0144:trace:virtual:NtMapViewOfSection handle=0x80 process=0xffffffffffffffff addr=(nil) off=0 size=0x100000 alloc_type=0x0 access=0x20
5509.493:0140:0144:trace:virtual:virtual_map_section handle=0x80 size=100000 offset=0
5509.493:0140:0144:trace:virtual:dump_view View: 0x1240000 - 0x133ffff c-r-x (anonymous)
5509.493:0140:0144:trace:virtual:dump_view       0x1240000 - 0x133ffff c-r-x
5509.493:0140:0144:trace:virtual:virtual_map_section status 0.

5509.493:0140:0144:trace:virtual:NtMapViewOfSection handle=0x80 process=0xffffffffffffffff addr=(nil) off=0 size=0x100000 alloc_type=0x0 access=0x4
5509.494:0140:0144:trace:virtual:virtual_map_section handle=0x80 size=100000 offset=0
5509.494:0140:0144:trace:virtual:dump_view View: 0x1340000 - 0x143ffff c-rw- (anonymous)
5509.494:0140:0144:trace:virtual:dump_view       0x1340000 - 0x143ffff c-rw-
5509.494:0140:0144:trace:virtual:virtual_map_section status 0.

So:

  • 0x1240000 - 0x133ffffSECTION_MAP_EXECUTE (access=0x20), mapped r-x
  • 0x1340000 - 0x143ffffSECTION_MAP_WRITE (access=0x4), mapped rw-

Both views back the same section. The protector unpacks code through the RW view and executes it through the RX view — a standard W^X bypass that never requests an RWX page.

The faulting rip = 0x1243b69 lies inside the RX view, at offset ~0x3B69 from its base. The region is never unmapped, never freed, and no NtProtectVirtualMemory call is applied to it — I grepped the full trace for the range and these two NtMapViewOfSection calls are the only operations touching it.

Wine's page permissions are therefore correct. Execution at 0x1243b69 is legitimate as far as Wine is concerned, which suggests the fault originates in the emulator rather than from a real permission violation.

My working hypothesis: FEX tracks self-modifying code against the mapping it executes from. The writes land at 0x1340000, while the translated block is keyed to 0x1240000, so the modification is not observed and stale or zero-filled bytes are translated.

One more possibly relevant detail: NtAreMappedFilesTheSame is called 3777 times during startup. The protector actively verifies that the two views refer to the same underlying object, so this aliasing is deliberate and load-bearing, not incidental.

Reproduction

  1. ARM64 host, Wine ARM64EC + FEX via Proton
  2. Install Diablo II: Resurrected (Steam)
  3. Launch from Steam
  4. Process aborts before any window appears

Reproduced on every attempt across two Proton builds.

What I tried

  • GE-Proton11-5 and cachyos-11.0-20260703-slr — identical failure, identical D2R_loader.dll offsets
  • Reinstalling the same Proton build to a different path — no change
  • FEX_SMCCHECKS=Full FEX_TSOENABLED=1 in the Steam launch options — no observable change; I could not confirm from the log whether these variables are read at all by the libarm64ecfex.dll shipped inside Proton. If there is a supported way to configure the in-Proton FEX build, or to confirm which settings are active, I am happy to re-test.

Log:
steam-16146081220290150400.log

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions