These extra names are related to the different exception handling model on x64. On x86 exceptions are stack based. On x64 they are table based. This has consequences for how the compiler treats except
and finally
blocks.
In particular, the compiler/linker has to be able output an exception table that describes the exception handling code. As I understand it, the names that you see with $pdata$
and $unwind$
are created by the compiler when it processes except
and finally
blocks. These names are then used by the linker to create the exception table that is written to the executable output file. And the compiler generates such unspeakable names so that they don't clash with the true function names.
My guess is that you are seeing these names in your stack traces because the JCL stack walker code is not clever enough to decipher these names. If you were using madExcept, for instance, you would see the names that you were expecting.
So fundamentally, the problem is that JCL is lacking functionality.
There really is a huge difference between x86 and x64 structured exception handling. For instance, it is an interesting fact to note that the compiled code for a finally
block appears twice in an x64 executable. Consider this short program:
procedure Foo;
begin
end;
procedure Main;
begin
try
finally
Foo;
end;
end;
begin
Main;
end.
The compiler transforms Main
into:
Project1.dpr.8: begin 0000000000409A30 55 push rbp 0000000000409A31 4883EC30 sub rsp,$30 0000000000409A35 488BEC mov rbp,rsp 0000000000409A38 48896D28 mov [rbp+$28],rbp Project1.dpr.9: try 0000000000409A3C 90 nop Project1.dpr.11: Foo; 0000000000409A3D 90 nop 0000000000409A3E E8DDFFFFFF call Foo Project1.dpr.13: end; 0000000000409A43 488D6530 lea rsp,[rbp+$30] 0000000000409A47 5D pop rbp 0000000000409A48 C3 ret 0000000000409A49 488D8000000000 lea rax,[rax+$00000000] Project1.dpr.11: Foo; 0000000000409A50 55 push rbp 0000000000409A51 4883EC20 sub rsp,$20 0000000000409A55 488BEC mov rbp,rsp 0000000000409A58 E8C3FFFFFF call Foo 0000000000409A5D 488D6520 lea rsp,[rbp+$20] 0000000000409A61 5D pop rbp 0000000000409A62 C3 ret
Note the two calls to Foo
. The first one is normal execution. That is when there are no exceptions, and the finally
block is entered normally. The second call to Foo
deals with the case where an exception is active.
This second version of the finally block is actually compiled as a separate function. It has the name Project1.$pdata$_ZN8Project13FooEv
according to my map file.
0005:00000A50 Project1.$pdata$_ZN8Project13FooEv
It is called from the main exception handler, System._DelphiExceptionHandler
. And it really is a separate function as can be see by the fact that it ends with ret
. If I throw an exception inside the try/finally
to get this variant of code to run, the stack trace in the IDE looks like this:
Project1.Main System._DelphiExceptionHandler($12FAB0,1244912,$12E820,$12E730) :00000000779F9DAD ; ntdll.dll :00000000779E8A4C ; ntdll.dll :00000000778E2D3E ; C:\Windows\system32\kernel32.dll System._DelphiExceptionHandler($12FAB0,1244976,$12F5C0,$12EF70) :00000000779F9D2D ; ntdll.dll :00000000779E91CF ; ntdll.dll :0000000077A21248 ; ntdll.dll :000007FEFDA7940D ; C:\Windows\system32\KERNELBASE.dll System._RaiseAtExcept(???,???) System._RaiseExcept(???) Project1.Main
So as you can see, the IDE is able to achieve what the JCL code cannot, and make sense of the table based exception handling.
Under x86 is looks quite different:
Project1.Main Project1.Project1 :7618336a kernel32.BaseThreadInitThunk + 0x12 :77be9f72 ntdll.RtlInitializeExceptionChain + 0x63 :77be9f45 ntdll.RtlInitializeExceptionChain + 0x36
So, these unspeakable names are all related to the management of table based exceptions. The behaviour is entirely to be expected.