https://bugs.winehq.org/show_bug.cgi?id=48229
Paul Gofman gofmanp@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |gofmanp@gmail.com
--- Comment #4 from Paul Gofman gofmanp@gmail.com --- Created attachment 66256 --> https://bugs.winehq.org/attachment.cgi?id=66256 Test program
I've tested the game.
The game hooks kernel32.CreateFileW() and expects calls to kernel32.CreateFileA() to be routed to the kernel32.CreateFileW() where it is supposed to meet the hook. The hook does some trickery to open the actual data file while the files directly requested in CreateFile call do not exist.
Funny thing is that the reason why it breaks on commit bisected above is not the same as the reason why it doesn't work in the current git.
At the moment of the switch to no-PIC build (commit bisected above) both CreateFilaA() and CreateFileW() were implemented in kernel32.dll.so. Switch to no-PIC build triggered some different behaviour of gcc compiler which started for some reason creating a copy of CreateFileW() function so the calls from within kernel32 went to this function instead of the one which was referenced through the import table (maybe it was partially inlined though I did not analyze that disassembly thoroughly). The proof of concept fix which let me to fix the issue in Wine 4.8 source was adding __attribute__((noinline)) to CreateFileW(). I should also note that with my 4.7 or 4.8 source builds with default flags the game was crashing right away, I worked that around by compiling kernel32.dll.so with -O0 flag. Fortunately this sort of issue is not reproducible on the current git.
Later CreateFile{A|W}() were moved to kernelbase, and their availability in kernel32.dll is now provided by the following spec entries:
@ stdcall -import CreateFileA(str long long ptr long long long) @ stdcall -import CreateFileW(wstr long long ptr long long long)
So both CreateFileA() and CreateFileW() are now routed directly from their import thunks to kernelbase, and hooking kernel32.CreateFileW() has no effect on CreateFileA().
The most straightforward change which fixes the issue is to define the functions in kernel32 spec file like this:
@ stdcall CreateFileA(str long long ptr long long long) kernelbase.CreateFileA @ stdcall CreateFileW(wstr long long ptr long long long) kernelbase.CreateFileW
But this appears to be not quite correct. With these functions directly forwarded to kernelbase GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateFileW") returns the address of the function in kernelbase (same address as for GetProcAddress(GetModuleHandle("kernelbase.dll"), "CreateFileW"). This appears to be not the case in my testing on Windows 7, where GetProcAddress return different addresses each in the corresponding module.
I am attaching the test program here which prints these function pointers and emulates such sort of hooking. The hook function is called when calling CreateFileA() on Windows 7 but not under Wine currently. So the correct fix seems to route kernel32.CreateFileA() to kernel32.CreateFileW() instead of kernelbase.CreateFileW().