使用共享库和不匹配的编译器
Using shared libraries and mismatched compilers
使用与您的程序不同的编译器版本编译的共享库引入问题的可能性有多大?
如果您的程序使用的语言标准与其不同怎么办?
即,如果我 link 在使用 gcc-6、c++14 编译我的代码时提升使用 gcc-4.8、c++11 编译的库,这会不会是个问题?
简短的回答,接近 100% 的人会遇到一些问题,除非库在设计时就考虑到了这一点。 Boost 在设计时根本没有考虑到这一点,它不会起作用。
长答案,它可能在某些非常特定的情况下有效(虽然不是为了提升)。有 2 件主要的事情在起作用:
- ABI 兼容性
- Sub-library 兼容性/内联代码
ABI 是比较简单的部分。如果例如编译器 A 对名称的处理方式与编译器 B 不同,它甚至不会 link。或者,如果它们有不同的调用约定(例如,参数如何通过 registers/stack 等传递),那么它可能 link 但它根本不起作用/以非常明显的方式崩溃。此外,同一平台上的大多数编译器都具有相同的调用约定(或者可以适当配置),所以这应该不是一个大问题。
Sub-library 兼容性和内联代码比较棘手。例如,假设您有一个传递已分配 object 的库,解除分配是客户的工作。如果库的分配器与客户端的分配器工作方式不同,那么这将导致问题(例如,库使用编译器 A 的 new
,而主程序使用编译器 B 的 delete
)。
或者 headers 中可能有代码(例如内联方法)。这两个编译器可能会以不同的方式编译它们,这会导致问题。
或者图书馆可能 return 一个 std::vector
。编译器 A 的 vector
的实现可能与编译器 B 的 vector
不同,所以这也不起作用。
或者可能传递了一个结构或 class。两个编译器可能 pack/pad 它们的方式不同,因此它们在内存中的布局方式不同,事情会崩溃。
因此,如果库在设计时考虑到这一点,那么它可能会起作用。一般来说,这意味着:
- 所有调用都必须
extern C
以避免名称混淆。
- 不传递任何外部定义的结构(例如 STL)
- 即使
structs
也可能导致问题,除非 packing/padding 在两个编译器中相同
- 图书馆分配的一切也必须由图书馆释放
- 不抛出异常(
extern C
隐含)
- 可能还有一些我现在忘记了
如果 ABI(和 API)相同,则可以正常工作,根据 gcc.gnu.org 的 ABI Policy and Guidelines、"given an application compiled with a given compiler ABI and library API, it will work correctly with a Standard C++ Library created with the same constraints."
使用不同的 ABI 编译的共享库可以 工作,但有些情况需要注意,因为它们可能会导致难以检测的重大错误。
gcc-4.8 和 gcc-6 具有不同的 ABI(应用程序二进制接口),因此在非常特殊的情况下可能会输出不同的编译代码,并导致应用程序崩溃。
但是,“GNU C++ 编译器 g++ 有一个编译器 command-line 选项可以在各种不同的 C++ ABI 之间切换。”(根据 ABI 政策和指南.)
您可以从 gcc.gnu.org 阅读有关特定 ABI 问题的更多详细信息:
使用与您的程序不同的编译器版本编译的共享库引入问题的可能性有多大?
如果您的程序使用的语言标准与其不同怎么办?
即,如果我 link 在使用 gcc-6、c++14 编译我的代码时提升使用 gcc-4.8、c++11 编译的库,这会不会是个问题?
简短的回答,接近 100% 的人会遇到一些问题,除非库在设计时就考虑到了这一点。 Boost 在设计时根本没有考虑到这一点,它不会起作用。
长答案,它可能在某些非常特定的情况下有效(虽然不是为了提升)。有 2 件主要的事情在起作用:
- ABI 兼容性
- Sub-library 兼容性/内联代码
ABI 是比较简单的部分。如果例如编译器 A 对名称的处理方式与编译器 B 不同,它甚至不会 link。或者,如果它们有不同的调用约定(例如,参数如何通过 registers/stack 等传递),那么它可能 link 但它根本不起作用/以非常明显的方式崩溃。此外,同一平台上的大多数编译器都具有相同的调用约定(或者可以适当配置),所以这应该不是一个大问题。
Sub-library 兼容性和内联代码比较棘手。例如,假设您有一个传递已分配 object 的库,解除分配是客户的工作。如果库的分配器与客户端的分配器工作方式不同,那么这将导致问题(例如,库使用编译器 A 的 new
,而主程序使用编译器 B 的 delete
)。
或者 headers 中可能有代码(例如内联方法)。这两个编译器可能会以不同的方式编译它们,这会导致问题。
或者图书馆可能 return 一个 std::vector
。编译器 A 的 vector
的实现可能与编译器 B 的 vector
不同,所以这也不起作用。
或者可能传递了一个结构或 class。两个编译器可能 pack/pad 它们的方式不同,因此它们在内存中的布局方式不同,事情会崩溃。
因此,如果库在设计时考虑到这一点,那么它可能会起作用。一般来说,这意味着:
- 所有调用都必须
extern C
以避免名称混淆。 - 不传递任何外部定义的结构(例如 STL)
- 即使
structs
也可能导致问题,除非 packing/padding 在两个编译器中相同 - 图书馆分配的一切也必须由图书馆释放
- 不抛出异常(
extern C
隐含) - 可能还有一些我现在忘记了
如果 ABI(和 API)相同,则可以正常工作,根据 gcc.gnu.org 的 ABI Policy and Guidelines、"given an application compiled with a given compiler ABI and library API, it will work correctly with a Standard C++ Library created with the same constraints."
使用不同的 ABI 编译的共享库可以 工作,但有些情况需要注意,因为它们可能会导致难以检测的重大错误。
gcc-4.8 和 gcc-6 具有不同的 ABI(应用程序二进制接口),因此在非常特殊的情况下可能会输出不同的编译代码,并导致应用程序崩溃。
但是,“GNU C++ 编译器 g++ 有一个编译器 command-line 选项可以在各种不同的 C++ ABI 之间切换。”(根据 ABI 政策和指南.)
您可以从 gcc.gnu.org 阅读有关特定 ABI 问题的更多详细信息: