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
恭喜~成功拉~~~~~~~~~~~
至此,所有的分析均结束,所有的分析均水到渠成,滴水不漏!