发布版和调试版之间的ABI兼容性

5
当我使用GCC编译相同的库时,有时会以release模式编译,有时又以debug模式编译,这样ABI兼容性是否得到保证?(在使用同一编译器的情况下)
我有一个可执行文件和一些共享对象(其中一些依赖于其他对象),我想能够替换release/debug共享对象而无需重新编译所有内容,只需重新编译感兴趣的共享对象即可。
这是可能的吗?还是说某些情况下我可能会出现未定义的行为?(假设我的代码在release和debug模式下都严格打包和填充)
编辑:
我将详细说明我们遇到的问题。我们有一个自定义版本的intrusive_ptr,在debug模式下,我们有自己的intrusive_ptr,它只有一个成员是boost::intrusive_ptr,在release中,我们仅使用boost::intrusive_ptr。我们的intrusive_ptr的API与boost::intrusive_ptr相同,并且该类中没有任何虚函数。我们看到的情况是:
如果我们使用所有debug libs或所有release libs,则一切正常。如果我们混合使用debug可执行文件和release libs,则会从intrusive_ptr中出现内存泄漏,它不会释放对象。
我们的intrusive_ptr和boost::intrusive_ptr的sizeof在debug和release中都是相同的(我们的类不增加任何额外的大小开销)。
所以我想知道可能会导致泄漏的原因,ABI差异是我想到的唯一的事情。
有什么想法吗?

3
在GCC领域中,“调试”和“发布”这些术语并没有很好的定义,这往往是IDE的一个功能。如果您的“调试”构建使用带有调试模式的libstdc++,那么安全迭代器功能将改变集合对象的布局。混合使用会导致随机内存损坏。 - Hans Passant
实际上,我已经检查过了,这段代码无法使用libstdc++编译。 - Max Shifrin
@MaxShifrin,你找出问题的原因了吗? - Felipe Lavratti
是的,在我们的情况下,ABI 不保证兼容性,我记得这是对象结构中的一些额外填充。因此,我们无法使用指针技巧来解决这个问题,不得不围绕这个问题进行设计。 - Max Shifrin
2个回答

1
通常来说,不会有区别,因为发布版本和调试版本的通常区别只是为了使调试步骤更加流畅的选项。但是如果在发布和调试版本之间定义其他选项(例如整数大小、目标架构等),可能会出现意外行为,因为调用方参数可能与被调用方期望不符。还可能存在异常安全性和const-ness问题,而运行时链接器可能无法检查。

我们显然在两个编译版本中使用相同的64位架构,对于我们使用的变量,整数变量大小在两个版本中也是相同的。 - Max Shifrin

1
我知道一些编译器会为发布版和调试版生成不兼容的代码(尽管这些编译器早已被淘汰)。事实上,除非它们使用完全相同的标志编译,否则我不会相信目标模块是完全兼容的。
这就是为什么像Eclipse这样的IDE和遵循GNU原则的makefile将发布/调试/性能对象构建到不同的目录中。以确保它们永远不会混淆。

我们通常在发布和调试之间使用相同的标志,主要更改优化级别并从发布版本中剥离调试符号。据我所知,所有其他选项都是相同的。显然,我们通常会构建成两个单独的目录,但在某些情况下,您可能希望混合调试和发布对象。例如,您在发布系统后想要调试给定模块中的某些特定功能,则理论上应该能够仅替换该模块为调试版本以进行调试(在开发期间)。 - Max Shifrin
1
@MaxShifrin:很少有编译器能够保证目标文件的兼容性。 - Martin York
为什么只有少数编译器提供这种保证?我期望除非你刻意搞砸编译过程,否则所有的编译器都应该始终提供这种保证。毕竟,目标文件所公开的是函数/类的地址——也就是它们的接口。无论我使用什么优化级别,这个接口都不应该在发布和调试版本之间有任何区别。据我所知,实现的内部细节是无关紧要的。我错在哪里? - Max Shifrin
1
打开一些优化选项将消除对象中的填充(以节省空间),从而留下ABI破坏,链接器将无法发现。这是我能想到的第一个,不需要太多就可以破坏ABI。 - Martin York
1
这也是为什么你会看到公司在发布库时会提供两个版本的原因。一个是调试版本,一个是发布版本(如果他们还提供库的静态和动态版本,则有四个版本)。 - Martin York
2
一些GCC编译选项会导致编译器生成的代码不符合平台的默认ABI。其他选项会导致实现定义特性的不同程序行为,这些特性未被ABI覆盖。 - Martin York

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