http://bugs.winehq.org/show_bug.cgi?id=22661
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |focht@gmx.net
--- Comment #20 from Anastasius Focht focht@gmx.net 2010-08-18 08:13:32 --- Hello,
interesting bug - though can't be fixed in Wine ;-)
Relevant app assembly snippet, annotated:
--- snip --- 0x004790b0: subl $0x108,%esp 0x004790b6: movl 0x005168e0,%eax 0x004790bb: xorl %esp,%eax 0x004790bd: movl %eax,0x104(%esp) 0x004790c4: movl 0x15c(%esi),%ecx 0x004790ca: pushl %ebx ; %ebx is zero on entry (value by chance, caller and parent callers don't explicitly init it) 0x004790cb: pushl $0x3714 0x004790d0: pushl $0x104 0x004790d5: leal 0xc(%esp),%ebx 0x004790d9: call 0x0046dff0 ; load format string "Analysis %1.f %%" 0x004790de: pushl $0x0 ; 1st (var) arg -> should be float (64 bit) but instead only 32 bits are passed on stack 0x004790e0: movl %ebx,%eax 0x004790e2: pushl %eax ; format string (= dest buffer) 0x004790e3: movl %ebx,%ecx 0x004790e5: pushl %ecx ; dest buffer 0x004790e6: movl $0x104,%eax ; register pass (max) buffer size in bytes arg for next sprintf-like call 0x004790eb: call 0x00473310 ; get formatted string using sprintf() like app logic 0x004790f0: addl $12,%esp 0x004790f3: movl %ebx,%edx 0x004790f5: pushl %edx ... 0x00479120: movl 0x108(%esp),%ecx 0x00479127: popl %ebx 0x00479128: xorl %esp,%ecx 0x0047912a: call 0x004bcf66 0x0047912f: addl $0x108,%esp 0x00479135: ret --- snip ---
The problem is that the 1st var argument to sprintf like function logic is incorrectly passed as 32-bit integer when it should be passed as 64 bit float (see format string). Because only 32 bits are passed, the other "ghost" 32 bits come from existing stack value which most likely happens to be zero on Windows (by chance!) and non-zero on Wine.
That "0x004790ca: pushl %ebx" actually "saves" the Windows side here. Because %ebx is zero on function entry (by chance!), that stack location gets zeroed where the "ghost" 32 bit part of float 64 bit value lives.
There is no way the compiler could have optimized %ebx value on a global level like this, retaining ebx == 0 so long through several call chains (even dynamic calls). I traced some caller levels up but there is no explicit %ebx -> 0. There are dynamic calls to window proc and the like.
The reason why this register is non-zero on (x86) Wine is the PIC code uses this register to store the GOT (global offset table) address, hence it's not zero at this point (still pointing to user32's _GLOBAL_OFFSET_TABLE_).
My guess is the app developer did something stupid like this:
sprintf( buf, "Analysis %1.f %%", 0);
but intended this:
sprintf( buf, "Analysis %1.f %%", 0.0);
The compiler should generate code like this for passing the "0.0" constant:
--- snip --- fldz fstpl <arg_stack_loc> --- snip ---
If you ask me -> not fixable in Wine.
Regards