It's weird that dlsym can import functions from stripped binaries.
Can anyone tell me why/how?
=== FILE: a.c ===
int a1() { return 1; }
int a2() { return 2; }
=== end of a.c ===
=== FILE: b.c ===
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
typedef int (*fint)();
fint dlsym_fint(void *handle, char *name)
{
fint x = (fint)dlsym(handle, name);
char *err = NULL;
if ((err = dlerror()) != NULL) {
printf("dlsym: %s\n", err);
exit(1);
}
return x;
}
int main()
{
void *dl = dlopen("a.so", RTLD_NOW);
fint a = NULL;
a = dlsym_fint(dl, "a1");
printf("%p: %d\n", a, a());
a = dlsym_fint(dl, "a2");
printf("%p: %d\n", a, a());
return 0;
}
=== end of b.c ===
$ gcc -shared -fPIC -o a.so a.c
$ nm a.so
...
00000000000004ec T a1
00000000000004f7 T a2
...
$ strip a.so
$ nm a.so
nm: a.so: no symbols
$ gcc -o b b.c -ldl
$ ./b
0x2aaaaaac74ec: 1
0x2aaaaaac74f7: 2
Try readelf -s a.so. The dynamic symbols are still there after that strip.
(Or just switch to nm -D a.so.)
strip removes debugging symbol tables, not the dynamic symbol tables used by the dynamic linker. To remove those symbols as well, use -fvisibility=hidden, and the symbol visibility function/variable attributes to select which functions you want to expose.
Related
I tried to make a simple kernel module invalidating all tlb entries through user-level ioctl.
Below is my example code [flush_tlb.c]
/*
* flush_tlb.c
*/
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/tlbflush.h>
#include <asm/tlb.h>
#include "flush_tlb.h"
MODULE_LICENSE("GPL");
static long flush_tlb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct flush_tlb_parv_t flush_tlb_parv;
// copy an argument from user-level
if (size != sizeof(struct flush_tlb_parv_t)) {
ret = -EINVAL;
goto out;
}
if (copy_from_user(&flush_tlb_parv, ubuf, sizeof(flush_tlb_parv))) {
ret = -EFAULT;
goto out;
}
if (flush_tlb_parv.addr) {
// Not Implemented
flush_tlb_all();
} else {
flush_tlb_all();
}
out:
return ret;
}
static const struct file_operations flush_tlb_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = flush_tlb_ioctl,
.compat_ioctl = flush_tlb_ioctl,
};
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "flush_tlb",
.fops = &flush_tlb_fops
};
static int __init flush_tlb_init(void)
{
int ret;
ret = misc_register(&binder_miscdev);
return ret;
}
module_init(flush_tlb_init)
static void __exit flush_tlb_exit(void)
{
misc_deregister(&binder_miscdev);
}
module_exit(flush_tlb_exit)
And here is my [Makefile]
obj-m += flush_tlb.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
It is successfully compiled, but when I insert the module by insmod, it fails with following kernel logs.
"flush_tlb: Unknown symbol flush_tlb_all (err 0)"
Also, I checked the /proc/kallsyms that flush_tlb_all exists in the symbol table.
$cat /proc/kallsyms | grep flush_tlb_all
ffffffff81022f90 T xen_flush_tlb_all
ffffffff810274a0 t trace_raw_output_xen_mmu_flush_tlb_all
ffffffff81027af0 t trace_event_raw_event_xen_mmu_flush_tlb_all
ffffffff81028c20 t perf_trace_xen_mmu_flush_tlb_all
ffffffff81072670 t do_flush_tlb_all
ffffffff81072bf0 T flush_tlb_all
ffffffff810792c0 t __flush_tlb_all
ffffffff8107d7cf t __flush_tlb_all
ffffffff81d76570 r __tracepoint_ptr_xen_mmu_flush_tlb_all
ffffffff81d77630 r __tpstrtab_xen_mmu_flush_tlb_all
ffffffff81e231f8 d print_fmt_xen_mmu_flush_tlb_all
ffffffff81e23f20 d trace_event_type_funcs_xen_mmu_flush_tlb_all
ffffffff81e24780 d event_xen_mmu_flush_tlb_all
ffffffff81f04720 d event_class_xen_mmu_flush_tlb_all
ffffffff81f103a0 D __tracepoint_xen_mmu_flush_tlb_all
ffffffff81f6772c t trace_event_define_fields_xen_mmu_flush_tlb_all
ffffffff8208c080 t __event_xen_mmu_flush_tlb_all
How can I fix it?
Thanks.
I'm trying to use the lib icu in my shared library.
So i generate my .so who use libicu functions, this parts works well.
Then i try to compile my main program, using the shared library that i created before. But i get the following error:
release//libstring.so: undefined reference to `u_errorName_53'
release//libstring.so: undefined reference to `ucnv_toUnicode_53'
release//libstring.so: undefined reference to `ucnv_close_53'
release//libstring.so: undefined reference to `ucnv_fromUChars_53'
release//libstring.so: undefined reference to `udat_open_53'
release//libstring.so: undefined reference to `udat_parse_53'
release//libstring.so: undefined reference to `udat_format_53'
release//libstring.so: undefined reference to `ucnv_toUChars_53'
release//libstring.so: undefined reference to `ucnv_open_53'
release//libstring.so: undefined reference to `udat_close_53'
I have installed the libicu with this help.
I compile my shared library with -licuuc, as well as my main program compile only with -l:libstring.so and -licuuc.
When i do ldd libstring.so, i don't have the .so of the libicu on the list of dependencies.
The problem probably come from this.
Thx
EDIT: I have create a little test:
icu.h:
#pragma once
#include <iostream>
#include <string>
#include <unicode/ucnv.h>
class c_icu
{
private:
void * p_priv;
public:
c_icu();
void open(const wchar_t *name);
void to_local(std::wstring &, std::wstring &);
void from_local(std::wstring &, std::wstring &);
void close(void);
~c_icu();
};
icu.cpp:
#include "icu.h"
c_icu::c_icu()
{
p_priv = NULL;
}
c_icu::~c_icu()
{
if (p_priv)
close();
}
void c_icu::open(const wchar_t *name)
{
UErrorCode status = U_ZERO_ERROR;
char conv_name[256];
if (wcstombs(conv_name, name, 256) == (size_t)-1)
{
std::cout << "ERROR: wcstombs failed" << std::endl;
exit(0);
}
if ((p_priv = (UConverter *)ucnv_open(conv_name, &status)) == NULL)
{
std::cout << "ERROR: ucnv_open failed" << std::endl;
exit(0);
}
}
void c_icu::to_local(std::wstring &src, std::wstring &dst)
{
UErrorCode status = U_ZERO_ERROR;
int32_t len;
len = ucnv_toUChars((UConverter *)p_priv, (UChar *)NULL, 0, (const char *)src.c_str(), src.size(), &status);
if (status == U_BUFFER_OVERFLOW_ERROR)
{
status = U_ZERO_ERROR;
dst.resize(len);
len = ucnv_toUChars((UConverter *)p_priv, (UChar *)dst.c_str(), dst.size(), (const char *)src.c_str(), src.size(), &status);
if (U_FAILURE(status))
{
std::cout << "ERROR: to_local failed" << std::endl;
exit(0);
}
}
}
void c_icu::from_local(std::wstring &src, std::wstring &dst)
{
UErrorCode status = U_ZERO_ERROR;
int32_t len;
len = ucnv_fromUChars((UConverter *)p_priv, (char *)NULL, 0, (const UChar *)src.c_str(), src.size(), &status);
if (status == U_BUFFER_OVERFLOW_ERROR)
{
status = U_ZERO_ERROR;
dst.reserve(len);
len = ucnv_fromUChars((UConverter *)p_priv, (char *)dst.c_str(), dst.size(), (const UChar *)src.c_str(), -1, &status);
}
}
void c_icu::close(void)
{
ucnv_close((UConverter *)p_priv);
p_priv = NULL;
}
test.cpp:
#include "icu.h"
int main(void)
{
c_icu converter;
std::wstring src = L"";
std::wstring dst;
const wchar_t add[11] = L"Just a test";
const wchar_t *unicode = L"koi8-r";
for (unsigned int i = 0; i < 1000; i++)
src.append(add, 11);
converter.open(unicode);
converter.from_local(src, dst);
converter.close();
}
Makefile:
NAME= test
LIB_NAME= libicutest.so
FLAGS= -W -Wall -Wextra
all: lib $(NAME)
$(NAME):
#echo "Main programm compiling ..."
g++ -c test.cpp -o test.o $(FLAGS)
g++ -o $(NAME) test.o -Wl,-rpath,'$$ORIGIN/' -licui18n -L. -licutest
#echo "Main programm compiled"
lib:
#echo "Lib Compiling ..."
g++ -std=gnu++11 -c -fPIC icu.cpp -o icu.o $(FLAGS)
g++ -shared -Wl,-soname,$(LIB_NAME) -o $(LIB_NAME) icu.o -licui18n
#echo "Lib Compiled"
Note: even with -licuuc instead of -licui18n i got the same error.
Output:
$> make
Lib Compiling ...
g++ -std=gnu++11 -c -fPIC icu.cpp -o icu.o -W -Wall -Wextra
g++ -shared -Wl,-soname,libicutest.so -o libicutest.so icu.o -licui18n
Lib Compiled
Main programm compiling ...
g++ -c test.cpp -o test.o -W -Wall -Wextra
g++ -o test test.o -Wl,-rpath,'$ORIGIN/' -licui18n -L. -licutest
./libicutest.so: undefined reference to `ucnv_close_53'
./libicutest.so: undefined reference to `ucnv_fromUChars_53'
./libicutest.so: undefined reference to `ucnv_toUChars_53'
./libicutest.so: undefined reference to `ucnv_open_53'
collect2: error: ld returned 1 exit status
make: *** [test] Error 1
If i do ldd on my shared library:
$> ldd libicutest.so
linux-vdso.so.1 => (0x00007ffd5eb7f000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f092b5e6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f092b221000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f092af1a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f092bb00000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f092ad04000)
EDIT2:
Ok, seems that the problem is easier to solve than expected. My program need function in icu_53, but i got icu_48 installed on my computer dunno why.
$> locate libicu
/usr/lib/x86_64-linux-gnu/libicudata.so.48
/usr/lib/x86_64-linux-gnu/libicudata.so.48.1.1
/usr/lib/x86_64-linux-gnu/libicui18n.so.48
/usr/lib/x86_64-linux-gnu/libicui18n.so.48.1.1
/usr/lib/x86_64-linux-gnu/libicuio.so.48
/usr/lib/x86_64-linux-gnu/libicuio.so.48.1.1
/usr/lib/x86_64-linux-gnu/libicule.so.48
/usr/lib/x86_64-linux-gnu/libicule.so.48.1.1
/usr/lib/x86_64-linux-gnu/libiculx.so.48
/usr/lib/x86_64-linux-gnu/libiculx.so.48.1.1
/usr/lib/x86_64-linux-gnu/libicutest.so.48
/usr/lib/x86_64-linux-gnu/libicutest.so.48.1.1
/usr/lib/x86_64-linux-gnu/libicutu.so.48
/usr/lib/x86_64-linux-gnu/libicutu.so.48.1.1
/usr/lib/x86_64-linux-gnu/libicuuc.so.48
/usr/lib/x86_64-linux-gnu/libicuuc.so.48.1.1
Does anyone know how to upgrade the version from icu_48 to icu_53 at least ?
Seems 'udat_parse_53' is located inside libicui18n.so.53 library. Try to compile your project with '-licui18n' option.
I have the following sequence
executable (main) ---- (dlopen)---> libapp.so ---(dynamically linked)--> libfoo.so
libfoo.so in turn dynamically links to libfoo_strong.so. libfoo.so invokes a function from
libfoo_strong.so, but also has a weak definition (within foo.c which is compiled into libfoo.so).
Now, libapp.so invokes a function from libfoo.so (say invoke_foo_func_ptr() and this function >invokes a function pointer which stores the symbol that is defined as weak. My expectation is that >invokes_foo_func_ptr invokes the strong symbol, but it always goes to the weak symbol. Pls see the >code below for details.
PS: Dont ask me to explain the reason particular sequence of execution, but I am open to >workarounds.
foo_strong.c --> gcc -g -fPIC -shared -rdynamic foo_strong.o -o libfoo_strong.so
foo.c: --> gcc -g -fPIC -shared -rdynamic -L/users/ardesiga/cprogs/ld_r foo.o -o libfoo.so
app.c: --> gcc -g -fPIC -shared -rdynamic -L/users/ardesiga/cprogs/ld_r -lfoo -lfoo_strong app.o -o > libapp.so
/* foo_strong.c */
int
foo_weak_func (char *msg)
{
printf("[%s:%s] Reached strong, with msg: %s\n", __FILE__, __FUNCTION__, msg);
}
/* foo.c */
#include <stdio.h>
#include <stdlib.h>
#include "foo_ext.h"
#include "foo_weak.h"
int __attribute__ ((weak)) foo_weak_func (char *msg)
{
printf("[%s:%s], Reached weak, with msg: %s\n", __FILE__, __FUNCTION__, msg);
}
typedef int (*func_ptr_t) (char *msg);
func_ptr_t foo_func_ptr = foo_weak_func;
void
invoke_foo_func_ptr (char *msg)
{
printf("Inside %s\n", __FUNCTION__);
if (foo_func_ptr) {
(*foo_func_ptr)(msg);
} else {
printf("foo_func_ptr is NULL\n");
}
}
/* app.c */
#include "foo.h"
int
app_init_func (char *msg)
{
printf("Inside %s:%s\n", __FILE__, __FUNCTION__);
invoke_foo_func_ptr(msg);
}
/* main.c */
int main (int argc, char *argv[])
{
void *dl_handle;
char *lib_name;
app_init_func_t app_init_func;
if (!(argc > 1)) {
printf("Library is not supplied, loading libapp.so\n");
lib_name = strdup("libapp.so");
} else {
lib_name = strdup(argv[2]);
}
printf("Loading library: %s\n", lib_name);
dl_handle = dlopen(lib_name, RTLD_LAZY);
if (!dl_handle) {
printf("Failed to dlopen on %s, error: %s\n", lib_name, dlerror());
exit(1);
}
app_init_func = dlsym(dl_handle, "app_init_func");
if (app_init_func) {
(*app_init_func)("Called via dlsym");
} else {
printf("dlsym did not file app_init_func");
}
return (0);
}
My expectation is that invokes_foo_func_ptr invokes the strong symbol, but it always goes to the weak symbol.
Your expectation is incorrect and everything is working as designed.
Weak symbols lose to strong symbols when you link a single ELF binary. If you were to link a normal (strong) function foo and a weak foo into libfoo.so, the the strong definition would have won.
When you have multiple ELF images, some with strong foo, and some with weak foo, the first ELF image to define foo (regardless of whether weak or strong) wins. The loader will simply not look for any additional ELF images in its search scope once it finds the first image that does provide a definition for foo.
Dont ask me to explain the reason particular sequence of execution
That is quite an obnoxious thing to say.
I have a guess as to what your reason may be, and a solution for it, but you'll have to provide your reason first.
I'm going to implement my custom module in which information for CPU is printed using print_cpu_info(). In order to call print_cpu_info(), I have included the needed header file, but it doesn't work. Here is my module.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/alternative.h>
#include <asm/bugs.h>
#include <asm/processor.h>
#include <asm/mtrr.h>
#include <asm/cacheflush.h>
extern struct cpuinfo_x86 boot_cpu_data;
int cpuinfox86_init(void)
{
print_cpu_info(&boot_cpu_data);
return 0;
}
void cpuinfox86_exit(void)
{
printk("good bye cpu\n");
}
module_init(cpuinfox86_init);
module_exit(cpuinfox86_exit);
MODULE_LICENSE("GPL");
After compiling this module, I get
make -C /lib/modules/3.2.28-2009720166/build SUBDIRS=/home/tracking/1031_oslab modules
make[1]: Entering directory `/usr/src/linux-3.2.28'
CC [M] /home/tracking/1031_oslab/module.o
Building modules, stage 2.
MODPOST 1 modules
WARNING: "print_cpu_info" [/home/tracking/1031_oslab/module.ko] undefined!
CC /home/tracking/1031_oslab/module.mod.o
LD [M] /home/tracking/1031_oslab/module.ko
make[1]: Leaving directory `/usr/src/linux-3.2.28'
any idea?
"print_cpu_info" is not exported symbol, so it can not be used by modules. However, you can use "kallsyms_lookup_name", which is exported, to get the address of "print_cpu_info" and execute the function call using a function pointer.
static void* find_sym( const char *sym ) { // find address kernel symbol sym
static unsigned long faddr = 0; // static !!!
// ----------- nested functions are a GCC extension ---------
int symb_fn( void* data, const char* sym, struct module* mod, unsigned long addr ) {
if( 0 == strcmp( (char*)data, sym ) ) {
faddr = addr;
return 1;
}
else return 0;
};
// --------------------------------------------------------
kallsyms_on_each_symbol( symb_fn, (void*)sym );
return (void*)faddr;
}
static void (*cpuInfo)(struct cpuinfo_x86 *);
How to use it:
unsigned int cpu = 0;
struct cpuinfo_x86 *c;
cpuInfo = find_sym("print_cpu_info");
for_each_online_cpu(cpu)
{
c = &cpu_data(cpu);
cpuInfo(c);
}
This is my first post and I am trying to adhere to the forum rules.
I know this question has been asked a number of times before, but the provided solutions do not work for me. I am not quite sure why this is so, probably I have overlooked something.
I want to access from a shared module (.so) symbols (variables and functions) which reside inside the main application.
I am always getting link errors ("undefined reference").
The scenario is similar as described here, with the one expection that I am using plain C instead of C++:
shared object can't find symbols in main binary, C++
Maybe this difference is the reason for that the proposed solution does not work.
I am using 32bit MinGW, version 4.6.2 under 32bit Windows (XP if that matters).
I also read these posts:
Receive "undefined symbol" error when loading library with dlopen
Dynamic loading and symbols sharing
Can I access to symbols of the host proccess from a shared object loaded in runtime? Any alternative?
The latter solution would work, I suppose, but is not an option because this is a porting project from Linux with 100+ symbols. It is a plugin system and I do not want to limit the possibilities avalable for the plugins.
I tried a number of commandline switches when linking the main application, such as -rdynamic (is apparently not supported in MinGW), --export-all-symbols, --export-dynamic.
I also tried different commandline options when linking the shared library, such as --allow-shlib-undefined, --enable-auto-import, --enable-runtime-pseudo-reloc
The application is a linux application and works well there; I managed it to make it work under Mac OSX (Darwin) as well. There I must not strip the main application executable during linking and specify "-bundle -bundle_loader 'app name'" when linking the shared objects.
Many thanks in advance!
Johannes
Well, I found out myself with some help of the codeguru forum (see http://forums.codeguru.com/showthread.php?536343-RESOLVED-accessing-symbols-in-appllication-from-within-dynamic-library-MinGW ).
The solution is not - as I was thinking first - some option to the linker; apparently automatic back-linking is not possible in Windows. You have to manully load the symbols you want to use from the main application at runtime; similiar to when you manually load a DLL.
I made a small test and demonstration project:
file "my_app.c":
#include <stdio.h>
#include <stdlib.h>
#include "my_app.h"
#include "plugins.h"
int a;
int b;
Plugin *P;
void hello ()
{
printf ("Hello World!\n");
}
int main ()
{
char choice;
char buf[BUFLEN];
char *plugin_name=NULL;
Plugin *p=NULL;
do
{
printf ("\n --- Menu items ---\n");
printf (" '1' .. select plugin\n");
printf (" '2' .. load plugin\n");
printf (" '3' .. execute plugin function\n");
printf (" '4' .. unload plugin\n");
printf (" 'x' .. exit\n");
printf ("\n Your choice: ");
fgets (buf, sizeof(buf), stdin);
putchar ('\n');
choice = buf[0];
switch (choice)
{
case '1':
plugin_name = plugin_select ();
break;
case '2':
if (plugin_name)
{
p = plugin_load (plugin_name);
if (p)
printf ("Plugin '%s' loaded successfully\n", plugin_name);
P = p;
} else
printf ("No plugin selected\n");
break;
case '3':
if (p)
{
printf ("Enter the 1st number: ");
fgets (buf, sizeof(buf), stdin);
a = atoi (buf);
printf ("Enter the 2nd number: ");
fgets (buf, sizeof(buf), stdin);
b = atoi (buf);
printf ("\nExecuting the plugin '%s' ...\n", plugin_name);
plugin_action (p);
} else
printf ("No plugin loaded\n");
break;
case '4':
if (p)
{
plugin_destroy (p);
p = NULL;
P = NULL;
} else
printf ("No plugin loaded\n");
break;
}
} while (choice != 'x');
return 0;
}
file "my_app.h":
#ifndef __MYAPP_H
#define __MYAPP_H
#include "plugins.h"
#define TRUE 1
#define FALSE 0
#define BUFLEN 120
extern int a;
extern int b;
extern void hello (void);
extern Plugin *P;
#endif /* __MYAPP_H */
file "plugins.c":
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <dlfcn.h>
#include "my_app.h"
#include "plugins.h"
Plugin *plugin_load (const char *filename)
{
Plugin *p;
int (*init)(Plugin *);
p = malloc (sizeof(Plugin));
if (p == NULL)
{
printf ("Couldn't allocate Plugin object\n");
return NULL;
}
strncpy (p->filename, filename, sizeof(p->filename));
if ((p->module = dlopen(p->filename, RTLD_LAZY)) == NULL)
{
printf ("%s\n", dlerror());
free (p);
return NULL;
}
init = dlsym (p->module, "init_plugin");
if (init == NULL)
{
printf ("%s\n", dlerror());
free (p);
return NULL;
}
if (!init(p))
{
perror ("Couldn't initialize plugin");
free (p);
return NULL;
}
p->load = TRUE;
return p;
}
void plugin_action (Plugin *p)
{
if (p == NULL)
{
printf ("Plugin not found!\n");
return;
}
p->plugin_cb();
}
char *plugin_select ()
{
DIR *dp;
struct dirent *e;
static char buf[BUFLEN];
char *p;
size_t len;
char *suffix;
dp = opendir (".");
if (dp == NULL)
{
perror ("Cannot open '.'");
return NULL;
}
printf ("\nFollowing plugins are available:\n");
while ((e=readdir(dp)) != NULL)
{
suffix = strrchr (e->d_name, '.');
if (suffix)
{
if (strcmp (suffix, PLUGIN_SUFFIX) == 0)
printf (" %s\n", e->d_name);
}
}
closedir (dp);
printf ("Your choice: ");
buf[0] = '.';
buf[1] = '/';
p = &buf[2];
fgets (p, sizeof(buf)-2, stdin);
len = strlen (buf);
if (len > 0)
len --;
buf[len] = '\0';
if (strchr(p, '.') == NULL)
strcat (buf, PLUGIN_SUFFIX);
return buf;
}
void plugin_destroy (Plugin *p)
{
void (*unload)(Plugin *);
unload = dlsym (p->module, "unload_plugin");
if (unload != NULL)
unload (p);
dlclose (p->module);
free (p);
}
file "plugins.h":
#ifndef __PLUGINS_H
#define __PLUGINS_H
#include <limits.h> /*PATH_MAX */
#define PLUGIN_SUFFIX ".so"
typedef struct _Plugin
{
char filename[PATH_MAX];
char *name;
char *description;
unsigned int show_in_menu;
unsigned int load;
void *module;
void (*plugin_cb)();
} Plugin;
Plugin *plugin_load (const char *filename);
void plugin_action (Plugin *p);
char *plugin_select (void);
void plugin_destroy (Plugin *p);
#endif /* __PLUGINS_H */
Now the plugins:
Hide the nasty function pointer declarations, etc.: "plugin_macros.h":
#ifndef __PLUGIN_MACROS_H
#define __PLUGIN_MACROS_H
#ifdef WIN32
#include <windows.h>
#define DECLARE_FUNC(type,name,par) typedef type (WINAPI *name ## _t)par; static name ## _t name;
#define DECLARE_VAR(type,name) static type * p ## name;
#define DECLARE_PTR(type,name) static type *name;
#define LOAD_FUNCC(name) if((name=(name ## _t)GetProcAddress(GetModuleHandle(NULL),#name))==NULL) return 0
#define LOAD_FUNC(name) (name=(name ## _t)GetProcAddress(GetModuleHandle(NULL),#name))
#define LOAD_VARC(type,name) if((p ## name=(type *)GetProcAddress(GetModuleHandle(NULL),#name))==NULL) return 0
#define LOAD_VAR(type,name) (name=(type *)GetProcAddress(GetModuleHandle(NULL),#name))
#define LOAD_PTRC(type,name) if((name=*(type **)GetProcAddress(GetModuleHandle(NULL),#name))==NULL) return 0
#define LOAD_PTR(type,name) (name=*(type **)GetProcAddress(GetModuleHandle(NULL),#name))
#else
/* not WIN32 */
#define DECLARE_FUNC(type,name,par)
#define DECLARE_VAR(type,name)
#define DECLARE_PTR(type,name)
#define LOAD_FUNCC(name)
#define LOAD_FUNC(name)
#define LOAD_VARC(type,name)
#define LOAD_VAR(type,name)
#define LOAD_PTRC(type,name)
#define LOAD_PTR(type,name)
#endif
#endif /* __PLUGIN_MACROS_H */
file "add.c":
#include <stdio.h>
#include "plugins.h"
#include "plugin_macros.h"
#ifdef WIN32
#include <windows.h>
DECLARE_FUNC(void, hello, (void));
DECLARE_VAR(int, a);
DECLARE_VAR(int, b);
DECLARE_PTR(Plugin, P);
#define a (*pa)
#define b (*pb)
#else
#include "my_app.h"
#endif
static void add ()
{
hello();
LOAD_PTR(Plugin, P);
printf ("name=%s\n", P->name);
printf ("description=%s\n\n", P->description);
printf ("%d + %d = %d\n", a, b, a+b);
}
int init_plugin (Plugin *p)
{
LOAD_FUNCC(hello);
LOAD_VARC(int, a);
LOAD_VARC(int, b);
p->name = "Add";
p->description = "Add two integers.";
p->plugin_cb = add;
P = p;
return TRUE;
}
file "multiply.c":
#include <stdio.h>
#include "plugins.h"
#include "plugin_macros.h"
#ifdef WIN32
#include <windows.h>
DECLARE_VAR(int, a);
DECLARE_VAR(int, b);
DECLARE_PTR(Plugin, P);
#define a (*pa)
#define b (*pb)
#else
#include "my_app.h"
#endif
static void multiply ()
{
LOAD_PTR(Plugin, P);
printf ("name=%s\n", P->name);
printf ("description=%s\n\n", P->description);
printf ("%d * %d = %d\n", a, b, a*b);
}
int init_plugin (Plugin *p)
{
LOAD_VARC(int, a);
LOAD_VARC(int, b);
p->name = "Multiply";
p->description = "Multiply two integers.";
p->plugin_cb = multiply;
P = p;
return TRUE;
}
... and the Makefile:
ifeq ($(OS), Windows_NT)
TARGET = plugintest.exe
CC = $(MINGW)\bin\gcc.exe
LDFLAGS = -Wl,--export-all-symbols
STRIP = -s
SHARED = -shared
RM = del
else
TARGET = plugintest
UNAME := $(shell uname -s)
ifeq ($(UNAME),Linux)
LDFLAGS = -Wl,--export-dynamic
STRIP = -s
SHARED = -shared
endif
ifeq ($(UNAME),Darwin)
LDFLAGS =
STRIP =
SHARED = -bundle -bundle_loader plugintest
endif
RM = rm -f
endif
PLUGINS = add.so multiply.so
OBJS = my_app.o plugins.o
LIBS = -ldl
CFLAGS = -Wall
all: $(TARGET) my_plugins
$(TARGET): $(OBJS)
$(CC) $(OBJS) $(LIBS) $(LDFLAGS) -o $(TARGET)
my_app.o: my_app.c my_app.h plugins.h
$(CC) $(CFLAGS) -c my_app.c -o my_app.o
plugins.o: plugins.c my_app.h plugins.h
$(CC) $(CFLAGS) -c plugins.c -o plugins.o
my_plugins: $(PLUGINS)
add.so: add.c my_app.h plugin_macros.h
$(CC) $(CFLAGS) $(SHARED) add.c -o add.so
multiply.so: multiply.c my_app.h plugin_macros.h
$(CC) $(CFLAGS) $(SHARED) multiply.c -o multiply.so
clean:
$(RM) *.o *.so $(TARGET)
The project uses the posix dynamic loading functions dlopen(), etc. They are not part of the MinGW installation, but you can download from here:
http://code.google.com/p/dlfcn-win32/
I hope this helps others who come across the same issue.
Any comments and questions are welcome!
Johannes