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

Reply via email to