1
0
mirror of https://github.com/janet-lang/janet synced 2024-06-18 11:19:56 +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 <string.h>
#ifdef _WIN32
#define EXPORTER __declspec(dllexport)
#else
#define EXPORTER
#endif
EXPORTER
int int_fn(int a, int b) {
return (a << 2) + b;
}
EXPORTER
double my_fn(int64_t a, int64_t b, const char *x) {
return (double)(a + b) + 0.5 + strlen(x);
}
EXPORTER
double double_fn(double x, double y, double z) {
return (x + y) * z * 3;
}
EXPORTER
double double_many(double x, double y, double z, double w, double a, double b) {
return x + y + z + w + a + b;
}
EXPORTER
double double_lots(
double a,
double b,
@ -32,6 +43,7 @@ double double_lots(
return i + j;
}
EXPORTER
double float_fn(float x, float y, float z) {
return (x + y) * z;
}
@ -47,16 +59,19 @@ typedef struct {
int c;
} intintint;
EXPORTER
int intint_fn(double x, intint ii) {
printf("double: %g\n", x);
return ii.a + ii.b;
}
EXPORTER
int intintint_fn(double x, intintint iii) {
printf("double: %g\n", x);
return iii.a + iii.b + iii.c;
}
EXPORTER
intint return_struct(int i) {
intint ret;
ret.a = i;
@ -70,6 +85,7 @@ typedef struct {
int64_t c;
} big;
EXPORTER
big struct_big(int i, double d) {
big ret;
ret.a = i;
@ -78,10 +94,12 @@ big struct_big(int i, double d) {
return ret;
}
EXPORTER
void void_fn(void) {
printf("void fn ran\n");
}
EXPORTER
void void_ret_fn(int 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
#
(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")
(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 int-fn-sig (ffi/signature :default :int :int :int))
@ -72,27 +75,6 @@
[]
(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
#
@ -129,4 +111,26 @@
(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.")

View File

@ -3684,7 +3684,7 @@
(defn make-sig []
(ffi/signature :default ret-type ;computed-type-args))
(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
~(defn ,name ,;meta [,;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 (st->size % el_align != 0) is_aligned = 0;
st->fields[i].offset = st->size;
st->size += el_size;
st->size += (uint32_t) el_size;
} else {
if (el_align > st->align) st->align = el_align;
st->fields[i].offset = (((st->size + el_align - 1) / el_align) * el_align);
st->size = el_size + st->fields[i].offset;
if (el_align > st->align) st->align = (uint32_t) el_align;
st->fields[i].offset = (uint32_t)(((st->size + el_align - 1) / el_align) * el_align);
st->size = (uint32_t)(el_size + st->fields[i].offset);
}
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++) {
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;
@ -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);
break;
case JANET_FFI_TYPE_FLOAT:
((float *)(to))[0] = janet_getnumber(argv, n);
((float *)(to))[0] = (float) janet_getnumber(argv, n);
break;
case JANET_FFI_TYPE_PTR:
((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);
break;
case JANET_FFI_TYPE_UINT8:
((uint8_t *)(to))[0] = janet_getuinteger64(argv, n);
((uint8_t *)(to))[0] = (uint8_t) janet_getuinteger64(argv, n);
break;
case JANET_FFI_TYPE_UINT16:
((uint16_t *)(to))[0] = janet_getuinteger64(argv, n);
((uint16_t *)(to))[0] = (uint16_t) janet_getuinteger64(argv, n);
break;
case JANET_FFI_TYPE_UINT32:
((uint32_t *)(to))[0] = janet_getuinteger64(argv, n);
((uint32_t *)(to))[0] = (uint32_t) janet_getuinteger64(argv, n);
break;
case JANET_FFI_TYPE_UINT64:
((uint64_t *)(to))[0] = janet_getuinteger64(argv, n);
@ -684,7 +684,7 @@ JANET_CORE_FN(cfun_ffi_signature,
#ifdef JANET_FFI_WIN64_ENABLED
case JANET_FFI_CC_WIN_64: {
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;
uint32_t next_register = 0;
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);
int is_register_sized = (el_size == 1 || el_size == 2 || el_size == 4 || el_size == 8);
if (next_register < 4) {
mappings[i].offset = next_register++;
mappings[i].offset = next_register;
if (is_register_sized) {
mappings[i].spec = JANET_WIN64_REGISTER;
/* Select variant based on position of floating point arguments */
if (mappings[i].type.prim == JANET_FFI_TYPE_FLOAT ||
mappings[i].type.prim == JANET_FFI_TYPE_DOUBLE) {
variant += 1 << next_register;
variant += 1 << (3 - next_register);
}
} else {
mappings[i].spec = JANET_WIN64_REGISTER_REF;
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 {
if (is_register_sized) {
mappings[i].spec = JANET_WIN64_STACK;
@ -723,7 +724,7 @@ JANET_CORE_FN(cfun_ffi_signature,
mappings[i].offset = stack_count;
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) {
/* Align size to 16 bytes */
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) {
janet_ffi_write_one(stack + arg.offset, argv, n, arg.type, JANET_FFI_MAX_RECUR);
} 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);
stack[args.offset] = (uint64_t) ptr;
stack[arg.offset] = (uint64_t) ptr;
} 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);
regs[args.offset].integer = (uint64_t) ptr;
regs[arg.offset].integer = (uint64_t) ptr;
} else {
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... */
switch (signature->variant) {
default:
janet_panic("unknown variant");
janet_panicf("unknown variant %d", signature->variant);
case 0:
ret_reg.integer = ((win64_variant_i_iiii *) function_pointer)(regs[0].integer, regs[1].integer, regs[2].integer, regs[3].integer);
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.") {
janet_arity(argc, 2, 3);
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);
janet_buffer_extra(buffer, el_size);
memset(buffer->data, 0, el_size);

View File

@ -36,6 +36,13 @@
#endif
#endif
#ifdef JANET_WINDOWS
#ifdef JANET_DYNAMIC_MODULES
#include <psapi.h>
#pragma comment (lib, "Psapi.lib")
#endif
#endif
#ifdef JANET_APPLE
#include <AvailabilityMacros.h>
#endif
@ -904,6 +911,7 @@ char *get_processed_name(const char *name) {
}
#if defined(JANET_WINDOWS)
static char error_clib_buf[256];
char *error_clib(void) {
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
@ -920,6 +928,35 @@ Clib load_clib(const char *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
/* Alloc function macro fills */

View File

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

View File

@ -164,9 +164,9 @@ extern "C" {
#endif
/* 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
#if !defined(JANET_WINDOWS) && !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64))
#if !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64))
#define JANET_FFI
#endif
#endif

View File

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