【问题标题】:How does g++ linker resolve symbols among .so filesg++链接器如何解析.so文件之间的符号
【发布时间】:2014-11-06 06:01:46
【问题描述】:

我了解对象排序在链接期间非常重要。在尝试让 ld 解析所有符号之前,我已经很头疼了。这次ld没有产生任何错误,但是输出错误!

项目很大(50K+ 行 C++),我无法生成简化版本,所以我将尝试描述我遇到的情况。希望高手能帮我解答一下。

g++ -o bad.out a.o b.o ... x.so y.so
g++ -o good.out a.o b.o ... y.so x.so

虽然 good.out 可以正确运行,但 bad.out 不能。 x.so 和 y.so 均由独立供应商提供,因此它们的顺序无关紧要。这里有更多线索:

  • a.o 使用 x.so
  • b.o 使用 y.so
  • a.o 和 b.o 是独立的,但它们都使用一些公共类

不正确的行为表现为从未回调的函数 OnRspLogin()。这是一个在 y.so 中定义并在 b.o 中实现的纯虚函数。 "grep OnRspLogin *.o *.so" 仅在 y.so 和 b.o 中找到匹配项。

显然 ld 没有将 OnRspLogin() 解析为 b.o 中的那个,但它解析到了哪一个?这让我很担心,因为链接器没有产生任何错误或警告。

我在 CentOS 6.5 上使用 gcc 4.4.7-4。

编辑: 我发现 x.so 和 y.so 都包含一些常见的符号(例如 T TcpClient),所以我猜链接器在解析 b.o:TcpClient 时选择了 x.so:TcpClient(而不是 y.so:TcpClient)。虽然更改 .so 顺序可能会解决这个问题,但恐怕链接器可能会错误地解析 a.o 中的一些其他符号。那么有没有办法告诉链接器只使用 y.so 来解析 b.o?请注意,这些 .so 文件由第 3 方提供,我无法更改它们。

【问题讨论】:

  • 为什么觉得函数没有解析?这是不可能的。如果它没有被调用,原因是没有代码路径调用它。 that 的原因可能是 x.so 和 y.so 定义了相同的符号,而 thsat 采用的执行路径取决于使用该符号的哪个版本。顺便说一句,使用nm 在目标文件中查找符号,直接 grepping 对象是没有信息的。
  • 我不是说函数没有解析;我说它没有解决正确的问题。实际上 x.so 和 y.so 包含共同的符号。有没有办法告诉 ld 使用哪个 .so?
  • 你说过只有一个功能,意思是它是正确的。在多个 .so 中有重复的定义是麻烦的迹象。您可能无法同时使用这两个 .so。
  • 加载的第一个符号将在整个程序中使用。这在您的情况下可能有效,也可能无效。除了尝试之外别无他法(答案当然永远不会确定)。

标签: linker g++


【解决方案1】:

我发现 x.so 和 y.so 都包含一些常见的符号(例如 TcpClient)

个问题。如果这些符号应该是不同的,那么您不能将这两个库链接在一起——它们不兼容链接。

供应商解决此类问题的通常方式是,他们在所有导出的符号(例如vendorA_TcpClient)上使用不同的供应商特定前缀并隐藏所有其他符号。 p>

请注意,这些 .so 文件由第 3 方提供,我无法更改它们。

您可以告诉供应商您不能使用他们的库,除非他们避免定义没有以其唯一标识符作为前缀的符号,并且除非他们解决了这个问题,否则您不会向他们付款。这样做时,供应商通常会变得非常敏感。

【讨论】:

  • 不幸的是,供应商不愿意更改他们的 .so 文件,因为它们已发布给许多客户,而且 QA 速度慢且有风险。他们只是互相指指点点......他们有任何可以更改符号名称的bintool吗?
  • 同样对于两家供应商来说,虽然 TcpClient 出现在他们的 .so 文件中,但它是纯粹内部的,从未在供应商 API 中公开。我想知道它是否重要,因为链接器不需要解决它。
  • 所以他们发布了暴露内部符号的库,然后拒绝更改它们,因为他们的客户开始依赖这些他们一开始就应该从未见过的符号?不错。
猜你喜欢
  • 2017-06-14
  • 2019-08-25
  • 2014-12-16
  • 1970-01-01
  • 1970-01-01
  • 2013-04-22
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
相关资源
最近更新 更多