Linux 應用程式除錯技術/編譯器
外觀
當要求最佳化時(-O2及以上),編譯器被授予“完全許可”來修改它:將未定義的行為(根據標準)視為不可能發生。因此,生成的程式碼在釋出最佳化模式下的行為與除錯模式不同。這種行為上的差異無法透過靜態分析器或程式碼審查發現。
1. 這是 C99 未定義的行為,編譯器假設不會發生溢位。因此,所有溢位檢查都被丟棄,以下在最佳化程式碼中是一個無限迴圈
int i, j=0;
for (i = 1; i > 0; i += i) {
++j;
}
2. 以下
(i * 2000) / 1000
被最佳化成
i * 2
- 使用-Wstrict-overflow=N與N>=3來訊號程式碼被最佳化掉的情況。
- 使用-fwrapv
如果足夠幸運地使用 gcc 4.9 及更高版本,可以使用ubsan 淨化器
- 使用-fsanitize=undefined選項編譯和連結程式
- 根據需要使用其他可用標誌。
編譯器假設無符號整數不會環繞。將無符號變數保持在範圍內[INT_MIN/2, INT_MAX/2]以避免意外。
該memset()呼叫可能會被移除,因為編譯器認為 buf 在程式碼中該點之後未使用,並且在
void do_something(void {
char buf[123];
... use buf...
/* Clear it. But removed by gcc: */
memset(buf, 0, sizeof(buf));
}
- 使用#pragma optimize()指令之後強制插入程式碼。
- 使用volatile.
程式碼可以被移動
volatile int flag = 0;
char buf[123];
void init_buf() {
for (size_t i=0; i<123; ++i) {
buf[i] = 0; //Do something
}
flag = 1; // Can move!
}
可以被最佳化成
volatile int flag = 0;
char buf[123];
void init_buf() {
flag = 1; // Moved!
for (size_t i=0; i<123; ++i) {
//Do something
}
}
- 使用編譯器內部屏障來阻止標誌移動。
迴圈可以被最佳化成只有一個讀取呼叫
void *ptr = address;
while ( *((volatile int*)ptr) & flag ) {}
- 使用restrict
- 使用-fno-delete-null-pointer-checks. 如果空指標檢查放在指標第一次使用之後,它們會被刪除。