收藏一篇關於QT移植比較精彩的文章

QT移植中的浮點問題的簡介與內容

作者:lanxinyuchs   http://lanxinyuchs.iteye.com/blog/1037265

浮點問題的由來
Inter Xscale這款新型高性能、低功耗的微構架兼容arm v5 te isa指令集,不過不支持浮點指令集。這是爲了節省處理器芯片體積和降低運行功耗,XScale體系結構沒有實現昂貴的浮點運算部件和除法部件。這些是嵌 入式應用中不常用的運算。當需要這類運算時,要通過軟件方法實現。浮點運算要大大複雜於整數運算,沒有浮點運算單元會有什麼問題?
當然有問題,難道你printf(”%f”, 0.5)都會出現指令非法的計算機能算是好用的計算機嘛?

在敘述接下來的問題之前,我提出幾個名詞,方便敘述。

工具鏈toolchain:交叉編譯的一系列工具,包含編譯,調試,反彙編,讀elf信息,等等工具。

硬浮點toolchain:工具鏈的編譯器gcc的glibc標準C庫裏的浮點計算直接使用處理器的浮點指令。

軟浮點toolchain:工具鏈的編譯器gcc的glibc標準C庫裏的浮點計算被不含浮點指令的整數算法替換,編譯出的目標文件不含浮點指令。

事實上沒有問題,解決此類問題有兩種方案:

第一種,linux內核內建浮點模擬器支持,在遇到浮點指令時產生的異常會被內核捕捉,進行轉換:

以下內容部分引用:

在配置內核時一定要選擇一個浮點模擬器NWFPE,如下
— At least one math emulation must be selected │ │
│ │ NWFPE math emulation │ │
│ │ [ ] Support extended precision │ │
│ │ FastFPE math emulation (EXPERIMENTAL)
假如沒有浮點模擬器,就會出現錯誤。比如:
undefined instruction 
實際上,即使使用了NWFPE,系統的浮點問題也並沒有完全解決。

NWFPE模擬浮點是利用了undefined instrction handler,即每次浮點指令操作,都會發生一次未定義指令異常(exception),在這個異常的handler裏面,用軟件的方法仿真一個浮點指令。

假如軟件裏的浮點運算比較多,那豈不是不停的請求CPU執行undefined instruction ,然後產生異常?

答對了,內核模擬浮點指令事實上就是這麼回事。

這麼做帶來的後果是帶來極頻繁的exception,大大增加中斷延遲,換句話說,降低系統實時性。另外,每發生一次浮點操作,都要陷入到exception,不難想見會帶來極大的性能開銷。
內核模擬浮點指令,要實現一個大的case switch,通常情況這個分支預測都會不命中,既要多浪費cpu clock,還會排空BTB,進一步降低後面分支預測命中率。

怎麼辦?怎麼辦?怎麼辦?

別急嘛……
第二個方法,使用軟浮點,也就是在編譯的過程中就把所有的浮點運算替換掉,處理器看不到任何的浮點指令。

軟浮點支持是由交叉工具鏈提供的功能,和Linux內核無關。當使用軟浮點工具鏈編譯浮點操作時,編譯器會用內聯的浮點庫替換掉浮點操作,使得生成 的機器碼完全不含浮點指令,但是又能夠完成正確的浮點操作。由於是在編譯時優化,這種方式能夠讓CPU即使作浮點運算時也能夠執行連續的指令,減少程式分 支。

使用軟浮點工具鏈編譯產生的浮點操作速度較快,比NWFPE模擬快一個數量級。

我們怎麼知道交叉工具鏈是否支持軟浮點呢?很簡單,使用編譯命令時加上-msoft-float這個CFLAGS就能夠了,比如
arm-linux-gcc test.c -msoft-float -o test

假如工具鏈支持軟浮點,那麼就能夠生成可執行文檔,如出錯,對不起,該工具鏈不支持。

編譯鏈是否支持浮點-msoft-float關鍵在於他的glibc標準C庫中是否含有浮點指令。絕大多數編譯鏈構建的時候是不會支持該參數的。

鑑定一個應用程序或一個庫是否是軟浮點的方法,使用
arm-linux-readelf -e test
能夠打印出elf文檔頭信息,在裏面看到software FP就是軟浮點格式,否則不是。
Flags: 0×202, has entry point, GNU EABI, software FP

我們使用的UP-TECHPXA270開發版附帶的工具鏈完善支持軟浮點,並且默認軟浮點,也就是自動會加上-msoft-float選項。

這裏加上一個小花絮,也許也有人遇到這樣的問題,在此分享。

編譯內核的時候也是需要編譯鏈的,難道也是使用這個軟浮點的編譯鏈麼?不是的。一般情況下,內核是不需要浮點運算的,浮點運算一般用於多媒體。除非人爲的添加一些模塊驅動中使用到了浮點運算。所以內核的編譯用不到軟浮點編譯鏈。

光盤中其實附帶兩套編譯鏈。一套是安裝到opt文件夾中的,並將路徑加到PATH環境變量裏,這個arm-linux工具集就是默認使用的toolchain。

還有一套在附帶的2.6源碼下

/up-techpxa270/arm-linux-tools/gcc-3.4.6-glibc-2.3.6/

細心的朋友可能發現了,這兩套gcc版本不同。

在編譯QT4庫的時候,用PATH裏的那套編譯鏈無法編譯過去,而人爲指定這套的時候就可以編譯成功,但是在開發板上運行的時候還是指令非法。

問題來了,爲什麼會有兩套toolchain?爲什麼一套不行一套行?是不是因爲gcc版本原因?還是什麼?

還記得剛剛分析的,內核不需要浮點運算麼?

我們打開內核源碼中的Makefile文件

/up-techpxa270/kernel/linux-2.6.9/Makefile

發現在195行裏有

CROSS_COMPILE = /up-techpxa270/arm-linux-tools/gcc-3.4.6-glibc-2.3.6/arm-linux-

這個指定了編譯內核所用的toolchain的路徑,按照之前的方法,我們測試出這個toolchain是硬浮點的。

聯想之前的內核分析,水到渠成。

內核使用不到浮點,所以編譯內核用硬浮點編譯鏈,人爲在Makefile指定。

應用程序使用浮點,需要使用軟浮點工具鏈編譯。所以將這個軟浮點編譯鏈加在PATH裏。編譯時直接使用arm-linux-gcc就可以了。

浮點問題至此分析完滿結束。

我們可以理解的是,使用編譯內核的硬浮點編譯鏈編譯出的QT4會含有浮點指令,在Xscale運行會失敗。可是爲什麼QT4的編譯使用軟浮點編譯鏈也會失敗呢?

我仔細檢查了QT4的configure選項,發現有

–no-arm-fpa

這個選項,這個選項直譯爲:arm處理器沒有浮點!那麼必須打開這個選項。

我再次分析。圖形庫隸屬於多媒體的範疇。多媒體在解碼計算時一般都有兩種算法,一種基於浮點,一種基於整數,一般來說,使用浮點的算法較快,所以默 認使用浮點算法,QT4也不例外。但是浮點運算如果需要模擬,就絕對不會比整數快了,所以這個選項就有存在的必要。QT4裏一定有算法是用匯編寫的,所以 纔會出現這個選項。

再次編譯,發現編譯器提示了新的錯誤。

我仔細觀察了出錯信息,發現是在編譯demo時出錯,而且是與demo的源文件文件夾下的附帶的隱藏的目標文件產生浮點衝突,很好理解了吧現在,將 demo隱藏的目標文件用arm-linux-readelf -e 讀出是硬浮點的,自然無法編譯成功,至於爲什麼開放源代碼的 QT 源碼裏會附帶事先編譯好的目標文件,這個就無從考證了,感興趣的人可以看看,研究好了給我發email吧。此時編譯已經到了尾聲,所有的庫應該都已經編譯成功,所以運行應該是不成問題的。

將庫複製到板子上,建立環境變量(這裏略過,與本文不相關,具體的是設置庫目錄,字庫目錄,觸摸屏,不懂的放狗去搜或者問我),運行example,OK,沒有提示指令非法~

開香檳慶祝~~~~疑?undefine symbol asof8eq934htpwer8gusa in /……/……/lib/libQtNetwork.so.1

還是不能運行?

這次是QT4的問題了,沒有定義的符號?這個錯誤他不是字面的意思。在vc裏,出現這樣的錯誤一般是因爲沒有引用一些頭文件,導致該源文件裏使用到的一些函數在link階段無法連到目標文件。

對於動態庫,就是運行的時候無法找到函數了。程序在運行,需要用到libQtNetwork.so裏的函數,那麼就去找唄,可是找了半天找不到……

爲什麼呢?我使用了懶辦法,我去libQtNetwork.so找找,到底那個函數哪兒去了。

arm-linux-readelf –symbol libQtNetwork.so

就可以讀出所有的symbol,哇,好多好多,至少幾千個吧,怎麼辦……

arm-linux-readelf –symbol libQtNetwork.so | grep hasodifyq0384hieuhfaousef

使用管道符定向到grep程序查找函數符號,不懂?放狗去搜……

結果是,應用程序運行出錯提示缺少的函數符號全部超過了25位,而讀出的符號列表正好在25位被截斷……這麼奇怪的數字?爲什麼不是16個32個?原因未知。

這個問題十分奇怪……

可是天意是神奇的……讓我這個絕不迷信的人也驚訝起來……

我恰好學過C++標準模板庫 C++ STL。這個東西同齡人之中應該很少學過吧?首先C++沒學過,就更加不會學幾乎什麼C++書都沒提到過的STL了。

就我所知,會導致函數名災難性的變長的就是STL,它的模板技術在經過編譯器複雜的處理以後爲了避免函數重名,將函數名位數變長,跟C++的編譯也是轉成C語言的代碼時也會將重載的函數變長一個道理。

我抱着嘗試的心裏搜索了一下QT4 configure時的參數。

./configure –help | grep stl

發現有QT4 configure參數如下

–no-stl

我的媽呀……太巧合了,就我有限的學識……居然也會發現這個奇怪的問題根源所在……

現在想想,應該是編譯鏈默認截斷函數名25位,而且在嵌入式,使用stl模板技術應該會讓程序變慢的,沒有必要使用stl來加快開發進度。

好的,加上。

其實我還編譯了zlib……唉,這個就不說了,還要改Makefile的……相關性不大

於是qt4編譯

./configure –no-arm-fpa –no-stl 加其餘選項 –arch 等等

再次複製4個so庫,字庫,example

運行

./example –qws

恭喜~成功拉~~~~~~~~~~~

至此,所有的分析均結束,所有的分析均水到渠成,滴水不漏!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章