How to find VPTR in C++ assembly code? - c++

class Base {
public:
Base() {}
virtual void Get() { }
};
class Derivered : public Base {
public:
virtual void Get() { }
};
int main() {
Base* base = new Derivered();
base->Get();
return 0;
}
I use gcc 5.4.0 to compile the code, and use objdump -S a.out to disassemble binary file. I want to find Base's vptr, but only display an unknown address 0x80487d4. The max address number is 0x80487b7, I cann't understand.
command list: g++ test.cpp -O0; objdump -S a.out
080486fe <_ZN4BaseC1Ev>:
80486fe: 55 push %ebp
80486ff: 89 e5 mov %esp,%ebp
8048701: ba d4 87 04 08 mov $0x80487d4,%edx
8048706: 8b 45 08 mov 0x8(%ebp),%eax
8048709: 89 10 mov %edx,(%eax)

080486fe <_ZN4BaseC1Ev>:
80486fe: 55 push %ebp
80486ff: 89 e5 mov %esp,%ebp
8048701: ba d4 87 04 08 mov $0x80487d4,%edx
8048706: 8b 45 08 mov 0x8(%ebp),%eax
8048709: 89 10 mov %edx,(%eax)
Is...
push %ebp ;- save frame pointer
mov %esp, %ebp ;- mov esp-> ebp -ebp is frame pointer
mov $0x80487d4, %edx ; load vptr address into edx
mov 0x8(%ebp), %eax ; ld eax with address of this
mov %edx,(%eax) ; store vptr in this byte 0

Related

Compiler mov'es this pointer to wrong address

I have a simple polymorphic construction, with one pure virtual function Foo.
The only flaw in the big project it's used in is that the project uses a couple of global statics for centralized parameter loading and for event logging (can't easily get rid of that legacy code).
Project info:
Platform toolset: v110_xp
MFC in static library
MBCS charset
calling convention: __cdecl
All optimizations disabled
Warning level 4, no warnings on the whole project
Code:
class Base
{
public:
Base(){}
virtual ~Base(void){}
virtual void Foo(void) = 0;
};
class Derived
: public Base
{
public:
Derived(void) : Base(){}
virtual void Foo(void) override
{
double a = sqrt(4.9);
double b = -a;
}
Calling code (doesn't really matter, same behaviour everywhere)
BOOL MainMFCApp::InitInstance()
{
Derived* d = new Derived();
d->Foo();
delete d;
...
}
The problem is that when run in debug (not tested with release), and when we end up inside function Foo, the this pointer is 'corrupted':
this = 0xcccccccc
this.__vfptr = <unable to read memory>
When I dive into the assembly code entering the function I see the following:
13:
14:
15: void Derived::Foo(void)
16: {
015E3570 55 push ebp
015E3571 8B EC mov ebp,esp
015E3573 83 E4 F8 and esp,0FFFFFFF8h
015E3576 81 EC EC 00 00 00 sub esp,0ECh
015E357C 53 push ebx
015E357D 56 push esi
015E357E 57 push edi
015E357F 51 push ecx
015E3580 8D BD 14 FF FF FF lea edi,[ebp-0ECh]
015E3586 B9 3B 00 00 00 mov ecx,3Bh
015E358B B8 CC CC CC CC mov eax,0CCCCCCCCh
015E3590 F3 AB rep stos dword ptr es:[edi]
015E3592 59 pop ecx
015E3593 89 8C 24 F0 00 00 00 mov dword ptr [esp+0F0h],ecx
17: double a = sqrt(4.9);
015E359A F2 0F 10 05 00 55 09 02 movsd xmm0,mmword ptr ds:[2095500h]
015E35A2 E8 72 D4 FD FF call __libm_sse2_sqrt_precise (015C0A19h)
015E35A7 F2 0F 11 84 24 E0 00 00 00 movsd mmword ptr [esp+0E0h],xmm0
18: double b = -a;
015E35B0 F2 0F 10 84 24 E0 00 00 00 movsd xmm0,mmword ptr [esp+0E0h]
015E35B9 66 0F 57 05 10 55 09 02 xorpd xmm0,xmmword ptr ds:[2095510h]
015E35C1 F2 0F 11 84 24 D0 00 00 00 movsd mmword ptr [esp+0D0h],xmm0
19: return;
20: }
015E35CA 5F pop edi
015E35CB 5E pop esi
015E35CC 5B pop ebx
015E35CD 8B E5 mov esp,ebp
015E35CF 5D pop ebp
015E35D0 C3 ret
--- No source file -------------------------------------------------------------
015E35D1 CC int 3
...
015E35EF CC int 3
Breakpoint at line 17, right before entering the function body: Using the watch window to inspect the object behind register ecx (with a cast to Derived*) shows that ecx contains the pointer I need (to the object), but for some reason it is mov'ed to the seemingly random address [esp+0F0h].
And now the really interesting/flabbergasting part: When I change
double b = -a;
to
double b = -1.0 * a;
and compile again, everything magically works. The function assembly has now changed to:
13:
14:
15: void Derived::Foo(void)
16: {
00863570 55 push ebp
00863571 8B EC mov ebp,esp
00863573 81 EC EC 00 00 00 sub esp,0ECh
00863579 53 push ebx
0086357A 56 push esi
0086357B 57 push edi
0086357C 51 push ecx
0086357D 8D BD 14 FF FF FF lea edi,[ebp-0ECh]
00863583 B9 3B 00 00 00 mov ecx,3Bh
00863588 B8 CC CC CC CC mov eax,0CCCCCCCCh
0086358D F3 AB rep stos dword ptr es:[edi]
0086358F 59 pop ecx
00863590 89 4D F8 mov dword ptr [this],ecx
17: double a = sqrt(4.9);
00863593 F2 0F 10 05 00 55 31 01 movsd xmm0,mmword ptr ds:[1315500h]
0086359B E8 79 D4 FD FF call __libm_sse2_sqrt_precise (0840A19h)
008635A0 F2 0F 11 45 E8 movsd mmword ptr [a],xmm0
18: double b = -1.0 * a;
008635A5 F2 0F 10 05 10 55 31 01 movsd xmm0,mmword ptr ds:[1315510h]
008635AD F2 0F 59 45 E8 mulsd xmm0,mmword ptr [a]
008635B2 F2 0F 11 45 D8 movsd mmword ptr [b],xmm0
19: return;
20: }
008635B7 5F pop edi
008635B8 5E pop esi
008635B9 5B pop ebx
008635BA 81 C4 EC 00 00 00 add esp,0ECh
008635C0 3B EC cmp ebp,esp
008635C2 E8 32 CD FC FF call __RTC_CheckEsp (08302F9h)
008635C7 8B E5 mov esp,ebp
008635C9 5D pop ebp
008635CA C3 ret
--- No source file -------------------------------------------------------------
008635CB CC int 3
...
008635EF CC int 3
Now the generated code nicely moves the pointer in register ecx to this. Other difference:
different memory addresses/offsets
mulsd instead of xorpd to negate the variable
and esp,0FFFFFFF8h disappeared (?? used to align the stack pointer esp ??)
more cleanup (after the function body)?? (add cmp call)
The assembly part where the parameters get pushed to the stack is the same for both situations:
53: d->Foo();
011A500B 8B 45 E0 mov eax,dword ptr [d]
011A500E 8B 10 mov edx,dword ptr [eax]
011A5010 8B F4 mov esi,esp
011A5012 8B 4D E0 mov ecx,dword ptr [d]
011A5015 8B 42 04 mov eax,dword ptr [edx+4]
011A5018 FF D0 call eax
Of course when I try to replicate this with a Minimal, Complete, and Verifiable example, everything works as intened. But in my big project, it fails consistently.
I'm not sure which parameters can influence compilation, and don't know enough of assembly to even see what's going on there;
therefor I'm asking here in the hope that someone has seen this before or recognizes this behaviour.
Note: it also works again, when I remove the sqrt call.
Update:
No problems in release
VS2012 SP4 (v11.0.61030.00)
problem persists when referencing member variables (iso no member references)
TODO: try without global statics

Why can't my template function be inlined

I wrote a class that can store lambda function in it to make sure all resources released before a function exits.
I test my code in MSVC2015, Release mode with /O2.
However, I find that GenerateScopeGuard cannot be inlined and a small function is generated.
int main()
{
01031C00 55 push ebp
01031C01 8B EC mov ebp,esp
01031C03 51 push ecx
auto func = GenerateScopeGuard([] {printf("hello\n"); });
01031C04 8D 4D FC lea ecx,[func]
01031C07 E8 24 00 00 00 call GenerateScopeGuard<<lambda_8b2f3596146f3fc3f8311b4d76487aed> > (01031C30h)
return 0;
01031C0C 80 7D FD 00 cmp byte ptr [ebp-3],0
01031C10 75 0D jne main+1Fh (01031C1Fh)
01031C12 68 78 61 03 01 push offset string "hello\n" (01036178h)
01031C17 E8 24 00 00 00 call printf (01031C40h)
01031C1C 83 C4 04 add esp,4
01031C1F 33 C0 xor eax,eax
}
01031C21 8B E5 mov esp,ebp
01031C23 5D pop ebp
01031C24 C3 ret
return ScopeGuard<T>(std::forward<T>(func));
01031C30 C6 41 01 00 mov byte ptr [ecx+1],0
01031C34 8B C1 mov eax,ecx
}
01031C36 C3 ret
Looks like exception handling related. Indeed if I disable C++ exception the function is inlined, but don't work with /EHsc. Why?
Here is my code.
template<typename T>
class ScopeGuard
{
public:
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
explicit ScopeGuard(T&& func) :
func_(std::forward<T>(func))
{}
ScopeGuard(ScopeGuard&& right) :
func_(std::move(right.func_))
{}
~ScopeGuard()
{
if (!dismissed_) func_();
}
void Dismiss()
{
dismissed_ = true;
}
private:
T func_;
bool dismissed_ = false;
};
template<typename T>
__forceinline ScopeGuard<T> GenerateScopeGuard(T&& func) noexcept
{
return ScopeGuard<T>(std::forward<T>(func));
}
int main()
{
auto func = GenerateScopeGuard([] {printf("hello\n"); });
return 0;
}
It seems MSVC compiler is getting confused by recently introduced non static member initialization feature. Constructor with initialization seems to fix the problem:
ScopeGuard()
: dismissed_(false)
{
}
Compiling with -O3 -std=c++14:
output from gcc 5.4:
.LC0:
.string "hello"
main:
subq $8, %rsp
movl $.LC0, %edi
call puts
xorl %eax, %eax
addq $8, %rsp
ret
output from clang 3.8:
main: # #main
pushq %rax
movl $.Lstr, %edi
callq puts
xorl %eax, %eax
popq %rcx
retq
.Lstr:
.asciz "hello"
This is without the nonstandard __forceinline attribute.
Are you sure you've enabled optimisation?
Is it that this function is generated but not called by main?

selecting address to change value in memory

This question/answer on SO shows how to use GDB to change a value in memory, but in the example given, it chooses an address to set the value that wasn't previously being used
For example, to change the return value to 22, the author does
set {unsigned char}0x00000000004004b9 = 22
However, why would this address 0x00000000004004b9 be the address to change? If you look at the output of disas/r the address 0x00000000004004b9 isn't being used, so why use this one to set to 22? I'm trying to understand how to know which address needs to be changed to (in this example) change the return value, if the output of disas/r doesn't show it.
code
$ cat t.c
int main()
{
return 42;
}
$ gcc t.c && ./a.out; echo $?
42
$ gdb --write -q ./a.out
(gdb) disas/r main
Dump of assembler code for function main:
0x00000000004004b4 <+0>: 55 push %rbp
0x00000000004004b5 <+1>: 48 89 e5 mov %rsp,%rbp
0x00000000004004b8 <+4>: b8 2a 00 00 00 mov $0x2a,%eax
0x00000000004004bd <+9>: 5d pop %rbp
0x00000000004004be <+10>: c3 retq
End of assembler dump.
(gdb) set {unsigned char}0x00000000004004b9 = 22
(gdb) disas/r main
Dump of assembler code for function main:
0x00000000004004b4 <+0>: 55 push %rbp
0x00000000004004b5 <+1>: 48 89 e5 mov %rsp,%rbp
0x00000000004004b8 <+4>: b8 16 00 00 00 mov $0x16,%eax <<< ---changed
0x00000000004004bd <+9>: 5d pop %rbp
0x00000000004004be <+10>: c3 retq
End of assembler dump.
(gdb) q
$ ./a.out; echo $?
22 <<<--- Just as desired
I'm trying to understand how to know which address needs to be changed to (in this example) change the return value, if the output of disas/r doesn't show it.
To understand this, you need to understand instruction encoding. The instruction here is "move immediate 32-bit constant to register". The constant is part of the instruction (that's what "immediate" means). It may be helpful to compile this instead:
int foo() { return 0x41424344; }
int bar() { return 0x45464748; }
int main() { return foo() + bar(); }
When you do compile it, you should see something similar to:
(gdb) disas/r foo
Dump of assembler code for function foo:
0x00000000004004ed <+0>: 55 push %rbp
0x00000000004004ee <+1>: 48 89 e5 mov %rsp,%rbp
0x00000000004004f1 <+4>: b8 44 43 42 41 mov $0x41424344,%eax
0x00000000004004f6 <+9>: 5d pop %rbp
0x00000000004004f7 <+10>: c3 retq
End of assembler dump.
(gdb) disas/r bar
Dump of assembler code for function bar:
0x00000000004004f8 <+0>: 55 push %rbp
0x00000000004004f9 <+1>: 48 89 e5 mov %rsp,%rbp
0x00000000004004fc <+4>: b8 48 47 46 45 mov $0x45464748,%eax
0x0000000000400501 <+9>: 5d pop %rbp
0x0000000000400502 <+10>: c3 retq
End of assembler dump.
Now you can clearly see where in the instruction stream each byte of the immediate constant resides (and also that x86 uses little-endian encoding for them).
The standard reference on instruction encoding for x86 is Intel instruction set reference. You can find 0xB8 instruction on page 3-528.

Retrieving vptr(pointer to virtual Table aka VTABLE)from the Objdump utility?

How can we know the address of the VTABLEs (i.e corresponding vptr) using the objdump utility and dissembled code.
vptr is generally stored in the first byte of object .(correct/edit this).
There is this simple code using the virtual function :
class base
{
public:
int x;
virtual void func()
{
//test function
}
};
class der : public base
{
void func()
{
//test function
}
};
/*
*
*/
int main() {
int s = 9;
base b;
der d ;
std::cout<<"the address of Vptr is = "<<(&b+0)<<std::endl;
std::cout<<"the value at Vptr is = "<<(int*)*(int*)((&b+0))<<std::endl;
return 0;
}
following is the output of the code :
the address of Vptr is = 0x7fff86a78fe0
the value at Vptr is = **0x400c30**
Following is the part of main function - diassembly of the code :
base b;
4009b4: 48 8d 45 d0 lea -0x30(%rbp),%rax
4009b8: 48 89 c7 mov %rax,%rdi
4009bb: e8 f4 00 00 00 callq 400ab4 <_ZN4baseC1Ev>
der d ;
4009c0: 48 8d 45 c0 lea -0x40(%rbp),%rax
4009c4: 48 89 c7 mov %rax,%rdi
4009c7: e8 fe 00 00 00 callq 400aca <_ZN3derC1Ev>
It shows here that _ZN4baseC1Ev is the address of the base object and
_ZN3derC1Ev is the address of the derived object.
in the _ZN4baseC1Ev
0000000000400ab4 <_ZN4baseC1Ev>:
400ab4: 55 push %rbp
400ab5: 48 89 e5 mov %rsp,%rbp
400ab8: 48 89 7d f8 mov %rdi,-0x8(%rbp)
400abc: 48 8b 45 f8 mov -0x8(%rbp),%rax
400ac0: 48 c7 00 30 0c 40 00 movq $0x400c30,(%rax)
400ac7: c9 leaveq
400ac8: c3 retq
400ac9: 90 nop
0000000000400aca <_ZN3derC1Ev>:
}
#include<iostream>
class base
{
public:
int x;
virtual void func()
400a8a: 55 push %rbp
400a8b: 48 89 e5 mov %rsp,%rbp
400a8e: 48 89 7d f8 mov %rdi,-0x8(%rbp)
{
//test function
}
400a92: c9 leaveq
400a93: c3 retq
0000000000400a94 <_ZN3der4funcEv>:
};
class der : public base
{
void func()
400a94: 55 push %rbp
400a95: 48 89 e5 mov %rsp,%rbp
400a98: 48 89 7d f8 mov %rdi,-0x8(%rbp)
{
//test function
}
400a9c: c9 leaveq
400a9d: c3 retq
0000000000400a9e <_ZN4baseC2Ev>:
*/
#include <stdlib.h>
#include<iostream>
class base
{
400a9e: 55 push %rbp
400a9f: 48 89 e5 mov %rsp,%rbp
400aa2: 48 89 7d f8 mov %rdi,-0x8(%rbp)
400aa6: 48 8b 45 f8 mov -0x8(%rbp),%rax
400aaa: 48 c7 00 50 0c 40 00 movq $0x400c50,(%rax)
400ab1: c9 leaveq
400ab2: c3 retq
400ab3: 90 nop
0000000000400ab4 <_ZN4baseC1Ev>:
400ab4: 55 push %rbp
400ab5: 48 89 e5 mov %rsp,%rbp
400ab8: 48 89 7d f8 mov %rdi,-0x8(%rbp)
400abc: 48 8b 45 f8 mov -0x8(%rbp),%rax
400ac0: 48 c7 00 50 0c 40 00 movq $0x400c50,(%rax)
400ac7: c9 leaveq
400ac8: c3 retq
400ac9: 90 nop
0000000000400aca <_ZN3derC1Ev>:
}
};
Here is the link to output of objdump -S exe file
also objdump -t virtualfunctionsize | grep vtable gives this :
0000000000400c40 w O .rodata 0000000000000018 vtable for base
0000000000601e00 g O .dtors 0000000000000000 .hidden __DTOR_END__
0000000000400b00 g F .text 0000000000000089 __libc_csu_init
0000000000400c20 w O .rodata 0000000000000018 vtable for der
I wanted to know
- what it is the VTABLE address and corresponding virtual function's denoted by it.
the address of Vptr is = 0x7fff86a78fe0 , what does this represent - VTABLE location?
the value at Vptr is = 0x400c30 - What does this represent - the first Virtual function of the base class?
How can the subsequent addresses of the virtual functions of the derived classes can be found?
Rgds,
softy
Gcc follows this C++ ABI: http://refspecs.linuxbase.org/cxxabi-1.83.html#vtable
To see vtable that gcc generates, you can compile it with "gcc -S" to produce assembly source, and then filter it through c++filt
You can see the following vtable generated:
.type vtable for base, #object
.size vtable for base, 24
vtable for base:
.quad 0
.quad typeinfo for base
.quad base::func()
You can see that first two 8-byte values are zero (which is offset-to-top, offset of derived class within the base), and pointer to typeinfo. Then the real vtable (pointers to virtual functions) begins. And that is the vptr value you see in debug output. That explains the +0x10 offset.
_ZN4baseC1Ev is the base::base(), the base constructor, _ZN3derC1Ev is the derived constructor. You can use a tool like c++filt to demangle the names. They are not the addresses of the actual objects.
The address of the base object b is 0x7fff86a78fe0, which is on the stack, as expected. For this compiler this is the same as the address of the vptr, which is an pointer to an array of function pointers to virtual members.
If you dereference it, you get the address of a pointer to the first virtual function (0x400c30) in your base class.
EDIT:
You need to dereference it one more time to obtain the address of base::func(). Use something like
(int*)*(int*)*(int*)(&b)

Using a C++ reference in inline assembly with GCC

I have a spin lock with the xchg instruction. The C++ function takes in the resource to be locked.
Following is the code
void SpinLock::lock( u32& resource )
{
__asm__ __volatile__
(
"mov ebx, %0\n\t"
"InUseLoop:\n\t"
"mov eax, 0x01\n\t" /* 1=In Use*/
"xchg eax, [ebx]\n\t"
"cmp eax, 0x01\n\t"
"je InUseLoop\n\t"
:"=r"(resource)
:"r"(resource)
:"eax","ebx"
);
}
void SpinLock::unlock(u32& resource )
{
__asm__ __volatile__
(
/* "mov DWORD PTR ds:[%0],0x00\n\t" */
"mov ebx, %0\n\t"
"mov DWORD PTR [ebx], 0x00\n\t"
:"=r"(resource)
:"r"(resource)
: "ebx"
);
}
This code is compiled with gcc 4.5.2 -masm=intel on a 64 bit intel machine.
The objdump produces following assembly for the above functions .
0000000000490968 <_ZN8SpinLock4lockERj>:
490968: 55 push %rbp
490969: 48 89 e5 mov %rsp,%rbp
49096c: 53 push %rbx
49096d: 48 89 7d f0 mov %rdi,-0x10(%rbp)
490971: 48 8b 45 f0 mov -0x10(%rbp),%rax
490975: 8b 10 mov (%rax),%edx
490977: 89 d3 mov %edx,%ebx
0000000000490979 <InUseLoop>:
490979: b8 01 00 00 00 mov $0x1,%eax
49097e: 67 87 03 addr32 xchg %eax,(%ebx)
490981: 83 f8 01 cmp $0x1,%eax
490984: 74 f3 je 490979 <InUseLoop>
490986: 48 8b 45 f0 mov -0x10(%rbp),%rax
49098a: 89 10 mov %edx,(%rax)
49098c: 5b pop %rbx
49098d: c9 leaveq
49098e: c3 retq
49098f: 90 nop
0000000000490990 <_ZN8SpinLock6unlockERj>:
490990: 55 push %rbp
490991: 48 89 e5 mov %rsp,%rbp
490994: 53 push %rbx
490995: 48 89 7d f0 mov %rdi,-0x10(%rbp)
490999: 48 8b 45 f0 mov -0x10(%rbp),%rax
49099d: 8b 00 mov (%rax),%eax
49099f: 89 d3 mov %edx,%ebx
4909a1: 67 c7 03 00 00 00 00 addr32 movl $0x0,(%ebx)
4909a8: 48 8b 45 f0 mov -0x10(%rbp),%rax
4909ac: 89 10 mov %edx,(%rax)
4909ae: 5b pop %rbx
4909af: c9 leaveq
4909b0: c3 retq
4909b1: 90 nop
The code dumps core when executing the locking operation.
Is there something grossly wrong here ?
Regards,
-J
First, why are you using truncated 32-bit addresses in your assembly code whereas the rest of the program is compiled to execute in 64-bit mode and operate with 64-bit addresses/pointers? I'm referring to ebx. Why is it not rbx?
Second, why are you trying to return a value from the assembly code with "=r"(resource)? Your functions change the in-memory value with xchg eax, [ebx] and mov DWORD PTR [ebx], 0x00 and return void. Remove "=r"(resource).
Lastly, if you look closely at the disassembly of SpinLock::lock(), can't you see something odd about ebx?:
mov %rdi,-0x10(%rbp)
mov -0x10(%rbp),%rax
mov (%rax),%edx
mov %edx,%ebx
<InUseLoop>:
mov $0x1,%eax
addr32 xchg %eax,(%ebx)
In this code, the ebx value, which is an address/pointer, does not come directly from the function's parameter (rdi), the parameter first gets dereferenced with mov (%rax),%edx, but why? If you throw away all the confusing C++ reference stuff, technically, the function receives a pointer to u32, not a pointer to a pointer to u32, and thus needs no extra dereference anywhere.
The problem is here: "r"(resource). It must be "r"(&resource).
A small 32-bit test app demonstrates this problem:
#include <iostream>
using namespace std;
void unlock1(unsigned& resource)
{
__asm__ __volatile__
(
/* "mov DWORD PTR ds:[%0],0x00\n\t" */
"movl %0, %%ebx\n\t"
"movl $0, (%%ebx)\n\t"
:
:"r"(resource)
:"ebx"
);
}
void unlock2(unsigned& resource)
{
__asm__ __volatile__
(
/* "mov DWORD PTR ds:[%0],0x00\n\t" */
"movl %0, %%ebx\n\t"
"movl $0, (%%ebx)\n\t"
:
:"r"(&resource)
:"ebx"
);
}
unsigned blah;
int main(void)
{
blah = 3456789012u;
cout << "before unlock2() blah=" << blah << endl;
unlock2(blah);
cout << "after unlock2() blah=" << blah << endl;
blah = 3456789012u;
cout << "before unlock1() blah=" << blah << endl;
unlock1(blah); // may crash here, but if it doesn't, it won't change blah
cout << "after unlock1() blah=" << blah << endl;
return 0;
}
Output:
before unlock2() blah=3456789012
after unlock2() blah=0
before unlock1() blah=3456789012
Exiting due to signal SIGSEGV
General Protection Fault at eip=000015eb
eax=ce0a6a14 ...