451 lines
14 KiB
C
451 lines
14 KiB
C
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
||
|
*
|
||
|
* Copyright (c) 2011 James R. McKaskill
|
||
|
*
|
||
|
* This software is licensed under the stock MIT license:
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
* DEALINGS IN THE SOFTWARE.
|
||
|
*
|
||
|
* ----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
#define _CRT_SECURE_NO_WARNINGS
|
||
|
#endif
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
# include <lua.h>
|
||
|
# include <lauxlib.h>
|
||
|
}
|
||
|
# define EXTERN_C extern "C"
|
||
|
#else
|
||
|
# include <lua.h>
|
||
|
# include <lauxlib.h>
|
||
|
# define EXTERN_C extern
|
||
|
#endif
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
#include <windows.h>
|
||
|
#else
|
||
|
#include <errno.h>
|
||
|
#include <unistd.h>
|
||
|
#include <dlfcn.h>
|
||
|
#include <sys/mman.h>
|
||
|
#endif
|
||
|
|
||
|
#if __STDC_VERSION__+0 >= 199901L && !defined(__TINYC__) //< @r-lyeh: handle tcc case
|
||
|
#include <complex.h>
|
||
|
#define HAVE_COMPLEX
|
||
|
#define HAVE_LONG_DOUBLE
|
||
|
#endif
|
||
|
|
||
|
#ifndef NDEBUG
|
||
|
#define DASM_CHECKS
|
||
|
#endif
|
||
|
|
||
|
struct jit;
|
||
|
#define Dst_DECL struct jit* Dst
|
||
|
#define Dst_REF (Dst->ctx)
|
||
|
#define DASM_EXTERN(a,b,c,d) get_extern(a,b,c,d)
|
||
|
|
||
|
#include "dynasm/dasm_proto.h"
|
||
|
|
||
|
#if defined LUA_FFI_BUILD_AS_DLL
|
||
|
# define EXPORT __declspec(dllexport)
|
||
|
#elif defined __GNUC__
|
||
|
# define EXPORT __attribute__((visibility("default")))
|
||
|
#else
|
||
|
# define EXPORT
|
||
|
#endif
|
||
|
|
||
|
EXTERN_C EXPORT int luaopen_ffi(lua_State* L);
|
||
|
|
||
|
static int lua_absindex2(lua_State* L, int idx) {
|
||
|
return (LUA_REGISTRYINDEX <= idx && idx < 0)
|
||
|
? lua_gettop(L) + idx + 1
|
||
|
: idx;
|
||
|
}
|
||
|
/* use our own version of lua_absindex such that lua_absindex(L, 0) == 0 */
|
||
|
#define lua_absindex(L, idx) lua_absindex2(L, idx)
|
||
|
|
||
|
#if LUA_VERSION_NUM == 501
|
||
|
static void lua_callk(lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k)
|
||
|
{
|
||
|
lua_call(L, nargs, nresults);
|
||
|
}
|
||
|
/*
|
||
|
** set functions from list 'l' into table at top - 'nup'; each
|
||
|
** function gets the 'nup' elements at the top as upvalues.
|
||
|
** Returns with only the table at the stack.
|
||
|
*/
|
||
|
static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
||
|
luaL_checkstack(L, nup, "too many upvalues");
|
||
|
for (; l && l->name; l++) { /* fill the table with given functions */
|
||
|
int i;
|
||
|
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||
|
lua_pushvalue(L, -nup);
|
||
|
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||
|
lua_setfield(L, -(nup + 2), l->name);
|
||
|
}
|
||
|
lua_pop(L, nup); /* remove upvalues */
|
||
|
}
|
||
|
#define lua_setuservalue lua_setfenv
|
||
|
#define lua_getuservalue lua_getfenv
|
||
|
#define lua_rawlen lua_objlen
|
||
|
static char* luaL_prepbuffsize(luaL_Buffer* B, size_t sz) {
|
||
|
if (sz > LUAL_BUFFERSIZE) {
|
||
|
luaL_error(B->L, "string too long");
|
||
|
}
|
||
|
return luaL_prepbuffer(B);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* architectures */
|
||
|
#if defined _WIN32 && defined UNDER_CE
|
||
|
# define OS_CE
|
||
|
#elif defined _WIN32
|
||
|
# define OS_WIN
|
||
|
#elif defined __APPLE__ && defined __MACH__
|
||
|
# define OS_OSX
|
||
|
#elif defined __linux__
|
||
|
# define OS_LINUX
|
||
|
#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
|
||
|
# define OS_BSD
|
||
|
#elif defined unix || defined __unix__ || defined __unix || defined _POSIX_VERSION || defined _XOPEN_VERSION
|
||
|
# define OS_POSIX
|
||
|
#endif
|
||
|
|
||
|
/* architecture */
|
||
|
#if defined __i386__ || defined _M_IX86
|
||
|
# define ARCH_X86
|
||
|
#elif defined __amd64__ || defined _M_X64
|
||
|
# define ARCH_X64
|
||
|
#elif defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm
|
||
|
# define ARCH_ARM
|
||
|
#elif defined OS_LINUX && defined __TINYC__ //< @r-lyeh: tcc+linux
|
||
|
# define ARCH_X64 //< @r-lyeh: tcc+linux
|
||
|
#else
|
||
|
# error
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
|
||
|
# ifdef UNDER_CE
|
||
|
static void* DoLoadLibraryA(const char* name) {
|
||
|
wchar_t buf[MAX_PATH];
|
||
|
int sz = MultiByteToWideChar(CP_UTF8, 0, name, -1, buf, 512);
|
||
|
if (sz > 0) {
|
||
|
buf[sz] = 0;
|
||
|
return LoadLibraryW(buf);
|
||
|
} else {
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
# define LoadLibraryA DoLoadLibraryA
|
||
|
# else
|
||
|
# define GetProcAddressA GetProcAddress
|
||
|
# endif
|
||
|
|
||
|
# define LIB_FORMAT_1 "%s.dll"
|
||
|
# define AllocPage(size) VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)
|
||
|
# define FreePage(data, size) VirtualFree(data, 0, MEM_RELEASE)
|
||
|
# define EnableExecute(data, size) do {DWORD old; VirtualProtect(data, size, PAGE_EXECUTE, &old); FlushInstructionCache(GetCurrentProcess(), data, size);} while (0)
|
||
|
# define EnableWrite(data, size) do {DWORD old; VirtualProtect(data, size, PAGE_READWRITE, &old);} while (0)
|
||
|
|
||
|
#else
|
||
|
# define LIB_FORMAT_1 "%s.so"
|
||
|
# define LIB_FORMAT_2 "lib%s.so"
|
||
|
# define LoadLibraryA(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL)
|
||
|
# define GetProcAddressA(lib, name) dlsym(lib, name)
|
||
|
# define AllocPage(size) mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0)
|
||
|
# define FreePage(data, size) munmap(data, size)
|
||
|
# define EnableExecute(data, size) mprotect(data, size, PROT_READ|PROT_EXEC)
|
||
|
# define EnableWrite(data, size) mprotect(data, size, PROT_READ|PROT_WRITE)
|
||
|
#endif
|
||
|
|
||
|
#if defined ARCH_X86 || defined ARCH_X64
|
||
|
#define ALLOW_MISALIGNED_ACCESS
|
||
|
#endif
|
||
|
|
||
|
struct token;
|
||
|
|
||
|
struct parser {
|
||
|
int line;
|
||
|
const char* next;
|
||
|
const char* prev;
|
||
|
unsigned align_mask;
|
||
|
};
|
||
|
|
||
|
struct page {
|
||
|
size_t size;
|
||
|
size_t off;
|
||
|
size_t freed;
|
||
|
};
|
||
|
|
||
|
struct jit {
|
||
|
lua_State* L;
|
||
|
int32_t last_errno;
|
||
|
dasm_State* ctx;
|
||
|
size_t pagenum;
|
||
|
struct page** pages;
|
||
|
size_t align_page_size;
|
||
|
void** globals;
|
||
|
int function_extern;
|
||
|
void* lua_dll;
|
||
|
void* kernel32_dll;
|
||
|
};
|
||
|
|
||
|
#define ALIGN_DOWN(PTR, MASK) \
|
||
|
(((uintptr_t) (PTR)) & (~ ((uintptr_t) (MASK)) ))
|
||
|
#define ALIGN_UP(PTR, MASK) \
|
||
|
(( ((uintptr_t) (PTR)) + ((uintptr_t) (MASK)) ) & (~ ((uintptr_t) (MASK)) ))
|
||
|
|
||
|
/* struct cdata/struct ctype */
|
||
|
|
||
|
#define PTR_ALIGN_MASK (sizeof(void*) - 1)
|
||
|
#define FUNCTION_ALIGN_MASK (sizeof(void (*)()) - 1)
|
||
|
#define DEFAULT_ALIGN_MASK 7
|
||
|
|
||
|
#ifdef OS_OSX
|
||
|
/* TODO: figure out why the alignof trick doesn't work on OS X */
|
||
|
#define ALIGNED_DEFAULT 7
|
||
|
#elif defined __GNUC__
|
||
|
#define ALIGNED_DEFAULT (__alignof__(void* __attribute__((aligned))) - 1)
|
||
|
#else
|
||
|
#define ALIGNED_DEFAULT PTR_ALIGN_MASK
|
||
|
#endif
|
||
|
|
||
|
extern int jit_key;
|
||
|
extern int ctype_mt_key;
|
||
|
extern int cdata_mt_key;
|
||
|
extern int cmodule_mt_key;
|
||
|
extern int callback_mt_key;
|
||
|
extern int constants_key;
|
||
|
extern int types_key;
|
||
|
extern int gc_key;
|
||
|
extern int callbacks_key;
|
||
|
extern int functions_key;
|
||
|
extern int abi_key;
|
||
|
extern int next_unnamed_key;
|
||
|
extern int niluv_key;
|
||
|
extern int asmname_key;
|
||
|
|
||
|
int equals_upval(lua_State* L, int idx, int* key);
|
||
|
void push_upval(lua_State* L, int* key);
|
||
|
void set_upval(lua_State* L, int* key);
|
||
|
struct jit* get_jit(lua_State* L);
|
||
|
|
||
|
/* both ctype and cdata are stored as userdatas
|
||
|
*
|
||
|
* usr value is a table shared between the related subtypes which has:
|
||
|
* name -> member ctype (for structs and unions)
|
||
|
* +ves -> member ctype - in memory order (for structs)
|
||
|
* +ves -> argument ctype (for function prototypes)
|
||
|
* 0 -> return ctype (for function prototypes)
|
||
|
* light userdata -> misc
|
||
|
*/
|
||
|
|
||
|
enum {
|
||
|
C_CALL,
|
||
|
STD_CALL,
|
||
|
FAST_CALL,
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
INVALID_TYPE,
|
||
|
VOID_TYPE,
|
||
|
FLOAT_TYPE,
|
||
|
DOUBLE_TYPE,
|
||
|
LONG_DOUBLE_TYPE,
|
||
|
COMPLEX_FLOAT_TYPE,
|
||
|
COMPLEX_DOUBLE_TYPE,
|
||
|
COMPLEX_LONG_DOUBLE_TYPE,
|
||
|
BOOL_TYPE,
|
||
|
INT8_TYPE,
|
||
|
INT16_TYPE,
|
||
|
INT32_TYPE,
|
||
|
INT64_TYPE,
|
||
|
INTPTR_TYPE,
|
||
|
ENUM_TYPE,
|
||
|
UNION_TYPE,
|
||
|
STRUCT_TYPE,
|
||
|
FUNCTION_TYPE,
|
||
|
FUNCTION_PTR_TYPE,
|
||
|
};
|
||
|
|
||
|
#define IS_CHAR_UNSIGNED (((char) -1) > 0)
|
||
|
#define IS_COMPLEX(type) ((type) == COMPLEX_FLOAT_TYPE || (type) == COMPLEX_DOUBLE_TYPE)
|
||
|
|
||
|
#define POINTER_BITS 2
|
||
|
#define POINTER_MAX ((1 << POINTER_BITS) - 1)
|
||
|
|
||
|
#define ALIGNOF(S) ((int) ((char*) &S.v - (char*) &S - 1))
|
||
|
|
||
|
/* Note: if adding a new member that is associated with a struct/union
|
||
|
* definition then it needs to be copied over in ctype.c:set_defined for when
|
||
|
* we create types based off of the declaration alone.
|
||
|
*
|
||
|
* Since this is used as a header for every ctype and cdata, and we create a
|
||
|
* ton of them on the stack, we try and minimise its size.
|
||
|
*/
|
||
|
struct ctype {
|
||
|
size_t base_size; /* size of the base type in bytes */
|
||
|
|
||
|
union {
|
||
|
/* valid if is_bitfield */
|
||
|
struct {
|
||
|
/* size of bitfield in bits */
|
||
|
unsigned bit_size : 7;
|
||
|
/* offset within the current byte between 0-63 */
|
||
|
unsigned bit_offset : 6;
|
||
|
};
|
||
|
/* Valid if is_array */
|
||
|
size_t array_size;
|
||
|
/* Valid for is_variable_struct or is_variable_array. If
|
||
|
* variable_size_known (only used for is_variable_struct) then this is
|
||
|
* the total increment otherwise this is the per element increment.
|
||
|
*/
|
||
|
size_t variable_increment;
|
||
|
};
|
||
|
size_t offset;
|
||
|
unsigned align_mask : 4; /* as (align bytes - 1) eg 7 gives 8 byte alignment */
|
||
|
unsigned pointers : POINTER_BITS; /* number of dereferences to get to the base type including +1 for arrays */
|
||
|
unsigned const_mask : POINTER_MAX + 1; /* const pointer mask, LSB is current pointer, +1 for the whether the base type is const */
|
||
|
unsigned type : 5; /* value given by type enum above */
|
||
|
unsigned is_reference : 1;
|
||
|
unsigned is_array : 1;
|
||
|
unsigned is_defined : 1;
|
||
|
unsigned is_null : 1;
|
||
|
unsigned has_member_name : 1;
|
||
|
unsigned calling_convention : 2;
|
||
|
unsigned has_var_arg : 1;
|
||
|
unsigned is_variable_array : 1; /* set for variable array types where we don't know the variable size yet */
|
||
|
unsigned is_variable_struct : 1;
|
||
|
unsigned variable_size_known : 1; /* used for variable structs after we know the variable size */
|
||
|
unsigned is_bitfield : 1;
|
||
|
unsigned has_bitfield : 1;
|
||
|
unsigned is_jitted : 1;
|
||
|
unsigned is_packed : 1;
|
||
|
unsigned is_unsigned : 1;
|
||
|
};
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
__declspec(align(16))
|
||
|
#endif
|
||
|
struct cdata {
|
||
|
const struct ctype type
|
||
|
#ifdef __GNUC__
|
||
|
__attribute__ ((aligned(16)))
|
||
|
#endif
|
||
|
;
|
||
|
};
|
||
|
|
||
|
typedef void (*cfunction)(void);
|
||
|
|
||
|
#ifdef HAVE_COMPLEX
|
||
|
typedef double complex complex_double;
|
||
|
typedef float complex complex_float;
|
||
|
#else
|
||
|
typedef struct {
|
||
|
double real, imag;
|
||
|
} complex_double;
|
||
|
|
||
|
typedef struct {
|
||
|
float real, imag;
|
||
|
} complex_float;
|
||
|
|
||
|
static double creal(complex_double c) {
|
||
|
return c.real;
|
||
|
}
|
||
|
static float crealf(complex_float c) {
|
||
|
return c.real;
|
||
|
}
|
||
|
|
||
|
static double cimag(complex_double c) {
|
||
|
return c.imag;
|
||
|
}
|
||
|
static float cimagf(complex_float c) {
|
||
|
return c.imag;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#define CALLBACK_FUNC_USR_IDX 1
|
||
|
|
||
|
void set_defined(lua_State* L, int ct_usr, struct ctype* ct);
|
||
|
struct ctype* push_ctype(lua_State* L, int ct_usr, const struct ctype* ct);
|
||
|
void* push_cdata(lua_State* L, int ct_usr, const struct ctype* ct); /* called from asm */
|
||
|
void push_callback(lua_State* L, cfunction f);
|
||
|
void check_ctype(lua_State* L, int idx, struct ctype* ct);
|
||
|
void* to_cdata(lua_State* L, int idx, struct ctype* ct);
|
||
|
void* check_cdata(lua_State* L, int idx, struct ctype* ct);
|
||
|
size_t ctype_size(lua_State* L, const struct ctype* ct);
|
||
|
|
||
|
int parse_type(lua_State* L, struct parser* P, struct ctype* type);
|
||
|
void parse_argument(lua_State* L, struct parser* P, int ct_usr, struct ctype* type, struct token* name, struct parser* asmname);
|
||
|
void push_type_name(lua_State* L, int usr, const struct ctype* ct);
|
||
|
|
||
|
int push_user_mt(lua_State* L, int ct_usr, const struct ctype* ct);
|
||
|
|
||
|
int ffi_cdef(lua_State* L);
|
||
|
|
||
|
void push_func_ref(lua_State* L, cfunction func);
|
||
|
void free_code(struct jit* jit, lua_State* L, cfunction func);
|
||
|
int x86_return_size(lua_State* L, int usr, const struct ctype* ct);
|
||
|
void compile_function(lua_State* L, cfunction f, int ct_usr, const struct ctype* ct);
|
||
|
cfunction compile_callback(lua_State* L, int fidx, int ct_usr, const struct ctype* ct);
|
||
|
void compile_globals(struct jit* jit, lua_State* L);
|
||
|
int get_extern(struct jit* jit, uint8_t* addr, int idx, int type);
|
||
|
|
||
|
/* WARNING: assembly needs to be updated for prototype changes of these functions */
|
||
|
int check_bool(lua_State* L, int idx);
|
||
|
double check_double(lua_State* L, int idx);
|
||
|
double check_complex_imag(lua_State* L, int idx);
|
||
|
float check_float(lua_State* L, int idx);
|
||
|
uint64_t check_uint64(lua_State* L, int idx);
|
||
|
int64_t check_int64(lua_State* L, int idx);
|
||
|
int32_t check_int32(lua_State* L, int idx);
|
||
|
uint32_t check_uint32(lua_State* L, int idx);
|
||
|
uintptr_t check_uintptr(lua_State* L, int idx);
|
||
|
int32_t check_enum(lua_State* L, int idx, int to_usr, const struct ctype* tt);
|
||
|
/* these two will always push a value so that we can create structs/functions on the fly */
|
||
|
void* check_typed_pointer(lua_State* L, int idx, int to_usr, const struct ctype* tt);
|
||
|
cfunction check_typed_cfunction(lua_State* L, int idx, int to_usr, const struct ctype* tt);
|
||
|
complex_double check_complex_double(lua_State* L, int idx);
|
||
|
complex_float check_complex_float(lua_State* L, int idx);
|
||
|
|
||
|
void unpack_varargs_stack(lua_State* L, int first, int last, char* to);
|
||
|
void unpack_varargs_reg(lua_State* L, int first, int last, char* to);
|
||
|
|
||
|
void unpack_varargs_stack_skip(lua_State* L, int first, int last, int ints_to_skip, int floats_to_skip, char* to);
|
||
|
void unpack_varargs_float(lua_State* L, int first, int last, int max, char* to);
|
||
|
void unpack_varargs_int(lua_State* L, int first, int last, int max, char* to);
|
||
|
|
||
|
|
||
|
|