[Bug 44650] New: Multiple Blizzard games need dxgi and d3d11 dlls mapped without hole between two LOAD segments (Diablo III v2. 6. 1. 49286+, World of Warcraft, Overwatch)
wine-bugs at winehq.org
wine-bugs at winehq.org
Sun Mar 4 07:24:48 CST 2018
https://bugs.winehq.org/show_bug.cgi?id=44650
Bug ID: 44650
Summary: Multiple Blizzard games need dxgi and d3d11 dlls
mapped without hole between two LOAD segments (Diablo
III v2. 6. 1. 49286+, World of Warcraft, Overwatch)
Product: Wine
Version: 3.3
Hardware: x86-64
OS: Linux
Status: NEW
Severity: normal
Priority: P2
Component: ntdll
Assignee: wine-bugs at winehq.org
Reporter: focht at gmx.net
Distribution: ---
Hello folks,
this is mentioned in bug 40479 (first in
https://bugs.winehq.org/show_bug.cgi?id=40479#c9 with some hack) but was never
properly explained to a wider audience.
Bug 40479 is already horribly messed up with various issues mixed in and
end-user support/discussion hence I'm tracking this interesting problem here to
discuss a proper solution.
The inaccessible memory "hole" is by design.
Relevant trace log:
--- snip ---
$ pwd
/home/focht/wine-games/wineprefix64-bnet/drive_c/Program Files (x86)/Overwatch
$ WINEDEBUG=+seh,+loaddll,+relay,+ntdll,+virtual wine64 ./Overwatch.exe
>>log.txt 2>&1
...
0030:trace:module:get_load_order looking for L"C:\\windows\\system32\\dxgi.dll"
0030:trace:module:get_load_order got hardcoded default for L"dxgi.dll"
0030:trace:module:load_dll L"C:\\windows\\system32\\dxgi.dll" is a fake Wine
dll
0030:trace:module:load_builtin_dll Trying built-in L"dxgi.dll"
0030:trace:virtual:virtual_create_builtin_view created
0x7fc4ad700000-0x7fc4ad938000
0030:trace:virtual:VIRTUAL_DumpView View: 0x7fc4ad700000 - 0x7fc4ad937fff
(builtin image)
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4ad700000 - 0x7fc4ad700fff c-r--
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4ad701000 - 0x7fc4ad936fff c-r-x
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4ad937000 - 0x7fc4ad937fff c-rw-
...
0030:trace:virtual:NtProtectVirtualMemory 0xffffffffffffffff 0x7fc4ad937000
00001000 00000008
0030:trace:virtual:mprotect_exec forcing exec permission on
0x7fc4ad937000-0x7fc4ad937fff
0030:trace:virtual:VIRTUAL_DumpView View: 0x7fc4ad700000 - 0x7fc4ad937fff
(builtin image)
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4ad700000 - 0x7fc4ad700fff c-r--
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4ad701000 - 0x7fc4ad936fff c-r-x
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4ad937000 - 0x7fc4ad937fff c-rW-
0030:trace:module:load_builtin_callback loaded dxgi.dll 0x65680 0x7fc4ad700000
0030:trace:module:load_dll Loaded module L"C:\\windows\\system32\\dxgi.dll"
(builtin) at 0x7fc4ad700000
0030:trace:virtual:NtProtectVirtualMemory 0xffffffffffffffff 0x7fc4adc01d98
00000018 00000004
0030:trace:virtual:mprotect_exec forcing exec permission on
0x7fc4adc01000-0x7fc4adc01fff
0030:trace:virtual:VIRTUAL_DumpView View: 0x7fc4ad970000 - 0x7fc4adc03fff
(builtin image)
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4ad970000 - 0x7fc4ad970fff c-r--
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4ad971000 - 0x7fc4adc00fff c-r-x
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4adc01000 - 0x7fc4adc01fff c-rW-
0030:trace:virtual:VIRTUAL_DumpView 0x7fc4adc02000 - 0x7fc4adc03fff c-rw-
0030:trace:imports:import_dll --- CreateDXGIFactory1 dxgi.dll.2 =
0x7fc4ad710440
0030:trace:imports:import_dll --- DXGID3D10CreateDevice dxgi.dll.4 =
0x7fc4ad71047c
0030:trace:imports:import_dll --- DXGID3D10RegisterLayers dxgi.dll.5 =
0x7fc4ad7104a0
...
0030:Call KERNEL32.VirtualAlloc(00000000,00238000,00001000,00000004,)
ret=14001f538
0030:trace:virtual:NtAllocateVirtualMemory 0xffffffffffffffff (nil) 00238000
1000 00000004
0030:trace:virtual:map_view got mem in reserved area 0x3690000-0x38c8000
0030:trace:virtual:create_view forcing exec permission on 0x3690000-0x38c7fff
0030:trace:virtual:VIRTUAL_DumpView View: 0x3690000 - 0x38c7fff (valloc)
0030:trace:virtual:VIRTUAL_DumpView 0x3690000 - 0x38c7fff c-rw-
0030:Ret KERNEL32.VirtualAlloc() retval=03690000 ret=14001f538
0030:trace:seh:NtRaiseException code=c0000005 flags=0 addr=0x14001f5fa
ip=14001f5fa tid=0030
0030:trace:seh:NtRaiseException info[0]=0000000000000000
0030:trace:seh:NtRaiseException info[1]=00007fc4ad737000
0030:trace:seh:NtRaiseException rax=00000000079e4455 rbx=00007fc4ad700040
rcx=0000000000201000 rdx=000000007bdb5210
0030:trace:seh:NtRaiseException rsi=00007fc4ad737000 rdi=00000000036c7000
rbp=00000000002360a0 rsp=0000000000235fa0
0030:trace:seh:NtRaiseException r8=0000000003690000 r9=000000014001f538
r10=0000000000000000 r11=0000000000000246
0030:trace:seh:NtRaiseException r12=0000000000000014 r13=000000007ffe0030
r14=000000007b472568 r15=0000000000065680
...
--- snip ---
Overwatch/WoW make private in-memory copies of 'dxgi.dll' and 'd3d11.dll' as
early as possible (loader notifications). This is done as another anti-cheat
measure/method to detect hooking/hotpatching/proxying of DirectX interfaces at
runtime by comparing real API entry points with the copies.
It assumes that all parts of the in-memory image are readable but it's not due
to the way the static and dynamic linker work.
Relevant 'strace' part, annotated.
--- snip ---
$ strace -e trace=mmap,mprotect,munmap,open -o strace.log wine64
./Overwatch.exe
...
open("/home/focht/projects/wine/wine.repo/install/bin/../lib64/wine/dxgi.dll.so",
O_RDONLY|O_CLOEXEC) = 11
; --- map first "LOAD" segment (.text, ...)
mmap(NULL, 2391592, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 11, 0) =
0x7f195c3e3000
; --- make alignment "hole" inaccessible
mprotect(0x7f195c42a000, 2093056, PROT_NONE) = 0
; --- map second "LOAD" segment (.data, .bss, ...)
mmap(0x7f195c629000, 8192, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 11, 0x46000) = 0x7f195c629000
; --- .got.plt .data sections within segment
mprotect(0x7f195c3e3000, 290816, PROT_READ|PROT_WRITE) = 0
mprotect(0x7f195c3e3000, 290816, PROT_READ|PROT_EXEC) = 0
mprotect(0x7f195c629000, 4096, PROT_READ) = 0
; --- remaining zero-filled sections (e.g. .bss)
mmap(0x7f195c400000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f195c400000
...
; --- Page fault due to game code accessing the "hole".
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7f195c42a000} ---
...
--- snip ---
Dumping the ELF headers:
--- snip ---
$ readelf -a install/lib64/wine/dxgi.dll.so
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x20320
Start of program headers: 64 (bytes into file)
Start of section headers: 693488 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 7
Size of section headers: 64 (bytes)
Number of section headers: 35
Section header string table index: 32
...
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.build-i NOTE 00000000000001c8 000001c8
0000000000000024 0000000000000000 A 0 0 4
[ 2] .gnu.hash GNU_HASH 00000000000001f0 000001f0
0000000000000044 0000000000000000 A 3 0 8
[ 3] .dynsym DYNSYM 0000000000000238 00000238
0000000000000270 0000000000000018 A 4 2 8
[ 4] .dynstr STRTAB 00000000000004a8 000004a8
00000000000001cb 0000000000000000 A 0 0 1
[ 5] .gnu.version VERSYM 0000000000000674 00000674
0000000000000034 0000000000000002 A 3 0 2
[ 6] .gnu.version_r VERNEED 00000000000006a8 000006a8
0000000000000050 0000000000000000 A 4 2 8
[ 7] .rela.dyn RELA 00000000000006f8 000006f8
000000000000eb98 0000000000000018 A 3 0 8
[ 8] .rela.plt RELA 000000000000f290 0000f290
0000000000000030 0000000000000018 AI 3 10 8
[ 9] .init PROGBITS 000000000000f2c0 0000f2c0
0000000000011024 0000000000000000 AX 0 0 4
[10] .plt PROGBITS 00000000000202f0 000202f0
0000000000000030 0000000000000010 AX 0 0 16
[11] .text PROGBITS 0000000000020320 00020320
00000000000149fd 0000000000000000 AX 0 0 16
[12] .fini PROGBITS 0000000000034d20 00034d20
0000000000000009 0000000000000000 AX 0 0 4
[13] .rodata PROGBITS 0000000000034d40 00034d40
000000000000bdb0 0000000000000000 A 0 0 32
[14] .eh_frame_hdr PROGBITS 0000000000040af0 00040af0
0000000000000974 0000000000000000 A 0 0 4
[15] .eh_frame PROGBITS 0000000000041468 00041468
000000000000556c 0000000000000000 A 0 0 8
[16] .init_array INIT_ARRAY 0000000000246dc8 00046dc8
0000000000000008 0000000000000000 WA 0 0 8
[17] .fini_array FINI_ARRAY 0000000000246dd0 00046dd0
0000000000000008 0000000000000000 WA 0 0 8
[18] .jcr PROGBITS 0000000000246dd8 00046dd8
0000000000000008 0000000000000000 WA 0 0 8
[19] .data.rel.ro PROGBITS 0000000000246de0 00046de0
0000000000000008 0000000000000000 WA 0 0 8
[20] .dynamic DYNAMIC 0000000000246de8 00046de8
00000000000001f0 0000000000000010 WA 4 0 8
[21] .got PROGBITS 0000000000246fd8 00046fd8
0000000000000028 0000000000000008 WA 0 0 8
[22] .got.plt PROGBITS 0000000000247000 00047000
0000000000000028 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000247030 00047030
0000000000000dc0 0000000000000000 WA 0 0 16
[24] .bss NOBITS 0000000000247df0 00047df0
0000000000000038 0000000000000000 WA 0 0 16
...
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000469d4 0x00000000000469d4 R E 200000
LOAD 0x0000000000046dc8 0x0000000000246dc8 0x0000000000246dc8
0x0000000000001028 0x0000000000001060 RW 200000
DYNAMIC 0x0000000000046de8 0x0000000000246de8 0x0000000000246de8
0x00000000000001f0 0x00000000000001f0 RW 8
NOTE 0x00000000000001c8 0x00000000000001c8 0x00000000000001c8
0x0000000000000024 0x0000000000000024 R 4
GNU_EH_FRAME 0x0000000000040af0 0x0000000000040af0 0x0000000000040af0
0x0000000000000974 0x0000000000000974 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000046dc8 0x0000000000246dc8 0x0000000000246dc8
0x0000000000000238 0x0000000000000238 R 1
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version
.gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr
.eh_fr
ame
01 .init_array .fini_array .jcr .data.rel.ro .dynamic .got .got.plt
.data .bss
02 .dynamic
03 .note.gnu.build-id
04 .eh_frame_hdr
05
06 .init_array .fini_array .jcr .data.rel.ro .dynamic .got
--- snip ---
* LOAD segment "00" -> align 0x200000 (x86_64 default)
* LOAD segment "01" -> align 0x200000 (x86_64 default)
ELF's "linking view":
The 0x200000 alignment of the two LOAD segments is default for static linker
'ld' on x86_64.
ELF's "execution view":
The dynamic linker/loader 'ld.so' will set the page protection of the alignment
gap between the LOAD segments to 'PROT_NONE', creating an inaccessible "hole"
by design.
https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-map-segments.h;h=d36f9bd2f6938756646a876d27158d34fe3f0f0a;hb=9ace4692b2fa96c1c92177bb51e5e36680de25ad#l21
--- snip ---
21 /* This implementation assumes (as does the corresponding implementation
22 of _dl_unmap_segments, in dl-unmap-segments.h) that shared objects
23 are always laid out with all segments contiguous (or with gaps
24 between them small enough that it's preferable to reserve all whole
25 pages inside the gaps with PROT_NONE mappings rather than permitting
26 other use of those parts of the address space). */
...
--- snip ---
There is a Wine-Staging patch here:
https://github.com/wine-staging/wine-staging/tree/master/patches/ntdll-Builtin_Prot
Specifically:
https://github.com/wine-staging/wine-staging/blob/master/patches/ntdll-Builtin_Prot/0001-ntdll-Fix-holes-in-ELF-mappings.patch
The patch essentially allows app code to access the memory between the LOAD
segments (alignment gaps/holes marked as 'VPROT_SYSTEM').
It's certainly doable this way ... but lets hear how Alexandre thinks of this.
An alternative to "fix" the holes is by using the static linker.
The default 'ld' setting for alignment of LOAD segments without using a custom
linker script can be adjusted by using '-z max-page-size=N' and/or '-z
common-page-size=N' (target page size).
Wine actually already makes use of this for modules which need to be
"prelinked" at fixed address ranges (core dlls):
https://source.winehq.org/git/wine.git/blob/HEAD:/tools/winegcc/winegcc.c#l1136
--- snip ---
1136 default:
1137 if (opts->image_base)
1138 {
1139 if (!try_link(opts->prefix, link_args,
"-Wl,-z,max-page-size=0x1000"))
1140 strarray_add(link_args, "-Wl,-z,max-page-size=0x1000");
1141 if (!try_link(opts->prefix, link_args,
strmake("-Wl,-Ttext-segment=%s", opts->image_base)))
1142 strarray_add(link_args, strmake("-Wl,-Ttext-segment=%s",
opts->image_base));
1143 else
1144 prelink = PRELINK;
1145 }
1146 break;
1147 }
--- snip ---
Example:
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/kernel32/Makefile.in#l6
--- snip ---
6 EXTRADLLFLAGS = -nodefaultlibs -Wb,-F,KERNEL32.dll
-Wl,--image-base,0x7b400000
--- snip ---
--- snip ---
$ readelf -a install/lib64/wine/kernel32.dll.so
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x000000007b400000 0x000000007b400000
0x00000000002c3bcc 0x00000000002c3bcc R E 1000
LOAD 0x00000000002c3dc8 0x000000007b6c4dc8 0x000000007b6c4dc8
0x00000000001b1448 0x00000000001b1ad0 RW 1000
--- snip ---
Due to platform (target) page granularity, there is no hole anymore in between
the two LOAD segments.
Giving 'dxgi' and 'd3d11' dlls fixed load addresses basically achieves the same
but at static link time:
--- snip ---
$ grep -Hrni image-base dlls/
dlls/dxgi/Makefile.in:4:EXTRADLLFLAGS = -Wl,--image-base,0x71710000
dlls/kernel32/Makefile.in:6:EXTRADLLFLAGS = -nodefaultlibs -Wb,-F,KERNEL32.dll
-Wl,--image-base,0x7b400000
dlls/ntdll/Makefile.in:6:EXTRADLLFLAGS = -nodefaultlibs
-Wl,--image-base,0x7bc00000
dlls/opengl32/Makefile.in:5:EXTRADLLFLAGS = -Wl,--image-base,0x7a800000
dlls/riched20/Makefile.in:4:EXTRADLLFLAGS = -Wl,--image-base,0x7ac00000
dlls/d3d11/Makefile.in:4:EXTRADLLFLAGS = -Wl,--image-base,0x77590000
--- snip ---
Alternatively, decouple the 'ld' option from Wine's 'image-base' option.
Anyway, chose between the static link time vs. runtime link time (permission
fixup -> staging patch) method ;-)
$ wine --version
wine-3.3
Regards
--
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.
More information about the wine-bugs
mailing list