对于带符号参数的循环,我有一个关于如何使用KLEE(符号执行工具)的问题:
int loop(int data) {
int i, result=0;
for (i=0;i<data ;i++){
result+= 1;
//printf("result%d\n", result); //-- With this line klee give different values for data
}
return result;
}
void main() {
int n;
klee_make_symbolic(&n, sizeof(int),"n");
int result=loop(n) ;
}
如果我们用这段代码执行klee,它只给出一个测试用例。 但是,如果我们取出printf(...)的注释,klee将需要某种类型的控制来停止执行,因为它将产生n的值: --max-depth = 200
我想理解为什么克莱有这种不同的行为,这对我没有意义。为什么如果我在这段代码中没有printf,它将不会产生相同的值。
我发现在使用选项--optimize时会发生这种情况 什么时候不存在相同的行为。有人知道Klee的工作是什么?
另一个关于同样的问题是,如果在论文中他们已经发表了,据我所知他们说他们的启发式搜索将使它不是无限的(他们使用避免饥饿) 因为我有它运行不会停止,在这个循环的情况下应该完成klee执行是真的吗?
提前致谢
答案 0 :(得分:0)
我想知道的是为什么这个选项有不同的行为 --optimize。感谢
在C / C ++中有“未定义行为”的概念(参见:C和C ++中未定义行为指南,Part1,Part 2,Part 3,每个C程序员都是什么应该了解未定义的行为[#1/3],[#2/3],[#3/3])。
signed
整数的溢出被定义为未定义的行为,以便允许编译器优化这样的东西:
bool f(int x){ return x+1>x ? true : false ; }
让我们认为...正常代数中的x + 1> x总是正确的,在这个模代数中它几乎总是正确的(除了一个溢出的情况),所以让我们把它变成:
true
这种做法可以实现大量的优化。 (顺便说一句。如果你想在溢出时定义行为,使用unsigned
整数 - 这个特性在加密算法实现中被广泛使用。)
另一方面,有时会导致惊人的结果, 喜欢这段代码:
int main(){
int s=1, i=0;
while (s>0) {
++i;
s=2*s;
}
return i;
}
优化为无限循环。这不是错误!这是强大的功能! (再次..对于已定义的行为,请使用unsigned
)。
让我们为上面的例子生成汇编代码:
$ g++ -O1 -S -o overflow_loop-O1.s overflow_loop.cpp
$ g++ -O2 -S -o overflow_loop-O2.s overflow_loop.cpp
循环部分的检查编译方式不同:
overflow_loop-O1.s:
(...)
.L2:
addl $1, %eax
cmpl $31, %eax
jne .L2
(...)
overflow_loop-O2.s:
(...)
.L2:
jmp .L2
(...)
我建议您检查代码在不同优化级别上的汇编(gcc -S -O0
vs gcc -S -O1
... -O3
)。
关于主题的好帖子:
[1],
[2],
[3],
[4],
[5],
[6]