2017-03-24 107 views
2

最近我把我的項目從ubuntu 14.04移到了ubuntu 16.04。當我使用從os倉庫安裝的gcc 4.8.5編譯我的項目時,遇到了一個錯誤「undefined __warn_memset_zero_len」。我從來沒有見過與GNU 4.8.4的Ubuntu 14.04錯誤。string3.h中__warn_memset_zero_len的定義在哪裏

__warn_memset_zero_len,它在mem3set中被聲明,以警告程序員的錯誤。要重現錯誤我做了下面的示例代碼,並與gcc --save-temps test_mem.c -D_FORTIFY_SOURCE=1 -Wall -O1 -v -Wl,-v

#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char str[] = "almost every programmer should know memset!"; 
    memset (str, 1, 0); 
    puts (str); 
    return 0; 
} 

此代碼也與GCC 4.8.4編譯僅在Ubuntu 14.04編譯它。所以,首先,我認爲新的libc可能缺少__warn_memset_zero_len的定義。爲了找出這個定義,我比較了ubuntu 14.04的舊libc和ubuntu 16.04的舊libc。這兩個版本都沒有,只有string3.h中的函數聲明。我錯了。 :<

作爲下一步,我認爲gcc可能會在編譯期間刪除對該函數的引用,因爲它是一個特殊的警告函數。但是,由這兩個系統創建的對象文件仍然有參考,由nm檢查。然後,源代碼仍然需要__warn_memset_zero_len的定義。我的想法轉向與測試代碼鏈接的共享對象。

/usr/bin/ld --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 \ 
--hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 \ 
-z relro /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o \ 
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crti.o \ 
/usr/lib/gcc/x86_64-linux-gnu/4.8/crtbegin.o \ 
-L/usr/lib/gcc/x86_64-linux-gnu/4.8 \ 
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu \ 
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib \ 
-L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu \ 
-L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../.. 
test_mem.o -v -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc \ 
--as-needed -lgcc_s --no-as-needed \ 
/usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o \ 
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crtn.o 

這是gcc用來鏈接ubuntu 14.04測試代碼的鏈接命令。我查找了鏈接對象和庫的所有符號,但找不到__warn_memset_zero_len。神祕感正在變得嚴重。

作爲最後的嘗試,我認爲屬性「警告」__warn_memset_zero_len的聲明使用可能允許舊的gcc編譯一個沒有定義的代碼。所以我做了以下測試代碼。

extern void __warning_test (void) __attribute__((__warning__ ("test_warning"))); 

int main(){ 
    __warning_test(); 
    return 0; 
} 

現在,我可以在兩個系統上得到未定義的符號錯誤。

/tmp/ccN1UbZh.o: In function `main': 
test.c:(.text+0x5): undefined reference to `__warning_test' 
collect2: error: ld returned 1 exit status 

我不知道爲什麼編譯在舊系統中,而不__warn_memset_zero_len的定義,以及爲什麼它無法在新的一個成功。另外,即使在舊系統上,爲什麼與__warn_memset_zero_len看起來完全相同的自定義函數失敗?你對這個問題有什麼想法嗎?

回答

1

__warn_memset_zero_len()在/usr/lib/x86_64-linux-gnu/libc.a中聲明,這是一個gcc的隱式庫。你可以使用nm命令來確認它。

+0

非常感謝。我不知道爲什麼,但在Ubuntu 16.04中的libc.a不包含那個符號。 – Hani

+1

@Hani如函數名所示,該函數用於檢查memset的參數。從gcc5.0(這是Ubuntu16.04上的默認編譯器),這個參數檢查是在gcc中實現的,因此在libc.a中包含一個檢查函數變得沒有必要。因此它在Ubuntu16.04中被刪除。 – gzh

0

我懷疑這是故意的錯誤,通過此行所引起:

memset (str, 1, 0); 

的聲明memset是:

void *memset(void *s, int c, size_t n); 

請注意,你的尺寸/長度值爲零。你真正想要的可能是這樣的:

char str[] = "almost every programmer should know memset!"; 
memset (str, 0, sizeof str); 

編輯:重新讀你的文章,我意識到你可能是故意創造這個錯誤?但是也許你正在試圖閱讀太多內容。這可能是在編譯器優化的某個最後時刻,gcc發現了這個問題,但遲遲沒有給出具體的錯誤,所以導致鏈接錯誤來強制停止。

還有一個想法:編譯器可能會將str寫入寫保護內存,因爲它認爲這是一個const字符串。如果發生這種情況,您的memset無論如何將會死亡。我認爲儘管只要str存儲在堆棧中(即在函數內部聲明,而不是靜態的),你還好。