Java Native Interface(JNI)是否受C++ ABI兼容性问题的影响?
我正在开发一个Java应用程序.我想使用Java Native Interface(JNI)来调用C++库中的函数.我可以访问C++库的代码,但我可以重建它,但我可能需要.(例如,我可以静态链接C++运行时.)
我可以要求我的用户拥有JRE 6或更高版本,但我不能要求他们拥有任何特定的C++运行时.
一位同事向我指出了这篇博客文章:http://www.trilithium.com/johan/2005/06/static-libstdc/,它建议不要使用动态加载的C++代码.
另一位同事向我指出了这个错误报告:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4694590,详细说明了如何在Java 1.4.2中解决这些问题.
根据我的理解,问题的要点是libstdc ++的二进制接口经常发生变化.如果C++应用程序加载了使用不同编译器构建的C++共享库,则会同时将两个不兼容的libstdc ++库加载到内存中.
错误报告解释了Java 1.4.2的解决方案:"我们静态链接JDK中的C++运行时并启用链接器脚本以隐藏libstdc ++和其他内部符号中的符号.结果,这些符号对JNI代码变得不可见,并且当某些符号时本机代码需要调用C++运行时,调用将使用适当的libstdc ++解析.所以.还有两个libstdc ++.所以同时加载,但它应该是良性的."
我有几个问题.
首先,OpenJDK是否继续采用这种方法?
[ 编辑:我在OpenJDK的build-dev邮件列表中问过这个问题.答案是肯定的,HotSpot仍然静态地链接libstdc ++,但显然"大多数Linux发行版补丁出来".另一位开发人员指出,这甚至不需要补丁:"设置STATIC_CXX = false应该足够了(默认为true)."]
其次,即使在这种情况下,拥有两个不兼容的libstdc ++是否真的是良性的.所以同时加载?
第三,这种方法(隐藏JDK中的符号)是否解决了所有兼容性问题?
上面引用的博客文章警告说"针对不同ABI编译的代码根本不是二进制兼容的".后来,"语言运行时支持通常依赖于一些共享数据,例如访问某种锁或全局数据结构(类似于C程序需要共享错误的方式)."
这使得听起来无法解决问题.
再说一次,也许ABI不兼容不再是问题.博客文章已有六年多了.另一个stackoverflow问题(GCC ABI兼容性)的答案断言"自从gcc-3.4.0起,ABI是向前兼容的".这是成功的吗?
我对这些问题的任何指导表示感谢.(嘿,谢谢阅读所有这些!)
EDITS
我的问题变得越来越长,所以我没有详细说明.解决威尔的评论:
我读到当程序进行函数调用时,被调用函数必须知道如何返回其调用者.
我的问题是:被调用函数如何知道如何返回其调用者?是否有通过编译器在幕后工作的机制?
所述X86-64 ABI指定两个返回寄存器:rax和rdx,在大小两个64位(8个字节).
假设x86-64是唯一的目标平台,这两个功能中的哪一个:
uint64_t f(uint64_t * const secondReturnValue) {
/* Calculate a and b. */
*secondReturnValue = b;
return a;
}
std::pair<uint64_t, uint64_t> g() {
/* Calculate a and b, same as in f() above. */
return { a, b };
}
Run Code Online (Sandbox Code Playgroud)
考虑到针对x86-64的C/C++编译器的当前状态,会产生更好的性能吗?使用一个版本或其他版本是否存在性能方面的任何陷阱?编译器(GCC,Clang)总是能够优化std::pair返回rax和rdx吗?
更新:通常,如果编译器优化std::pair方法(使用GCC 5.3.0和Clang 3.8.0的二进制输出示例),则返回一对更快.如果f()没有内联,编译器必须生成代码以将值写入内存,例如:
movq b, (%rdi)
movq a, %rax
retq
Run Code Online (Sandbox Code Playgroud)
但是如果g()编译器满足要求:
movq a, %rax …Run Code Online (Sandbox Code Playgroud) 在尝试优化x86_64上的返回值时,我注意到一件奇怪的事情.即,给出代码:
#include <cstdint>
#include <tuple>
#include <utility>
using namespace std;
constexpr uint64_t a = 1u;
constexpr uint64_t b = 2u;
pair<uint64_t, uint64_t> f() { return {a, b}; }
tuple<uint64_t, uint64_t> g() { return tuple<uint64_t, uint64_t>{a, b}; }
Run Code Online (Sandbox Code Playgroud)
Clang 3.8输出此汇编代码f:
movl $1, %eax
movl $2, %edx
retq
Run Code Online (Sandbox Code Playgroud)
这个用于g:
movl $2, %eax
movl $1, %edx
retq
Run Code Online (Sandbox Code Playgroud)
哪个看起来最佳.但是,使用GCC 6.1编译时,生成的程序集f与Clang输出相同,生成的程序集为g:
movq %rdi, %rax
movq $2, (%rdi)
movq $1, 8(%rdi)
ret
Run Code Online (Sandbox Code Playgroud)
看起来返回值的类型被GCC归类为MEMORY,而Clang归类为INTEGER.我可以确认将Clang代码与GCC代码链接这样的代码可能会导致分段错误(Clang调用GCC编译 …
我正在将一些 AArch64/ARM64/Apple Silicon 汇编代码从 Linux 移植到 macOS。
\n此代码使用所有 31 个可用寄存器(堆栈指针不算在内)来避免几乎所有溢出情况;Linux 调用约定允许我使用那么多寄存器。
\n如果迫不得已,我承认溢出一个额外的寄存器(从而将其减少到使用 30 个寄存器)是可行的,因为性能受到的影响最小,但如果限制为 29 个或更少的可用寄存器,性能将受到更大的影响。因此,我真的希望至少有 30 个可用寄存器,最好是 31 个。
\n我刚刚从 Apple 官方文档中了解到,保留了两个额外的寄存器,超出了 Linux 调用约定的要求:
\n\n\n尊重特定 CPU 寄存器的用途
\nARM 标准将某些决策委托给平台设计者。Apple 平台遵循以下选择:
\n\n
\n- 该平台保留寄存器x18。不要\xe2\x80\x99 使用该寄存器。
\n- 帧指针寄存器 (x29) 必须始终寻址有效的帧记录。某些函数 \xe2\x80\x94 例如叶函数或尾部调用 \xe2\x80\x94 可能会选择不在该列表中创建条目。因此,即使没有调试信息,堆栈跟踪也始终是有意义的。
\n
尽管有这些说法,我的代码在没有它的情况下似乎运行良好。
\n现在,我完全明白忽略此类 ABI 要求是一件非常糟糕的事情 (TM)。但是,我想确切地了解代码如何因使用 x18 和 x29 而中断。
\n例如,通过阅读上述文档,我的理解是 x29 是为了支持调试或故障转储。假设我不关心调试这个函数(实际上我不关心),或者任何生成的故障转储是否有意义。那么,使用x29有什么坏处吗?
\n至于x18,知道它有什么用吗?我假设(零支持证据)如果在该代码运行时执行中断或上下文切换,则不会保存 x18,因此一旦返回就会破坏我的函数的结果。这将是一个更严重的情况,我会听取建议,在这种情况下不要使用 x18。
\n另请注意,所讨论的代码是叶函数,因此破坏从其中调用的任何函数都没有问题。
\n我的库有两个类,一个基类和一个派生类.在库的当前版本中,基类具有虚函数foo(),派生类不会覆盖它.在下一个版本中,我希望派生类可以覆盖它.这会破坏ABI吗?我知道引入一个新的虚函数通常会,但这似乎是一个特例.我的直觉是它应该改变vtbl中的偏移量,而不是实际改变表的大小.
显然,由于C++标准没有强制要求特定的ABI,这个问题在某种程度上是特定于平台的,但实际上,在大多数编译器中,断开和维护ABI是类似的.我对GCC的行为感兴趣,但是人们可以回答的编译器越多,这个问题就越有用;)
c++ virtual-functions backwards-compatibility abi subclassing
在独立类中更改公共非虚拟非内联重载方法的顺序是否会破坏ABI?
之前:
class MyFinalClass
{
public:
// ...
void doSomething(char c, int i, int n);
void doSomething(char c, int i);
// ...
};
Run Code Online (Sandbox Code Playgroud)
后:
class MyFinalClass
{
public:
// ...
void doSomething(char c, int i);
void doSomething(char c, int i, int n);
// ...
};
Run Code Online (Sandbox Code Playgroud)
谢谢!
如何在没有源代码的情况下使用 Remix 与以太坊上已部署的合约(不是我的合约)进行交互,但我有 ABI。
我想要这样做的原因是因为某些合同有超过 20 个 .sol 文件,我不想手动复制并粘贴到 Remix 中。
abi ×10
c++ ×6
assembly ×2
c ×2
x86-64 ×2
arm64 ×1
compiler-bug ×1
ethereum ×1
gcc ×1
java ×1
libstdc++ ×1
macos ×1
performance ×1
printf ×1
remix ×1
stack-frame ×1
subclassing ×1
tuples ×1
variadic ×1