如何将程序链接到具有相同函数名称但实现不同的两个库?

问题描述

我正在使用库 foo1.afoo2.a 编写程序。

foo2.a 中,它使用 foo3.a,它实现了一个函数 funcfoo。但是 foo1.a 包含实现其自己的 funcfoo 的相同函数。我的主程序想使用 funcfoo 中的 foo1.a,以及 foo2.a 中的一些其他函数,同时还确保 foo2.a 只使用 {{1} 中的 funcfoo }}。

无论如何我可以强制执行此操作吗?

解决方法

简短的回答:你不能

长答案:你仍然不能,因为名称(如函数名称以及全局变量或枚举名称......任何名称)在你的二进制文件中必须是唯一的[1]

但是

也许你可以采取一些行动。

  1. 如果您拥有涉及此混乱的任何库的源代码,请使用相同的内容创建一个完整的新库,但已重命名。

基本上,我说“为您的库创建一个新的主要版本,因为您将更改库中存在的函数名称,从而破坏了复古兼容性”。 如果您的库是包含函数“min”的“foo1.a”,则使用“foo2_min”函数创建“foo2.a”。 用某些东西作为内部函数(无论是在库中还是直接在二进制文件中)的前缀/后缀通常是一种很好的做法。就像您的公司名称是“My Little Pony”一样,“MLP_”听起来是一个很好的前缀(做谷歌搜索,以防万一)。 这就是 user694733 在评论中所说的。

如果您使用的是 IDE,那么使用“重命名重构”功能应该可以轻松快捷地完成此操作。 我建议尽可能地重命名,以避免出现像您现在遇到的情况那样的进一步情况。

  1. 您没有源代码,但库许可允许您对其进行修改。

如果它的格式是开放的(就像旧的一样),你可以使用 objcopy。 我读过它可以做到这一点,但我从来没有自己做过所以......祝你好运

如果它是一种封闭格式,要么你有关于它的文档并且你必须做很多工作,要么你完全卡住了。

  1. 第三方拥有代码源,和/或库许可不允许您修改它

如果是第三方库,可能你的公司已经订阅了他的支持,所以你最好直接联系他们。

.

据我所知,不可能在 C 中原生地将库“封装”到“空间名称”中。

我希望这个回答对您有所帮助,而且我希望它是准确和完整的。

[1] 好吧,这并不完全正确,因为您可以使用全局变量的名称创建一个局部变量,这将编译并运行。在本地上下文中,将使用本地变量。 gcc 可以使用 -Wshadow 选项警告这种情况。 但除非您进行一些阴暗的黑客攻击,否则您通常希望避免这种情况。

,

当您在命令行上提供库时,大多数链接器都遵循库的顺序。如果您首先将您的程序与“foo1.a”链接,它将通过“foo1.a”的实现来解析您程序中funcfoo 的引用。将“foo2.a”放在第二个位置,这将留下对 funcfoo 的开放引用。将“foo3.a”放在第三位,这将通过第二个实现来解决。

编辑 1:

恐怕我错了。快速检查(就在睡觉前)显示,当链接“foo2.a”时,对 funcfoo 的引用已通过已加载的“foo1.a”的实现解析。 :-(

我会做更多的研究,但请不要屏住呼吸。

编辑 2:

好的,这花了一些时间,但是在“objcopy”的帮助下,按照建议工作。即使在库中,您也可以使用其选项 --redefine-sym old=new 来“重命名”符号。

我准备了一个小例子来遵循这些步骤作为概念证明。在实际项目中,库已经构建好了,所以只需要显示列表中的最后一个命令。

让我们从主要来源开始:

#include "foo1.h"
#include "foo2.h"

int main(void) {
  funcfoo();
  funcbar();
  return 0;
}

它包含以下头文件,第一个“foo1.h”和第二个“foo2.h”:

#ifndef FOO1_H
#define FOO1_H
void funcfoo(void);
#endif
#ifndef FOO2_H
#define FOO2_H
void funcbar(void);
#endif

当然,两者都有实现,第一个是“foo1.c”,第二个是“foo2.c”:

#include <stdio.h>

#include "foo1.h"

void funcfoo(void) {
  puts("funcfoo() in foo1");
}
#include "foo2.h"
#include "foo3.h"

void funcbar(void) {
  funcfoo();
}

第三个库“foo3”实现与“foo1”相同的功能,首先是头文件,然后是实现文件:

#ifndef FOO3_H
#define FOO3_H
void funcfoo(void);
#endif
#include <stdio.h>

#include "foo3.h"

void funcfoo(void) {
  puts("funcfoo() in foo3");
}

这些是构建应用程序的命令:

gcc -c -Wall -Wextra foo1.c -o foo1.o
ar cr libfoo1.a foo1.o

gcc -c -Wall -Wextra foo2.c -o foo2.o
ar cr libfoo2.a foo2.o

gcc -c -Wall -Wextra foo3.c -o foo3.o
ar cr libfoo3.a foo3.o

# In the real prject,only the following steps are needed:
gcc -c -Wall -Wextra main.c -o main.o
objcopy --redefine-sym funcfoo=funcfoo2 libfoo2.a libfoo2n.a
objcopy --redefine-sym funcfoo=funcfoo2 libfoo3.a libfoo3n.a
gcc main.o -L. -lfoo1 -lfoo2n -lfoo3n -o app

如你所见,常用“binutils”的工具“objcopy”将冲突函数的名称从funcfoo重新定义为funcfoo2。我还让它创建了一个新的输出库来保存原件。

运行时,应用程序打印:

funcfoo() in foo1
funcfoo() in foo3