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