C++ 如何捕获x86上的数据对齐错误(Sparc上的又称SIGBUS)

C++ 如何捕获x86上的数据对齐错误(Sparc上的又称SIGBUS),c++,c,gcc,g++,C++,C,Gcc,G++,即使在i386上,也有可能捕捉到数据对齐错误吗?可能通过设置i386特定的机器寄存器或类似的方式 在Solaris Sparc上,在本例中,我收到了一个SIGBUS,但在i386上,一切正常 环境: 32位应用程序 因果报应 gcc/g++v4.4.1 编辑: 这就是为什么我要问这个问题: 我们的应用程序在带有SIGBUS的Sol Sparc上崩溃。为了进行调试,我将尝试在i386平台上获得类似的行为 我们的Sol sparc机器非常慢,因此编译和调试需要花费大量时间。我们的i386机器速

即使在i386上,也有可能捕捉到数据对齐错误吗?可能通过设置i386特定的机器寄存器或类似的方式

在Solaris Sparc上,在本例中,我收到了一个SIGBUS,但在i386上,一切正常

环境:

  • 32位应用程序
  • 因果报应
  • gcc/g++v4.4.1
编辑: 这就是为什么我要问这个问题:

  • 我们的应用程序在带有SIGBUS的Sol Sparc上崩溃。为了进行调试,我将尝试在i386平台上获得类似的行为
  • 我们的Sol sparc机器非常慢,因此编译和调试需要花费大量时间。我们的i386机器速度惊人(8核,32G内存)
  • 即使在i386平台上,数据对齐故障也会带来性能损失。因此,我希望尽可能修复数据对齐错误

英特尔从一开始就内置了未对齐的传输,这是x86推出时的卖点之一。我理解您想要捕获未对齐访问的原因,但我认为这是不可能的


编辑:很高兴被证明是错的。

与此同时,我发现了一份有关此主题的英特尔CPU文档

把这些东西放在一起似乎很难。然而,这听起来并不是完全不可能的。有趣的一章是4.10.5检查对齐

编辑(上述文件中的一些浓缩材料):

第5-60页

Interrupt 17 Alignment Check Exception (#AC)

to enable alignment checking, the following conditions must be true:

AM flag is set(bit 18 of control regisster CR0)
AC flag is set (bit 18 of the EFLAGS)
The CPL is 3 (protected mode or virtual-8086 mode).
此外,14.8.2.6中提到了内存控制器错误。我不知道是否相同,只是换言之:

table 14-11, Encoding of MMM and CCCC Sub-Fields
Address/Command Error  AC  011

我找到了一个非常简单的SOF解决方案!请参阅:

int main(int argc,char**argv)
{
#如果定义为i386
/*编辑:启用交流检查*/
asm(“pushf

“orl$(1要扩展Vokuhila Oliba的答案,请查看“”线程,gcc似乎可以生成错误对齐内存访问的代码。好吧,您对此没有任何控制权。”

在gcc编译的代码上启用对齐检查是个坏主意。好的C代码可能会出现SIGBUS错误


重新编辑:很抱歉,

英特尔非常支持未对齐的加载。如果我必须在英特尔平台上检测此类加载,我想我必须修改以将未对齐的加载视为错误。这样的修改并非微不足道,但valgrind的设计理念是用户可以创建新的“工具”。我认为一个简单的操作修改
memcheck
工具将检测到未对齐的引用。错误报告非常好。

多年后:如果您的gcc/clang足够新(gcc 4.9,clang 3.3?),您可能可以使用未定义的行为净化剂构建代码(
-fsanize=undefined
)要获取关于给定平台上未对齐访问的警告(但请记住,不同的平台有不同的对齐要求,不同的编译器将选择不同的布局等)。有关详细信息,请参阅和。

不幸的是,用户模式代码都在环3中运行;以上不会生成异常。我对这些CPU“环”缺乏了解“。但是,如果一个普通程序使用ring-3,那就足够了。对不起,我之前读错了;“处理器在特权级别0、1或2下运行时不会生成对齐异常”肯定会清除环3。太糟糕了,CR0不能在用户模式下修改。如果你想在CR0中设置AM标志,你需要让你的操作系统配合。听起来像是在
qemu
中运行测试(可以针对SPARC)可能比在实际硬件上运行要快?我从来没有尝试过qemu,但听起来很有趣。没有某种“系统ROM”它能工作吗或者类似的东西?QEMU项目捆绑了
openbios sparc
,这足以让
QEMU系统sparc
像一台真正的机器。还有
QEMU sparc
,它在模拟下只运行一个Linux可执行文件,将系统调用转换为本机内核。请注意
QEMU用户-*
(这让您“只需要”为不同的体系结构运行可执行文件,而不是模拟整个VM的
qemu系统-*
)不模拟/发送在目标体系结构上发生的内存对齐错误。@alexandre:我想得到这些SIGBUS错误。当然不是在生产代码中,只是为了调试。在SPARC上工作的代码可能会有SIGBUS的风险。似乎x86 gcc在变量初始化之类的地方会进行错误对齐的访问,我不确定您是否可以改变这种行为。@alexandre:你是对的!我自己的第二个答案说明了如何在i386上检查对齐失败。但是这段代码在Sparc上运行得非常好。在i386上它接收SIGBUS。i386的GCCs行为可以通过使用例如属性(aligned(sizeof(char*))来改变。但是,通过设置全局GCC标志来全局执行此操作是不可能的。只能对数据项进行单独的属性设置。这对于我正在处理的项目来说太费劲了:-(@Vokuhila Oliba即使您修复了gcc或使用了另一个编译器,您也必须重新编译库或禁用代码中每个库调用的对齐检查。@alexandre:“…即使您修复了gcc…您也必须重新编译所有库”:说得好!我只是在预热手指来修补gcc。谢谢你帮了我这么多忙!你没有完全错。虽然SIGBUS即使在i386上也可以强制执行,但没有多大帮助。这是因为在SPARC上出现了“堆栈错误对齐”不被认为是有害的,不会导致SIGBUS。但在i386上,即使设置AC标志时出现无害的“堆栈错误对齐”,也会导致SIBGUS。
int main(int argc, char **argv)
{
# if defined i386
    /* EDIT: enable AC check */
    asm("pushf; "
    "orl $(1<<18), (%esp); "
    "popf;");
# endif

    char d[] = "12345678";  /* yep! - causes SIGBUS even on Linux-i386 */
    return 0;
}