我是C ++符号表和库的新手,想了解符号表的行为。 我们正在使用一个具有本地支持的android应用程序。在分析共享库的符号表的过程中,我注意到.so文件中存在重复的符号。请找到符号表的示例列表。
0162502c w DO .data 00000004 Base boost::asio::error::get_addrinfo_category()::instance
00aaa4f4 w DF .text 0000009c Base boost::asio::error::get_misc_category()
01626334 w DO .bss 00000004 Base guard variable for boost::asio::error::get_misc_category()::instance
00aab4d0 w DF .text 0000003c Base boost::asio::error::detail::misc_category::~misc_category()
00aab368 w DF .text 0000003c Base boost::asio::error::detail::addrinfo_category::~addrinfo_category()
00aab3a4 w DF .text 00000034 Base boost::asio::error::detail::addrinfo_category::name() const
00aab3d8 w DF .text 000000f8 Base boost::asio::error::detail::addrinfo_category::message(int) const
00aab50c w DF .text 0000003c Base boost::asio::error::detail::misc_category::~misc_category()
在这里您会注意到以下符号“ boost :: asio :: error :: detail :: misc_category :: ~~ misc_category()”出现了两次。
我想了解为什么我们在符号表中得到重复的符号。还想知道为什么当有重复的符号时我的应用程序运行正常[理想的链接器应该抛出重复的符号错误]也想知道在符号表中重复符号会增加“ so”的大小,最终导致应用程序的大小
如果发生这种情况,如何确保在符号表中仅获得唯一的条目。 注意:-我们正在使用clang
答案 0 :(得分:2)
我注意到.so文件中存在重复的符号
喜欢吗?
$ cat foo.c
int foo(void)
{
return 42;
}
编译:
$ gcc -Wall -fPIC -c foo.c
检查目标文件中的foo
符号:
$ readelf -s foo.o | grep foo
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
8: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 foo
一击。
创建共享库:
$ gcc -Wall -shared -o libfoo.so foo.o
在共享库中检查foo
中的符号:
$ readelf -s libfoo.so | grep foo
5: 000000000000057a 11 FUNC GLOBAL DEFAULT 9 foo
29: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
44: 000000000000057a 11 FUNC GLOBAL DEFAULT 9 foo
现在有2首。
这里没有错。看到更多图片:
$ readelf -s foo.o | egrep '(foo|Symbol table|Ndx)'
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
8: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 foo
一个目标文件有一个符号表,其静态符号表.symtab
,
链接器将其用于链接时符号解析。但是:
$ readelf -s libfoo.so | egrep '(foo|Symbol table|Ndx)'
Symbol table '.dynsym' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
5: 000000000000057a 11 FUNC GLOBAL DEFAULT 9 foo
Symbol table '.symtab' contains 48 entries:
Num: Value Size Type Bind Vis Ndx Name
29: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
44: 000000000000057a 11 FUNC GLOBAL DEFAULT 9 foo
一个共享库具有两个符号表:一个静态符号表.symtab
,例如
目标文件,加上动态符号表.dynsym
,供加载程序用于运行时符号解析。
将对象文件链接到共享库时,默认情况下,链接器会转录
GLOBAL
个符号从它们的.symtab
到共享的.symtab
和 .dynsym
库,但目标文件中具有HIDDEN
visibility的那些符号除外
(它们是通过用attribute of hidden visibility定义的
编译时)。
目标文件中任何具有GLOBAL
可见性的HIDDEN
符号都将被转录为LOCAL
符号
对共享库的DEFAULT
具有.symtab
的可见性,并且不会被转录
进入共享库的.dynsym
。因此,当共享库与
其他任何事情,链接器和加载器都看不到编译时HIDDEN
的全局符号。
但是除了隐藏符号(通常不包含这些符号)之外,它们是相同的全局符号
将显示在共享库的.symtab
和.dynsym
表中。每个定义的符号
两个表中出现的地址具有相同的定义。
稍后,OP评论
我通过运行objdump -T命令获取了符号表,理想情况下应该列出仅在动态符号表中存在的符号。
这引导我们做出另一种解释,因为objdump -T
确实仅报告了
动态符号表(如readelf --dyn-syms
)。
请注意,该符号报告了两次:
...
00aab4d0 w DF .text 0000003c Base boost::asio::error::detail::misc_category::~misc_category()
...
00aab50c w DF .text 0000003c Base boost::asio::error::detail::misc_category::~misc_category()
...
在第2列中被归类为w
(以及代码段中的所有其他符号)。 objdump
的意思是
该符号为weak。
让我们拒绝观察:
foo.hpp
#pragma once
#include <iostream>
struct foo
{
explicit foo(int i)
: _i{i}
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
~foo()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int _i = 0;
};
bar.cpp
#include "foo.hpp"
foo bar()
{
return foo(2);
}
gum.cpp
#include "foo.hpp"
foo gum()
{
return foo(1);
}
编译并创建共享库:
$ g++ -Wall -Wextra -c -fPIC bar.cpp gum.cpp
$ g++ -shared -o libbargum.so bar.o gum.o
查看objdump
的{em> dynamic 符号struct foo
报告如下:
$ objdump -CT libbargum.so | grep 'foo::'
00000000000009bc w DF .text 0000000000000046 Base foo::foo(int)
00000000000009bc w DF .text 0000000000000046 Base foo::foo(int)
构造函数foo::foo(int)
的弱导出重复。就像你一样
注意到了。
不过还是a一息。 foo::foo(int)
是C ++方法签名,但不是
实际上是链接器可以识别的符号。这次我们再做一次
无需拆包:
$ objdump -T libbargum.so | grep 'foo'
00000000000009bc w DF .text 0000000000000046 Base _ZN3fooC1Ei
00000000000009bc w DF .text 0000000000000046 Base _ZN3fooC2Ei
现在,我们看到链接器看到的符号,并且不再可见重复项:
_ZN3fooC1Ei
!= _ZN3fooC2Ei
,尽管两个符号都有相同的地址和
$ c++filt _ZN3fooC1Ei
foo::foo(int)
$ c++filt _ZN3fooC2Ei
foo::foo(int)
它们都分解为同一事物foo::foo(int)
。实际上有5
不同符号-_ZN3fooC
N Ei
,表示1 <= N <= 5-与foo::foo(int)
互斥。
(并且g++
实际上在对象中使用_ZN3fooC1Ei
,_ZN3fooC2Ei
和_ZN3fooC5Ei
文件bar.o
和gum.o
)。
因此,实际上,动态符号表中没有重复的符号: 名称解析映射的多对一偷偷摸摸的性质使它看起来像那样。
但是为什么?
对于这个问题,恐怕答案太长且太复杂了。
执行摘要
GCC C ++编译器使用了两个弱符号 相同地分解,以不同方式引用全局内联类方法,作为 其股票公式,可以成功地在排名独立代码中链接全球内联类方法。 对于任何编译器而言,这都是一个不容忽视的问题,并且针对它的GCC公式并不是唯一可能的问题。 lang有不同 解决方案,它确实涉及使用同义但不同的符号,因此不 引起您所看到的符号的虚幻的“重复”。