我的库有两个类,一个基类和一个派生类.在库的当前版本中,基类具有虚函数foo(),派生类不会覆盖它.在下一个版本中,我希望派生类可以覆盖它.这会破坏ABI吗?我知道引入一个新的虚函数通常会,但这似乎是一个特例.我的直觉是它应该改变vtbl中的偏移量,而不是实际改变表的大小.
显然,由于C++标准没有强制要求特定的ABI,这个问题在某种程度上是特定于平台的,但实际上,在大多数编译器中,断开和维护ABI是类似的.我对GCC的行为感兴趣,但是人们可以回答的编译器越多,这个问题就越有用;)
c++ virtual-functions backwards-compatibility abi subclassing
我有一个用LLVM编写的编译器,我正在寻求提高我的ABI合规性.例如,我发现很难在Windows x86或Linux上找到C ABI的规范文档.我发现的那些用RAX/EAX /等解释它,而不是我可以使用的IR术语.
到目前为止,我认为我已经认为LLVM无形地对待聚合 - 也就是说,它将每个成员视为一个独特的参数.因此,例如,在Windows x64上,如果我想处理像文档所说的聚合,我将需要强制转换为该大小的单个整数,如8,16,32或64位.否则,通过指针传递.
对于Windows x86,似乎__cdecl和__stdcall不需要我的任何操作,因为所有参数都在堆栈上传递.__fastcall说前两个32位或更小的参数是寄存器传递的,所以我需要强制大小或更小的聚合.__thiscall在寄存器中传递它,其余的在堆栈中传递,所以看起来我不需要在这里执行任何调整.
对于__vectorcall,通过整数强制传递不超过sizeof(void*)的聚合.对于其他聚合,如果它们是HVAs,则按值传递; else在x86上传递值或在x64上传递指针.
这似乎很简单(相对而言),但是LLVM文档sext清楚地表明"这表明代码生成器应该将参数或返回值符号扩展到目标的ABI所需的范围(通常是32位)由调用者(对于参数)或被调用者(对于返回值)." x86调用约定的Microsoft页面没有提及任何扩展到任何宽度的内容.
我观察到Clang生成的LLVM IR byval在Windows上生成属性.我从上面收集到的理解从未要求byval使用.
如何将各种平台C ABI降低到LLVM IR?
对于32位Windows应用程序,使用ESP下面的堆栈内存进行临时交换空间而不显式减少ESP是有效的吗?
考虑一个返回浮点值的函数ST(0).如果我们的价值目前在EAX,我们会,例如,
PUSH EAX
FLD [ESP]
ADD ESP,4 // or POP EAX, etc
// return...
Run Code Online (Sandbox Code Playgroud)
或者不修改ESP寄存器,我们可以:
MOV [ESP-4], EAX
FLD [ESP-4]
// return...
Run Code Online (Sandbox Code Playgroud)
在这两种情况下都会发生同样的事情,除了在第一种情况下我们注意在使用内存之前递减堆栈指针,然后在之后递增它.在后一种情况下,我们没有.
尽管有任何实际需要在堆栈上持久保存这个值(重入问题,在PUSH返回值和读取值之间的函数调用等),有没有任何根本原因,为什么写入ESP下面的堆栈这样会无效?
它是否意味着保证相同的std::type_info::hash_code()值意味着相同的类型?
Cplusplus.com似乎声称:
此函数为任何两个比较相等的type_info对象返回相同的值,而不同的值则返回不同的值.[强调我的]
Cppreference似乎另有说法:
返回一个未指定的值,对于对象,它引用相同的类型.没有给出其他保证,特别是,值可以在同一程序的调用之间改变.[强调我的]
相关标准段落是:
§p18.7.1p7-8
size_t hash_code()const noexcept;
7返回:一个未指定的值,除了在程序的单次执行中,它应为任何两个比较相等的type_info对象返回相同的值.
8备注:实现应为两个不比较相等的type_info对象返回不同的值.[强调我的]
" 应该 "应该在上面的语境中是什么意思?如果段落8是一个要求,那么似乎不可能实现,除非运行时对程序中的所有符号名称进行某种全局统一以确保缺少哈希冲突,这似乎是标准的一个相当大的负担强加于实现,特别是对于一个被调用的函数hash_code().(Itanium实际上需要这个,但它显然是高于标准的额外要求.)
如果" 应该 "并不意味着具有约束力,那么这句话似乎是毫无意义的,也就是标准中的缺陷,因为要求实现尝试满足一个无法依赖的困难要求,不会产生任何价值,只会引起混淆和碎片化.任何人都知道为什么会这样吗?
编辑:也许"缺陷"太强了,但至少它是一个可能混淆的点,应该澄清,因为它显然误导了至少一个参考网站,并传递误导任何依赖它的人.此外,它实际上是可能实现的要求(只要受实现支持的类型的数量比范围更小size_t),如果全球uniquing是在运行时完成,如果标准是试图表明这为目前还不清楚理想的实施策略与否.
的x86_64的SysV的ABI的函数调用约定定义整数参数#4中的传递rcx寄存器.另一方面,Linux内核系统调用ABI r10用于同样的目的.所有其他参数都在函数和系统调用的相同寄存器中传递.
这会导致一些奇怪的事情.例如,检查mmapx32平台的glibc实现(存在相同的差异):
00432ce0 <__mmap>:
432ce0: 49 89 ca mov %rcx,%r10
432ce3: b8 09 00 00 40 mov $0x40000009,%eax
432ce8: 0f 05 syscall
Run Code Online (Sandbox Code Playgroud)
因此,所有的寄存器都已经到位,但我们移动rcx到r10.
我想知道为什么不将syscall ABI定义为与函数调用ABI相同,考虑到它们已经非常相似.
我想使用android studio与ndkbuild集成.
我的"本机"部分只为armeabi-v7a-hard和x86构建,如果我只是ndk-build在jni目录中运行,那么一切正常.我有适当的路线Application.mk:
APP_ABI := armeabi-v7a-hard x86
Run Code Online (Sandbox Code Playgroud)
为了将项目集成到android studio中,我将这些行添加到build.gradle:
externalNativeBuild {
ndkBuild {
path 'src/lib/jni/Android.mk'
}
}
Run Code Online (Sandbox Code Playgroud)
但由于某些原因gradle build尝试使用APP_ABI=armeabi和构建本机代码,因为我的代码只能构建armeabi-v7a-hard.
所以,我怎么能告诉gradle这个建立我的代码只为armeabi-v7a-hard和x86,或者只是不能忽视APP_ABI来自行Application.mk?
我尝试这样的变种:
defaultConfig {
ndk {
abiFilters 'x86', 'armeabi-v7a-hard'
}
}
Run Code Online (Sandbox Code Playgroud)
但是gradle失败了这样的消息:
ABI [armeabi-v7a-hard]不适用于平台,不包括在建筑和包装中.可用的ABI是[armeabi,armeabi-v7a,arm64-v8a,x86,x86_64,mips,mips64].
请注意,我用NDK 10,没有持续一(NDK 13),那里有armeabi-v7a-hard,并且ndk.dir在local.properties以正确的价值.
我需要部署到Red Hat 4.1.2框(具有gcc 4.1.2).我在Ubuntu 11.10上使用GCC 4.6.1进行开发.不幸的是,我的构建过程创建的一些二进制文件在RedHat机器上不可用.原因似乎是ABI更改,根据另一个Stackoverflow问题导致STT_GNU_IFUNC符号的引入.有没有办法阻止导出任何这样的符号,以便我的二进制文件可以使用旧的ABI?我使用nm在我的二进制文件中查找"i"类型的任何符号,但没有找到.
我问这个,因为我的一些其他的二进制文件,以及一些第三方库我建(TBB,升压),不使用新的ABI等运行RedHat的机器上的罚款.
希望很清楚.提前致谢.
我最近花了相当多的时间来追踪一个问题,原因是编译了一个库-D_GLIBCXX_DEBUG(告诉libstdc ++使用带有额外检查的标准库的调试版本),但没有编译客户端程序.这导致了ABI兼容性问题.
有什么方法可以通过GCC自动检测这样的问题吗?Visual Studio提供了我认为可以用于此目的的detect_mismatch编译指示,但我不知道任何GCC等价物.GCC通过嵌入符号名称(例如GLIBCXX_3.4.9)做了一些事情,我可以想象如果相应的符号(例如mylib_debug_stl)不存在则会因为未定义的符号而导致链接错误的方案,但是我能想到的唯一方法是使用那个符号真是太烂了.
或者,其他人如何避免这个问题?将库的已检查版本构建为不同的名称或类似的名称?
Ulrich Drepper关于线程本地存储的论文概述了几种不同cpu架构的TLS ABI,但我发现它不足以作为实现TLS的基础,原因有两个:
例如,i386唯一的实际ABI要求是:
%gs:0 指向指向自身的指针.___tls_get_addr并且__tls_get_addr函数必须以正确的语义存在,以便查找任意TLS段.特别是,DTV的存在或布局不是 ABI的一部分,也不是主程序之外的TLS段的排序/布局.
似乎任何使用"TLS变体II"的拱门都具有大致上述ABI要求.但我完全不了解"TLS变体I"的要求,而且从阅读来源(在uClibc和glibc中)看来,甚至可能存在"变体I"的几种变体.
有没有更好的文件我应该看一下,或者熟悉TLS工作的人能向我解释ABI的要求吗?
我有一些编译ARM cortex-m设备的裸机代码以及Linux核心,uBoot和Beaglebone Black(BBB)的应用程序的经验(更多特色ARM与MMU,生活在摇滚之下).从那以后,我认为cortex-m代码应该使用arm-none-eabi-gcc编译(因为没有操作系统),BBB的应用程序代码应该用arm-linux-gnueabi-gcc编译(因为那里)是一个操作系统,可以进行系统调用,程序加载器和可以使用的共享对象.
我不明白为什么uBoot和内核也应该用arm-linux-gnueabi-gcc编译.在我看来,uBoot至少是一个裸机程序,没有花哨的操作系统来解释.这一直困扰着我,但我找不到答案.那里有没有人可以开导我?
abi ×10
c ×3
gcc ×3
linux ×3
assembly ×2
c++ ×2
android ×1
android-ndk ×1
arm ×1
debugging ×1
elf ×1
g++ ×1
glibc ×1
linux-kernel ×1
llvm ×1
llvm-codegen ×1
rtti ×1
subclassing ×1
system-calls ×1
u-boot ×1
windows ×1
x86 ×1
x86-64 ×1