Linking against a Win32-DLL
Jascha Wetzel
firstname at mainia.de
Mon Jul 9 13:05:30 PDT 2007
BLS wrote:
> Hallo Jascha,
> I wonder how to setup Ddbg to figure out this information. Not nessesary
> to say that a step by step (4 dummies) guideline is more than welcome !
> MANY Thanks in advance.
> Bjoern
here's log from the ddbg session.
i've added comments starting with a #
C:\>ddbg wintabtest.exe
Ddbg 0.10 beta - D Debugger
Copyright (c) 2007 Jascha Wetzel
see http://ddbg.mainia.de/doc.html for documentation
Loading symbols from wintabtest.exe
# use line 1 as a shortcut to "the first source line in the file"
->bp win:1
Breakpoint set: wintabtest.d:8 0x402010 all threads
->r
ntdll.dll loaded at 0x7c900000
KERNEL32.dll loaded at 0x7c800000
Wintab32.dll loaded at 0x10000000
USER32.dll loaded at 0x7e410000
GDI32.dll loaded at 0x77f10000
ADVAPI32.dll loaded at 0x77dd0000
RPCRT4.dll loaded at 0x77e70000
Breakpoint 0 hit at wintabtest.d:8 0x402010 thread(5112)
void main(char[][] args)
->da
wintabtest.d:8 void main(char[][] args)
00402010: 55 push ebp
00402011: 8bec mov ebp, esp
wintabtest.d:12 writefln("Getting WTInfo...");
00402013: ff359c504100 push dword [0x41509c]
00402019: ff3598504100 push dword [0x415098]
0040201f: b800514100 mov eax, 0x415100
00402024: 50 push eax
00402025: e8fe030000 call 0x402428 std.stdio.writefln
wintabtest.d:13 WTInfoA(0, 0, null);
0040202a: 6a00 push 0x0
0040202c: 6a00 push 0x0
0040202e: 6a00 push 0x0
00402030: e8d3250100 call 0x414608 _WTInfoA
wintabtest.d:14 writefln("Received WTInfo");
00402035: ff35b4504100 push dword [0x4150b4]
0040203b: ff35b0504100 push dword [0x4150b0]
00402041: b900514100 mov ecx, 0x415100
00402046: 51 push ecx
00402047: e8dc030000 call 0x402428 std.stdio.writefln
0040204c: 31c0 xor eax, eax
0040204e: 83c424 add esp, 0x24
# here, DMD cleans up the stack for all 3 calls at once
# note that 0x24 = 9*4 accounts for all the 9 PUSH instructions above
# this means also cleaning up for the WTInfoA call
wintabtest.obj
00402051: 5d pop ebp
00402052: c3 ret
->ov
wintabtest.d:12 0x402013 thread(5112)
writefln("Getting WTInfo...");
->
Getting WTInfo...
wintabtest.d:13 0x40202a thread(5112)
WTInfoA(0, 0, null);
# now we're just before the 3 PUSHes for the WTInfo parameters
# let's check out the ESP value
->dr
EAX = 00000000 EBX = 008903c4 ECX = 0012ff74 EDX = 0000000b
EDI = 00000001 ESI = 00000001 EBP = 0012ff30 ESP = 0012ff24
EIP = 0040202a EFL = 00000202
CS = 0000001b DS = 00000023 ES = 00000023 FS = 0000003b
GS = 00000000 SS = 00000023
# and step over the PUSHes and the call
->ov
wintabtest.d:14 0x402035 thread(5112)
writefln("Received WTInfo");
# check ESP again
->dr
EAX = 00001157 EBX = 008903c4 ECX = 7ec7fb9c EDX = 10019e38
EDI = 00000001 ESI = 00000001 EBP = 0012ff30 ESP = 0012ff24
EIP = 00402035 EFL = 00000296
CS = 0000001b DS = 00000023 ES = 00000023 FS = 0000003b
GS = 00000000 SS = 00000023
# ESP is still 0x12ff24, therefore WTInfo cleaned up the stack by itself
We have two calls to variadic D functions (writefln), which don't clean
up the stack (see D's ABI docs). Each call has 3 dword PUSHes, which
gives us 24 = 0x18 bytes. So the "add esp, 0x24" moves the stack pointer
12 bytes to much.
Now consider the other version of Marc's snippet, that saves the return
value in a local variable:
wintabtest.d:8 void main(char[][] args)
00402010: c8040000 enter 0x4, 0x0
wintabtest.d:20 writefln("Getting WTInfo...");
00402014: ff359c504100 push dword [0x41509c]
0040201a: ff3598504100 push dword [0x415098]
00402020: b800514100 mov eax, 0x415100
00402025: 50 push eax
00402026: e801040000 call 0x40242c std.stdio.writefln
wintabtest.d:21 int value = WTInfoA(0, 0, null);
0040202b: 6a00 push 0x0
0040202d: 6a00 push 0x0
0040202f: 6a00 push 0x0
00402031: e8d2250100 call 0x414608 _WTInfoA
00402036: 8945fc mov [ebp-0x4], eax
wintabtest.d:22 writefln("Received WTInfo");
00402039: ff35b4504100 push dword [0x4150b4]
0040203f: ff35b0504100 push dword [0x4150b0]
00402045: b900514100 mov ecx, 0x415100
0040204a: 51 push ecx
0040204b: e8dc030000 call 0x40242c std.stdio.writefln
00402050: 31c0 xor eax, eax
00402052: 83c424 add esp, 0x24
wintabtest.obj
00402055: c9 leave
00402056: c3 ret
The ENTER at the beginning of the function saves the current ESP to EBP
(among other things). The "add esp, 0x24" still corrupts the stack
pointer, but the LEAVE instruction restores the saved value from EBP, so
it doesn't hurt.
Now that we know that WTInfo cleans up the stack, we can narrow down the
possible calling conventions to __stdcall, __fastcall and PASCAL.
Since we were able to link against the library using C convention (after
adding leading underscores to the function names using the /s switch on
implib), that eliminates __stdcall and __fastcall, because they require
more decoration than the leading underscore.
So we change the declaration to extern(Pascal) and observe OPTLINK
complaining about a missing function "WTINFOA". Apparently DMD's idea of
function names in Pascal convention is all uppercase, while wintab32.dll
has C-style function names (which you can also see by looking at the DLL
file in a text editor).
All this is definitely under-the-hood-type debugging.
Just because it came up in other threads a couple of times: this is not
the only kind of stuff you can do with a debugger :)
You don't need to know assembly language to make use of a debugger. You
can simply run it to have line numbers for access violations, extensive
stack traces or variable contents without having to add printf's and
recompile.
Debuggers are friendly little creatures, they don't bite :)
More information about the Digitalmars-d
mailing list