2

这是我的第一篇文章,我正在努力遵守论坛规则。我知道这个问题之前已经被问过很多次了,但是提供的解决方案对我不起作用。我不太清楚为什么会这样,可能我忽略了一些东西。

我想从驻留在主应用程序中的共享模块 (.so) 符号(变量和函数)访问。我总是收到链接错误(“未定义的引用”)。

该场景与此处描述的相似,但期望我使用的是纯 C 而不是 C++:

共享对象在主二进制文件中找不到符号,C++

也许这种差异是建议的解决方案不起作用的原因。我在 32 位 Windows 下使用 32 位 MinGW,版本 4.6.2(如果重要的话是 XP)。

我还阅读了这些帖子:

使用 dlopen 加载库时收到“未定义符号”错误

动态加载和符号共享

我可以从运行时加载的共享对象访问主机进程的符号吗?有什么选择吗?

我想后一种解决方案会起作用,但不是一个选择,因为这是一个来自 Linux 的移植项目,有 100 多个符号。这是一个插件系统,我不想限制插件的可能性。

我在链接主应用程序时尝试了许多命令行开关,例如 -rdynamic(MinGW 显然不支持)、--export-all-symbols、--export-dynamic。在链接共享库时,我还尝试了不同的命令行选项,例如 --allow-shlib-undefined、--enable-auto-import、--enable-runtime-pseudo-reloc

该应用程序是一个 linux 应用程序,并且在那里运行良好;我设法让它在 Mac OSX (Darwin) 下也能工作。在那里,我不能在链接期间剥离主应用程序可执行文件,并在链接共享对象时指定“-bundle -bundle_loader 'app name'”。

提前谢谢了!

约翰内斯

4

1 回答 1

0

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

于 2013-04-20T07:16:04.867 回答