C++ 不一致警告:变量可能被‘;longjmp’;或‘;vWork’;
我几乎确信我遇到了一些g++4.8.3错误,但我想我应该先问一下这个列表,因为我对setjmp/longjmp的经验很少。我已将相关代码简化为以下foo.cxx:C++ 不一致警告:变量可能被‘;longjmp’;或‘;vWork’;,c++,g++4.8,setjmp,C++,G++4.8,Setjmp,我几乎确信我遇到了一些g++4.8.3错误,但我想我应该先问一下这个列表,因为我对setjmp/longjmp的经验很少。我已将相关代码简化为以下foo.cxx: #include <setjmp.h> #include <string.h> // Changing MyStruct to be just a single int makes the compiler happy. struct MyStruct { int a; int b; };
#include <setjmp.h>
#include <string.h>
// Changing MyStruct to be just a single int makes the compiler happy.
struct MyStruct
{
int a;
int b;
};
// Setting MyType to int makes the compiler happy.
#ifdef USE_STRUCT
typedef MyStruct MyType;
#elif USE_INT
typedef int MyType;
#endif
void SomeFunc(MyType val)
{
}
static void static_func(MyType val)
{
SomeFunc(val);
}
int main(int argc, char **argv)
{
jmp_buf env;
if (setjmp(env))
{
return 1;
}
MyType val;
#ifdef USE_STRUCT
val.a = val.b = 0;
#elif USE_INT
val = 0;
#endif
// Enabling the below memset call makes the compiler happy.
//memset(&val, 0, sizeof(val));
// Iterating 1 or 2 times makes the compiler happy.
for (unsigned i = 0; i < 3; i++)
{
// calling SomeFunc() directly makes the compiler happy.
static_func(val);
}
return 0;
}
foo.cxx:在函数“int main(int,char**)”中:
foo.cxx:26:5:错误:变量“val”可能被“longjmp”或“vfork”删除[-Werror=clobbered]
但使用简单的int也可以:
g++-DUSE_INT-Wextra-Wno未使用的参数-O3-Werror-fPIC foo.cxx
有人能给我解释一下,如果val是一个struct,它为什么会被删除,而如果它是int,它就不会被删除?如代码中的注释所示,您对使用struct成功编译的其他方法有何见解?或者这是指向一个编译器错误
非常感谢您的任何见解和评论。setjmp()
保存当前堆栈。因为它是在声明val
之前调用的,所以该变量不在保存的堆栈中
在setjmp()
之后,该变量将被初始化,如果代码稍后跳回到setjmp
点,该变量将被再次初始化,并与旧变量发生碰撞。如果有一个非平凡的析构函数应该在旧实例上调用,这是未定义的行为(§18.10/4):
如果将setjmp
和longjmp
替换为catch
和throw
将调用任何自动对象的非平凡析构函数,则setjmp
调用对具有未定义的行为
可能不会调用旧实例的析构函数。我的猜测是,gcc不会对基本类型发出警告,因为它们没有析构函数,但会对可能出现问题的更复杂类型发出警告。这里有几个因素在起作用:
struct
而不是int
memset
(我承认我不明白这怎么会让事情变得更糟)-fPIC
命令行选项(这将生成位置独立代码)setjmp
这是否是一个bug还有待商榷——代码可能仍然有效(尽管我还没有测试它)。但无论如何,问题似乎已经在4.9版中得到了解决,因此显而易见的解决方案是升级
以下是机器代码(NSFW):
通过
setjmp
etc进行攻击可能与在寄存器中有关。关于Basile的评论,打开可能是这个bug,如果降低优化级别会发生什么?您是否检查过编译器生成的程序集(甚至中间)代码?它可能会给你一个提示,告诉你发生了什么。任何优化级别的编译都会失败。关闭优化,编译器会很高兴。我还没有看过任何程序集。我不能用GCC4.9.1复制它,所以它可能会被修复(或者被干扰)。这不是你的例子,你可能已经知道,但只是为了记录:退出一个范围,需要通过代码不冗长的堆栈解开通过代码> LojJMP < /C> >调用未定义的行为在C++中。但是OP的<代码> MyStult是POD类型,所以没有初始化。执行行MyStruct val后立即执行代码>、<代码> Val.A<代码>和<代码> Val.B./C> >在“<代码> > LojJMP 。@ 5Gun12EdE:Trtrue之后不再定义。但是,您必须考虑编译器对警告是否正确有多大的困难。
SomeFunc(MyStruct):
rep; ret
main:
pushq %r12
pushq %rbp
pushq %rbx
subq $224, %rsp
leaq 16(%rsp), %rdi
call _setjmp@PLT
testl %eax, %eax
movl %eax, %ebp
jne .L5
movl $3, %ebx
movabsq $-4294967296, %r12
.L4:
movq 8(%rsp), %rdx
andq %r12, %rdx
movl %edx, %eax
movq %rax, %rdi
movq %rax, 8(%rsp)
call SomeFunc(MyStruct)@PLT
subl $1, %ebx
jne .L4
.L3:
addq $224, %rsp
movl %ebp, %eax
popq %rbx
popq %rbp
popq %r12
ret
.L5:
movl $1, %ebp
jmp .L3