重新定义带有静态链接的malloc/free会出现多个定义错误。

8

最近,我的公司希望从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++ new使用。答案说链接器对库文件(.a)和目标文件(.o)进行不同的处理。现在我知道了为什么静态链接会出现错误而动态链接不会。

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

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

提前致谢。

编辑:很抱歉我没有表达清楚。在这里使用#define malloc ...可能没有帮助。因为我们的程序是C++。这个#define习惯用法只能影响malloc()/free()函数。但我们的程序实际上使用new/delete来分配/释放内存。不管怎样,还是谢谢:D


2
嗨@yoco,你最终解决了静态链接的问题吗?我也遇到了同样的问题。 - user1147800
@user1147800 我没有静态链接的解决方案。最终我使用了动态链接。 - yoco
你可以静态链接 libstdc++.a 并动态链接 libc.so - Basile Starynkevitch
6个回答

5
如果您的主要关注点是目标系统中libstdc++.so的可用性,那么为什么不将更新的版本与您的应用程序一起分发呢?
我认为,在任何情况下静态链接都不是一个好的解决方案。编译项目变得更加困难,如果您自己使用共享对象(例如使用自己的插件),那么静态链接将停止工作,因为每个二进制文件(可执行文件等)都需要链接一个单独的静态库副本。当程序被加载时,如果有多个全局变量、锁等实例,则会发生什么?我告诉你:崩溃。
所以不要静态链接,将libstdc++.so复制到一个私有目录中(我不知道您的应用程序安装在哪里,但如果它有一个私有前缀,那就很简单了,使用$prefix/lib即可)。
然后,要么设置LD_LIBRARY_PATH,要么使用-rpath将路径编码到二进制文件中,以便链接器找到它。当然,这意味着您链接的所有可能也使用libstdc++的库也应该与您的应用程序一起分发。
但从大小上看,它与静态链接大致相同,您将携带该代码。

谢谢,这正是我需要解决问题的东西。我真的很感激你知道我想要什么,即使我不能很好地表达我的问题。 - yoco

3
您可以使用rpath来进行动态链接。请参考“man ld”和“man ld.so”。 $ORIGIN扩展可能会很有用:将您需要的所有.so文件捆绑在程序相同的目录(或子目录)中,并在使用ld进行链接时使用“-rpath $ORIGIN”或“-rpath,'$ORIGIN/lib'”。
许多程序使用此方法捆绑其自己的私有库。
另一种方法是使用.sh脚本设置LD_LIBRARY_PATH,然后调用真正的程序(下载firefox二进制文件并查看run-mozilla.sh)。但是LD_LIBRARY_PATH会泄漏到子进程中。因此,它不太干净,但可能更适用于非GNU系统。

2

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

例如:

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

你可以将它与宏结合起来,将分配函数重定向到你的函数中,但我不建议这样做。你需要检查所有源代码是否包含此头文件,并且编译器可以根据使用的编译器和选项警告你有关宏重新定义的问题。
#define malloc(x) myProject_malloc(x)
#define free() myProject_free()

如果您不想使用标准库,应该使用gcc选项:“-nostdlib”。但是,如果这样做,您将无法使用标准库的任何其他函数。

我确实想使用标准库,但只想替换malloc/free函数。我正在阅读GCC文档,看是否有答案。 - yoco
然后,你应该定义自己的malloc函数并赋予不同的名称,如我所写,并使用宏重新定义malloc为你自己的函数。你不能称之为malloc,因为你将会有两个不同的malloc函数实现:一个在你的库中,另一个在libc中。 - Phong

1

如果您使用GNU libc,可以使用GNU malloc Hooks。但我对此API的设计不太满意,因此不建议使用。

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

使用相同的思路,您可以尝试获取libc.a,删除所有包含malloc和相关函数的.o文件(这应该是与malloc/*.c对应的大部分.o文件),并使用您的实现重新打包libc.a


谢谢。我也调查过这个解决方案。但是使用它需要一些额外的编码工作。所以对我来说,rpath解决方案更好:D - yoco

0
最可能的情况是你需要更改自定义分配例程的名称。之后,您应该使用答案中描述的方法节省时间,这样您就不必更改所有对新名称的调用:
#define malloc myMalloc
#define free myFree

谢谢您的回复:D 但是#define习语无法帮助。我已经编辑了我的帖子。 - yoco

0

如果您只需要针对C ++执行此操作,可以覆盖new,delete,new[],delete[]运算符。请参见18.6.1“存储分配和释放”

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

"

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

但我不知道它是否适用于静态链接(不知道它是如何实现的)。

"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接