我可以在编译时检测到可能的分段错误吗?
我理解分段错误的情况。但我很好奇GCC作为编译器是否有一些标志来检查导致分段错误的基本场景。
这有助于在发布图书馆之前采取预防措施。
答案 0 :(得分:6)
我可以在编译时检测到可能的分段错误吗?
有时,但不是,你无法在编译时完美地检测这些场景。考虑这个C代码中的一般情况:
volatile extern int mem[];
void foo (int access)
{
mem[access];
}
如果在编译时警告这种访问,编译器会太嘈杂,代码是有效的C,并且警告通常是不合适的。除非您有完整程序或链接时分析机制,否则静态分析无法对此代码执行任何操作。
GCC 4.8中的附加优化标志有时会在循环中捕获一些越界访问,这是“-faggressive-loop-optimizations”。这在去年的SPEC基准测试套件中发现了许多问题(http://blog.regehr.org/archives/918)
我了解分段错误的情况。但我很好奇GCC作为编译器是否有一些标志来检查导致分段错误的基本场景。
GCC 4.8附带一个地址清理程序,它可以帮助捕获一些运行时唯一的问题(超出范围/使用后免费错误)。你可以用它
-fsanitize=address.
http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Debugging-Options.html#Debugging-Options
GCC 4.9(将在未来几个月内发布)附带一个未定义的行为清理程序和更积极的NULL指针路径优化,这可能会帮助您捕获更多问题。当它出现时,它将与-fsanitize=undefined
http://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#Debugging-Options
但请注意,这些都不是“编译时”解决方案,它们都依赖于检测二进制文件并执行运行时检查。
答案 1 :(得分:0)
是的,有一些方法可以检测可能导致运行时错误(例如分段错误)的某些错误。这些方式称为警告。许多警告消息是您具有未定义行为的地方,未定义的行为通常是导致运行时崩溃的主要原因。
构建时,我始终使用-Wall
,-Wextra
和-pedantic
标记。
除此之外,除了严格的编码指南,代码审查和大量测试之外,确实没有很好的方法来检测可能导致分段错误(或其他运行时错误)的所有地方。
答案 2 :(得分:0)
gcc -Wall
-Werror
是非常好的想法。您也可以使用其他编译器。一些报告更多的内存问题。我认为你在编译时不能做更多的事情。
在运行时,我强烈推荐Valgrind
,这是一个用于检测内存问题的神奇工具。 (不要忘记使用-g
选项进行编译)
答案 3 :(得分:0)
我可以在编译时检测到可能的分段错误吗?
是的,这是可能的。不幸的是,编译器可以做的非常有限。这是一个错误的代码示例和gcc和clang的输出:
#include <stdlib.h>
int main() {
int a[4];
int x, y;
a[5]=1;
if(x)
y = 5;
x = a[y];
int* p = malloc(3*sizeof(int));
p[5] = 0;
free(p);
free(p);
}
对于这个有缺陷的代码,gcc -Wall -Wextra corrupt.c
给出了
corrupt.c: In function ‘main’: corrupt.c:13:1: warning: control reaches end of non-void function [-Wreturn-type] corrupt.c:6:7: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
clang抓住了更多:
corrupt.c:5:5: warning: array index 5 is past the end of the array (which contains 4 elements) [-Warray-bounds] a[5]=1; ^ ~ corrupt.c:3:5: note: array 'a' declared here int a[4]; ^ corrupt.c:6:8: warning: variable 'y' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized] if(x) ^ corrupt.c:8:11: note: uninitialized use occurs here x = a[y]; ^ corrupt.c:6:5: note: remove the 'if' if its condition is always true if(x) ^~~~~ corrupt.c:4:13: note: initialize the variable 'y' to silence this warning int x, y; ^ = 0 corrupt.c:6:8: warning: variable 'x' is uninitialized when used here [-Wuninitialized] if(x) ^ corrupt.c:4:10: note: initialize the variable 'x' to silence this warning int x, y; ^ = 0 3 warnings generated.
我相信上面的代码示例可以让您了解所期望的内容。 (尽管我尝试过,但我无法让static analyzer in clang工作。)
这有助于在发布图书馆之前采取预防措施。
正如您在上面所看到的,遗憾的是,它不会是巨大的帮助。我只能确认检测是当前调试代码的最佳方法。这是另一个代码示例:
#include <stdlib.h>
int main() {
int* p = malloc(3*sizeof(int));
p[5] = 0; /* line 4 */
free(p);
p[1]=42; /* line 6 */
free(p); /* line 7 */
}
编译为clang -O0 -fsanitize=address -g -Weverything memsen.c
。 (GCC 4.8也有地址santizier但我只有gcc 4.7.2。)输出:
==3476==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000f004 at pc 0x4887a7 bp 0x7fff9544be30 sp 0x7fff9544be28 WRITE of size 4 at 0x60200000f004 thread T0 #0 0x4887a6 in main /home/ali/tmp/memsen.c:4 [...]
太棒了,我们知道出了什么问题(堆缓冲区溢出)和哪里(在主/home/ali/tmp/memsen.c:4中)。现在,我评论第4行并得到:
==3481==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200000eff4 at pc 0x4887d7 bp 0x7fff27a00d50 sp 0x7fff27a00d48 WRITE of size 4 at 0x60200000eff4 thread T0 #0 0x4887d6 in main /home/ali/tmp/memsen.c:6 [...]
再次,我们看到出了什么问题,在哪里。最后,我评论第6行。
==3486==ERROR: AddressSanitizer: attempting double-free on 0x60200000eff0 in thread T0: #0 0x46dba1 in free /home/ali/llvm/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:65 #1 0x48878c in main /home/ali/tmp/memsen.c:7 [...]
也遇到了问题。
如果您的代码有测试,或者至少您可以在释放库之前在您的计算机上使用不同的输入运行代码,那么您可能会找到大部分错误。不幸的是,它不是编译时解决方案,您可能不希望发布已检测的代码(使用-fsanitize=*
标志编译的代码)。因此,如果用户使用触发错误的输入运行代码,程序仍将因分段错误而崩溃。