【问题标题】:C++ shared libraries have duplicate symbolsC++ 共享库有重复的符号
【发布时间】:2019-02-18 15:18:03
【问题描述】:

我是 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

【问题讨论】:

    标签: android c++11 android-ndk .so


    【解决方案1】:

    我注意到 .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
    

    现在有两个命中。

    这里没有错。查看更多图片:

    $ 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 符号从他们的.symtabs 变成.symtab 共享的.dynsym 库,除了那些在目标文件中有 HIDDEN visibility 的符号 (它们是通过attribute of hidden visibility 定义的 编译时)。

    目标文件中具有HIDDEN 可见性的任何GLOBAL 符号都被转录为LOCAL 符号 具有DEFAULT 对共享库的.symtab 的可见性并且不会被转录 完全进入共享库的.dynsym。所以当共享库链接到 否则,链接器和加载器都无法看到编译时为 HIDDEN 的全局符号。

    但除了隐藏符号(通常没有这些符号)之外,相同的全局符号 将出现在共享库的.symtab.dynsym 表中。每个定义的符号 出现在两个表中的定义相同。

    后来,OP cmets

    我通过运行 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(与您的 sn-p 中的所有其他符号一样)。 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
    

    查看objdumpstruct 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) 的重复弱导出。就像你一样 注意到了。

    请稍等。 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 不同的符号 - _ZN3fooCNEi,表示 1 N foo::foo(int) 分离。 (而g++实际上在对象中使用了_ZN3fooC1Ei_ZN3fooC2Ei_ZN3fooC5Ei 文件bar.ogum.o)。

    所以实际上,动态符号表中没有重复的符号: 名称拆解映射的鬼鬼祟祟的多对一特性使它看起来如此。

    但是为什么呢?

    恐怕这里的答案太长太复杂了。

    执行摘要

    GCC C++ 编译器使用两个弱符号 demangle 以不同方式引用 全局内联类方法,作为 它的股票公式用于在 Position Independent Code 中实现全局内联类方法的成功链接。 对于任何编译器来说,这都是一个不可忽视的问题,而且它的 GCC 公式并不是唯一可能的问题。 Clang有不同的 解决方案,这确实涉及使用同义但不同的符号,因此不 引起你所看到的符号的虚幻“重复”。

    【讨论】:

    • 那么从上面的解释中,是否总是期望符号表中有重复的符号?
    • @Teja 如前所述,共享库有两个符号表:其动态符号表中的符号是其静态符号表中符号的子集。
    • 感谢您的耐心等待!!我通过运行 objdump -T 命令获取了符号表,理想情况下,该命令应该列出仅存在于动态符号表中的符号。尽管使用了“-T”标志,但我注意到重复的符号,这是预期的吗?如果是,为什么会这样?
    • @Teja 更新的答案可能有帮助
    • 感谢您的详细分析,我现在明白了为什么我看到重复的符号 [这是去重的效果],所以理想情况下,在您的示例中,如果还有一个名为“Test”的方法并返回 foo (3),然后一个条目将被添加到符号表中。真的吗 ?即使我们使用 Clang [请注意我们使用的是 clang],这也是预期的吗?
    猜你喜欢
    • 1970-01-01
    • 2018-04-14
    • 2016-09-26
    • 2010-11-18
    • 1970-01-01
    • 2021-10-21
    • 2011-02-28
    • 1970-01-01
    • 2010-11-04
    相关资源
    最近更新 更多