Exetools

Exetools (https://forum.exetools.com/index.php)
-   Source Code (https://forum.exetools.com/forumdisplay.php?f=46)
-   -   [C] Winapi Call Dynamically and easily (https://forum.exetools.com/showthread.php?t=18714)

0xall0c 03-19-2018 17:30

[C] Winapi Call Dynamically and easily
 
This Small Function Let You call winapi dynamically, without having to define function definition, or writing nonsense wrappers.

Code:

void* DynCall(void *ptr, ...)
{
        char* function;
        char* library;
        va_list va;
        void *p;
        int i = 0;
        DWORD argBuf[32];
        DWORD ret;
        HANDLE lib;

        va_start(va, ptr);
        library = ptr;
        function = va_arg(va, void*);
        p = va_arg(va, void *);
        if(!(lib = GetModuleHandleA(library)))
                lib = _LoadLibraryA(library);
        if (!lib)
                error("Cant load libarary %s", library);
        void *funcAddress = _GetProcAddress(lib, function);
        if (!funcAddress)
                error("Cant Find Dynamic Address %s", function);
        for (; p != 0xb33f; p = va_arg(va, void *)) {
                argBuf[i++] = p;
        }
       
        for (i--; i >= 0; i--)
        {
                p = argBuf[i];
                _asm {
                        push p
                }
        }
        va_end(va);
        _asm {
                push lb
                jmp funcAddress
        lb:
                mov ret, eax
        }
        return ret;
}

Example :

Code:

DWORD dwResult = DynCall("ntdll.dll","NtUnmapViewOfSection",
                                        PI.hProcess,
                                        (LPVOID)(NtHeader->OptionalHeader.ImageBase),0xb33f
                                );

Dont forget to add one extra paramater at end i.e 0xb33f

I think it will be useful to someone.

Pansemuckl 03-19-2018 18:25

Using this with a wrong number of arguments OR screwing up stack (e.g. using wrong calling convention) ... good luck debugging this!

0xall0c 03-19-2018 20:12

when you dont know, number of arguments or calling convention of the function, you should not call that function,

its not for a commercial application (approx no maintainance), its for different scenarios.

vic4key 04-03-2018 19:18

I think the parameter number of arguments is better than 0xb33f. Ex. void* DynCall(void *ptr, int nargs, ...).
More, need checking for 32/64-bit and calling convention (32-bit can be __stdcall, __cdecl, ... 64-bit can be C-D-8-9 ordered registers, ...).

0xall0c 04-04-2018 17:34

yes number of arguments would be better, it will help in debugging,
(you can modify the function, i have just given a way to call winapi pure dynamically, which i wa not able to find anywhere)

also the function was written for 32bit, no 64bit support
but as you posted i got a requirement to port it in 64 bit
(coincidence!!)

dosprog 04-04-2018 19:01

It's likely not a good idea.
Any of imported functions must be correctly declared individulally,
and thus must be called normally.
In addition - without of using __asm directive.
In addition2 - repeated calls will be performed much faster.
In addition3 - [less or more] universal solution for x32/x64.

..It's likely not a good idea but it works..

--Add--

Add 1st argument of function as enum {C_CALL,STD_CALL}
and produce separate __asm code for this conventions ?



--Add2--

Quote:

Originally Posted by 0xall0c (Post 112705)
Example :

Code:

DWORD dwResult = DynCall("ntdll.dll","NtUnmapViewOfSection",
                                        PI.hProcess,
                                        (LPVOID)(NtHeader->OptionalHeader.ImageBase),0xb33f
                                );

Dont forget to add one extra paramater at end i.e 0xb33f

This example rewritten without of DynCall():
Quote:

DWORD(__stdcall*_NtUnmapViewOfSection)(DWORD,DWORD);

void main(void)
{
DWORD result;

if(!(_NtUnmapViewOfSection=(DWORD(__stdcall*)(DWORD,DWORD))GetProcAddress(LoadLibrary("ntdll.dll"),"NtUnmapViewOfSection")))goto dos_exit;

result=_NtUnmapViewOfSection(0,0);

dos_exit:;
}



0xall0c 04-05-2018 01:55

this code resolves problem when you don't want imports to be created in an executables, you can use getprocaddress and loadlibrary obviously, but using them and declaring each function prototype is very frustrating.

mcp 04-05-2018 03:07

@0xalloc: I don't understand what problem this solves in the first place? In which cases do you call an API while at the same time not knowing its function signature? I mean, you cannot even correctly call any API without knowing its signature, i.e., how many bytes to push/pop off the stack?

0xall0c 04-05-2018 16:14

Quote:

Originally Posted by mcp (Post 112893)
@0xalloc: I don't understand what problem this solves in the first place? In which cases do you call an API while at the same time not knowing its function signature? I mean, you cannot even correctly call any API without knowing its signature, i.e., how many bytes to push/pop off the stack?

in c/c++ you need to define function prototype first, with this no prototypes are needed.

0xall0c 04-05-2018 16:16

Quote:

Originally Posted by dosprog (Post 112890)
It's likely not a good idea.
Any of imported functions must be correctly declared individulally,
and thus must be called normally.
In addition - without of using __asm directive.
In addition2 - repeated calls will be performed much faster.
In addition3 - [less or more] universal solution for x32/x64.

..It's likely not a good idea but it works..

--Add--

Add 1st argument of function as enum {C_CALL,STD_CALL}
and produce separate __asm code for this conventions ?



--Add2--


This example rewritten without of DynCall():




see the function prototype you defined, when there are lot functions to call, that's an extra headache.

mcp 04-05-2018 16:35

Quote:

Originally Posted by 0xall0c (Post 112898)
in c/c++ you need to define function prototype first, with this no prototypes are needed.

Right, but you cannot invoke an API without knowing its prototype! So that means you know the prototype already when using the "DynCall" mechanism. So the only thing it saves you is a one-line typedef? But you loose type correctness for all API arguments...that seems like a very bad trade-off.

Pansemuckl 04-05-2018 16:44

Quote:

Originally Posted by 0xall0c (Post 112899)
see the function prototype you defined, when there are lot functions to call, that's an extra headache.

Not if you're using variadic templates (C++ 11)...
It produces even simpler, smaller and clean code.

0xall0c 04-05-2018 17:25

Quote:

Originally Posted by mcp (Post 112900)
Right, but you cannot invoke an API without knowing its prototype! So that means you know the prototype already when using the "DynCall" mechanism. So the only thing it saves you is a one-line typedef? But you loose type correctness for all API arguments...that seems like a very bad trade-off.

trade-off is bad or not is subjective to where it is used.

0xall0c 04-05-2018 17:27

Quote:

Originally Posted by Pansemuckl (Post 112901)
Not if you're using variadic templates (C++ 11)...
It produces even simpler, smaller and clean code.

i didn't used variadic templates before. thank you for pointing out.
:)

mcp 04-05-2018 18:44

Quote:

Originally Posted by 0xall0c (Post 112902)
trade-off is bad or not is subjective to where it is used.

Well, it's pretty uncontroversial that losing type correctness for your API calls *is* bad for overall correctness of your program. The amount of work you're saving is marginal, and at the same time you have to deal with the dangers of type system violations. How is that not a bad trade-off? And we haven't even talked about the loss of readability of DynCall vs regular API calls...

dosprog 04-06-2018 07:08

Quote:

Originally Posted by 0xall0c (Post 112899)
see the function prototype you defined, when there are lot functions to call, that's an extra headache.

It's programming..

In MASM32 we can invoke function using prototype or call function without prototype.
I tend to use the invoke.
It is convenient, however it is necessary to get used to.



0xall0c 04-06-2018 15:44

its just one more way to call api's, i am just saying use it if you want to else dont. i never said it is the best way to call an api, it is just another way. as i said previously it is subjective you like your own way of programming same way everyone like thiers.

IT IS JUST ONE ANOTHER WAY OF DOING IT.

mcp 04-06-2018 16:50

Sure, it's another way of doing it (i never questioned that), and I argued rationally why it doesn't give significant benefits, what it's downsides are, and thus why it isn't worth the trouble.
If you don't want your advice to be discussed and have the pros & cons be weighted against each other, then why even post it? This forum obviously should foster technical discussions, which is exactly what i did.

Note that not everyone has the ability to discriminate good from bad solutions, so it's valuable to have these kinds of discussions in order to avoid spreading non-ideal solutions.

Also, no need for screaming...

dosprog 04-06-2018 17:45

1 Attachment(s)
Quote:

Originally Posted by 0xall0c (Post 112913)
IT IS JUST ONE ANOTHER WAY OF DOING IT.

) Ok.
I'm written once more usage example of DynCall(). [minor fixed]

See attached archive.

Also it is here - Pass: exetools



mcp 04-06-2018 17:51

Taking what I said previously into account, a much safer variant could look like this:

Code:

template < typename RetType, typename... ArgTypes >
RetType DynCall(const char* dll_name, const char* api_name, ArgTypes...args) {
  typedef RetType(__stdcall* F)(ArgTypes...);
  HMODULE module = LoadLibrary(dll_name);
  assert(module != INVALID_MODULE_HANDLE && "Failed to load library");
  F fptr = (F)GetProcAddress(module, api_name);
  assert(fptr && "Unable to resolve API");
  return fptr(args...);
}

Usage would look like this:

Code:

int main() {
  std::cout << "GetTickCount1: "
            << DynCal< DWORD >("kernel32.dll", "GetTickCount") << "\n";
  DynCall< VOID, DWORD >("kernel32.dll", "Sleep", 1000);
  std::cout << "GetTickCount2: "
            << DynCall< DWORD >("kernel32.dll", "GetTickCount") << "\n"
}


produces this output:
Code:

GetTickCount1: 16462234
GetTickCount2: 16463250
Press any key to continue . . .

The first template argument specifies the return type of the API, after that you pass the argument types. Note that the function assumes stdcall!

The advantage here is that the compiler does all the type checking for you, so this cannot fail unless you mess up the function prototype itself - but that problem cannot be fixed anyhow unless we're using a correct import library which would defeat the whole purpose of this exercise.

Things to improve:
  • Throw an exception if loading library or resolving the function fails. Alternatively, wrap the result into std::optional so callers are aware they need to check for error.
  • Add string type as template param so code is agnostic of whether compiled with unicode or MBCS.
  • Invoke FreeLibrary after API was invoked; possibly add an overload that takes an already loaded library handle without freeing it (i.e. do not transfer ownership).

Edit: the template argument do not display correctly due to forum system :/ I fixed it by adding a blank to the template arguments, now it displays correctly.

0xall0c 04-06-2018 18:05

sorry for taking criticism in a wrong way, thank you, for your valuable opinions, they are appreciated.

appologies

mcp 04-06-2018 18:36

absolutely no worries :)

mcp 04-06-2018 18:50

Another idea worth exploring could be to return a functor from DynCall (instead of invoking the API directly). The functor would basically just capture the function pointer in the c'tor, and provide an overloaded operator(), so the user could call the same API as often as they like (it would also make the code more readable). The functor could then invoke FreeLibrary in the d'tor to free the OS resources.

0xall0c 04-06-2018 19:45

that would be nice.

bugficks 04-09-2018 08:30

1 Attachment(s)
i have to deal a lot w/ dyn sym libs/calls for my stuff @samygo and came up w/ some self-including .h file.
its basically for *nix but i ve hacked some windows support though not all features are available there

- should compile as C and C++
- type safe
- easy to use (imho)
- supports logging

here some example usage:
PHP Code:

// msvc doesn't support weak linking
#define DYNSYM_LOG __dynsym_log

#define DYNSYM_CALL_DECL WINAPI
#define DYNSYM_H "u32.h"
#include "dynsym.h"

#define DYNSYM_CALL_DECL WINAPI
#define DYNSYM_H "k32.h"
#include "dynsym.h"

void main()
{
    if(
user32_init() > 0)
    {
        
user32.MessageBoxA(0"test""test"0);
        
user32_deinit();
    }
    
    if(
k32_init() > 0)
    {
        
k32.Sleep(0);
        
k32_deinit();
    }


u32.h:
PHP Code:

#ifdef DYNSYM_BEGIN

DYNSYM_BEGIN("user32"user32)
DYNSYM_DEF(intMessageBoxAHWND hWndLPCSTR lpTextLPCSTR lpCaptionUINT uType)
DYNSYM_END(user32)

#endif // #ifdef DYNSYM_BEGIN 

dynsym.h macro hell :)
PHP Code:

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// bugficks@samygo
// (c) 2016 - 2018
//
// License: GPLv3
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//lib_blub.h:
// DYNSYM_BEGIN("lib_blub.so.0", lib_blub)
// supports patterns (glob w/ braces) and colon-separated list of libs
// //DYNSYM_BEGIN("{/usr/lib/{lib_blub.so,lib_bla.so*}}", lib_blub)
// //DYNSYM_BEGIN("lib_blub.so*:lib_bla.so*", lib_blub)
// //DYNSYM_BEGIN("lib_blub.so:lib_blub.so.0:lib_blub.so.1", lib_blub)
// // required symbols
// DYNSYM_DEF(int, func_bla, int)
// DYNSYM_DEF_EX(int, short_bla, func_loooooooong_bla, int)
// //optional symbols
// DYNSYM_OPT(int, func_blub, int)
// DYNSYM_OPT_EX(int, short_blub, func_loooooooong_blub, int)
// DYNSYM_END(lib_blub)
//
// // add additional data to lib_blub structure
//#ifndef DYNSYM_DATA
//#define DYNSYM_DATA \
//    int bla; \
//    void *blub;
//#endif
//
//code:
// #define DYNSYM_H    "lib_blub.h"
// optionally supply a colon-separated list of directories as library search path
// #define DYNSYM_LIB_PATH "/usr/lib:/some/lib/dir"
// #include <samygo/dynsym.h>
//
// lib_blub_init();
// lib_blub.func_bla(0);
// if(lib_blub.func_blub)
//    lib_blub.func_blub(0);
// lib_blub_deinit();

// < 0 : error
// else: num resolved symbols
// int xxx_init();
// int xxx_init_ex(void *lib);
//
// void xxx_deinit();

#ifndef __DYNSYM_DONE_H__

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef DYNSYM_H
#error DYNSYM_H is not defined.
#endif

#ifdef DYNSYM_BEGIN
#undef DYNSYM_BEGIN
#endif

#ifdef DYNSYM_END
#undef DYNSYM_END
#endif

#ifdef DYNSYM_DEF
#undef DYNSYM_DEF
#endif

#ifdef DYNSYM_DEF_EX
#undef DYNSYM_DEF_EX
#endif

#ifdef DYNSYM_VAR
#undef DYNSYM_VAR
#endif

#ifdef DYNSYM_VAR_EX
#undef DYNSYM_VAR_EX
#endif

#ifdef DYNSYM_OPT
#undef DYNSYM_OPT
#endif

#ifdef DYNSYM_OPT_EX
#undef DYNSYM_OPT_EX
#endif

#ifdef DYNSYM_VAR_OPT
#undef DYNSYM_VAR_OPT
#endif

#ifdef DYNSYM_VAR_OPT_EX
#undef DYNSYM_VAR_OPT_EX
#endif

#ifdef DYNSYM_INIT
#undef DYNSYM_INIT
#endif

#ifdef DYNSYM_DEINIT
#undef DYNSYM_DEINIT
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if __DYNSYM_INC_LEVEL == 1
#undef __DYNSYM_INC_LEVEL
#define __DYNSYM_INC_LEVEL 2
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef DYNSYM_DATA
#define DYNSYM_DATA void *_dummy_;
#endif

#define DYNSYM_BEGIN(DLL, STRUCT) \
    
typedef union \
    { \
        
struct \
        { \
            const 
uint32_t nSyms;

#define DYNSYM_END(STRUCT) \
            
DYNSYM_DATA \
        }; \
        
dynsym_tab_t symtab; \
    } 
STRUCT##_t; \
    
DYNSYM_STRUCT_DATA(STRUCT);

#define DYNSYM_DEF(RET, NAME, ...) \
    
DYNSYM_DEF_EX(RETNAMENAME__VA_ARGS__)

#define DYNSYM_DEF_EX(RET, FUNC, NAME, ...) \
    
struct FUNC##_t FUNC; const char *name_##FUNC; };

#define DYNSYM_VAR(TYPE, NAME) \
    
DYNSYM_VAR_EX(TYPENAMENAME)

#define DYNSYM_VAR_EX(TYPE, VNAME, SNAME) \
    
struct TYPE *VNAME; const char *name_##VNAME; };

#define DYNSYM_OPT(RET, NAME, ...) \
    
DYNSYM_DEF_EX(RETNAMENAME__VA_ARGS__)

#define DYNSYM_OPT_EX(RET, FUNC, NAME, ...) \
    
DYNSYM_DEF_EX(RETFUNCNAME__VA_ARGS__)

#define DYNSYM_VAR_OPT(TYPE, NAME) \
    
DYNSYM_VAR_EX(TYPENAMENAME)

#define DYNSYM_VAR_OPT_EX(TYPE, VNAME, SNAME) \
    
DYNSYM_VAR_EX(TYPEVNAMESNAME)

#define DYNSYM_STRUCT_DATA(STRUCT) \
    
typedef struct \
    { \
        
DYNSYM_DATA \
    } 
STRUCT##_data_t;


#include DYNSYM_H

#undef DYNSYM_DATA
#undef DYNSYM_STRUCT_DATA

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#elif __DYNSYM_INC_LEVEL == 2
#undef __DYNSYM_INC_LEVEL
#define __DYNSYM_INC_LEVEL 3
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define DYNSYM_BEGIN(DLL, STRUCT) \
    
static STRUCT##_t STRUCT = \
    
{ {\
        (((
sizeof(STRUCT##_t) - sizeof(STRUCT##_data_t)) - sizeof(STRUCT.symtab)) / sizeof(dynsym_sym_t)),

#define DYNSYM_END(STRUCT) \
    
} }; \
    
DYNSYM_INIT(DLLSTRUCT) \
    
DYNSYM_DEINIT(DLLSTRUCT)

#define DYNSYM_DEF(RET, NAME, ...) \
    
DYNSYM_DEF_EX(RETNAMENAME__VA_ARGS__)

#define DYNSYM_DEF_EX(RET, FUNC, NAME, ...) \
    
0#NAME },

#define DYNSYM_VAR(TYPE, NAME) \
    
DYNSYM_VAR_EX(TYPENAMENAME)

#define DYNSYM_VAR_EX(TYPE, VNAME, SNAME) \
    
0#SNAME },

#define DYNSYM_OPT(RET, NAME, ...) \
    
DYNSYM_OPT_EX(RETNAMENAME__VA_ARGS__)

#define DYNSYM_OPT_EX(RET, FUNC, NAME, ...) \
    
{ (FUNC##_t)eDYNSYM_OPTIONAL, #NAME },

#define DYNSYM_VAR_OPT(TYPE, NAME) \
    
DYNSYM_VAR_OPT_EX(TYPENAMENAME)

#define DYNSYM_VAR_OPT_EX(TYPE, VNAME, SNAME) \
    
{ (TYPE*)eDYNSYM_OPTIONAL#SNAME },


#ifndef DYNSYM_LIB_PATH
#define DYNSYM_LIB_PATH "/lib:/usr/lib"
#endif

#define DYNSYM_INIT(DLL, STRUCT) \
    
static int STRUCT##_init_ex(void *lib) \
    
{ \
        return 
dynsym_init_ex(lib, &STRUCT##_lib, &STRUCT.symtab); \
    
} \
    \
    static 
int STRUCT##_init() \
    
{ \
        return 
dynsym_init(&STRUCT##_lib, &STRUCT.symtab, DYNSYM_LIB_PATH); \
    
}

#define DYNSYM_DEINIT(DLL, STRUCT) \
    
static void STRUCT##_deinit() \
    
{ \
        
dynsym_tab_deinit(&STRUCT##_lib, &STRUCT.symtab); \
        
STRUCT##_lib.lib = 0; \
    
}


#include DYNSYM_H

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#elif __DYNSYM_INC_LEVEL == 3
#define __DYNSYM_DONE_H__

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#else
#define __DYNSYM_INC_LEVEL 1
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __DYNSYM_H__
#define __DYNSYM_H__

#include <stdint.h>
#include <errno.h>

#ifdef _WIN32

#include <windows.h>
#include <tchar.h>
#define  __attribute__(...)
#define PATH_MAX MAX_PATH

enum {
    
RTLD_LAZY 0x0001
};
inline voiddlopen(const char *nint)
{
    return (
void*)LoadLibraryA(n);
}
inline int dlclose(void *h)
{
    return 
FreeLibrary((HMODULE)h) ? : -1;
}
inline voiddlsym(void *h, const char *n)
{
    return (
void*)GetProcAddress((HMODULE)hn);
}

#define LIBRARY_PATH        "PATH"
#define TOKEN_PATH_SPLIT    ";"
#define strtok_r(A, B, C) strtok(A, B)

#else

#include <dlfcn.h>
#include <link.h>
#include <glob.h>

#define LIBRARY_PATH        "LD_LIBRARY_PATH"
#define TOKEN_PATH_SPLIT    ":"

#endif

typedef struct
{
    const 
char *name;//[PATH_MAX];
    
void *lib;
dynsym_lib_t;

typedef struct
{
    
void *fn;
    const 
char *name;
dynsym_sym_t;

typedef struct
{
    
uint32_t nSyms;
    
dynsym_sym_t syms[];
dynsym_tab_t;

typedef enum
{
    
eDYNSYM_DEFAULT       0,
    
eDYNSYM_OPTIONAL      = (<< 0),
    
eDYNSYM_MASK          3,
dynsym_flags_t;

typedef enum
{
    
eDYNSYM_LOG_NONE,
    
eDYNSYM_LOG_ERROR,
    
eDYNSYM_LOG_WARN,
    
eDYNSYM_LOG_INFO,
    
eDYNSYM_LOG_DBG
dynsym_log_level_t;


void dynsym_log(
        
int l, const char *fmt, ...) __attribute__ ((weak));

#ifndef DYNSYM_LOG
#define DYNSYM_LOG(L, ...) \
    
{ \
        if(
dynsym_log) \
            
dynsym_log(L__VA_ARGS__); \
        else \
            
__dynsym_log(L__VA_ARGS__); \
    }
#endif

static void __dynsym_log(
        
int l, const char *fmt, ...)
{
    
va_list ap;
    
va_start(apfmt);
    switch(
l)
    {
        case 
eDYNSYM_LOG_ERROR:
            
printf("[E]");
            break;
        case 
eDYNSYM_LOG_WARN:
            
printf("[W]");
            break;
        case 
eDYNSYM_LOG_INFO:
            
printf("[I]");
            break;
        case 
eDYNSYM_LOG_DBG:
            
printf("[D]");
            break;
        default:
            break;
    }
    
printf("[DYNSYM] ");
    
vprintf(fmtap);
    
va_end(ap);
    
fflush(stdout);
}

static 
inline const char *dynsym_libname(
        const 
dynsym_lib_t *lib)
{
    const 
char *lib_name "(nil)";
    if(!
lib)
        return 
lib_name;
    if(!
lib->name)
        return 
lib_name;
    if(!
lib->name[0])
        return 
lib_name;
    return 
lib->name;
}

static 
int dynsym_tab_init(
        
dynsym_lib_t *libdynsym_tab_t *symtab)
{
    
int n 0;
    const 
char *lib_name dynsym_libname(lib);
    
dynsym_sym_t *syms symtab->syms;
    for(
uint32_t i 0symtab->nSymsi++)
    {
        
uintptr_t flags = (uintptr_t)syms[i].fn;
        
int opt flags eDYNSYM_OPTIONAL;
        
void *fn dlsym(lib->libsyms[i].name);
        if(!
fn)
        {
            
int l opt eDYNSYM_LOG_WARN eDYNSYM_LOG_ERROR;
            
DYNSYM_LOG(l,"dlsym lib: '%s' '%s' failed.\n"lib_namesyms[i].name);
            if(!
opt)
                return -
1;
        }
        else
        {
            
DYNSYM_LOG(eDYNSYM_LOG_INFO,"dlsym lib: '%s' '%s' %p.\n"lib_namesyms[i].namefn);
            
n++;
        }

        
syms[i].fn fn;
    }
    return 
n;
}

static 
void dynsym_tab_deinit(
        
dynsym_lib_t *libdynsym_tab_t *symtab)
{
    
void *_lib lib->lib;
    if(
_lib)
    {
        
lib->lib 0;
        
dynsym_sym_t *syms symtab->syms;
        for(
uint32_t i 0symtab->nSymsi++)
            
syms[i].fn 0;

        
dlclose(_lib);
    }
    const 
char *name lib->name;
    if(
name)
    {
        
lib->name 0;
        
free((void*)name);
    }
}

static 
int dynsym_init_ex(
        
void *libdynsym_lib_t *dynsymlibdynsym_tab_t *symtab)
{
    if(!
lib)
        return -
1;
    if(
dynsymlib->lib)
        return -
1;

    
dynsymlib->lib lib;

#ifndef _WIN32
    
struct link_map *lm 0;
    
dlinfo(libRTLD_DI_LINKMAP, &lm);
    if(
lm && lm->l_name && lm->l_name[0])
        
dynsymlib->name strdup(lm->l_name);
    else if(!
dynsymlib->name || !dynsymlib->name[0])
        
dynsymlib->name realpath(program_invocation_name0);
#else
    
{
        
char tmp[MAX_PATH];
        
memset(tmp0sizeof(tmp));
        if(
GetModuleFileNameA((HMODULE)libtmpARRAYSIZE(tmp)))
            
dynsymlib->name _strdup(tmp);
    }
#endif

    
DYNSYM_LOG(eDYNSYM_LOG_DBG,"lib: '%s' %p.\n"dynsym_libname(dynsymlib), dynsymlib->lib);

    
int n dynsym_tab_init(dynsymlibsymtab);
    return 
n;
}

typedef int (*sgo_log_t)(
        
int l, const char *tag, const char *comp, const char *fmt, ...);

static 
void *dynsym_dlopen(
        const 
char *name_patterndynsym_lib_t *dynsymlib, const char *const _LIB_PATHsgo_log_t logger)
{
    if(!
name_pattern)
    {
        
void *lib dlopen(0RTLD_LAZY);
        return 
lib;
    }

    
char *strtok_name 0;
    
char name_wrk[PATH_MAX];
    
memset(name_wrk0sizeof(name_wrk));
    
strncpy(name_wrkname_patternsizeof(name_wrk));
    const 
char *name strtok_r(name_wrkTOKEN_PATH_SPLIT, &strtok_name);
    while(
name)
    {
        
// no pattern, just use dlopen
        
if(!strpbrk(name"?*[]{}"))
        {
            
void *lib dlopen(nameRTLD_LAZY);
            if(
lib)
                return 
lib;
        }
#ifdef HAVE_GLOB
        // pattern, we need absolute path for dlopen, fill lib search dirs
        
char LIB_PATH[PATH_MAX] = { };
        if(!
strchr(name'/'))
        {
            const 
char *const LD_LIBRARY_PATH getenv(LIBRARY_PATH);

            if(
LD_LIBRARY_PATH && LD_LIBRARY_PATH[0])
                
snprintf(LIB_PATHsizeof(LIB_PATH) - 1"%s:"LD_LIBRARY_PATH);
            
strncat(LIB_PATH_LIB_PATHsizeof(LIB_PATH) - 1);

            
DYNSYM_LOG(eDYNSYM_LOG_DBG"LIB_PATH: '%s'\n"LIB_PATH);
        }

        
char *strtok_dir 0;
        const 
char *libdir strtok_r(LIB_PATHTOKEN_PATH_SPLIT, &strtok_dir);
        
void *lib 0;
        do
        {
            
char libpath[PATH_MAX];
            
memset(libpath0sizeof(libpath));
            if(
libdir)
                
snprintf(libpathsizeof(libpath) - 1"%s/%s"libdirname);
            else
                
strncpy(libpathnamesizeof(libpath) - 1);

            
glob_t blob;
            
int r glob(libpathGLOB_BRACE0, &blob);
            if(
r)
            {
                
DYNSYM_LOG(eDYNSYM_LOG_DBG"glob('%s') failed. [%s]\n"libpathstrerror(ENOENT));
            }
            else
            {
                for(
size_t i 0blob.gl_pathci++)
                {
                    const 
char *name blob.gl_pathv[i];
                    
lib dlopen(nameRTLD_LAZY);
                    if(
lib)
                        break;
                }
            }

            
globfree(&blob);

            if(
lib)
                return 
lib;

            
libdir strtok_r(0TOKEN_PATH_SPLIT, &strtok_dir);
        }
        while(
libdir);
#endif

        
name strtok_r(0TOKEN_PATH_SPLIT, &strtok_name);
    }

    return 
0;
}

static 
int dynsym_init(
        
dynsym_lib_t *dynsymlibdynsym_tab_t *symtab, const char *const LIB_PATH)
{
    
void *lib dynsym_dlopen(dynsymlib->namedynsymlibLIB_PATH0);
    if(!
lib)
    {
        
DYNSYM_LOG(eDYNSYM_LOG_ERROR,"dlopen lib: '%s' failed.\n"dynsym_libname(dynsymlib));
    }

    return 
dynsym_init_ex(libdynsymlibsymtab);
}

#endif // #ifndef __DYNSYM_H__


#ifndef DYNSYM_CALL_DECL
#define DYNSYM_CALL_DECL
#endif

#define DYNSYM_DEF(RET, NAME, ...) \
    
DYNSYM_DEF_EX(RETNAMENAME__VA_ARGS__)

#define DYNSYM_DEF_EX(RET, FUNC, NAME, ...) \
    
typedef RET (DYNSYM_CALL_DECL FUNC##_t)(__VA_ARGS__);

#define DYNSYM_VAR(T, NAME) \
    
DYNSYM_VAR_EX(TVNAMESNAME)

#define DYNSYM_VAR_EX(T, VNAME, SNAME)

#define DYNSYM_OPT(RET, NAME, ...) \
    
DYNSYM_DEF_EX(RETNAMENAME__VA_ARGS__)

#define DYNSYM_OPT_EX(RET, FUNC, NAME, ...) \
    
DYNSYM_DEF_EX(RETFUNCNAME__VA_ARGS__)

#define DYNSYM_VAR_OPT(T, NAME) \
    
DYNSYM_VAR_OPT_EX(TNAMENAME)

#define DYNSYM_VAR_OPT_EX(T, VNAME, SNAME)

#define DYNSYM_BEGIN(DLL, STRUCT) \
    
static dynsym_lib_t STRUCT##_lib = { DLL, 0 };

#define DYNSYM_END(STRUCT)

#include DYNSYM_H

#undef DYNSYM_CALL_DECL

#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#if __DYNSYM_INC_LEVEL < 4
#include "dynsym.h"
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#else // #ifndef __DYNSYM_DONE_H__

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#undef __DYNSYM_DONE_H__

#ifdef DYNSYM_LIB_PATH
#undef DYNSYM_LIB_PATH
#endif

#undef DYNSYM_H
#undef __DYNSYM_INC_LEVEL

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif  // #ifndef __DYNSYM_DONE_H__

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 


bugficks 04-14-2018 08:39

just for the fun of it, a c++14 version :)

PHP Code:

#include <string>
#include <memory>
#include <map>
#include <system_error>

template <typename Ftypename... ArgTypes>
auto DynCallT(LPCTSTR libNameLPCSTR funcNameArgTypes... args)
{
    
using tstring std::basic_string<TCHARstd::char_traits<TCHAR>, std::allocator<TCHAR>>;
    
using lib_ptr std::shared_ptr<std::remove_pointer_t<const HMODULE>>;
    
using lib_map std::map<const tstringlib_ptr>;

    static 
lib_map libMap;

    
HMODULE h nullptr;
    const 
auto &libMap.find(libName);
    if(
!= libMap.end())
        
l->second.get();
    else if((
= ::LoadLibrary(libName)) != nullptr)
        
libMap[libName] = lib_ptr(h, ::FreeLibrary);
    else
        throw 
std::system_error(std::make_error_code(std::errc::invalid_argument));

    
F f reinterpret_cast<F>(::GetProcAddress(hfuncName));
    if(!
f)
        throw 
std::system_error(std::make_error_code(std::errc::function_not_supported));

    return 
f(args...);


usage:
PHP Code:

    // using 'decltype' when you have a prototype 
    
auto r DynCallT<decltype(&MessageBoxA)>(_T("user32"), "MessageBoxA"nullptr"123""321"0);

    
// or using func decl as template arg
    
auto tc1 DynCallT<DWORD(WINAPI*)()>(_T("kernel32"), "GetTickCount");

    
// using "i dont give a shit" decl :)
    
auto tc2 DynCallT<void*(WINAPI*)(...)>(_T("kernel32"), "GetTickCount"1"asd");

    
void WINAPI blub(DWORD);
    
DynCallT<decltype(&blub)>(_T("kernel32"), "Sleep"0); 


mcp 04-14-2018 18:04

What is C++14 specific about this code?
One drawback is that it's not thread safe.

bugficks 04-14-2018 18:38

auto as return type is c++14 specific.

well thread safety wasn't my goal but it should be virtually thread safe. i mean the worst thing that can happen is that you replace a already loaded lib which isn't much of a problem

mcp 04-14-2018 23:40

Ah right, didn't spot auto return type deduction.

Data races yield undefined behavior, so the worst thing that could happen is that this code produces hard to find bugs/crashes. There is no such thing as "virtually thread safe" ;)

dosprog 04-15-2018 11:35

Quote:

Originally Posted by bugficks (Post 113034)
auto as return type is c++14 specific.

There is such a thing as the portability of the source code.

bugficks 04-15-2018 16:17

Quote:

Originally Posted by mcp (Post 113039)
Data races yield undefined behavior, so the worst thing that could happen is that this code produces hard to find bugs/crashes. There is no such thing as "virtually thread safe" ;)

good luck trying to get it to crash :)
anyways, this should fix it:
Code:

    static thread_local lib_map libMap;
Quote:

Originally Posted by dosprog (Post 113041)
There is such a thing as the portability of the source code.

really? feel free to do so. i ve already posted a highly portable version

mcp 04-15-2018 18:48

Well, if the code yields UB, that needs to be fixed since it violates the C++ standard. This code violates assumptions made by the compiler when reasoning about/optimizing your program - I shouldn't have to get your program to crash. Above reasoning should already convince you that this code needs to be fixed.
Also, I would move the caching outside of the function, or simply use a mutex - these are much faster than people usually assume, and always safe.

bugficks 04-16-2018 07:24

i ve never stated that this is thread safe assuming that it is is wrong.
this snippet is thought as c++ type safe replacement for original post.
i agree its not production code where youd better use some loader/lib/functor classes but thats not the purpose of these few lines and neither is original post. its rather for quick testing and only requiring copy&paste a few lines of code.

fwiw this aint thread safe either:
PHP Code:

    if(!(lib GetModuleHandleA(library)))
        
lib _LoadLibraryA(library); 

thread X frees library in between those calls, you ll end up w/ an invalid lib handle. GetModuleHandle does NOT increase ref count.

gigaman 04-19-2018 01:14

It could be changed to GetModuleHandleEx - but there's probably no point in not calling LoadLibrary directly?

bugficks 04-19-2018 07:08

if you simply replace it w/ GetModuleHandleEx or LoadLibrary you ll increase lib ref count every call.

mcp 04-19-2018 16:34

Which is what you want, unless performance is your top priority (likely isn't). You simply have to make sure to invoke FreeLibrary accordingly. That's why I subsequently proposed to make the actual call through a functor which would invoke FreeLibrary in the destructor.

bugficks 04-19-2018 18:05

youre mixing stuff up. that GetModuleHandle/LoadLibrary is from original post and thats C, functor in C w/ destructor?
my c++14 version keeps lib ref count constant, thats the sole purpose of the map (what you call 'cache')
also you want to keep lib loaded as dlls might have some init/deinit functions or keep some state between calls.

so if you want to fix version from original post use GetModuleHandleEx and call FreeLibrary later only if GetModuleHandleEx returned a valid handle.
for my c++ version just use thread_local on map like i ve already said before. this will also fix if a dll uses DLL_THREAD_ATTACH which a mutex does not.

mcp 04-19-2018 19:03

I was referring to the ref count problem: that can be trivially solved by having correct pairing of Load/FreeLibrary (and doing what i proposed earlier with a functor would do the right thing). I do not see how that is solved with the c++14 approach you posted? How are LoadLibrary / FreeLibrary calls matched without leaking library handles?

edit: ah got it, it's the shared pointer that points a custom deleter to FreeLibrary. ok. However, for all practical purposes this just keeps libraries loaded until the thread exits - which may or may not be what you want - you have no control over it.

edit2: to clarify, the functor approach was suggested because it inherently avoids unnecessary API lookups, e.g., if you invoke an API in a loop, you'll do the same lookup over and over again because you have coupled lookup with invocation of the API. The functor approach however decouples lookup from invoking, is thread-safe by definition and doesn't need any synchronization mechanisms.

bugficks 04-19-2018 20:40

w/ thread_local its a per thread map and lib is loaded/freed per thread, if that thread exits is see no problem there. OS lib ref count is the same as it was before thread was entered.

well again, this was just a c++ type safe replacement for original post, nothing more nothing less. if you want to do an uber lib loader youre free to do so :)

if you want to optimize lookup you could as well just add an optional opaque pointer and store some infos there.
e.g.:
PHP Code:

template <typename Ftypename... ArgTypes>
auto DynCallT(void **opaqueLPCTSTR libNameLPCSTR funcNameArgTypes... args)

    if(
opaque && *opaque
    { (
cast_opaque_t)(opaque) ...} 
    ... 
    if(
opaque
    { *
opaque=... }
    ...
}
void *ppp=0;
for(...) 
DynCallT(ppp, ...); 

anyways, it is what it is and it doesnt want to be more :)

aliali 05-08-2018 05:08

Quote:

Originally Posted by bugficks (Post 113073)
w/ thread_local its a per thread map and lib is loaded/freed per thread, if that thread exits is see no problem there. OS lib ref count is the same as it was before thread was entered.

well again, this was just a c++ type safe replacement for original post, nothing more nothing less. if you want to do an uber lib loader youre free to do so :)

if you want to optimize lookup you could as well just add an optional opaque pointer and store some infos there.
e.g.:
PHP Code:

template <typename Ftypename... ArgTypes>
auto DynCallT(void **opaqueLPCTSTR libNameLPCSTR funcNameArgTypes... args)

    if(
opaque && *opaque
    { (
cast_opaque_t)(opaque) ...} 
    ... 
    if(
opaque
    { *
opaque=... }
    ...
}
void *ppp=0;
for(...) 
DynCallT(ppp, ...); 

anyways, it is what it is and it doesnt want to be more :)

Is it possible to have a pure ANSI C implementation of the above code :)


All times are GMT +8. The time now is 11:25.

Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2026, vBulletin Solutions, Inc.
Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX