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


93

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


2
这些静态库还是动态链接?
Alnitak

我们需要更多详细信息...这些名称是否已导出?还是仅在内部使用?你能改名字吗?
Johannes Schaub-litb

它们都是动态链接的。由于我不拥有这些库,因此无法更改名称。
09年

好问题。当然,它不会有,如果所有的符号都用一个唯一的ID前缀,这两个库的问题(例如vorbis_...sf_...sdl_...)。本质上,这就是C ++对命名空间函数的符号名称所做的事情。
Vortico

这是一个非常有趣的问题,但令人遗憾的是它不够精确,这就是答案太多的原因。
yugr,

Answers:


52
  • 如果您控制一个或两个,请执行以下操作:编辑一个以更改名称并重新编译,或者等效地查看Benunknown的答案,这些答案在无需访问源代码的情况下将起作用。
  • 如果您不控制它们中的任何一个,则可以将它们之一包装起来。那是编译另一个静态链接!)库,它除了重新导出原始文件的所有符号(除了有问题的那个库)外什么都不做,该冒犯的是通过具有备用名称的包装器到达的。真麻烦
  • 稍后添加:由于qeek说他正在谈论动态库,因此Ferrucciomouviciel建议的解决方案可能是最好的。(我似乎生活在很早以前的日子,当时默认情况下是静态链接。这使我的想法更加生动。)

适当地加上注释:“导出”是指使链接到库的模块可见-等效extern于文件范围内的关键字。如何控制它取决于OS和链接器。这是我始终必须查找的内容。


那也是我的第一个想法,但难道您不会遇到相同的碰撞问题吗?最后,整个项目必须链接-在编译/链接时或在运行时-这两个违规库都必须按原样加载。
Sniggerfardimungus

@未知:包装器必须使用静态链接进行编译,并且不应导出有问题的符号。然后,您仍然可以动态链接包装器。编辑为更清晰,谢谢。
dmckee ---前主持人小猫,

如果qeek的问题出在ddl而不是静态库上,那么如何用包装器制作一个新的库呢?既然如此,包装器库就必须动态地包装您不想首先链接到的库中的函数。
jeffD

@dmckee-“导出”是什么意思?

4
也许有人可以提供这种技术的简单示例?一个exe,两个库,每个库包含一个具有相同名称的函数。

52

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

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


1
真好 添加到Makefile中将是微不足道的。如果对库进行过更新,则与其他一些解决方案相比,更新objcopy咒语要容易得多。
sigjuice

8
也不要忘记在头文件中重命名符号。
mouviciel

^ sed / awk / perl也会对自动重命名标题中的符号很有用
Alex Reinking

16

在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上的有效。
user877329'2013-10-3


8

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

更新: 我只是在这方面做到了,它似乎起作用了。 当然,我还没有对它进行彻底的测试-这可能仅仅是用hexedit shot弹枪击退腿部的好方法。


其实不是一个可怕的解决方案。有点骇人听闻,但您要做的就是更改符号表中的字符串。在这方面没有真正的功能危害。
Evan Teran

您可能还想重命名该库,以免其他人出现,试图再次加载该库。您将从一场冲突发展到数十个或数百个。=]我喜欢stackoverflow:我们有一个问题的经过测试的答案,它有3票。第一个(不完整的)答案:17。=]
Sniggerfardimungus

重命名的机会是有限的,因为您只能使名字更短。同样在Linux上,您将很难更新ELF哈希表。
yugr,

7

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

#include <dlfcn.h>

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

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

就像Ferruccio在https://stackoverflow.com/a/678453/1635364中所述,通过执行显式加载要使用的库(选择您喜欢的标志)

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

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

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


谢谢。没考虑这个。虽然,我想同时拥有两者。
09年

如果我想同时使用两者怎么办?
QZHua

@QZHua:其他符号(例如,涉及符号重命名)应该可以解决您的问题。
mouviciel '16

6

如果那里有.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文件中的特定符号。

4

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

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

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


1
哦是的 对于这个一般性问题,这似乎是一个很好的答案。但是,如果在同一编译器中一起编译所有内容,则名称空间很酷。万岁,没有名字冲突。但是,如果您获得二进制形式的库,并希望将其与另一个编译器集成,那么-祝您好运。目标文件中的名称处理规则只是第一个障碍(外部“ C”可能会有所帮助,这会撤消名称空间的作用)。
Tomasz Gandor 2014年

3

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


12
发誓绝对是第一步。毫无疑问。
dmckee ---前主持人小猫,

1
“您无能为力”-这仍然有意义吗?其他答案提供了许多不同的解决方案。
yugr,

2

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

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

无论哪种方式,使用这两种方法都是一项艰巨的工作。



1

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

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

对于Windows,动态加载库是一种解决方案,而永久性的解决方案就像Linux中的dlopen替代方案一样。但是,dlopen()和LoadLibrary()都添加了额外的代码,如果唯一的问题是重复的名称,则可以避免这些代码。在这里,Windows解决方案比objcopy方法更为优雅:只需告诉链接程序其他名称即可知道库中的符号,并使用该名称。要做一些步骤。您需要制作一个def文件,并在EXPORTS部分中提供名称转换。请参阅https://msdn.microsoft.com/zh-cn/library/hyx1zcd3.aspx(VS2015,它最终将被较新版本取代)或http://www.digitalmars.com/ctg/ctgDefFiles.html(可能更永久)以获取def文件的完整语法详细信息。该过程将为其中一个库创建一个def文件,然后使用该def文件构建一个lib文件,然后与该lib文件链接。(对于Windows DLL,lib文件仅用于链接,而不用于代码执行。)有关构建lib文件的过程,请参见在具有.dll文件和头文件时如何制作.lib文件。这里唯一的区别是添加别名。

对于Linux和Windows,请重命名其别名为别名的库头中的函数。另一个可行的选择是,在引用新名称的文件中,#define old_name new_name,#include要对其别名进行导出的库的头,然后在调用方中#undef old_name。如果使用该库的文件很多,一个更简单的选择是制作一个包含定义,包含和未定义的头文件,然后使用该头文件。

希望此信息对您有所帮助!


0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.