Access Violation casting FARPROC to Any in Silver

IDE: Visual Studio 2015
Version: 10.0.0.2255
Target: Island (Windows x64 32-bit binary)
Description:

Trying to make the cast from FARPROC to Func<SomethingElse> I asked about here I tried to pass through Any since that’s the only thing that compiled, and anything is supposed to be castable to Any as I understand.

I tried something like:

var hSilverDLL1 = LoadLibrary("SilverDLL1.dll")
var pFoo = GetProcAddress(hSilverDLL1, "_Foo@0");
var pFoo2 = pFoo as? Any
var pFoo3 = pFoo2 as? Func<Bool>

x = pFoo3()

Note that I’m using as? rather than as!. So even if the casts are supposed to fail because I’m doing something wrong, I’m supposed to get a nil. What I’m getting is a crash due to access violation.

After returning from the cal to GetProcAddress I see the following disassembly:

0093547c e8aea20400      call    ConsoleApplication2!GetProcAddress (0097f72f)
00935481 8945f4          mov     dword ptr [ebp-0Ch],eax ss:002b:005df6a8=00000000
00935484 85c0            test    eax,eax
00935486 7412            je      ConsoleApplication2!ConsoleApplication2.Program::WndProc+0xba (0093549a)
00935488 68c0ed8e00      push    offset ConsoleApplication2!GC_uobjfreelist_ptr+0xe54 (008eedc0)
0093548d 50              push    eax
0093548e e8fd160000      call    ConsoleApplication2!RemObjects.Elements.System.WindowsException::.+0x50 (00936b90)
00935493 83c408          add     esp,8
00935496 85c0            test    eax,eax
00935498 7502            jne     ConsoleApplication2!ConsoleApplication2.Program::WndProc+0xbc (0093549c)
0093549a 31c0            xor     eax,eax
0093549c 8945f8          mov     dword ptr [ebp-8],eax
0093549f 85c0            test    eax,eax
009354a1 7412            je      ConsoleApplication2!ConsoleApplication2.Program::WndProc+0xd5 (009354b5)
009354a3 6850dd9100      push    offset ConsoleApplication2!_real+0xd8 (0091dd50)
009354a8 50              push    eax
009354a9 e8e2160000      call    ConsoleApplication2!RemObjects.Elements.System.WindowsException::.+0x50 (00936b90)
009354ae 83c408          add     esp,8
009354b1 85c0            test    eax,eax
009354b3 7502            jne     ConsoleApplication2!ConsoleApplication2.Program::WndProc+0xd7 (009354b7)
009354b5 31c0            xor     eax,eax
009354b7 8945e8          mov     dword ptr [ebp-18h],eax
009354ba 8945ec          mov     dword ptr [ebp-14h],eax
009354bd 50              push    eax
009354be e81d490300      call    ConsoleApplication2!Swift::ms_t15_Swift_d_Bit$Extension15_get__debugDescriptionp7_$mapped_r_tb_Swift_d_Bit+0x1230 (00969de0)
009354c3 83c404          add     esp,4
009354c6 8845ff          mov     byte ptr [ebp-1],al
009354c9 f6d0            not     al
009354cb a801            test    al,1
009354cd 740d            je      ConsoleApplication2!ConsoleApplication2.Program::WndProc+0xfc (009354dc)

GetProcAddress returned the correct address and after stepping through the mov on address 0x00935481 I see that the pFoo holds the correct value:

0:000> gu
eax=62e7b470 ebx=00000000 ecx=5e52e37a edx=00000002 esi=002904fe edi=009353e0
eip=00935481 esp=005df69c ebp=005df6b4 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ConsoleApplication2!ConsoleApplication2.Program::WndProc+0xa1:
00935481 8945f4          mov     dword ptr [ebp-0Ch],eax ss:002b:005df6a8=00000000
0:000> r eax
eax=62e7b470

0:000> ln @eax
[longpath\program.swift @ 16] (62e7b470)   SilverDLL1!SilverDLL1.<Global>::fgdgdf   |  (62e7b4c0)   SilverDLL1!SilverDLL1.<Global>::bar123
Exact matches:
    SilverDLL1!SilverDLL1.<Global>::fgdgdf (void)

0:000> t
eax=62e7b470 ebx=00000000 ecx=5e52e37a edx=00000002 esi=002904fe edi=009353e0
eip=00935484 esp=005df69c ebp=005df6b4 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ConsoleApplication2!ConsoleApplication2.Program::WndProc+0xa4:
00935484 85c0            test    eax,eax

0:000> dv /v
005df6bc                     hWnd = 0x002904fe
005df6c0                  message = 0x111
005df6c4                   wParam = 0
005df6c8                   lParam = 0n2427230
005df6b3                        x = true
005df6a4              hSilverDLL1 = 0x62e10000
005df6a8                     pFoo = 0x62e7b470
005df6ac                    pFoo2 = 0x00000000
005df6a0                    pFoo3 = 0x0025095e

ConsoleApplication2!GC_uobjfreelist_ptr+0xe54 seems to be a description of what kind of cast to perform (maybe some sort of type object). The multiple test eax, eaxs are the check for nil. ConsoleApplication2!RemObjects.Elements.System.WindowsException::.+0x50 seems the cast function.

We call the cast function. Test eax after it. If it’s not null we store it in [ebp-8] which we know is pFoo2.

0:000> ? ebp-8
Evaluate expression: 6157996 = 005df6ac

If we didn’t fail we call the cast function again, this time with ConsoleApplication2!_real+0xd8. If we succeed we store the result of this cast in pFoo3 (and in [ebp-18] which I don’t recognize).

The we are somehow supposed to make the call through the thing on 0x009354be.

But I don;'t get there.

Stepping till the first call to the cast results in:

0:000> p
(2728.dcc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=c16af957 ebx=008eedc0 ecx=62e7b470 edx=89744371 esi=79077a48 edi=83e58955
eip=00936bd4 esp=005df678 ebp=005df68c iopl=0         nv up ei ng nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010286
ConsoleApplication2!RemObjects.Elements.System.WindowsException::.+0x94:
00936bd4 8b4710          mov     eax,dword ptr [edi+10h] ds:002b:83e58965=????????

(There’s no handler. It becomes a second-chance exception and if not handled crashed the program.)

I have no idea what I did wrong, but even if the cast was bad I think I’m supposed to get a nil, not an AV and a crash. Swift ain’t Rust, but it’s supposed to be safe enough with a flow like this as far as I understand.

Expected Behavior: Get nil as a result of a cast.

Actual Behavior: Crash.


Bonus:

If I skip the calls in the debugger as if they succeeded I get another crash inside ConsoleApplication2!Swift::ms_t15_Swift_d_Bit$Extension15_get__debugDescriptionp7_$mapped_r_tb_Swift_d_Bit+0x1230.

The big problem here is that it shouldn’t allow this in the first place (same issue as FARPROC to Func). Func and friends are “block” delegates, in that they capture a self pointer too and end up as a class (Delegate in Island/Echoes), which isn’t compatible with function pointers.

We don’t currently have a syntax for function pointers in swift, but I see Swift now has one, so expect this one to work for fridays build:

typealias MyFunc = @convention(c) () -> Bool

var pFoo = GetProcAddress(hSilverDLL1, "_Foo@0");
var x = pFoo as? MyFunc

Thanks, logged as bugs://79733

bugs://79733 got closed with status fixed.

Is this a Silver limitation? The original Swift documentation said that

AnyObject can represent an instance of any class type.
Any can represent an instance of any type at all, apart from function types

But the Swift 4.1, Swift 3.0, and even the Swtift 2.0 documentation say that (emphasis mine):

Swift provides two special types for working with nonspecific types:

  • Any can represent an instance of any type at all, including function types.
  • AnyObject can represent an instance of any class type.

Also, the types - as far as the PDB was concerned - of pFoo and pFoo3 in my code were simple void*. No context or anything like that. Actually, except for the @convention attribute you added, Func<Bool> is exactly () -> Bool you proposed.

It’s a current limitation yes. We can add it eventually but it’s not very high on my list.

That sounds wrong. I’ll log an issue for that.

Note that there is a huge difference between

convention(c) () -> (Bool)

and

() -> (Bool)

The first does not have an implied “this” (same goes for apple swift) and doesn’t/can’t capture scope variables.

Thanks, logged as bugs://79749

bugs://79749 got closed with status fixed.

Then maybe this should go to Differences and Limitations :wink:

I couldn’t find this attribute anywhere in the documentation. What other values can be passed to @convention except c?

yep, will do.

1 Like

It’s mentioned here: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html

1 Like