减少第三方库的导出符号

Reduce exported symbols of a third party library

我有工作正常的代码,但是如果我 link 我的项目到第三方库 libabc.so (源不可用),那么我突然遇到分段错误。

我有一个看起来像这样的主

#include <opencv2/imgcodecs.hpp>
#include "Abc.h"

int main(int argc, char **argv)
{
    Abc dummyAbc;
    auto img = cv::imread("dummy.png");
    cv::imwrite("123.png", img);
    return 0;
}

CMakeList.txt如下

cmake_minimum_required(VERSION 3.1)
set(CMAKE_C_STANDARD 11)
find_package(OpenCV COMPONENTS core highgui imgcodecs)
include_directories(${OpenCV_INCLUDE_DIR})

add_executable(my_project Main.cpp)
target_link_libraries(my_project ${OpenCV_LIBRARIES} abc)

这编译得很好,但在 运行 时会出现段错误。如果我删除行

Abc dummyAbc;

然后一切正常(即没有丢失文件或 opencv 的问题)。

如果我检查段错误的堆栈,我会看到:

Thread 1 "my_project" received signal SIGSEGV, Segmentation fault.
0x00007fdea96836b3 in png_destroy_write_struct () from /usr/local/lib/libabc.so

其中 png_destroy_write_structcv::imwrite 调用。

libpng.solibabc.so (!!) 导出 png_destroy_write_struct 它实际上导出所有的 libpng API (我假设它是静态的 link 到?)。我假设这是问题所在?我不希望 openCV see 任何 libabc.so 导出...我该怎么做?

我尝试使用 objcopy --prefix-symbols abc_ libabc.so 但不知何故它没有帮助,现在崩溃发生在 abc_png_destroy_write_struct

I assume this is the problem?

是:很有可能:libabc.so 已静态链接(可能是不同版本的)libpng,并引入了符号冲突。

I do no want openCV to see whatever libabc.so exports... How can I do this?

你不能。您必须联系 libabc.so 开发人员,并告诉他们 隐藏 libpng 符号。

唯一的其他选项(对于单进程执行)是动态加载 libabc.so

这可以通过 dlopen("liabc.so.", RTLD_LOCAL) 完成,即使那样也可能不起作用(具体取决于 libabc.so 的链接方式)——它可能会导致 libabc.so 绑定到您的版本libpng,然后崩溃。

在 Linux 上,您还可以使用 dlmopen(LM_ID_NEWLM, "libabc.so", ...),它将 完全 libabc.so 与您的代码的其余部分隔离开来,并且 可能 如果 libabc.so 被链接以包含其所有依赖项(或者您可以将它们明确地引入新的加载程序命名空间)。

最后,正如 Eljay 在这里评论的那样,您可以使用进程间通信并拥有完全独立的进程负载 libabc.so。这将比直接使用 libabc.so 性能差很多,但总比没有好。

我会尝试 strip。详情见此:https://linux.die.net/man/1/strip

添加到 EmployedRussian 关于使用基于 dlmopen 的方法将 libabc.so 与代码的其余部分隔离开来的答案:在这种情况下避免混淆 dlsym 和函数指针你可以通过 Implib.so:

为所需的库函数自动生成包装器
$ cat mysymbols.txt
foo
bar
$ cat mycallback.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C"
#endif

// Dlopen callback that loads library to dedicated namespace
void *mycallback() {
  void *h = dlmopen(LM_ID_NEWLM, "libabc.so", RTLD_LAZY | RTLD_DEEPBIND);
  if (h)
    return h;
  fprintf(stderr, "dlmopen failed: %s\n", dlerror());
  exit(1);
}

$ implib-gen.py --dlopen-callback=mycallback --symbol-list=mysymbols.txt libabc.so
$ ... # Link your app with libabc.tramp.S, libabc.init.c and mycallback.c