真誇張:最後是 這一篇 王聰 先生的blog : __umoddi3()的問題 有比較清楚的說明: (copy 一下,以免消失.. 對不起)busybox -- qemu boot with preload kernel -- ref jserv's tutorial -- build kernel fail -- workaround on CFLAG -- GCC 4.3 problem -- GCC 4.3 optimize functions -- example test code
繞了好大一圈,已經離 busybox 很遠了..
以下 copy 自 王聰 先生的 blog 文章:
===========================================================
在編譯內核時有人遇到下面這個問題:
kernel/built-in.o: In function `getnstimeofday':
(.text+0xb6ae): undefined reference to `__umoddi3'
kernel/built-in.o: In function `getnstimeofday':
(.text+0xb6ce): undefined reference to `__udivdi3'
這個問題可以在用戶空間重現,不過不是很容易,我實驗了一下,在i386上,並不是所有的64位整數操作都會被轉化成
調用__umoddi3,gcc bugzilla上有演示程序,如下:
PLAIN TEXT
C:
1.#define NSEC_PER_SEC 1000000000UL
2.int rmg(void);
3.
4.int main(void)
5.{
6. /* int sec; */
7. return rmg();
8.}
9.
10.int rmg(void)
11.{
12. static unsigned long long nsec = 0;
13. static int sec = 0;
14. while (sec <1 ) {
15. nsec++;
16. while (__builtin_expect(nsec>= NSEC_PER_SEC, 0)) {
17. nsec -= NSEC_PER_SEC;
18. ++sec;
19. }
20. }
21. return sec;
22.}
這樣編譯它:% gcc -nostdlib -O2 -o umoddi3 umoddi3.c,就會得到:
/tmp/ccycM684.o: In function `rmg':
umoddi3.c:(.text+0x87): undefined reference to `__udivdi3'
collect2: ld returned 1 exit status
問題重現了。這裡的問題是,對於nsec來說,內層的循環其實等價於求模運算,gcc在優化時發現了這一點,而且硬件
本身也不支持對64位整數直接進行算術運算,所以gcc會把這一步優化成調用內部函數__udivdi3()和
__umoddi3(),這兩個函數在libgcc中(見gcc源代碼 gcc/libgcc2.c),libgcc默認和libc一樣是要被加載
的,但如果我們加了-nostdlib(Linux內核是更好的例子),這個問題就會出現了。
知道原因了,怎麼解決?網上有兩種方法,一種是像這個補丁那樣,在循環中插入下面這條內聯彙編:
asm("" : "+r"(ns));
這句是告訴gcc把ns這個變量放到寄存器中,並且既有讀操作也有寫操作,所以後面再用它時必須重新讀取,這樣就消
除了上面的優化。
另一種解決方法是添加新的編譯選項:-fno-tree-scev-cprop,這個選項似乎沒有文檔,至少我沒找到。說說它的
大體意思。scev 應該是SCalar EVolutions,什麼意思不知道。:( cprop應該是Copy PROPagation,這個應
該很容易理解,就是賦值的傳播,比如:
i = 10;
a = i;
b = i;
其實就是:
a = 10;
b = 10;
可見,編譯優化是門大學問,寫個編譯器絲毫不比寫個內核容易。:-P
============王聰先生的文章copy到此==============
沒有留言:
張貼留言