我们最近被要求提供一个库的Linux版本,之前我们是在Linux下开发的,在Windows上部署库通常容易得多.我们遇到的问题是将导出的符号剥离为仅expose 接口中的符号.想要这样做有三个很好的理由

  • 保护我们技术的专有方面不被出口符号expose .
  • 以防止用户遇到符号名称冲突的问题.
  • 加快图书馆的加载速度(至少我听说是这样).

举个简单的例子:

测验cpp

#include <cmath>

float private_function(float f)
{
    return std::abs(f);
}

extern "C" float public_function(float f)
{
    return private_function(f);
}

用(g++4.3.2,ld 2.18.93.20081009)编译

g++ -shared -o libtest.so 测验cpp -s

并用

nm -DC libtest.so

给予

         w _Jv_RegisterClasses
0000047c T private_function(float)
000004ba W std::abs(float)
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
00000508 T _fini
00000358 T _init
0000049b T public_function

显然不够.所以接下来我们将公共函数重新声明为

extern "C" float __attribute__ ((visibility ("default"))) 
    public_function(float f)

并用

g++ -shared -o libtest.so 测验cpp -s -fvisibility=hidden

which 给予

         w _Jv_RegisterClasses
0000047a W std::abs(float)
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
000004c8 T _fini
00000320 T _init
0000045b T public_function

这很好,除了std::absexpose 在外.更麻烦的是,当我们开始链接到我们控制之外的其他(静态)库时,all of the symbols we use from those libraries get exported.此外,当我们开始使用STL容器时:

#include <vector>
struct private_struct
{
    float f;
};

void other_private_function()
{
    std::vector<private_struct> v;
}

我们最终得到了来自C++库的许多额外出口.

00000b30 W __gnu_cxx::new_allocator<private_struct>::deallocate(private_struct*, unsigned int)
00000abe W __gnu_cxx::new_allocator<private_struct>::new_allocator()
00000a90 W __gnu_cxx::new_allocator<private_struct>::~new_allocator()
00000ac4 W std::allocator<private_struct>::allocator()
00000a96 W std::allocator<private_struct>::~allocator()
00000ad8 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::_Vector_impl()
00000aaa W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::~_Vector_impl()
00000b44 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_deallocate(private_struct*, unsigned int)
00000a68 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_get_Tp_allocator()
00000b08 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_base()
00000b6e W std::_Vector_base<private_struct, std::allocator<private_struct> >::~_Vector_base()
00000b1c W std::vector<private_struct, std::allocator<private_struct> >::vector()
00000bb2 W std::vector<private_struct, std::allocator<private_struct> >::~vector()

注意:如果启用了优化功能,则需要确保实际使用了向量,这样编译器就不会优化未使用的符号.

我相信我的同事已经设法构建了一个特别的解决方案,包括版本文件和修改STL头(!)这似乎有效,但我想问:

Is there a clean way to strip all unnecessary symbols (IE ones that are not part of the exposed library functionality) from a linux shared library?我已经try 了很多关于g++和ld的选项,但几乎没有成功,所以我更喜欢已知有效的答案,而不是相信有效的答案.

特别地:

  • 不导出(封闭源代码)静态库中的符号.
  • 不会导出标准库中的符号.
  • 不会导出对象文件中的非公共符号.

我们导出的接口是C.

我知道其他类似的问题,所以:

但在答案上几乎没有成功.

推荐答案

因此,我们目前的解决方案如下:

测验cpp

#include <cmath>
#include <vector>
#include <typeinfo>

struct private_struct
{
    float f;
};

float private_function(float f)
{
    return std::abs(f);
}

void other_private_function()
{
    std::vector<private_struct> f(1);
}

extern "C" void __attribute__ ((visibility ("default"))) public_function2()
{
    other_private_function();
}

extern "C" float __attribute__ ((visibility ("default"))) public_function1(float f)
{
    return private_function(f);
}

出口.版本

LIBTEST 
{
global:
    public*;
local:
    *;
};

g++ -shared 测验cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=出口.版本

给予

00000000 A LIBTEST
         w _Jv_RegisterClasses
         U _Unwind_Resume
         U std::__throw_bad_alloc()
         U operator delete(void*)
         U operator new(unsigned int)
         w __cxa_finalize
         w __gmon_start__
         U __gxx_personality_v0
000005db T public_function1
00000676 T public_function2

这和我们想要的非常接近.但也有一些问题:

  • 我们必须确保在内部代码中不使用"exported"前缀(在这个简单的例子中是"public",但在我们的例子中显然是更有用的).
  • 许多符号名仍然出现在字符串表中,这似乎取决于RTTI,-fno-RTTI使它们在我的简单测试中消失,但这是一个相当核心的解决方案.

我很乐意接受任何人提出的更好的解决方案!

Linux相关问答推荐

如何注释掉SLURM中的延迟调度命令?

为什么在Linux上STD::SLEEP_FOR(STD::Chrono::Hors::Max())会立即返回?

Linux-如何区分目录中名称相同但扩展名不同的所有文件

我想使用排序命令对第 5 列进行日期排序.但问题是格式不一致,有什么方法可以做到吗?

Bash 更新 yaml 文件中的图像值

编译过度对齐的动态分配变量时出现 icpc 错误

如何在 shell 脚本中只读取一个字符

如何比较两个压缩包的内容

在 64 位 Linux 操作系统上编译 32 位程序导致致命错误

在 C 中设置环境变量

如何在 Linux 中查找所有以 .rb 结尾的文件?

bash shell 脚本函数定义(如f () {})中使用的括号是什么?它与使用function关键字不同吗?

如何在 Linux 上传递带感叹号的参数?

以原子方式移动目录

如何在 shell 脚本中向文件中添加一行?

在 Unix 上计算每行/字段的字符出现次数

带有日期和时间的 Linux 命令历史记录

readelf vs. objdump:为什么都需要

当我已经 ssh 进入远程机器时,如何 scp 回到本地?

Linux shell 中的排序和唯一性