https://bugs.winehq.org/show_bug.cgi?id=49195
Bug ID: 49195 Summary: Multiple 4k demoscene OpenGL demos crash on startup (failure to lookup atom 0xC019 'static' from global atom tables) Product: Wine Version: 5.8 Hardware: x86-64 OS: Linux Status: NEW Severity: normal Priority: P2 Component: user32 Assignee: wine-bugs@winehq.org Reporter: focht@gmx.net Distribution: ---
Hello folks,
extracted from bug 48898
My comment: https://bugs.winehq.org/show_bug.cgi?id=48898#c12
--- quote --- To me it looks like bug 18490 ("Multiple games fail to set pixel format on D3D device context created on desktop window (Empire: Total War, Napoleon: Total War, Utopia City)") but for GL device context on the desktop window.
According to online information it's not allowed to do any rendering using this context but enough to call functions like glGetString() etc.
Maybe some of the Wine graphics folks could weigh in with an opinion if this limitation is already known / tracked in a bug? --- quote ---
Henri's answer: https://bugs.winehq.org/show_bug.cgi?id=48898#c13
--- quote --- I'm not aware of an existing bug report for this issue, no. It does strike me as similar to bug 36506 though, and it would not particularly surprise me if wglGetProcAddress() is supposed to return results consistent with GL_EXTENSIONS/GL_VERSION here, even without a current GL context.
I seem to recall having established in the context of bug 18490 that creating a GL context (or more specifically, setting a pixel format) on the desktop window is indeed supposed to fail. In any case, this seems like of of those bugs that would benefit from tests to establish what's supposed to happen. --- quote ---
Turns out this was a side-effect of an earlier problem. It was not supposed to be GetDC(NULL) (Desktop window).
--- snip --- $ WINEDEBUG=+seh,+relay,+server,+class,+win,+msg wine ./End\ of\ time\ 720p.exe
log.txt 2>&1
... 0024:Call user32.ChangeDisplaySettingsA(00421288,00000004) ret=004200da ... 0024: get_desktop_window( force=0 ) 0024: get_desktop_window() = 0 { top_window=00010020, msg_window=00010026 } ... 0024:Ret user32.ChangeDisplaySettingsA() retval=00000000 ret=004200da 0024:Call KERNEL32.CreateThread(00000000,00000000,004201ca,004304e8,00000000,00000000) ret=004200ee ... 0024:Ret KERNEL32.CreateThread() retval=00000080 ret=004200ee 00bc: *fd* 17 <- 38 00bc: init_thread( unix_pid=3683, unix_tid=3729, debug_level=1, teb=7ffd4000, entry=004201ca, reply_fd=15, wait_fd=17, cpu=x86 ) 0024:Call user32.CreateWindowExA(00000000,0000c019,00000000,91000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000) ret=004200f4 0024:trace:win:WIN_CreateWindowEx (null) #c019 ex=00000000 style=91000000 0,0 0x0 parent=(nil) menu=(nil) inst=(nil) params=(nil) 00bc: init_thread() = 0 { pid=0020, tid=00bc, server_start=1d62d3b280579be (-2.9701360), info_size=0, version=603, all_cpus=00000003, suspend=0 } 0024:trace:win:dump_window_styles style: WS_POPUP WS_VISIBLE WS_MAXIMIZE 0024:trace:win:dump_window_styles exstyle: ... 00bc:Starting thread proc 0x4201ca (arg=0x4304e8) ... 0024: create_window( parent=00010020, owner=00000000, atom=c019, instance=00000000, dpi=96, awareness=2, class=L"" ) 0024: create_window() = INVALID_HANDLE { handle=00000000, parent=00000000, owner=00000000, extra=0, class_ptr=00000000, dpi=0, awareness=0 } 0024:warn:win:create_window_handle error 6 creating window 0024:trace:class:GetClassInfoExW (nil) #c019 0x1518f9e0 0024:trace:class:CLASS_FindClass #c019 0x7e910000 -> not found 0024:Ret user32.CreateWindowExA() retval=00000000 ret=004200f4 --- snip ---
The failing window creation resulted in NULL handle which got passed to user32.GetDC():
--- snip --- 0024:Call user32.GetDC(00000000) ret=004200fb 0024:trace:win:GetDCEx hwnd 0x10020, hrgnClip (nil), flags 00000003 ... 0024: get_visible_region( window=00010020, flags=00000013 ) 0024: get_visible_region() = 0 { top_win=00010020, top_rect={-1920,0;-1919,1}, win_rect={0,0;3200,1080}, paint_flags=00000001, total_size=16, region={{-1920,0;1280,1080}} } 0024:Call winex11.drv.GetDC(00030039,00010020,00010020,1518fde0,1518fdf0,00000013) ret=7e97bd3e 0024:Ret winex11.drv.GetDC() retval=00000001 ret=7e97bd3e 0024:trace:win:GetDCEx (0x10020,(nil),0x13): returning 0x30039 (updated) 0024:Ret user32.GetDC() retval=00030039 ret=004200fb --- snip ---
Well, the rest of the OpenGL story is known and the current behaviour makes sense. Back to the actual problem...
The creation of the window which ought the be used for OpenGL context/rendering fails because atom 0xc019 can't be found/resolved. I should have looked more carefully at the trace log and the demo disassembly.
--- snip --- ... 004200B6 | xor esi,esi | 004200B8 | push esi | 004200B9 | push esi | 004200BA | push esi | 004200BB | push esi | 004200BC | push esi | 004200BD | push esi | 004200BE | push esi | 004200BF | push esi | 004200C0 | push esi | 004200C1 | push 91000000 | 004200C6 | push esi | 004200C7 | push C019 | global atom! 004200CC | push esi | 004200CD | push 4 | 004200CF | push end of time 720p.421288 | 004200D4 | call dword ptr ds:[<&user32.ChangeDisplaySettingsA>] | 004200DA | push esi | 004200DB | push esi | 004200DC | push end of time 720p.4304E8 | 004200E1 | push end of time 720p.4201CA | 004200E6 | push esi | 004200E7 | push esi | 004200E8 | call dword ptr ds:[<&KERNEL32.CreateThread>] | 004200EE | call dword ptr ds:[<&user32.CreateWindowExA>] | fails 004200F4 | push eax | 004200F5 | call dword ptr ds:[<&user32.GetDC>] | 004200FB | mov edi,eax | 004200FD | mov dword ptr ds:[3AB08EC],eax | 00420102 | push esi | 00420103 | push edi | 00420104 | push esi | 00420105 | push end of time 720p.42125C | 0042010A | push edi | 0042010B | call dword ptr ds:[<&gdi32.ChoosePixelFormat>] | 00420111 | push eax | 00420112 | push edi | 00420113 | call dword ptr ds:[<&gdi32.SetPixelFormat>] | 00420119 | call dword ptr ds:[<&opengl32.wglCreateContext>] | 0042011F | push eax | 00420120 | push edi | 00420121 | call dword ptr ds:[<&opengl32.wglMakeCurrent>] | 00420127 | call dword ptr ds:[<&user32.ShowCursor>] | 0042012D | call end of time 720p.4202CC | --- snip ---
The demoscene guys are crazy and fight for each saved byte ;-)
Courtesy of: http://www.pouet.net/topic.php?which=9894
--- quote --- I was able to get 4 bytes off of a test 1k intro (Peach GLSL Shader of Japan) by replacing the "edit" string with 0xC018, making it 1006 bytes instead of 1010. Nice.
...and then I got it down to 1004 bytes, because now "test eax, eax" compressed better than "test ax, ax", which used to compress better sometime before this newest change. ;) This 1k intro business is crazy, you'd really need some kind of brute-force code masher that tries all possible combinations of code that perform the same thing. --- quote ---
--- quote --- That´s mine since 2009 using DirectX9...someone told me using "static" instead of "edit" would be better back then in some other thread...was iq if i remember correctly. The ATOM for "static" is 0xC019 btw, but be warned: it added 7 bytes here in my first test, opposed to chopping off 4 bytes in case of "edit". --- quote ---
--- quote --- Oh, shit...i did the test the wrong way around...meaning the ATOM actually chopped off 7 bytes in case of "static" vs. "0xC019" :/ :D Yay, thanks for the nice trick, man! :) --- quote ---
Also relevant, shows dump of global and typical local atom tables:
https://github.com/lungetech/ICAS/blob/master/logs/volatility/win32/output/a...
Wine source:
https://source.winehq.org/git/wine.git/blob/9e26bc811656ad8eb901bffa5528b9ce...
--- snip --- 407 /*********************************************************************** 408 * CLASS_FindClass 409 * 410 * Return a pointer to the class. 411 */ 412 static CLASS *CLASS_FindClass( LPCWSTR name, HINSTANCE hinstance ) 413 { 414 static const WCHAR comctl32W[] = {'c','o','m','c','t','l','3','2','.','d','l','l',0}; 415 struct list *ptr; 416 ATOM atom = get_int_atom_value( name ); 417 418 GetDesktopWindow(); /* create the desktop window to trigger builtin class registration */ 419 420 if (!name) return NULL; 421 422 name = CLASS_GetVersionedName( name, NULL, NULL, TRUE ); 423 424 for (;;) 425 { 426 USER_Lock(); 427 428 LIST_FOR_EACH( ptr, &class_list ) 429 { 430 CLASS *class = LIST_ENTRY( ptr, CLASS, entry ); 431 if (atom) 432 { 433 if (class->atomName != atom) continue; 434 } 435 else 436 { 437 if (strcmpiW( class->name, name )) continue; 438 } 439 if (!class->local || class->hInstance == hinstance) 440 { 441 TRACE("%s %p -> %p\n", debugstr_w(name), hinstance, class); 442 return class; 443 } 444 } 445 USER_Unlock(); 446 447 if (atom) break; 448 if (!is_comctl32_class( name )) break; 449 if (GetModuleHandleW( comctl32W )) break; 450 if (!LoadLibraryW( comctl32W )) break; 451 TRACE( "%s retrying after loading comctl32\n", debugstr_w(name) ); 452 } 453 454 TRACE("%s %p -> not found\n", debugstr_w(name), hinstance); 455 return NULL; 456 } --- snip ---
https://source.winehq.org/git/wine.git/blob/9e26bc811656ad8eb901bffa5528b9ce...
$ sha1sum atz-end_of_time.zip 3a4ce3fd92e2fdd1a4533ee67d4809d3f2184f6b atz-end_of_time.zip
$ du -sh atz-end_of_time.zip 3.3M atz-end_of_time.zip
$ wine --version wine-5.8-173-g9e26bc8116
Regards
https://bugs.winehq.org/show_bug.cgi?id=49195
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- URL| |http://www.pouet.net/prod.p | |hp?which=77102 Keywords| |download, obfuscation
--- Comment #1 from Anastasius Focht focht@gmx.net --- Hello folks,
additionally the stable link via Internet Archive:
https://web.archive.org/web/20200408083804/http://xtr1m.com/downloads/atz-en...
Regards
https://bugs.winehq.org/show_bug.cgi?id=49195
Chris Moeller chris@kode54.net changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |chris@kode54.net
https://bugs.winehq.org/show_bug.cgi?id=49195
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- URL|http://www.pouet.net/prod.p |https://web.archive.org/web |hp?which=77102 |/20200408083804/http://xtr1 | |m.com/downloads/atz-end_of_ | |time.zip
https://bugs.winehq.org/show_bug.cgi?id=49195
--- Comment #2 from Nikolay Sivov bunglehead@gmail.com --- I'm guessing exact values rely on the order in which they happen to be registered by first application (explorer?) to touch user32 in give winstation? Or maybe it's not preserved as an important compatibility artifact.
https://bugs.winehq.org/show_bug.cgi?id=49195
--- Comment #3 from Anastasius Focht focht@gmx.net --- Hello Nikolay,
--- quote --- I'm guessing exact values rely on the order in which they happen to be registered by first application (explorer?) to touch user32 in give winstation? Or maybe it's not preserved as an important compatibility artifact. --- quote ---
when talking about global atom constants the demo scene thread http://www.pouet.net/topic.php?which=9894 references another (Japanese) blog:
http://www5d.biglobe.ne.jp/~noocyte/Programming/Windows/WindowsTips.html
https://translate.google.com/translate?hl=&sl=ja&tl=en&u=http%3A...
"ATOM values for various control classes" -> "System-defined Controls" (user32.dll)
--- quote -- The DLGITEMTEMPLATEEX Structure states that these classes are system definitions. Then is ATOM a (virtually) constant?
The value of ATOM is the value of windowClass written on the above page plus 0xBF97
(" DLGITEMTEMPLATEEX Structure " says these controls are predefined system classes, and the ATOM values seem to be (DLGITEMTEMPLATEEX :: windowClass + 0xBF97).) --- quote --
MSDN page refers to this kind of information:
https://web.archive.org/web/20140920223136/http://msdn.microsoft.com/en-us/l...
--- quote --- windowClass
Type: sz_Or_Ord
A variable-length array of 16-bit elements that specifies the window class of the control. If the first element of this array is any value other than 0xFFFF, the system treats the array as a null-terminated Unicode string that specifies the name of a registered window class.
If the first element is 0xFFFF, the array has one additional element that specifies the ordinal value of a predefined system class. The ordinal can be one of the following atom values. Value Meaning
0x0080 Button 0x0081 Edit 0x0082 Static 0x0083 List box 0x0084 Scroll bar 0x0085 Combo box
... --- quote ---
So according to the blog's formula:
user32 'Button' atom: 0xBF97 + 0x0080 ('Button' system class ordinal) = 0xC017 user32 'Edit atom: 0xBF97 + 0x0081 ('Edit' system class ordinal) = 0xC018 user32 'Static' atom: 0xBF97 + 0x0082 ('Static' system class ordinal) = 0xC019
which matches:
https://github.com/lungetech/ICAS/blob/master/logs/volatility/win32/output/a...
--- snip --- Offset(P) Session WindowStation Atom RefCount HIndex Pinned Name --------- ------- ------------- ------- -------- ------ ------ ---- ... 0xd48ca68 n/a n/a 0xc017 1 23 1 Button ... 0xd48ca68 n/a n/a 0xc018 1 24 1 Edit .... 0xd48ca68 n/a n/a 0xc019 1 25 1 Static --- snip ---
Interestingly the blog is the only reference I could find about this magic offset. Maybe it's a legacy user16/32 implementation detail from Win9x or even Win 3.x era. Who knows.
Regards
https://bugs.winehq.org/show_bug.cgi?id=49195
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Blocks| |43747
https://bugs.winehq.org/show_bug.cgi?id=49195
Fabian Maurer dark.shadow4@web.de changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |dark.shadow4@web.de
--- Comment #4 from Fabian Maurer dark.shadow4@web.de --- Still present in wine-9.0-rc1