📄 directcall.cpp
字号:
if (p) {
// (1)the current DLL handle needs to be non-NULL, but doesn't have to be sensible
// This is just to force Parser::declare_function() to call add_dll_function()
// below
set_dll_handle((void*)-1);
// (2)The modifier flag will be passed to add_dll_function(), so we give it
// a special value so it will pick up the pointer directly....
Parser::state.modifier = UseAddr;
mDirectAddr = p;
} else {
set_current_lib_file(0);
mDirectAddr = NULL;
}
}
bool add_dll_function(Function *pfn, int modifier, string& name)
{
PClass pc = pfn->class_context();
CALLFN proc;
// *change 0.9.4 Need explicit extern "C" now - (used to simply be class context)
// *add 1.1.4 Implicit self-link for those systems which need it...
if (modifier == UseAddr) proc = (CALLFN) mDirectAddr;
#ifdef CANNOT_SELF_LINK
else if (get_dll_handle()==IMPLICIT_LINK) {
name = pfn->name(); // *fix 1.2.4 pass back name in case we can't find
proc = lookup_self_link(pc,name);
}
#endif
else if(! Parser::in_extern_C()) { // mangled C++ name
proc = (CALLFN)Import::load_method_entry(pfn,name);
if (name=="") name = "?" + pfn->name();
} else { // extern "C"
name = pfn->name();
if (modifier == Stdcall) name = "_" + name + "@" + itos(pfn->signature()->byte_size());
proc = (CALLFN)get_proc_address(s_lib,name.c_str());
}
if (!proc) return false;
if (pc != NULL) pc->set_imported();
Sig ssig(pfn->return_type());
Signature *sig = pfn->signature();
Signature::iterator tli;
for(tli = sig->begin(); tli != sig->end(); ++tli) ssig << (Type)*tli;
ssig.set_const(sig->is_const()); // *fix 1.1.0 Must respect constness of signature!
// *fix 1.2.3 Important also to mke the signature reflect any extra unnamed arguments....
if (sig->stdarg()) ssig << t_void; // the convention used by the parser...
// *fix 1.1.0 We can now distinguish between ordinary cdecl method calls (GCC)
// and so-called '__thiscall' calls (MS)
// *add 1.2.3 Note that direct imports into the DLL are NOT stdcall under Linux!
int calling_convention = Function::CDECL;
if (modifier == Stdcall || modifier == Api
#ifdef _WIN32
|| modifier == UseAddr
#endif
)
calling_convention = Function::_STDCALL;
if(pfn->is_method()) {
if (pc->import_scheme()->uses_stdmethod()) calling_convention = Function::STDMETHOD;
}
add(ssig,pfn->name().c_str(),proc,false,calling_convention);
return true;
}
//----------------------- generating native stubs -------------------------
// Basically this is a mad (and v. limited) x86 macro assembler.
typedef unsigned long ulong;
typedef char *pchar;
typedef short *& pshort;
typedef ulong *& pulong;
// Disable some silly warnings:
// truncation of const value and 'int' to 'short'.
#pragma warning(disable:4309)
#pragma warning(disable:4305)
void emit1(pchar& pc, char ch) { *pc++ = ch; }
void emit2(pchar& pc, short s) { *pshort(pc)++ = s; }
void emit4(pchar& pc, ulong l) { *pulong(pc)++ = l;}
void pushc(pchar& pc, ulong val) { emit1(pc,0x68); emit4(pc,val); }
void pushv(pchar& pc, void *ptr) { emit2(pc,0x35FF); emit4(pc,(ulong)ptr); }
void popv(pchar& pc, void *ptr) { emit2(pc,0x058F); emit4(pc,(ulong)ptr); }
void callf(pchar& pc, void *pfn) {
emit1(pc,0xBA); emit4(pc,(ulong)pfn); // mov edx, offset copy array
emit2(pc,0xD2FF); // call edx
}
void mov_acc(pchar& pc, ulong val) { emit1(pc,0xB8); emit4(pc,val); } // mov eax,...
void mov_ptr(pchar& pc, void *val) { emit1(pc,0xB9); emit4(pc,(ulong)val); } // mov ecx..
void copy_acc(pchar& pc, void *ptr) { emit1(pc,0xA1); emit4(pc,(ulong)ptr); }
void copy_cx(pchar& pc, void *ptr) { emit2(pc,0x0D89); emit4(pc,(ulong)ptr); }
void sub_esp(pchar& pc, int val) { emit2(pc,0xEC81); emit4(pc,(ulong)val); }
void push_qword(pchar& pc, void *ptr) { emit2(pc,0x05DD); emit4(pc,(ulong)ptr); }
void ret(pchar& pc) { emit1(pc,(char)0xC3); }
void *generate_native_stub(Function *pfn)
{
// The stub must copy the arguments by calling copy_array(), and then calls Engine::execute().
char cde_buff[200];
Signature *sig = pfn->signature();
int no_args = sig->byte_size()/sizeof(int);
void *rra = new ulong;
ArgBlock *xargs = new ArgBlock;
int flags = Engine::ARGS_PASSED;
char *pc = cde_buff;
// *fix 1.1.4 Plain function callbacks are often cdecl...but think about this!
// *change 1.2.3 Functions export as cdecl, unless __stdcall is used;
// MS methods don't and GCC methods do; determined by is_cdecl().
bool fun_is_cdecl;
if (pfn->is_method()) {
fun_is_cdecl = pfn->is_cdecl();
} else
fun_is_cdecl = pfn->export_as_cdecl();
popv(pc,rra); // save return addr
if (pfn->is_method()) {
if (pfn->class_context()->import_scheme()->uses_stdmethod()) // is this a stdmethod call?
copy_cx(pc,&xargs->OPtr); // obj ptr was in ecx
else {
popv(pc,&xargs->OPtr); // obj ptr was on the stack
no_args++; // as an _extra_ arg
}
flags = flags | Engine::METHOD_CALL;
}
Type rt = pfn->return_type();
if (rt.is_double()) flags = flags | Engine::RETURN_64; else
if (!rt.is_void()) flags = flags | Engine::RETURN_32;
if (no_args > 0) {
// two different strategies here, depending on the compiler
#ifndef __GNUC__
mov_ptr(pc,xargs);
mov_acc(pc,no_args); // call copy_array
callf(pc,(void *)©_array);
// copy_array mucks w/ ESP by explicitly popping the arguments;
// cdecl calls assume that caller will sort out ESP.
if (fun_is_cdecl)
sub_esp(pc, sizeof(int)*no_args); // restore esp if cdecl
#else
pushc(pc,(unsigned long)xargs);
pushc(pc,no_args);
callf(pc,(void *)©_array);
sub_esp(pc, -8); // because copy_array is cdecl...
if (! fun_is_cdecl) // if we're NOT cdecl then clean up like a good boy!
sub_esp(pc, -sizeof(int)*no_args);
#endif
}
pushc(pc,(ulong)xargs);
pushc(pc,flags);
pushc(pc,(ulong)pfn->fun_block());
callf(pc,(void *)&Engine::stub_execute); // and call the stack engine!
if (flags & Engine::RETURN_32) copy_acc(pc,&xargs->ret1); // put result in eax, if needed
else
if (flags & Engine::RETURN_64) push_qword(pc,&xargs->ret2); // or onto fp stack *add 1.1.1
pushv(pc,rra); // restore return addr & return
ret(pc);
// and copy the code block
int sz = (ulong)pc - (ulong)cde_buff;
char *cp = new char[sz+1];
memcpy(cp,cde_buff,sz+1);
return cp;
}
// *add 1.1.4 The Linux tool chain currently doesn't allow you to link to self,
// so we have to explicitly specify self-exports.
#ifndef CANNOT_SELF_LINK
static const int sNoSelfLink = false;
#else
static const int sNoSelfLink = true;
#endif
//-------------------- shared library management ----------------
static string s_file;
bool set_current_lib_file(char *file)
{
if (!file) {
s_lib = NULL;
cleanup_ordinal_lookup();
} else {
// *add 1.1.2 Linking to #self finds the actual full path of UC pgm
// *fix 1.1.4 It's now #pragma dlink $self,etc! (otherwise argues with prepro)
if (file[0]=='#') file[0] = '$';
bool explicit_link = true;
if (strcmp(file,"$self")==0) {// self-linking case!
// *ch 1.2.9 patch
#ifdef _WIN32
file = Main::uc_exec_name();
#else
file = "";
#endif
explicit_link = ! sNoSelfLink;
} else
if (strcmp(file,"$caller")==0) {
file = NULL;
}
// *add 1.1.2 Try looking in the UC_LIB directory!
string sfile = file ? file : "$caller";
if (explicit_link) {
s_lib = load_library(file);
if (!s_lib) {
sfile = Main::uc_lib_dir() + sfile;
s_lib = load_library(sfile.c_str());
}
} else s_lib = IMPLICIT_LINK;
lib_list.push_back(s_lib);
if (!s_lib) return false;
// *fix 1.1.4 Subsequent #lib where the loaded DLL is the same - don't reset!
if (sfile != s_file) {
Import::reset(true);
s_file = sfile;
}
}
return true;
}
string get_current_lib_file()
{ return s_file; }
void unload_library(Handle hlib)
{
set_dll_handle(hlib);
Import::reset(false);
if (hlib != IMPLICIT_LINK && hlib != NULL) {
free_library(hlib);
cleanup_ordinal_lookup();
}
}
// *fix 1.2.4 if passed NULL (i.e. no current DLL _importing_ taking place)
// this function will try to unload the last handle loaded.
void unload_lib(Handle hlib)
{
if (hlib == NULL) {
hlib = lib_list.back();
}
unload_library(hlib);
lib_list.remove(hlib);
s_lib = NULL;
}
void *get_dll_handle() { return (void *)s_lib; }
void set_dll_handle(void *dl)
{
s_lib = (Handle)dl;
}
// Looking up DLL entries using ordinal lookup
typedef std::map<string,int> SIMap;
static SIMap *s_ord_lookup;
static bool s_lookup_is_ordinal;
bool using_ordinal_lookup()
{ return s_ord_lookup != NULL; }
int lookup_ordinal(const char *name)
{
SIMap::iterator simi = s_ord_lookup->find(name);
if (simi != s_ord_lookup->end()) return simi->second;
else return 0;
}
// *change 1.2.2 (Eric) imp file format is now more relaxed;
// (a) ignore any line that begins with a "# ", and
// (b) everything after the mangled name
// *add 1.2.4 Will look in UC LIB directory a la .DLLs
// *add 1.2.4 UC2 type means that value is not ordinal but address
int convert_ordinal(char *buf)
{
if (s_lookup_is_ordinal)
return atoi(buf);
else {
unsigned long l;
sscanf(buf,"%x",&l);
return (int)l;
}
}
bool lookup_is_ordinal()
{ return s_lookup_is_ordinal; }
bool generate_ordinal_lookup(const char *index_file)
{
string name,magic,compiler;
char buf[1024];
ifstream in;
in.open(index_file);
if (! in || in.eof()) {
string sfile = Main::uc_lib_dir() + index_file;
in.open(sfile.c_str());
if (! in || in.eof()) {
cerr << "cannot find '" << sfile << "'\n";
return false;
}
}
in >> magic >> compiler;
if (magic != "UC1" && magic != "UC2") return false;
s_lookup_is_ordinal = magic == "UC1";
if (! Import::set_scheme(compiler)) return false;
s_ord_lookup = new SIMap;
// *fix 1.2.3a (Eric) Attempted to read ordinal twice
while (! in.eof()) {
in >> buf;
if (*buf && *buf != '#') {
in >> name;
(*s_ord_lookup)[name] = convert_ordinal(buf);
}
in.getline(buf,sizeof(buf));
}
return true;
}
void cleanup_ordinal_lookup()
{
delete s_ord_lookup;
s_ord_lookup = NULL;
}
// *fix 1.2.3 Under Win32, it is a Bad Idea to try free the process handle.
void finis()
{
Handle process_handle = get_process_handle();
HandleList::iterator ili;
for(ili = lib_list.begin(); ili != lib_list.end(); ++ili) {
Handle lib = *ili;
if (lib != process_handle)
unload_library(lib);
}
}
CALLFN lookup_self_link(PClass pc,const string& name) { return NULL; }
} // namespace Builtin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -