我有这样的代码如下
try {
doSomething();
} catch(InterruptException) {
goto rewind_code;
}
if(0) {
rewind_code:
longjmp(savepoint, 1);
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,当我goto离开catch块时,C++运行时存储的异常对象是免费的吗?或者运行时是否允许缓存它,直到周围的函数存在或类似的东西?我只是想确保如果我多次执行上面的代码,每次采用倒带代码,我都不会泄漏内存(因为longjmp不会执行编译器在函数序列之前或之前发出的清理代码).
我有两个场景.假设我有3个导出C++符号的共享库,每个库都使用VS7.1,VS8和VS9构建.我在VS9中编译了所有3个.出于某种原因,这是有效的.我不需要在VS9中为VS9链接器重新编译前两个库,以成功找到符号并链接它们.
现在,如果我有一个只使用C语法(extern"C")导出符号的库,这是一样的吗?我听说有人说ABI for C是标准化的,所以在某种程度上保证你可以在所有版本的Visual Studio中使用Visual Studio 8中编译的C库.
基本上,所有这些事情的结合令人困惑.我不确定在不同版本的Visual Studio之间链接C++和基于C的共享库(使用相应的导入库)之间有什么保证.我想听听关于任何其他版本的Visual Studio上C 和 C++导入或静态库的向前/向后兼容性的一致意见.
这对我来说是因为我使用的闭源库是在Visual Studio .NET 2003(VS7.1)中编译的.我的团队认为这将我们锁定到VS 7.1编译器,但是我已经在VS8和VS9中测试了这些库,甚至是VS2010,它们链接得很好.但是,我不确定这个内在的危险.请注意,所讨论的库具有C变体和C++变体.基本上,C变量是标准C导出,C++库是C库和导出类的抽象.
当C++标准化委员会调查STL的修改时,要特别注意不引入ABI破坏性变化.
是什么导致ABI破坏以及什么不引入C++中的ABI破坏?((欢迎关注课程或文件的链接)
我正在尝试使用以下代码在我的gradle中进行abi分割
splits {
abi {
enable true
reset()
include 'x86', 'armeabi-v7a'/*, 'arm64-v8a', 'x86_64'*/
universalApk true
}
}
def abiVersionCodes = ['armeabi-v7a': 1, 'x86': 2, /*'arm64-v8a': 2,'x86_64': 4*/]
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
def filter = output.getFilter(OutputFile.ABI)
if (filter != null) {
output.versionCodeOverride = abiVersionCodes.get(output.getFilter(OutputFile.ABI)) * 1000000 + android.defaultConfig.versionCode
}
}
}
Run Code Online (Sandbox Code Playgroud)
它正在生成正确命名的输出,当我使用apk分析器时,在lib目录下,armeabi-v7a apk将只包含一个lib/armeabi-v7a目录,同样适用于x86 apk.但是,armeabi-v7a还包含一个armeabi和x86文件夹,只有一个META-INF文件夹只包含一个MANIFEST.MF文件,不是这样.
将这些多个apks上传到开发控制台时,每个拆分apk都列在
区分apk详细信息:原生平台无(+ 3常见)
和通用apk有
本机平台arm64-v8a,x86_64(+ 3常见)控制台上的arm和通用apk错误是
"完全投影APK PROBLEM此APK不会被提供给任何用户,因为它完全被一个或多个具有更高版本代码的APK所遮蔽.解决方案从您的版本中删除此APK或查看您所包含的APK的定位和版本代码在这个版本中."
当我添加包装选项时
packagingOptions {
exclude '**/x86/**'
}
Run Code Online (Sandbox Code Playgroud)
然后生成的apks,split和universal,将不支持x86.但是,我没有看到为每个应用程序变体指定packagingOptions的方法,因此这不是一个可行的解决方案,并且它显然不是正确的方法,即使它是.问题似乎是apk被认为是支持x86只是因为它有一个x86目录,即使它大部分都是空的,并且这导致两个分裂apx通告他们支持完全相同的体系结构,当他们不支持时. …
我很好奇,如果标记现有的派生C++类final以允许去虚拟化优化将在使用C++ 11时改变ABI.我的期望是,它应该有没有影响,因为我认为这主要是提示编译器有关它如何能够优化虚函数,因此我看不到任何方式,它会改变结构或V表的大小,但也许我错过了什么?
我知道这里更改API,以便从这个派生类进一步派生的代码将不再起作用,但我只关注这个特殊情况下的ABI.
对于不修复 C++ 的某些问题的常见解释是,它会破坏 ABI 并需要重新编译,但另一方面,我遇到这样的语句:
老实说,这对于几乎所有 C++ 非 POD 类型都是如此,而不仅仅是例外。可以跨库边界使用 C++ 对象,但通常前提是所有代码都使用相同的工具和标准库进行编译和链接。这就是为什么 MSVC 的所有主要版本都有 boost 二进制文件。
(来自这个SO答案)
那么C++有稳定的ABI吗?
如果是这样,我可以混合和匹配在同一平台上使用不同工具集编译的可执行文件和库(例如 Windows 上的 VC++ 和 GCC)吗?如果没有,有什么办法可以做到吗?
更重要的是,如果 C++ 中没有稳定的 ABI,为什么人们如此担心破坏它?
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
我的问题变得越来越长,所以我没有详细说明.解决威尔的评论:
是否有任何非奇特的架构/ OS /编译器的变化:
void func(void *, int, int)
Run Code Online (Sandbox Code Playgroud)
至:
int func(void *, int, int)
Run Code Online (Sandbox Code Playgroud)
打破ABI?(即,使用"int"版本运行时,为"void"版本的共享库编译的程序会中断)
在尝试优化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