如果两个库提供同名函数产生冲突怎么办?

Posted

技术标签:

【中文标题】如果两个库提供同名函数产生冲突怎么办?【英文标题】:What should I do if two libraries provide a function with the same name generating a conflict? 【发布时间】:2010-10-15 06:18:23 【问题描述】:

如果我有两个提供同名函数的库,我该怎么办?

【问题讨论】:

这些是静态库还是动态链接的? 我们需要更多详细信息...这些名称是否已导出?还是仅在内部使用?可以改名字吗? 它们都是动态链接的。我无法更改名称,因为我不拥有这些库。 好问题。当然,如果所有符号都以唯一 ID 为前缀(例如 vorbis_...sf_...sdl_...),这两个库不会有问题。这本质上就是 C++ 对命名空间函数的符号名称所做的。 这是一个非常有趣的问题,但遗憾的是太不精确了,这就是答案太多太宽泛的原因。 【参考方案1】:

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

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

【讨论】:

不错。这将是微不足道的添加到 Makefile。如果库被更新,objcopy 咒语将比其他一些解决方案更容易更新。 不要忘记重命名头文件中的符号。 ^ sed/awk/perl 对于自动重命名标头中的符号也很有用【参考方案2】: 如果您控制一个或两个:编辑一个以更改名称并重新编译或等效地查看Ben 和unknown 的答案,这将在访问源代码的情况下工作。李> 如果您不控制其中任何一个,您可以将其中一个包裹起来。那就是编译 another (staticallylinked!) 库,除了重新导出原始符号的所有符号之外,除了有问题的符号之外什么都不做,这是通过带有备用名称。真麻烦。 稍后添加: 既然 qeek 说他在谈论动态库,那么 Ferruccio 和 mouviciel 建议的解决方案可能是最好的。 (我似乎生活在很久以前,静态链接是默认设置。它影响了我的思考。)

建议 cmets:“导出”是指使链接到库的模块可见 --- 相当于文件范围内的 extern 关键字。这是如何控制的取决于操作系统和链接器。这是我总是必须查找的东西。

【讨论】:

这也是我的第一个想法,但你不会遇到同样的碰撞问题吗?最后,整个项目必须链接——在编译/链接时或运行时——此时两个有问题的库都必须按原样加载。 @unknown:包装器必须使用静态链接编译,并且不应导出有问题的符号。然后您仍然可以动态链接包装器。为更清晰而编辑,谢谢。 如果 qeek 的问题在于 ddl 而不是静态库,那么如何使用包装器创建一个新库?因为,包装器库必须动态包装库中您不想首先链接的函数。 @dmckee - “出口”是什么意思? 也许有人可以提供这种技术的简单示例?一个 exe,两个库,每个库都包含一个同名函数。【参考方案3】:

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

例如

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 系统支持类似的功能,但我想不出它们的名字。

【讨论】:

dlopen dlsymdlclose。但是,Unix 上的封装可能不如 Windows 上那么有效。 An automated solution for Linux【参考方案4】:

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

总结:

    objcopy --prefix-symbols=pre_string test.o重命名.o文件中的符号

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

【讨论】:

【参考方案5】:

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

更新:我只是为此做了它,它似乎有效。当然,我没有彻底测试过 - 这可能只是一个很好的方法来吹你的腿使用六角编辑霰弹枪。

【讨论】:

其实不是一个糟糕的解决方案。有点骇人听闻,但您要做的就是更改符号表中的字符串。没有真正的功能损害。 您可能还想重命名库 - 以免其他人出现,试图再次加载该东西。你会从一场冲突发展到数十或数百场冲突。 =]我喜欢***的这一点:我们对一个问题有一个经过测试的答案,它有3票。第一个(不完整的)答案:17. =] 重命名机会有限,因为您只能缩短名称。同样在 Linux 上,您将很难更新 ELF 哈希表。【参考方案6】:

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

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

【讨论】:

谢谢。没有考虑这个。不过,我想同时拥有两者。 如果我想同时使用两者怎么办? @QZHua:其他答案(例如,涉及符号重命名)应该可以解决您的问题。【参考方案7】:

假设你使用的是linux你首先需要添加

#include <dlfcn.h>

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

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

就像https://***.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);

【讨论】:

Automation of this approach【参考方案8】:

发誓?据我所知,如果您有两个库公开具有相同名称的链接点并且您需要针对这两个库进行链接,那么您无能为力。

【讨论】:

发誓绝对是第一步。毫无疑问。 “你无能为力” - 这仍然有意义吗?其他答案提供了许多不同的解决方案。【参考方案9】:

这个问题是 c++ 有命名空间的原因。对于 2 个具有相同名称的第三方库,在 c 中并没有真正好的解决方案。

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

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

【讨论】:

哦,是的。对于这个一般性问题,这似乎是一个很好的答案。但是 - 如果您在同一个编译器中将所有内容一起编译,命名空间会很酷。万岁,没有名字冲突。但是如果你得到一个二进制形式的库,并且想将它与另一个编译器集成,那么 - 祝你好运。目标文件中的名称修改规则只是第一个障碍(extern "C" 可能会有所帮助,它会消除命名空间的影响)。【参考方案10】:

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

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

无论哪种方式,两者都使用,这将是一项骇人听闻的工作。

【讨论】:

Automation of this solution via Implib.so【参考方案11】:

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

正如已经回答的那样,带有 --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。如果有很多文件使用该库,一个更简单的替代方法是制作一个或多个包含定义、包含和取消定义的标头,然后使用该标头。

希望这些信息对您有所帮助!

【讨论】:

【参考方案12】:

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

【讨论】:

以上是关于如果两个库提供同名函数产生冲突怎么办?的主要内容,如果未能解决你的问题,请参考以下文章

js冲突怎么解决

解决python开发中模块冲突的具体方法?

手机没有安装相同包名的应用,怎么会发生包名冲突呢?

用 keil编译的时候有两个警告怎么回事

如果构造函数参数与 C++ 中的成员变量同名怎么办?

自定义SpringBoot+Swagger中@ApiModel默认名称