如果两个库提供具有相同名称的函数产生冲突,我该怎么办?

时间:2009-03-24 16:47:28

标签: c conflict name-collision

如果我有两个提供等效名称功能的库,我该怎么办?

12 个答案:

答案 0 :(得分:42)

  • 如果您控制其中一个或两个:编辑一个以更改名称并重新编译或等效地查看Benunknown的答案,这些答案将在没有访问源的情况下代码。
  • 如果您不控制其中任何一个,您可以将其中一个包起来。这是编译另一个静态链接!)库,除了重新导出原始符号之外什么都不做,除了有问题的符号,这是通过包装器到达的替代名称。真是个麻烦。
  • 稍后添加:由于qeek说他正在谈论动态库,Ferrucciomouviciel建议的解决方案可能是最好的。 (我似乎很久以前就生活过静态链接是默认的。它为我的想法着色。)

适用评论:通过“导出”,我的意思是使链接到库的模块可见 - 相当于文件范围内的extern关键字。如何控制它取决于操作系统和链接器。这是我总是必须要查找的内容。

答案 1 :(得分:41)

可以使用objcopy --redefine-sym old=new file重命名目标文件中的符号(参见man objcopy)。

然后只需使用新名称调用函数并链接到新的目标文件。

答案 2 :(得分:12)

在Windows下,您可以使用LoadLibrary()将其中一个库加载到内存中,然后使用GetProcAddress()获取需要调用的每个函数的地址,并通过函数指针调用函数。

e.g。

HMODULE lib = LoadLibrary("foo.dll");
void *p = GetProcAddress(lib, "bar");
// cast p to the approriate function pointer type (fp) and call it
(*fp)(arg1, arg2...);
FreeLibrary(lib);

将获取foo.dll中名为bar的函数的地址并调用它。

我知道Unix系统支持类似功能,但我想不出他们的名字。

答案 3 :(得分:8)

这是一个想法。在十六进制编辑器中打开其中一个违规库,并将所有出现的违规字符串更改为其他内容。然后,您应该可以在以后的所有呼叫中使用新名称。

更新:我刚刚做到这一点似乎有效。当然,我没有彻底测试过这个问题 - 它可能只是一个非常好的方法来吹你的腿使用hexedit霰弹枪。

答案 4 :(得分:6)

你不应该一起使用它们。如果我没记错的话,链接器会在这种情况下发出错误。

我没有尝试过,但可以使用dlopen()dlsym()dlclose()的解决方案,它允许您以编程方式处理动态库。如果您不需要同时使用这两个函数,则可以在使用第二个库/函数之前打开第一个库,使用第一个函数并关闭第一个库。

答案 5 :(得分:6)

假设您使用linux,首先需要添加

#include <dlfcn.h>

在适当的上下文中声明函数指针变量,例如

int (*alternative_server_init)(int, char **, char **);

https://stackoverflow.com/a/678453/1635364中所说的Ferruccio一样, 通过执行(选择你最喜欢的标志)显式加载你想要使用的库

void* dlhandle;
void* sym;

dlhandle = dlopen("/home/jdoe/src/libwhatnot.so.10", RTLD_NOW|RTLD_LOCAL);

阅读稍后要调用的函数的地址

sym = dlsym(dlhandle, "conflicting_server_init");

分配和转换如下

alternative_server_init = (int (*)(int, char**, char**))sym;

以与原始方式类似的方式拨打电话。最后,通过执行

卸载
dlclose(dlhandle);

答案 6 :(得分:4)

这个问题是c ++具有命名空间的原因。对于具有相同名称的2个第三方库,c实际上并不是一个很好的解决方案。

如果它是动态对象,您可以显式加载共享对象(LoadLibrary / dlopen / etc)并以这种方式调用它。或者,如果您在同一代码中不需要同时使用这两个库,则可以使用静态链接执行某些操作(如果您有.lib / .a文件)。

当然,这些解决方案都不适用于所有项目。

答案 7 :(得分:3)

宣誓?据我所知,如果你有两个库可以公开具有相同名称的链接点而你需要链接两者,那么你就无法做到。

答案 8 :(得分:3)

如果你有.o文件,那么这里有一个很好的答案:https://stackoverflow.com/a/6940389/4705766

要点:

  1. objcopy --prefix-symbols=pre_string test.o重命名.o文件中的符号
    1. objcopy --redefine-sym old_str=new_str test.o重命名.o文件中的特定符号。

答案 9 :(得分:2)

你应该在其中一个周围写一个包装器库。 您的包装器库应该公开具有唯一名称的符号,而不是公开非唯一名称的符号。

您的另一个选择是重命名头文件中的函数名称,并重命名库对象存档中的符号。

无论哪种方式,要使用两者,这将是一项黑客工作。

答案 10 :(得分:0)

我从未使用过dlsym,dlopen,dlerror,dlclose,dlvsym等,但是我正在查看手册页,它给出了打开libm.so并提取cos函数的示例。 dlopen是否经历了寻找碰撞的过程?如果没有,OP可以手动加载两个库,并为其库提供的所有函数分配新名称。

答案 11 :(得分:0)

这个问题已接近十年之久,但一直有新的搜索......

正如已经回答的那样,带有--redefine-sym标志的objcopy在Linux中是一个不错的选择。例如,请参阅https://linux.die.net/man/1/objcopy以获取完整文档。它有点笨重,因为您实际上在进行更改时复制整个库,每次更新都需要重复此工作。但至少它应该有用。

对于Windows,动态加载库是一种解决方案,而像Linux中的dlopen替代方案那样是永久性的。但是,dlopen()和LoadLibrary()都会添加额外的代码,如果唯一的问题是重复的名称,则可以避免这些代码。这里的Windows解决方案比objcopy方法更优雅:只需告诉链接器库中的符号已被其他名称所知并使用该名称。这样做有几个步骤。您需要创建一个def文件并在EXPORTS部分中提供名称翻译。有关def文件的完整语法详细信息,请参阅https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx(VS2015,最终将被更新版本替换)或http://www.digitalmars.com/ctg/ctgDefFiles.html(可能更长久)。该过程将为其中一个库创建一个def文件,然后使用此def文件构建一个lib文件,然后链接到该lib文件。 (对于Windows DLL,lib文件仅用于链接,而不是代码执行。)有关构建lib文件的过程,请参阅How to make a .lib file when have a .dll file and a header file。这里唯一的区别是添加别名。

对于Linux和Windows,重命名名称为别名的库的标题中的函数。另一个应该起作用的选项是,在引用新名称的文件中,#define old_name new_name,#include包含其出口别名的库的标题,然后在调用者中使用#undef old_name。如果有很多文件使用该库,则更容易的替代方法是创建包含定义,包含和undef的头或标题,然后使用该标头。

希望这些信息有用!