用静态链接重新定义malloc/free有多个定义错误

Redefine malloc/free with static linking has multiple definition error

本文关键字:定义 free 错误 malloc 链接 新定义 静态      更新时间:2023-10-16

最近,我的公司想把编译器从gcc-3.4更新到gcc-4.5。但是,我们客户的机器可能没有最新的libstdc++.so,所以我们想静态链接我们的二进制文件。

我们的程序需要定制malloc()/free()以满足非常高的性能要求。

我修改了makefile,在链接时添加了-static,并得到了以下错误信息:

/usr/lib64/libc.a(malloc.o)(.text+0x18c0): In function `free':
: multiple definition of `free'
../../ic/src/memmgr/libmemmgr_mt_thread.a(memmgr_mt_thread.o)(.text+0x3430): first defined here
/usr/bin/ld: Warning: size of symbol `free' changed from 271 in ../../ic/src/memmgr/libmemmgr_mt_thread.a(memmgr_mt_thread.o) to 255 in /usr/lib64/libc.a(malloc.o)
/usr/lib64/libc.a(malloc.o)(.text+0x3970): In function `malloc':
: multiple definition of `malloc'
../../ic/src/memmgr/libmemmgr_mt_thread.a(memmgr_mt_thread.o)(.text+0x29c0): first defined here
/usr/bin/ld: Warning: size of symbol `malloc' changed from 281 in ../../ic/src/memmgr/libmemmgr_mt_thread.a(memmgr_mt_thread.o) to 461 in /usr/lib64/libc.a(malloc.o)
/usr/lib64/libc.a(malloc.o)(.text+0x4050): In function `realloc':
: multiple definition of `realloc'
../../ic/src/memmgr/libmemmgr_mt_thread.a(memmgr_mt_thread.o)(.text+0x3e80): first defined here
/usr/bin/ld: Warning: size of symbol `realloc' changed from 335 in ../../ic/src/memmgr/libmemmgr_mt_thread.a(memmgr_mt_thread.o) to 927 in /usr/lib64/libc.a(malloc.o)

好的,这是合理的,因为libc.a已经有malloc()/free()了。

但是让我困惑的是为什么在动态链接时没有错误。我搜索了一下,发现了这个问题:如何在Linux中重新定义malloc()以便在c++中使用。答案是链接器对库文件(a)和目标文件(.o)的处理方式不同。现在我知道为什么静态链接会出错,而动态链接不会。

然而,我尝试了答案中描述的解决方案,直接用目标文件替换了库文件,但没有区别。我仍然得到了多个定义链接错误。我也尝试了-static-libgcc(因为我不知道该怎么做,我只是尝试了我在gcc手册页中看到的一切),但它也没有帮助。

我不必使用静态链接。我只是想解决libstdc++.so版本的问题。如有任何建议,我将不胜感激。

提前感谢。

对不起,我没有说清楚。使用#define malloc ...在这里可能没有帮助。因为我们的程序是c++。#define习语只能影响malloc()/free()函数。但是我们的程序实际上使用new/delete来分配/释放内存。D

如果您主要关心的是libstdc++的可用性。因此,在目标系统中,为什么不简单地将新版本与应用程序一起发布呢?

我认为静态链接在任何情况下都不是一个好的解决方案,编译项目变得更加困难,如果你自己使用共享对象(例如使用你自己的插件时),那么静态链接就会停止工作,因为静态库的单独副本需要链接到每个二进制文件,可执行文件等等。你能想象如果在同一个程序中有多个全局变量的实例,锁等当它被加载时会发生什么吗?我告诉你:崩溃。

所以不要静态链接,复制libstdc++。所以到一个私有目录(我不知道你的应用程序安装在哪里,但如果它有一个私有前缀,那很简单,使用$prefix/lib)。

然后设置LD_LIBRARY_PATH,或者使用-rpath将路径编码为二进制,以便链接器找到它。当然,这意味着你链接的所有库,也可能使用libstdc++,也应该和你的应用一起发布。

但是大小方面,它是相同的,对于静态链接,您也将携带该代码。

可以通过rpath使用动态链接。见"man ld"answers"man ld.so"

$ORIGIN扩展可能是有用的:将你需要的每一个。so包在与程序(或子目录)相同的目录中,并在与ld链接时使用"-rpath $ORIGIN"或"-rpath,'$ORIGIN/lib"。

许多程序使用这种方法来捆绑它们自己的私有库。

另一种方法是使用.sh脚本设置LD_LIBRARY_PATH,然后调用真正的程序(下载firefox二进制文件并查看run-mozilla.sh)。然而,LD_LIBRARY_PATH将泄漏给子进程。所以它不是那么干净,但可能更适合非GNU系统。

您应该在项目中使用自己的free/alloc/realloc函数,但我强烈建议您使用与标准库相同的名称。

例如

void* myProject_malloc(...)
void myProject_free()

你可以将它与宏结合起来,将分配函数重定向到你的函数,但我不建议你这样做。你必须检查你所有的源代码是否包括这个头文件,编译器可以提醒你宏重定义(取决于编译器和你正在使用的选项)

#define malloc(x) myProject_malloc(x)
#define free() myProject_free()

如果你不想使用标准库,你应该使用gcc选项:"- nosdlib "。但是如果这样做,就不能使用标准库的任何其他函数。

如果使用GNU libc,可以使用GNU malloc Hooks。我对这个API的设计不太满意,所以我不建议使用它。

您可以尝试修补libc。删除malloc/中的所有代码,并将其替换为您的实现。

使用相同的想法,您可以尝试使用libc.a,删除包含malloc和朋友的所有.o文件(这应该是与malloc/*.c对应的大部分.o文件)并重新打包libc.a与您的实现。

很可能需要更改自定义分配例程的名称。之后,您应该使用答案中描述的方法来节省时间,这样您就不必将所有调用更改为新名称:

#define malloc myMalloc
#define free myFree

如果你只需要为c++做,你可以覆盖new, delete, new[], delete[]操作符。参见18.6.1 "存储分配和释放"

  void* operator new(std::size_t size);
  [...]
  void operator delete(void* ptr);

"可替换的:c++程序可以定义一个带有此函数签名的函数,以取代c++标准库定义的默认版本。"

我不知道它是否与静态链接工作然而(不知道它是如何实现的)。