/*- Author: Brian Tiffin Dedicated to the public domain Date started: December 2016 Modified: 2019-02-23/16:23-0500 btiffin tectonics: gcc -o uninative.so -shared -fPIC native.c +*/ /* A new Unicon C native FFI layer, with a little assembler thrown in */ #include #include #include "icall.h" #include "natives.h" static void *dlHandle; int addLibrary(int argc, descriptor argv[]) { //union block dlBlock; //descriptor dlBlock; /* Add a new library to the dynamic search path */ ArgString(1) if (!dlHandle) { dlHandle = dlopen(StringVal(argv[1]), RTLD_LAZY | RTLD_GLOBAL); if (!dlHandle) { fprintf(stderr, "dlHandle error\n"); fflush(stderr); Error(500); } } //dlBlock = mkExternal(dlHandle, sizeof(dlHandle)); //RetExternal(dlBlock); RetInteger((long)dlHandle); } //static void *(*func)(); static void (*func)(); union blob { long lvalue; double rvalue; float fvalue; char *svalue; }; static union blob fromc; static union blob fromf; /* Let the good times roll */ int native(int argc, descriptor argv[]) { /* a dlsym function pointer */ char *dlMsg; /* Integers and pointers go in RDI, RSI, RDX, RCX/R10, R8, R9 */ /* Floating point in XMM0 - XMM6 */ /* They are two seperate sets */ union blob ipregs[7]; union blob fregs[7]; long retType; char inType; int ips = 1; /* count of integer/pointer args, reserve 0 */ int rs = 1; /* count of floating args, reserved 0, might not use */ /* first the function name */ ArgString(1); /* second is return type, encoded in natives.inc matched in natives.h */ ArgInteger(2); retType = IntegerVal(argv[2]); /* Load the function pointer */ dlerror(); *(void **)(&func) = dlsym(dlHandle, StringVal(argv[1])); dlMsg = dlerror(); if (dlMsg) { fprintf(stderr, "dlsym fail: %s\n", dlMsg); fflush(stderr); Error(500); } if (!func) Fail; /* marshalling by assembly to set up the variant call frames */ for (int argi = 3; argi <= argc; argi++) { inType = IconType(argv[argi]); /* Integers and pointers go in RDI, RSI, RDX, RCX/R10, R8, R9 */ /* Floating point in XMM0 - XMM6 */ /* They are two seperate sets */ switch(inType) { case 'i': ArgInteger(argi); ipregs[ips++].lvalue = IntegerVal(argv[argi]); break; case 'r': ArgReal(argi); fregs[rs++].rvalue = RealVal(argv[argi]); break; case 's': ArgString(argi); ipregs[ips++].svalue = StringVal(argv[argi]); break; } } /* Function vector in r11 */ asm("movq func(%%rip), %%r11;" : /* no output operand */ : /* no input operand */ :"%r11" ); /* Take values from ipregs ints and fregs floats */ asm("movq %0, %%r9;" : /* no output operand */ :"r"(ipregs[6]) :"%r9" /* clobber */ ); asm("movq %0, %%r8;" : :"r"(ipregs[5]) :"%r8" ); asm("movq %0, %%rcx;" : :"r"(ipregs[4]) :"%rcx" ); asm("movq %0, %%r10;" /* For Linux kernel calls instead of RCX */ : :"r"(ipregs[4]) :"%r10" ); asm("movq %0, %%rdx;" : :"r"(ipregs[3]) :"%rdx" ); asm("movq %0, %%rsi;" : :"r"(ipregs[2]) :"rsi" ); asm("movq %0, %%rdi;" : :"r"(ipregs[1]) :"%rdi" ); asm("movsd %0, %%xmm5;" : :"m"(fregs[6]) :"xmm5" ); asm("movsd %0, %%xmm4;" : :"m"(fregs[5]) :"xmm4" ); asm("movsd %0, %%xmm3;" : :"m"(fregs[4]) :"xmm3" ); asm("movsd %0, %%xmm2;" : :"m"(fregs[3]) :"xmm2" ); asm("movsd %0, %%xmm1;" : :"m"(fregs[2]) :"xmm1" ); asm("movsd %0, %%xmm0;" : :"m"(fregs[1]) :"xmm0" ); asm("call *%r11"); asm("movsd %xmm0, fromf(%rip)"); asm("movq %rax, fromc(%rip)"); /* Return type, as specfied in argument 2 enum */ switch(retType) { case TYPEVOID: Return; break; case TYPESTAR: RetInteger(fromc.lvalue); break; case TYPEINT: RetInteger((long)fromc.lvalue); break; case TYPEFLOAT: RetReal((float)fromf.rvalue); break; case TYPEDOUBLE: RetReal((double)fromf.rvalue); break; case TYPESTRING: RetString(fromc.svalue); break; default: RetInteger((long)fromc.lvalue); break; } } /* Delta between managing float versus double */ /* Requires some refactoring to merge with above */ int nativeFloat(int argc, descriptor argv[]) { /* a dlsym function pointer */ char *dlMsg; /* Integers and pointers go in RDI, RSI, RDX, RCX/R10, R8, R9 */ /* Floating point in XMM0 - XMM6 */ /* They are two seperate sets here, duplicate work, more coverage */ union blob ipregs[7]; union blob fregs[7]; long retType; char inType; int ips = 1; /* count of integer/pointer args, reserve 0 */ int rs = 1; /* count of floating args, reserved 0, might not use */ /* first the function name */ ArgString(1); /* second is return type, encoded in natives.inc */ ArgInteger(2); retType = IntegerVal(argv[2]); /* load the function pointer */ dlerror(); *(void **)(&func) = dlsym(dlHandle, StringVal(argv[1])); dlMsg = dlerror(); if (dlMsg) { fprintf(stderr, "dlsym fail: %s\n", dlMsg); fflush(stderr); Error(500); } if (!func) Fail; /* marshalling by assembly to set up the variant call frames */ for (int argi = 3; argi <= argc; argi++) { inType = IconType(argv[argi]); /* Integers and pointers go in RDI, RSI, RDX, RCX/R10, R8, R9 */ /* Floating point in XMM0 - XMM6 */ /* They are two seperate sets */ switch(inType) { case 'i': ArgInteger(argi); ipregs[ips++].lvalue = IntegerVal(argv[argi]); break; case 'r': ArgReal(argi); fregs[rs++].fvalue = (float)RealVal(argv[argi]); break; case 's': ArgString(argi); ipregs[ips++].svalue = StringVal(argv[argi]); break; } } /* Function vector in r11 */ asm("movq func(%%rip), %%r11;" : /* no output operand */ : /* no input operand */ :"%r11" ); /* Take values from ipregs ints and fregs floats */ asm("movq %0, %%r9;" : /* no output operand */ :"r"(ipregs[6]) :"%r9" /* clobber */ ); asm("movq %0, %%r8;" : :"r"(ipregs[5]) :"%r8" ); asm("movq %0, %%rcx;" : :"r"(ipregs[4]) :"%rcx" ); asm("movq %0, %%r10;" /* For Linux kernel calls instead of RCX */ : :"r"(ipregs[4]) :"%r10" ); asm("movq %0, %%rdx;" : :"r"(ipregs[3]) :"%rdx" ); asm("movq %0, %%rsi;" : :"r"(ipregs[2]) :"rsi" ); asm("movq %0, %%rdi;" : :"r"(ipregs[1]) :"%rdi" ); asm("movss %0, %%xmm5;" : :"m"(fregs[6]) :"xmm5" ); asm("movss %0, %%xmm4;" : :"m"(fregs[5]) :"xmm4" ); asm("movss %0, %%xmm3;" : :"m"(fregs[4]) :"xmm3" ); asm("movss %0, %%xmm2;" : :"m"(fregs[3]) :"xmm2" ); asm("movss %0, %%xmm1;" : :"m"(fregs[2]) :"xmm1" ); asm("movss %0, %%xmm0;" : :"m"(fregs[1]) :"xmm0" ); asm("call *%r11"); asm("movss %xmm0, fromf(%rip)"); asm("movq %rax, fromc(%rip)"); /* Return type, as specfied in argument 2 enum */ switch(retType) { case TYPEVOID: Return; break; case TYPESTAR: RetInteger(fromc.lvalue); break; case TYPEINT: RetInteger((long)fromc.lvalue); break; case TYPEFLOAT: RetReal((float)fromf.fvalue); break; case TYPEDOUBLE: RetReal((double)fromf.fvalue); break; case TYPESTRING: RetString(fromc.svalue); break; default: RetInteger((long)fromc.lvalue); break; } }