Skip to content

Instantly share code, notes, and snippets.

@ynkdir
Created September 15, 2012 10:14
Show Gist options
  • Save ynkdir/3727256 to your computer and use it in GitHub Desktop.
Save ynkdir/3727256 to your computer and use it in GitHub Desktop.
libcallex x64
#ifdef _WIN32
#include <windows.h>
#define EXPORT _declspec(dllexport)
#else
#include <dlfcn.h>
#define EXPORT
#endif
#include <vector>
#include <sstream>
#include <ffi.h>
#include "picojson.h"
extern "C" {
static std::string ptr2str(void* p) {
std::stringstream ss;
ss << p;
return ss.str();
}
static void* str2ptr(std::string s) {
void *p;
std::stringstream ss(s);
ss >> p;
return p;
}
EXPORT
const char* libcallex_load(const char* libname) {
static std::string _r;
void *p;
#ifdef _WIN32
p = (void*)LoadLibrary(libname);
#else
p = (void*)dlopen(libname, RTLD_LAZY);
#endif
_r = ptr2str(p);
return _r.c_str();
}
EXPORT
const char* libcallex_call(const char* context) {
static std::string r;
ffi_cif cif;
std::vector<ffi_type*> args;
std::vector<void*> values;
std::vector<void*> regptr;
std::vector<long> regint;
ffi_type* rtype;
void* rvalue;
char* rvalue_str = 0;
long rvalue_long = 0;
picojson::value v;
std::string err = picojson::parse(v, context, context + strlen(context));
if (!err.empty()) {
return "failed to parse arguments";
}
if (!v.is<picojson::object>()) {
return "unknown type of arguments";
}
picojson::object obj = v.get<picojson::object>();
try {
void* h = str2ptr(obj["handle"].get<std::string>());
std::string f = obj["function"].get<std::string>();
#ifdef _WIN32
void* p_ = (void*) GetProcAddress((HMODULE) h, f.c_str());
#else
void* p_ = (void*) dlsym((void *) h, f.c_str());
#endif
std::string rettype = obj["rettype"].get<std::string>();
picojson::array arg = obj["arguments"].get<picojson::array>();
regptr.reserve(arg.size());
regint.reserve(arg.size());
for (picojson::array::iterator it = arg.begin();
it != arg.end(); it++) {
if (it->is<std::string>()) {
args.push_back(&ffi_type_pointer);
regptr.push_back((void*)it->get<std::string>().c_str());
values.push_back(&regptr.back());
} else
if (it->is<double>()) {
args.push_back(&ffi_type_slong);
regint.push_back((long)(it->get<double>()));
values.push_back(&regint.back());
} else
if (it->is<bool>()) {
args.push_back(&ffi_type_slong);
regint.push_back((long)(it->get<bool>()));
values.push_back(&regint.back());
}
}
if (rettype.empty() || rettype == "number") {
rtype = &ffi_type_slong;
rvalue = &rvalue_long;
} else
if (rettype == "string") {
rtype = &ffi_type_pointer;
rvalue = &rvalue_str;
} else
if (rettype == "boolean") {
rtype = &ffi_type_slong;
rvalue = &rvalue_long;
}
if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), rtype, &args[0]) == FFI_OK) {
ffi_call(&cif, FFI_FN(p_), rvalue, &values[0]);
}
std::stringstream ss;
if (rettype.empty() || rettype == "number") {
ss << rvalue_long;
} else
if (rettype == "string" && rvalue) {
ss << rvalue_str;
} else
if (rettype == "boolean") {
ss << rvalue_long; // shouldn't return string 'true/false'
}
obj["return"] = ss.str();
v = obj;
r = v.serialize();
} catch(...) {
// perhaps, can't catch access violation of windows for gcc
#ifdef _WIN32
LPVOID lpMessageBuffer;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMessageBuffer,
0,
NULL);
obj["error"] = (char*)lpMessageBuffer;
LocalFree(lpMessageBuffer);
GetLastError();
#else
obj["error"] = dlerror();
#endif
r = v.serialize();
}
return r.c_str();
}
EXPORT
const char* libcallex_free(const char* context) {
picojson::value v;
std::string err = picojson::parse(v, context, context + strlen(context));
if (!err.empty() || !v.is<picojson::object>()) {
return "";
}
picojson::object obj = v.get<picojson::object>();
void* h = str2ptr(obj["handle"].get<std::string>());
#ifdef _WIN32
FreeLibrary((HMODULE) h);
#else
dlclose(h);
#endif
return "";
}
}
diff --git a/autoload/libcallex.cxx b/autoload/libcallex.cxx
index 7f2dc43..4d73864 100644
--- a/autoload/libcallex.cxx
+++ b/autoload/libcallex.cxx
@@ -8,15 +8,38 @@
#include <sstream>
#include "picojson.h"
+#ifdef _WIN64
+# define INTPTR_T long long
+#else
+# define INTPTR_T long
+#endif
+
extern "C" {
+static std::string ptr2str(void* p) {
+ std::stringstream ss;
+ ss << p;
+ return ss.str();
+}
+
+static void* str2ptr(std::string s) {
+ void *p;
+ std::stringstream ss(s);
+ ss >> p;
+ return p;
+}
+
EXPORT
-const int libcallex_load(const char* libname) {
+const char* libcallex_load(const char* libname) {
+ static std::string _r;
+ void *p;
#ifdef _WIN32
- return (long ) LoadLibrary(libname);
+ p = (void*)LoadLibrary(libname);
#else
- return (long ) dlopen(libname, RTLD_LAZY);
+ p = (void*)dlopen(libname, RTLD_LAZY);
#endif
+ _r = ptr2str(p);
+ return _r.c_str();
}
EXPORT
@@ -31,10 +54,10 @@ const char* libcallex_call(const char* context) {
return "unknown type of arguments";
}
picojson::object obj = v.get<picojson::object>();
- unsigned long* args = NULL;
+ INTPTR_T* args = NULL;
try {
- unsigned long r_ = 0;
- void* h = (void*) (long) obj["handle"].get<double>();
+ INTPTR_T r_ = 0;
+ void* h = str2ptr(obj["handle"].get<std::string>());
std::string f = obj["function"].get<std::string>();
#ifdef _WIN32
@@ -48,24 +71,60 @@ const char* libcallex_call(const char* context) {
std::string rettype = obj["rettype"].get<std::string>();
picojson::array arg = obj["arguments"].get<picojson::array>();
unsigned long narg = 0;
- args = new unsigned long[arg.size()];
+ args = new INTPTR_T[arg.size()];
for (picojson::array::iterator it = arg.begin();
it != arg.end(); it++) {
- args[narg] = 0;
if (it->is<std::string>()) {
- args[narg] = (unsigned long)(it->get<std::string>().c_str());
+ args[narg] = (INTPTR_T)(it->get<std::string>().c_str());
} else
if (it->is<double>()) {
- args[narg] = (unsigned long)(it->get<double>());
+ args[narg] = (INTPTR_T)(it->get<double>());
} else
if (it->is<bool>()) {
- args[narg] = (unsigned long)(it->get<bool>());
+ args[narg] = (INTPTR_T)(it->get<bool>());
}
narg++;
}
-#if defined(_MVC_VER)
+#if defined(_WIN64) && defined(_MVC_VER)
+ // XXX: NOT TESTED
+ // XXX: replace push to mov
+ // at lease 32 byte, aligned to 16 byte
+ INTPTR_T stackroom = 32;
+ if (narg > 4)
+ stackroom += 16 * (narg % 2);
+ _asm sub rsp, stackroom
+ for (unsigned long n = narg; n > 4; n--) {
+ INTPTR_T a_ = args[n - 1];
+ _asm push a_
+ }
+ if (narg > 3) {
+ INTPTR_T a_ = args[3];
+ _asm mov a_, r9
+ }
+ if (narg > 2) {
+ INTPTR_T a_ = args[2];
+ _asm mov a_, r8
+ }
+ if (narg > 1) {
+ INTPTR_T a_ = args[1];
+ _asm mov a_, rdx
+ }
+ if (narg > 0) {
+ INTPTR_T a_ = args[0];
+ _asm mov a_, rcx
+ }
+ _asm {
+ call p_
+ mov r_, rax
+ }
+ if (narg > 4) {
+ INTPTR_T a_ = (narg - 4) * sizeof(void *);
+ _asm add rsp, a_
+ }
+ _asm add rsp, stackroom
+#elif defined(_WIN32) && defined(_MVC_VER)
for (unsigned long n = 0; n < narg; n++) {
- unsigned long a_ = args[narg-n-1];
+ INTPTR_T a_ = args[narg-n-1];
_asm {
mov eax, a_
push eax
@@ -75,17 +134,59 @@ const char* libcallex_call(const char* context) {
call p_
mov r_, eax
}
-#elif defined(__GNUC__)
+#elif defined(_WIN64) && defined(___GNUC__)
+ // XXX: NOT TESTED
+ // XXX: replace push to mov
+ // at lease 32 byte, aligned to 16 byte
+ INTPTR_T stackroom = 32;
+ if (narg > 4)
+ stackroom += 16 * (narg % 2);
+ __asm__ ("subq %0, %%rsp"::"r"(stackroom));
+ for (unsigned long n = narg; n > 4; n--)
+ __asm__ ("pushq %0"::"r"(args[n-1]));
+ if (narg > 3) __asm__ ("movq %0, %%r9"::"r"(args[3]));
+ if (narg > 2) __asm__ ("movq %0, %%r8"::"r"(args[2]));
+ if (narg > 1) __asm__ ("movq %0, %%rdx"::"r"(args[1]));
+ if (narg > 0) __asm__ ("movq %0, %%rcx"::"r"(args[0]));
+ __asm__ ("call %0":"=r"(r_):"r"(p_));
+ if (narg > 4)
+ __asm__ ("addq %0, %%rsp"::"r"((narg - 4) * sizeof(void*)));
+ __asm__ ("addq %0, %%rsp"::"r"(stackroom));
+#elif defined(_WIN32) && defined(__GNUC__)
+ for (unsigned long n = 0; n < narg; n++) {
+ INTPTR_T a_ = args[narg-n-1];
+ __asm__ (
+ "push %0"
+ ::"r"(a_)
+ );
+ }
+ __asm__ (
+ "call %0"
+ :"=r"(r_)
+ :"r"(p_)
+ );
+#elif defined(__linux__) && defined(__x86_64__) && defined(__GNUC__)
+ for (unsigned long n = narg; n > 6; n--)
+ __asm__ ("pushq %0"::"r"(args[n-1]));
+ if (narg > 5) __asm__ ("movq %0, %%r9" ::"r"(args[5]));
+ if (narg > 4) __asm__ ("movq %0, %%r8" ::"r"(args[4]));
+ if (narg > 3) __asm__ ("movq %0, %%rcx"::"r"(args[3]));
+ if (narg > 2) __asm__ ("movq %0, %%rdx"::"r"(args[2]));
+ if (narg > 1) __asm__ ("movq %0, %%rsi"::"r"(args[1]));
+ if (narg > 0) __asm__ ("movq %0, %%rdi"::"r"(args[0]));
+ __asm__ ("call %0":"=r"(r_):"r"(p_));
+ if (narg > 6)
+ __asm__ ("addq %0, %%rsp"::"r"((narg - 6) * sizeof(void*)));
+#elif defined(__linux__) && defined(__i386__) && defined(__GNUC__)
for (unsigned long n = 0; n < narg; n++) {
- unsigned long a_ = args[narg-n-1];
+ INTPTR_T a_ = args[narg-n-1];
__asm__ (
- "mov %%eax, %0;"
- "push %%eax;"
- ::"r"(a_):"%eax"
+ "push %0"
+ ::"r"(a_)
);
}
__asm__ (
- "call %%eax;"
+ "call %0"
:"=r"(r_)
:"r"(p_)
);
@@ -137,7 +238,7 @@ const char* libcallex_free(const char* context) {
return "";
}
picojson::object obj = v.get<picojson::object>();
- void* h = (void*) (long) obj["handle"].get<double>();
+ void* h = str2ptr(obj["handle"].get<std::string>());
#ifdef _WIN32
FreeLibrary((HMODULE) h);
#else
diff --git a/autoload/libcallex.vim b/autoload/libcallex.vim
index 1878d3b..34c8ca5 100644
--- a/autoload/libcallex.vim
+++ b/autoload/libcallex.vim
@@ -33,7 +33,7 @@ function! s:transform(obj)
throw "can't treat unknown type"
endfunction
-let s:template = {'libname': '', 'handle': 0}
+let s:template = {'libname': '', 'handle': ''}
function! s:template.call(func, ...) dict
let arguments = []
@@ -77,14 +77,14 @@ function! s:template.free()
call remove(self, 'call')
call remove(self, 'free')
call libcall(s:libfile, 'libcallex_free', s:transform(self))
- let self.handle = 0
+ let self.handle = ''
endfunction
function! libcallex#load(name)
let lib = copy(s:template)
let lib.libname = a:name
- let lib.handle = libcallnr(s:libfile, 'libcallex_load', a:name)
- if lib.handle == 0
+ let lib.handle = libcall(s:libfile, 'libcallex_load', a:name)
+ if lib.handle == ''
throw "can't load library: \"" . a:name . "\""
endif
return lib
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment