如果这是一个非常简单的概念,我很抱歉,但我发现很难获得正确的心态,以便正确使用clang
提供的消毒剂。
float foo(float f) { return (f / 0); }
我用
编译这个小片段clang++ -fsanitize=float-divide-by-zero -std=c++11 -stdlib=libc++ -c source.cpp -o osan
我还在不使用消毒剂
的情况下编译了我的对象的“普通”版本clang++ -std=c++11 -stdlib=libc++ -c source.cpp -o onorm
我期待一些详细的输出,或者来自控制台的一些错误,但在使用nm
检查文件时,我发现只有1个差异
nm o* --demangle
onorm:
0000000000000000 T foo(float)
osan:
U __ubsan_handle_divrem_overflow
0000000000000000 T foo(float)
所以在消毒版本中有一个未定义的符号,其名称类似于我在编译时使用的消毒剂;但是一切都非常“沉默”,铿锵的前端根本没有输出。
我应该如何使用消毒剂以及什么是正确的工作流程?这个未定义的符号有什么意义?
答案 0 :(得分:11)
未定义的符号是实现清洁剂检查的功能。如果你看一下生成的代码:
没有消毒剂:
_Z3foof: # @_Z3foof
.cfi_startproc
# BB#0:
xorps %xmm1, %xmm1
divss %xmm1, %xmm0
ret
使用消毒剂:
_Z3foof: # @_Z3foof
.cfi_startproc
.long 1413876459 # 0x54460aeb
.quad _ZTIFffE
# BB#0:
pushq %rax
.Ltmp1:
.cfi_def_cfa_offset 16
movss %xmm0, 4(%rsp) # 4-byte Spill
movd %xmm0, %esi
movl $__unnamed_1, %edi
xorl %edx, %edx
callq __ubsan_handle_divrem_overflow
xorps %xmm1, %xmm1
movss 4(%rsp), %xmm0 # 4-byte Reload
divss %xmm1, %xmm0
popq %rax
ret
你看到它添加了使用该功能进行检查的代码。
编译器应自动链接到相应的清理程序库中,然后为我提供以下完整程序:
float foo(float f) { return (f / 0); }
int main() {
foo(1.0f);
}
执行时生成以下输出:
main.cpp:1:32: runtime error: division by zero
我使用命令clang++ -fsanitize=undefined main.cpp && ./a.out
如果要进行编译时检查,则需要启用更多编译器警告或静态分析器。但是,似乎没有任何警告或静态分析检查浮点被零除错误。
这是一个生成分析报告的程序:
#include <malloc.h>
int main() {
int *i = (int*) malloc(sizeof(int));
}
使用clang++ -std=c++11 main.cpp
编译时,它不会产生任何诊断信息,但使用clang++ -std=c++11 --analyze main.cpp
进行编译时会报告以下内容:
main.cpp:4:10: warning: Value stored to 'i' during its initialization is never read
int *i = (int*) malloc(sizeof(int));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:5:1: warning: Potential leak of memory pointed to by 'i'
}
^
也可以使用-Weverything [-Wunused-value]检测死存储,但只能通过分析器检测泄漏。
默认情况下,完整分析结果将写入plist文件。您还可以使用以下命令运行分析器:
clang++ --analyze -Xanalyzer -analyzer-output=text main.cpp
clang++ --analyze -Xanalyzer -analyzer-output=html -o html-dir main.cpp
分别在标准输出或通过带注释的源代码的html显示中详细了解检测到的问题,而不是在plist中。
列出了分析器检查here。
请注意,为了最好地工作,分析器需要分析整个程序,这意味着它需要绑定到构建系统。通常的界面是通过IDE(Xcode)或带有make的scan-build
工具。 CMake有一些铿锵的功能,比如产生clang JSON compilation database files,但如果CMake对clang分析器有任何内置的支持,我不确定。
答案 1 :(得分:5)
因此,如果我们查看Controlling Code Generation所说的文档(强调我的):
为各种形式的未定义或可疑行为启用运行时检查。
此选项控制Clang是否为各种形式的未定义或可疑行为添加运行时检查,默认情况下处于禁用状态。如果检查失败,则会在运行时生成诊断消息,说明问题。
所以这些是运行时检查而不是编译时间检查。因此,如果您在代码中使用foo
,则会看到以下输出:
运行时错误:除以零
使用-fsanitize=undefined
float foo(float f) { return (f / 0); }
int main()
{
int x = 1 << 100 ;
foo( 2.0f ) ;
}
它会生成两条运行时消息:
main.cpp:6:19:运行时错误:移位指数100对于32位类型来说太大了&#39; int&#39;
main.cpp:2:36:运行时错误:除以零
更新
关于静态检查器,在我对A C++ implementation that detects undefined behavior?的回答中,我提到了几个工具:STACK,kcc,当然还有Frama-C。
显然clang
允许您使用--analyze to run it's static checker但似乎最终可能会被禁用,而运行它的正确方法是通过scan-build。
同样在我自己回答的问题Why do constant expressions have an exclusion for undefined behavior?中,我展示了如何在编译时使用constexprs
来捕获未定义的行为。