查看: 256|回复: 0

浮点库应用,你也有困惑吗?

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3300

    主题

    6547

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32035
    最后登录
    2024-4-26
    发表于 2023-12-25 10:39:07 | 显示全部楼层 |阅读模式
    浮点库应用,你也有困惑吗?


    今天小编想要给大家带来的是最近在调试一个项目时候发现的怪事,同样的函数库在使用不同的IDE时,得到的运行结果竟然是不一致的。相信眼尖的读者朋友已经从标题中猜出一二了,正是因为库中依赖了浮点计算库所导致的问题。那么就请和小编一起,探究下详细的来龙去脉吧!

    项目背景
    首先再详细描述以下项目背景:我们使用了一个由GCC工具链构建的函数库,编译器选项为-mfloat-abi=hard,即在编译时,使能了硬件浮点单元指令加速。但在我们将该库与Keil项目链接后,发现结果不正确。当然,我们最先怀疑的当然是库编译的有问题啦。但是奇怪的事情发生了,我当我们使用GCC工具链链接编译相同的工程,并运行后,得到了正确的结果。那么可以基本确定,库应该是没有问题的。那么问题出在哪呢?


    问题分析
    由于小编所拿到的库并不包含调试信息,只能通过一些技术手段进行破解。通过反汇编库代码,我们发现库依赖了一些浮点计算库的C函数,如sqrtf、expf等,为了简便,让我们称之为xxf函数,由于GCC并没有提供其具体实现,因此需要由库的使用者链接这些函数,而经过进一步的debug,我们发现这些函数导致了错误的结果,换句话说,这些函数的返回值是错的。


    问题调试
    那就有意思了,为了简单起见,让我们先编写一段简单的测试代码来复现这个问题,代码很简单,我们直接定义一个开平方根的函数:
    1. #include "math.h"

    2. float calc_sqrt(float a){

    3.        return  sqrtf(a);

    4. }
    复制代码
    使用gcc工具链对其进行编译:
    arm-none-eabi-gcc.exe -mcpu=cortex-m7  -mthumb  -mfpu=fpv5-sp-d16  -mfloat-abi=hard test.c -fshort-wchar -c -o test.o


    随后,随便找到一个Keil的测试工程,我们这里选择一个Hello_World示例工程,将编译出来的.o文件添加到工程中:
    14.png
    并在主程序中添加调用代码:
    1. float calc_sqrt(float a)
    2. volatile float a = calc_sqrt(4.0f);
    复制代码
    编译链接下载程序,并让程序停在函数调用处:
    15.png
    单步进入calc_sqrt函数内部,到这里,我们可以发现对这些函数的调用顺序是正确的。通过将参数传递给S0(对于float)如下所示,S0中寸的就是待计算的数据4.0f:
    16.png
    看起来好像没有问题,再进一步现在让我们检查由Keil链接的sqrtf函数的汇编实现:
    17.png
    相信大家发现了奇怪的事情了,链接的sqrtf将S0中的值传递给S0,而此时R0的值其实为0,但正如之前所说,浮点值已经由库的代码传递给了S0。因此,由于S0中实际要计算的值被临时替换了,就导致了一个错误的结果。
    18.png
    这里要强调一下,如果在Keil中直接调用sqrtf时候,或是使用keil编译器所编译出来的.o文件,Keil运行时库会使用“__hardfp_sqrtf”作为sqrtf的混淆名称:
    19.png
    而因为我们所使用库来自GCC工具链,因此Keil并不会对其进行替换,而是会将C库中叫做sqrtf的函数直接链接进去,而这个函数的默认实现,是使用R0作为参数传递的寄存器。这也就导致,实际要被计算的数丢失,最终导致结果计算错误。


    那么怎么解决这个问题,让keil不去链接这个奇怪的sqrtf呢,这就要用到Keil的一个小技巧了:
    1. float $Sub$sqrtf(float a){
    2.      return __builtin_sqrtf(a);
    3. }
    复制代码
    这样一来,调用sqrtf函数的地方,就会编程对$Sub$$sqrtf的调用:
    20.png
    而结果也变为正确的结果2.0了:
    21.png
    当然,大家可能会想啊,那我开了gcc优化之后,是不是就可以自动展开sqrtf了呢?让我们来看看:


    arm-none-eabi-gcc.exe -mcpu=cortex-m7  -mthumb  -mfpu=fpv5-sp-d16  -mfloat-abi=hard test.c -fshort-wchar -c -o3 -o test.o


    代码并没有变化:
    22.png
    结论
    小编想用这个例子和大家说明下,在涉及到跨工具链开发时,一定要注意浮点库的使用或依赖问题,由于不同编译器对于浮点运算的实现可能有些许不同,会导致意想不到的奇怪问题出现。最好的方案,还是根据不同的工具链都构建一个专属的库来使用。

    签到签到
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-28 01:38 , Processed in 0.122701 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表