This is a followup of Bug 9729.
The generated interface thunk now look like this.
_TMP3 LABEL NEAR
sub eax, 8 ; 0050 _ 83. E8, 08
call ?_007 ; 0053 _ E8, 00000000
?_007 LABEL NEAR
pop ebx ; 0058 _ 5B
add ebx, offset _GLOBAL_OFFSET_TABLE_-$+1H ; 0059 _ 81. C3, 00000003(GOT r)
jmp _D3bug4Lock4lockMFZv ; 005F _ E9, FFFFFFFC(PLT r)
The problem here is that the EBX is not restored after the direct jump which leads to bug when it was used in the calling function.
Not sure what the best solution to this is. Replacing the jump with a call is not a good solution because of it alters the stack, i.e. parameters and return values don't fit.
Comment #1 by bugzilla — 2013-06-29T12:31:44Z
The code that generates this in cod3_thunk().
Comment #2 by code — 2013-06-29T12:45:31Z
We could probably assume, that any interface call kills EBX so that the caller would have to save it. But a solution in accordance with the ABI would be better.
The fix seems to work but I found another corner case.
Calling an interface thunk through a delegate still crashes.
----
cat > bug.d << CODE
void call(int delegate() dg)
{
assert(dg() == 7);
}
interface I { int opCall(); }
class C : I { int opCall() { return 7; } }
void test()
{
I i = new C;
call(&i.opCall);
}
CODE
cat > main.d << CODE
import bug;
void main() { bug.test(); }
CODE
${DMD} -g -m32 -fPIC -shared bug.d -oflibbug.so
${DMD} -g -m32 main.d -L-L. -L-lbug -L-rpath=.
./main
----
The code generated to call the delegate trashes EBX.
<_D3bug4callFDFZiZv>:
...
mov 0x8(%ebp),%eax // loads context ptr
mov -0x4(%ebp),%ebx // correctly loads GOT into EBX
mov 0xc(%ebp),%edx // loads function ptr
mov 0x8(%ebp),%ebx // overwrites EBX with context ptr ???
call *%edx
The interface thunk call through call *%edx needs a correct EBX.
<_TMP3>:
sub $0x8,%eax
jmp d3e0 <_D3bug1C6opCallMFZi@plt>
So the problematic instruction is the additional load into EBX.
This works correctly with optimized builds btw.