Created
September 15, 2012 10:14
-
-
Save ynkdir/3727256 to your computer and use it in GitHub Desktop.
libcallex x64
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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(®ptr.back()); | |
} else | |
if (it->is<double>()) { | |
args.push_back(&ffi_type_slong); | |
regint.push_back((long)(it->get<double>())); | |
values.push_back(®int.back()); | |
} else | |
if (it->is<bool>()) { | |
args.push_back(&ffi_type_slong); | |
regint.push_back((long)(it->get<bool>())); | |
values.push_back(®int.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 ""; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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