1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-26 00:10:27 +00:00

Allow binding pre-loaded symbols in windows FFI.

Mimic the posix RTLD_NOW setting for dlopen by iterating
opened DLLs to look for symbols.
This commit is contained in:
bakpakin 2022-08-14 13:26:13 -05:00
parent 5b2169e0d1
commit f8a9efa8e4
8 changed files with 110 additions and 49 deletions

View File

@ -2,22 +2,33 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#ifdef _WIN32
#define EXPORTER __declspec(dllexport)
#else
#define EXPORTER
#endif
EXPORTER
int int_fn(int a, int b) { int int_fn(int a, int b) {
return (a << 2) + b; return (a << 2) + b;
} }
EXPORTER
double my_fn(int64_t a, int64_t b, const char *x) { double my_fn(int64_t a, int64_t b, const char *x) {
return (double)(a + b) + 0.5 + strlen(x); return (double)(a + b) + 0.5 + strlen(x);
} }
EXPORTER
double double_fn(double x, double y, double z) { double double_fn(double x, double y, double z) {
return (x + y) * z * 3; return (x + y) * z * 3;
} }
EXPORTER
double double_many(double x, double y, double z, double w, double a, double b) { double double_many(double x, double y, double z, double w, double a, double b) {
return x + y + z + w + a + b; return x + y + z + w + a + b;
} }
EXPORTER
double double_lots( double double_lots(
double a, double a,
double b, double b,
@ -32,6 +43,7 @@ double double_lots(
return i + j; return i + j;
} }
EXPORTER
double float_fn(float x, float y, float z) { double float_fn(float x, float y, float z) {
return (x + y) * z; return (x + y) * z;
} }
@ -47,16 +59,19 @@ typedef struct {
int c; int c;
} intintint; } intintint;
EXPORTER
int intint_fn(double x, intint ii) { int intint_fn(double x, intint ii) {
printf("double: %g\n", x); printf("double: %g\n", x);
return ii.a + ii.b; return ii.a + ii.b;
} }
EXPORTER
int intintint_fn(double x, intintint iii) { int intintint_fn(double x, intintint iii) {
printf("double: %g\n", x); printf("double: %g\n", x);
return iii.a + iii.b + iii.c; return iii.a + iii.b + iii.c;
} }
EXPORTER
intint return_struct(int i) { intint return_struct(int i) {
intint ret; intint ret;
ret.a = i; ret.a = i;
@ -70,6 +85,7 @@ typedef struct {
int64_t c; int64_t c;
} big; } big;
EXPORTER
big struct_big(int i, double d) { big struct_big(int i, double d) {
big ret; big ret;
ret.a = i; ret.a = i;
@ -78,10 +94,12 @@ big struct_big(int i, double d) {
return ret; return ret;
} }
EXPORTER
void void_fn(void) { void void_fn(void) {
printf("void fn ran\n"); printf("void fn ran\n");
} }
EXPORTER
void void_ret_fn(int x) { void void_ret_fn(int x) {
printf("void fn ran: %d\n", x); printf("void fn ran: %d\n", x);
} }

View File

@ -2,10 +2,13 @@
# Simple FFI test script that tests against a simple shared object # Simple FFI test script that tests against a simple shared object
# #
(def ffi/loc "examples/ffi/so.so") (def is-windows (= :windows (os/which)))
(def ffi/loc (string "examples/ffi/so." (if is-windows "dll" "so")))
(def ffi/source-loc "examples/ffi/so.c") (def ffi/source-loc "examples/ffi/so.c")
(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px) (if is-windows
(os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px))
(def module (ffi/native ffi/loc)) (def module (ffi/native ffi/loc))
(def int-fn-sig (ffi/signature :default :int :int :int)) (def int-fn-sig (ffi/signature :default :int :int :int))
@ -72,27 +75,6 @@
[] []
(ffi/call void-fn-pointer void-fn-sig)) (ffi/call void-fn-pointer void-fn-sig))
#
# Call functions
#
(pp (void-fn))
(pp (int-fn 10 20))
(pp (double-fn 1.5 2.5 3.5))
(pp (double-many 1 2 3 4 5 6))
(pp (double-lots 1 2 3 4 5 6 7 8 9 10))
(pp (float-fn 8 4 17))
(pp (intint-fn 123.456 [10 20]))
(pp (intintint-fn 123.456 [10 20 30]))
(pp (return-struct-fn 42))
(pp (struct-big-fn 11 99.5))
(assert (= 60 (int-fn 10 20)))
(assert (= 42 (double-fn 1.5 2.5 3.5)))
(assert (= 21 (double-many 1 2 3 4 5 6)))
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
(assert (= 204 (float-fn 8 4 17)))
# #
# Struct reading and writing # Struct reading and writing
# #
@ -129,4 +111,26 @@
(check-round-trip s [1 3 5 123.5]) (check-round-trip s [1 3 5 123.5])
(check-round-trip s [-1 -3 -5 -123.5]) (check-round-trip s [-1 -3 -5 -123.5])
#
# Call functions
#
(pp (void-fn))
(pp (int-fn 10 20))
(pp (double-fn 1.5 2.5 3.5))
(pp (double-many 1 2 3 4 5 6))
(pp (double-lots 1 2 3 4 5 6 7 8 9 10))
(pp (float-fn 8 4 17))
(pp (intint-fn 123.456 [10 20]))
(pp (intintint-fn 123.456 [10 20 30]))
(pp (return-struct-fn 42))
(pp (double-lots 1 2 3 4 5 6 700 800 9 10))
#(pp (struct-big-fn 11 99.5))
(assert (= 60 (int-fn 10 20)))
(assert (= 42 (double-fn 1.5 2.5 3.5)))
#(assert (= 21 (double-many 1 2 3 4 5 6)))
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
(assert (= 204 (float-fn 8 4 17)))
(print "Done.") (print "Done.")

View File

@ -3684,7 +3684,7 @@
(defn make-sig [] (defn make-sig []
(ffi/signature :default ret-type ;computed-type-args)) (ffi/signature :default ret-type ;computed-type-args))
(defn make-ptr [] (defn make-ptr []
(assert (ffi/lookup (if lazy (llib) lib) raw-symbol) "failed to find symbol")) (assert (ffi/lookup (if lazy (llib) lib) raw-symbol) (string "failed to find ffi symbol " raw-symbol)))
(if lazy (if lazy
~(defn ,name ,;meta [,;formal-args] ~(defn ,name ,;meta [,;formal-args]
(,ffi/call (,(delay (make-ptr))) (,(delay (make-sig))) ,;formal-args)) (,ffi/call (,(delay (make-ptr))) (,(delay (make-sig))) ,;formal-args))

View File

@ -355,11 +355,11 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
if (all_packed || pack_one) { if (all_packed || pack_one) {
if (st->size % el_align != 0) is_aligned = 0; if (st->size % el_align != 0) is_aligned = 0;
st->fields[i].offset = st->size; st->fields[i].offset = st->size;
st->size += el_size; st->size += (uint32_t) el_size;
} else { } else {
if (el_align > st->align) st->align = el_align; if (el_align > st->align) st->align = (uint32_t) el_align;
st->fields[i].offset = (((st->size + el_align - 1) / el_align) * el_align); st->fields[i].offset = (uint32_t)(((st->size + el_align - 1) / el_align) * el_align);
st->size = el_size + st->fields[i].offset; st->size = (uint32_t)(el_size + st->fields[i].offset);
} }
i++; i++;
} }
@ -477,7 +477,7 @@ static void janet_ffi_write_one(void *to, const Janet *argv, int32_t n, JanetFFI
} }
for (int32_t i = 0; i < els.len; i++) { for (int32_t i = 0; i < els.len; i++) {
JanetFFIType tp = st->fields[i].type; JanetFFIType tp = st->fields[i].type;
janet_ffi_write_one(to + st->fields[i].offset, els.items, i, tp, recur - 1); janet_ffi_write_one((char *) to + st->fields[i].offset, els.items, i, tp, recur - 1);
} }
} }
break; break;
@ -485,7 +485,7 @@ static void janet_ffi_write_one(void *to, const Janet *argv, int32_t n, JanetFFI
((double *)(to))[0] = janet_getnumber(argv, n); ((double *)(to))[0] = janet_getnumber(argv, n);
break; break;
case JANET_FFI_TYPE_FLOAT: case JANET_FFI_TYPE_FLOAT:
((float *)(to))[0] = janet_getnumber(argv, n); ((float *)(to))[0] = (float) janet_getnumber(argv, n);
break; break;
case JANET_FFI_TYPE_PTR: case JANET_FFI_TYPE_PTR:
((void **)(to))[0] = janet_ffi_getpointer(argv, n); ((void **)(to))[0] = janet_ffi_getpointer(argv, n);
@ -509,13 +509,13 @@ static void janet_ffi_write_one(void *to, const Janet *argv, int32_t n, JanetFFI
((int64_t *)(to))[0] = janet_getinteger64(argv, n); ((int64_t *)(to))[0] = janet_getinteger64(argv, n);
break; break;
case JANET_FFI_TYPE_UINT8: case JANET_FFI_TYPE_UINT8:
((uint8_t *)(to))[0] = janet_getuinteger64(argv, n); ((uint8_t *)(to))[0] = (uint8_t) janet_getuinteger64(argv, n);
break; break;
case JANET_FFI_TYPE_UINT16: case JANET_FFI_TYPE_UINT16:
((uint16_t *)(to))[0] = janet_getuinteger64(argv, n); ((uint16_t *)(to))[0] = (uint16_t) janet_getuinteger64(argv, n);
break; break;
case JANET_FFI_TYPE_UINT32: case JANET_FFI_TYPE_UINT32:
((uint32_t *)(to))[0] = janet_getuinteger64(argv, n); ((uint32_t *)(to))[0] = (uint32_t) janet_getuinteger64(argv, n);
break; break;
case JANET_FFI_TYPE_UINT64: case JANET_FFI_TYPE_UINT64:
((uint64_t *)(to))[0] = janet_getuinteger64(argv, n); ((uint64_t *)(to))[0] = janet_getuinteger64(argv, n);
@ -684,7 +684,7 @@ JANET_CORE_FN(cfun_ffi_signature,
#ifdef JANET_FFI_WIN64_ENABLED #ifdef JANET_FFI_WIN64_ENABLED
case JANET_FFI_CC_WIN_64: { case JANET_FFI_CC_WIN_64: {
size_t ret_size = type_size(ret.type); size_t ret_size = type_size(ret.type);
size_t ref_stack_count = 0; uint32_t ref_stack_count = 0;
ret.spec = JANET_WIN64_REGISTER; ret.spec = JANET_WIN64_REGISTER;
uint32_t next_register = 0; uint32_t next_register = 0;
if (ret_size != 1 && ret_size != 2 && ret_size != 4 && ret_size != 8) { if (ret_size != 1 && ret_size != 2 && ret_size != 4 && ret_size != 8) {
@ -699,20 +699,21 @@ JANET_CORE_FN(cfun_ffi_signature,
size_t el_size = type_size(mappings[i].type); size_t el_size = type_size(mappings[i].type);
int is_register_sized = (el_size == 1 || el_size == 2 || el_size == 4 || el_size == 8); int is_register_sized = (el_size == 1 || el_size == 2 || el_size == 4 || el_size == 8);
if (next_register < 4) { if (next_register < 4) {
mappings[i].offset = next_register++; mappings[i].offset = next_register;
if (is_register_sized) { if (is_register_sized) {
mappings[i].spec = JANET_WIN64_REGISTER; mappings[i].spec = JANET_WIN64_REGISTER;
/* Select variant based on position of floating point arguments */ /* Select variant based on position of floating point arguments */
if (mappings[i].type.prim == JANET_FFI_TYPE_FLOAT || if (mappings[i].type.prim == JANET_FFI_TYPE_FLOAT ||
mappings[i].type.prim == JANET_FFI_TYPE_DOUBLE) { mappings[i].type.prim == JANET_FFI_TYPE_DOUBLE) {
variant += 1 << next_register; variant += 1 << (3 - next_register);
} }
} else { } else {
mappings[i].spec = JANET_WIN64_REGISTER_REF; mappings[i].spec = JANET_WIN64_REGISTER_REF;
mappings[i].offset2 = ref_stack_count; mappings[i].offset2 = ref_stack_count;
ref_stack_count += (el_size + 15) / 16; ref_stack_count += (uint32_t)((el_size + 15) / 16);
} }
next_register++;
} else { } else {
if (is_register_sized) { if (is_register_sized) {
mappings[i].spec = JANET_WIN64_STACK; mappings[i].spec = JANET_WIN64_STACK;
@ -723,7 +724,7 @@ JANET_CORE_FN(cfun_ffi_signature,
mappings[i].offset = stack_count; mappings[i].offset = stack_count;
stack_count++; stack_count++;
mappings[i].offset2 = ref_stack_count; mappings[i].offset2 = ref_stack_count;
ref_stack_count += (el_size + 15) / 16; ref_stack_count += (uint32_t)((el_size + 15) / 16);
} }
} }
} }
@ -746,7 +747,7 @@ JANET_CORE_FN(cfun_ffi_signature,
if (mappings[i].spec == JANET_WIN64_STACK_REF || mappings[i].spec == JANET_WIN64_REGISTER_REF) { if (mappings[i].spec == JANET_WIN64_STACK_REF || mappings[i].spec == JANET_WIN64_REGISTER_REF) {
/* Align size to 16 bytes */ /* Align size to 16 bytes */
size_t size = (type_size(mappings[i].type) + 15) & ~0xFUL; size_t size = (type_size(mappings[i].type) + 15) & ~0xFUL;
mappings[i].offset2 = stack_count - mappings[i].offset2 - (size / 8); mappings[i].offset2 = (uint32_t)(stack_count - mappings[i].offset2 - (size / 8));
} }
} }
@ -972,13 +973,13 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
if (arg.spec == JANET_WIN64_STACK) { if (arg.spec == JANET_WIN64_STACK) {
janet_ffi_write_one(stack + arg.offset, argv, n, arg.type, JANET_FFI_MAX_RECUR); janet_ffi_write_one(stack + arg.offset, argv, n, arg.type, JANET_FFI_MAX_RECUR);
} else if (arg.spec == JANET_WIN64_STACK_REF) { } else if (arg.spec == JANET_WIN64_STACK_REF) {
uint8_t *ptr = (uint8_t *)(stack + args.offset2); uint8_t *ptr = (uint8_t *)(stack + arg.offset2);
janet_ffi_write_one(ptr, argv, n, arg.type, JANET_FFI_MAX_RECUR); janet_ffi_write_one(ptr, argv, n, arg.type, JANET_FFI_MAX_RECUR);
stack[args.offset] = (uint64_t) ptr; stack[arg.offset] = (uint64_t) ptr;
} else if (arg.spec == JANET_WIN64_REGISTER_REF) { } else if (arg.spec == JANET_WIN64_REGISTER_REF) {
uint8_t *ptr = (uint8_t *)(stack + args.offset2); uint8_t *ptr = (uint8_t *)(stack + arg.offset2);
janet_ffi_write_one(ptr, argv, n, arg.type, JANET_FFI_MAX_RECUR); janet_ffi_write_one(ptr, argv, n, arg.type, JANET_FFI_MAX_RECUR);
regs[args.offset].integer = (uint64_t) ptr; regs[arg.offset].integer = (uint64_t) ptr;
} else { } else {
janet_ffi_write_one((uint8_t *) &regs[arg.offset].integer, argv, n, arg.type, JANET_FFI_MAX_RECUR); janet_ffi_write_one((uint8_t *) &regs[arg.offset].integer, argv, n, arg.type, JANET_FFI_MAX_RECUR);
} }
@ -987,7 +988,7 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
/* the seasoned programmer who cut their teeth on assembly is probably quietly shaking their head by now... */ /* the seasoned programmer who cut their teeth on assembly is probably quietly shaking their head by now... */
switch (signature->variant) { switch (signature->variant) {
default: default:
janet_panic("unknown variant"); janet_panicf("unknown variant %d", signature->variant);
case 0: case 0:
ret_reg.integer = ((win64_variant_i_iiii *) function_pointer)(regs[0].integer, regs[1].integer, regs[2].integer, regs[3].integer); ret_reg.integer = ((win64_variant_i_iiii *) function_pointer)(regs[0].integer, regs[1].integer, regs[2].integer, regs[3].integer);
break; break;
@ -1121,7 +1122,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
"or to files. Returns a modifed buffer or a new buffer if one is not supplied.") { "or to files. Returns a modifed buffer or a new buffer if one is not supplied.") {
janet_arity(argc, 2, 3); janet_arity(argc, 2, 3);
JanetFFIType type = decode_ffi_type(argv[0]); JanetFFIType type = decode_ffi_type(argv[0]);
size_t el_size = type_size(type); uint32_t el_size = (uint32_t) type_size(type);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size); JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
janet_buffer_extra(buffer, el_size); janet_buffer_extra(buffer, el_size);
memset(buffer->data, 0, el_size); memset(buffer->data, 0, el_size);

View File

@ -36,6 +36,13 @@
#endif #endif
#endif #endif
#ifdef JANET_WINDOWS
#ifdef JANET_DYNAMIC_MODULES
#include <psapi.h>
#pragma comment (lib, "Psapi.lib")
#endif
#endif
#ifdef JANET_APPLE #ifdef JANET_APPLE
#include <AvailabilityMacros.h> #include <AvailabilityMacros.h>
#endif #endif
@ -904,6 +911,7 @@ char *get_processed_name(const char *name) {
} }
#if defined(JANET_WINDOWS) #if defined(JANET_WINDOWS)
static char error_clib_buf[256]; static char error_clib_buf[256];
char *error_clib(void) { char *error_clib(void) {
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
@ -920,6 +928,35 @@ Clib load_clib(const char *name) {
return LoadLibrary(name); return LoadLibrary(name);
} }
} }
void free_clib(HINSTANCE clib) {
if (clib != GetModuleHandle(NULL)) {
FreeLibrary(clib);
}
}
void *symbol_clib(HINSTANCE clib, const char *sym) {
if (clib != GetModuleHandle(NULL)) {
return GetProcAddress(clib, sym);
} else {
/* Look up symbols from all loaded modules */
HMODULE hMods[1024];
DWORD needed = 0;
if (EnumProcessModules(GetCurrentProcess(), hMods, sizeof(hMods), &needed)) {
needed /= sizeof(HMODULE);
for (DWORD i = 0; i < needed; i++) {
void *address = GetProcAddress(hMods[i], sym);
if (NULL != address) {
return address;
}
}
} else {
janet_panicf("ffi: %s", error_clib());
}
return NULL;
}
}
#endif #endif
/* Alloc function macro fills */ /* Alloc function macro fills */

View File

@ -140,8 +140,8 @@ typedef int Clib;
#elif defined(JANET_WINDOWS) #elif defined(JANET_WINDOWS)
#include <windows.h> #include <windows.h>
typedef HINSTANCE Clib; typedef HINSTANCE Clib;
#define free_clib(c) FreeLibrary((c)) void *symbol_clib(Clib clib, const char *sym);
#define symbol_clib(lib, sym) GetProcAddress((lib), (sym)) void free_clib(Clib clib);
Clib load_clib(const char *name); Clib load_clib(const char *name);
char *error_clib(void); char *error_clib(void);
#else #else

View File

@ -164,9 +164,9 @@ extern "C" {
#endif #endif
/* Enable or disable the FFI library. Currently, FFI only enabled on /* Enable or disable the FFI library. Currently, FFI only enabled on
* x86-64, non-windows operating systems. */ * x86-64 operating systems. */
#ifndef JANET_NO_FFI #ifndef JANET_NO_FFI
#if !defined(JANET_WINDOWS) && !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64)) #if !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64))
#define JANET_FFI #define JANET_FFI
#endif #endif
#endif #endif

View File

@ -33,6 +33,7 @@
# FFI check # FFI check
(compwhen has-ffi (compwhen has-ffi
(ffi/context)) (ffi/context))
(compwhen has-ffi (compwhen has-ffi
(ffi/defbind memcpy :ptr [dest :ptr src :ptr n :size])) (ffi/defbind memcpy :ptr [dest :ptr src :ptr n :size]))
(compwhen has-ffi (compwhen has-ffi