【问题标题】:Can ODR violation be avoided by using hidden visibility?是否可以通过使用隐藏可见性来避免 ODR 违规?
【发布时间】:2021-12-30 06:56:50
【问题描述】:

例如,我的一个项目中有一个稍微复杂的库依赖案例:


              /--------------------------------\
              |                                |
      /----> GRPC <------------------\         |
      |                              |         |
      |        (c++)                 |         |        
      \-------   A  -------------->  B         |
                 |                 (rust)      |
                 |                             |
                 \------------------> c++  <---/ 

Rust 默认更喜欢使用静态链接。可执行文件 A 也被构建为静态链接 lib(std)c++。因此,据我了解,A 和 B 中都会有两个 STL 实现副本。这正是https://developer.android.com/ndk/guides/cpp-support#sr 建议避免的模式。

但是,查看B的动态链接表(例如通过nm -D);我看不到导出的 lib(std)c++/grpc 符号。这是因为默认情况下 rust 会将它们标记为隐藏。

  • 那么,如果 B 中的所有常用符号都被隐藏,是否安全(或符合 ODR)?

【问题讨论】:

  • B 和 A 是如何关联的?
  • B 是共享库。
  • 个人建议动态链接libstdc++。它并不大(0.5MB 未压缩,在我的 Rust + C++ + Android 项目中)
  • 共享链接阻止 PLT/GOT 被优化。这是团队的担忧之一。由于我们要迁移到 LLVM,另一个问题是我们可能需要将 libc++.so 与我们的发行版一起发布。

标签: c++ rust linker shared-libraries one-definition-rule


【解决方案1】:

符合 ODR

单一定义规则是C++编程语言的一部分。它与 C++ 语言有关。这与其他任何事情都无关。 C++ 没有“外部”的 ODR。 C++ 标准不适用于 C++ 之外,它只适用于 C++ 编程语言。

没有广泛采用的关于语言互操作性的可移植超级标准。这些只是工具,没有定义和规则。 ODR 或 C++ 中的任何其他规则不适用于此处。

试图将 C++ 标准规则应用于不相关的上下文是没有意义的。 Rust 不是 C++ 的一部分。 RPC 是特定于平台的,超出了 C++ 编程语言的范围。

因此,推断某些 C++ 规则在使用平台特定工具(共享库、“动态链接表”)和使用多种编程语言的工作链中会或不会被破坏,但在这里并不适用。

在 C++ 的意义上,这一切实际上都是“未定义的行为”——C++ 标准中没有任何规则可以适用于此。

是否可以通过使用隐藏可见性来避免 ODR 违规?

当然。

【讨论】:

  • 非常感谢您的回复。但是这里对 ODR 的关注急剧上升,因为涉及到 c++。 C++ 可执行文件 (A) 和 rust 共享库 (B) 都包含一个静态链接的 lib(std)c++.a。凉爽的。如果隐藏所有符号将按照您在回复的最后部分中所述工作。我也保持静态链接!再次感谢。
【解决方案2】:

android 文档说:

在这种情况下,STL,包括全局数据和静态构造函数,将出现在两个库中。这个应用程序的运行时行为是未定义的,在实践中崩溃是很常见的。其他可能的问题包括:

在一个库中分配内存,在另一个库中释放,导致内存泄漏或堆损坏。

你提到那些符号是隐藏的。但是,是否等于,全局数据呈现两次?

如果全局数据可以呈现两次,则可能会出现“内存分配在一个库中,而在另一个库中释放,导致内存泄漏或堆损坏”的情况。例如,如果在 Rust 中分配了一些内存,我们将其转移到 C++,然后 C++ 将其释放,那么我们将面临这种情况。当然你的代码可能没有这种情况,但是如果以后有代码违反了,或者如果有一天你使用了违反它的第三方库,那么我们就有麻烦了(而且似乎很难调试)。

确实,让 C++静态链接 Rust 代码怎么样?然后你会得到一个巨大的 .so 文件,其中包含你的 C++、Rust、依赖项等。然而,即使你能做到,我们可能仍然需要小心:巨型 .so 文件是否只有 在您的整个 Android 应用程序中使用一个?换句话说,你确定你没有,也永远不会有任何其他原生库吗?如果没有,恕我直言,我们可能仍然会再次面临这个问题。

无论如何,我不是 Android/C++ 方面的专家。几个月前我遇到了类似的问题(将“C++ 代码”替换为“用 C++ 编写的 Flutter 引擎”等等)并做了一些解决方法。所以这篇文章并不是真正的答案,而是一些(可能是错误的)想法和建议。希望有大神指正!

【讨论】:

    猜你喜欢
    • 2020-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-10
    • 2011-05-01
    • 2018-09-28
    • 2018-10-04
    • 1970-01-01
    相关资源
    最近更新 更多