Okay, here the small snippet of code, which does the trick! :)
Compile it with:
gcc calltest.c callgate.s -o calltest
Here, a fn1() and fn2()
is equal functions (almost), which imitating the native code, which
moved during the VM call.
And fnCallGate() is a special function, which needs to be called
instead of actual function,
which potentially could move our code.
extern void fnCallGate(void* fn, ...);
it take a first argument - a function which needs to be called, and
then rest of arguments (if any),
which belong to that function.
This gate function using another function, which returns a current
value of a method oop.
In terms of Squeak VM, this is a method oop, which it retrieving
before calling a VM function
and after calling it (interpreterProxy->primitiveMethod).
So, in case if method oop is moved during GC, it will change the
return address by the same offset
as a difference between old method oop value and new one.
And so, with this trick, a native code could safely call any VM
function (through a gate function)
without the risk that if native code is moved during GC, it will
return to wrong address and cause a system crash.
On 6 April 2010 23:49, Igor Stasenko <[email protected]> wrote:
> On 6 April 2010 14:46, Marcus Denker <[email protected]> wrote:
>> Hi,
>>
>> I always liked how Smalltalk X did it... they have
>>
>> -> an ivar in CompiledMethod that is the "native code pointer".
>> -> Primitive methods are just methods that have this pointer set
>> to the primitive in the vm.
>> -> Methods with embedded C-code are compiled and linked with
>> the Smtk->C cross-compiler, the pointer than points to that
>> function
>> -> the JIT just put a pointer to the code it generates.
>>
>> So they merge primitives / static compiling(+embedded c-code) *and* the JIT
>> into one not that ugly mechanism. In STX, the memory is managed by the VM,
>> though (the
>> code is not allocated in the GCed object memory by the JIT).
>>
>> So: Yes, I like this :-) and your mechanism is a nice way to get it easily
>> integrated into the current
>> system.
>>
>> Q: what happens when code is moved by the GC?
>>
> Yeah, it will break the whole thing :)
> To avoid that, a native code should not issue any memory allocation,
> which may cause GC.
>
> Perhaps, there is a way to counter this , while still being able to
> move the code.
> A primitive could set a flag that its going to call a native code,
> embedded into compiled method,
> so, then, after GC, if this flag is set, VM knows, that memory
> allocation is issued from native code,
> and should fix the return address (1) on stack, before returning to
> native code, which moved to the new code location:
>
> primitive -> native code --(1)-> VM allocation procedure -> GC
>
> (1) - a return address , which should be fixed.
>
>> --
>> Marcus Denker -- http://www.marcusdenker.de
>> INRIA Lille -- Nord Europe. Team RMoD.
>>
>>
>> _______________________________________________
>> Pharo-project mailing list
>> [email protected]
>> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>>
>
>
>
> --
> Best regards,
> Igor Stasenko AKA sig.
>
--
Best regards,
Igor Stasenko AKA sig.
.file "callgate.s"
.text
.p2align 4,,15
.globl _fnCallGate
.def _fnCallGate; .scl 2; .type 32; .endef
_fnCallGate:
call *(_getCurrentAddressFn) /* retrieve a primitive method oop */
movl %eax, _savedAddress
popl _returnAddress /* save return address */
popl %eax
call *%eax /* call the function */
pushl _returnAddress
pushl %eax /* save return value */
pushl %edx
call *(_getCurrentAddressFn) /* retrieve a primitive method oop */
subl _savedAddress, %eax
addl %eax, 8(%esp) /* current - old + returnAddress */
popl %edx
popl %eax
ret
.comm _returnAddress, 16 # 4
.comm _savedAddress, 16 # 4
#include <stdio.h>
extern void fnCallGate(void* fn, ...); /* defined in assembly */
typedef void * (*primMethodFn)(void);
primMethodFn getCurrentAddressFn;
void fn1();
void fn2();
void * fnAddress;
void thingWhichMovesTheCode(int arg1)
{
fnAddress = fn2;
printf ("inside a function , which moving the code, arg=%d\n", arg1);
}
void * getMethodOop()
{
return fnAddress;
}
void fn1() {
printf ("entered fn1\n");
fnCallGate(thingWhichMovesTheCode, 10);
printf ("returning from fn1\n");
}
void fn2() {
printf ("entered fn2\n");
fnCallGate(thingWhichMovesTheCode, 10);
printf ("returning from fn2\n");
}
int main(int argc, char** argv)
{
fnAddress = (void*)fn1;
getCurrentAddressFn = getMethodOop;
fn1();
return 0;
}
_______________________________________________
Pharo-project mailing list
[email protected]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project