在移植uboot时编译一切正常,但uboot启动中载入自己写的网卡驱动出现故障,一直在打印raise:Signal #8 caught

google  百度了一番,也有非常多人遇到了这个问题,大家都说出了解决这个问题的办法,

就是自己编写的驱动中有出现除以0的误操作,就会一直打印raise:Signal #8 caught

将除操作改为位移操作,或者避免除数为0,就能够解决问题。

那为什么有除以0的操作就会引发raise: Signal #8 caught ? 来分析一番!

遇到错误打印,首先要找到打印来源于哪个函数,在uboot源代码中grep一把,找到该打印的来源,在arch/arm/lib/eabi_compat.c中,例如以下:

int raise (int signum)
{
/* Even if printf() is available, it's large. Punt it for SPL builds */
#if !defined(CONFIG_SPL_BUILD)
printf("raise: Signal # %d caught\n", signum);
#endif
return 0;
}

eabi_compat.c是为符合eabi接口的工具链提供一些通用的函数。

看到raise函数的实现,感觉參数signum好像是代表信号的意思(C语言中变量名是多么重要啊!),想起在网络编程中有一个函数raise是用来自身进程发信号。

那么这里一直打印signal #8 caught,是不是一直在发8号信号,搜了一下linux下的64种信号,8号代表的是SIGFPE,当一个进程遇到一个错误的算术操作时就会发送该信号。

这不就跟大家给出的解决的方法是一致的嘛,由于出现了除以0的操作,所以会发SIGFPE信号!

可是这种解释还是有非常大漏洞,raise发出信号,谁调它发的SIGFPE,grep源代码,根本找不到调用raise的地方!

linux系统下用户空间出现除以0的非法操作,进程就发SIGFPE信号,信号是什么,事实上就是软中断,使用指令SWI使处理器陷入内核态,内核态中的异常处理捕获处理该错误。

写一个简单的測试程序,例如以下:

#include <stdio.h>
void main()
{
int a = 100;
int c= 0; c = a / 0;
}

静态交叉编译(将全部须要函数都拷贝进来),然后objdump反汇编,查看反汇编文件,找到main函数:

00008428 <main>:
8428: e92d4800 push {fp, lr}
842c: e28db004 add fp, sp, #4 ; 0x4
8430: e24dd008 sub sp, sp, #8 ; 0x8
8434: e3a03064 mov r3, #100 ; 0x64
8438: e50b300c str r3, [fp, #-12]
843c: e3a03000 mov r3, #0 ; 0x0
8440: e50b3008 str r3, [fp, #-8]
8444: e51b300c ldr r3, [fp, #-12]
8448: e3a02000 mov r2, #0 ; 0x0
844c: e1a00003 mov r0, r3
8450: e1a01002 mov r1, r2
8454: eb000003 bl 8468 <__aeabi_idiv>
8458: e1a03000 mov r3, r0
845c: e50b3008 str r3, [fp, #-8]
8460: e24bd004 sub sp, fp, #4 ; 0x4
8464: e8bd8800 pop {fp, pc}

大体阅读下,

除操作‘/’编译之后成了调用__aeabi_idiv,网上搜索一番,原来对于满足eabi(嵌入式arm应用程序二进制接口)的arm工具链,编译时编译器将编译对象的'/'操作替换为调用__aeabi_idiv函数,__aeabi_idiv是由libgcc.so或gcc.a库提供的。

反汇编文件里也有__aeabi_idiv的反汇编实现,大体看下,例如以下:

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">00008468 <__aeabi_idiv>:
8468: e3510000 cmp r1, #0 ; 0x0
846c: 0a000081 beq 8678 <.divsi3_nodiv0+0x208> 00008470 <.divsi3_nodiv0>:
8470: e020c001 eor ip, r0, r1
8474: 42611000 rsbmi r1, r1, #0 ; 0x0
8478: e2512001 subs r2, r1, #1 ; 0x1

首先推断r1是否为0,是main传来的參数,r0是被除数,r1是除数,这里是0,所以跳到divsi3_nodiv0+0x208处,假设不为0则运行divsi3_nodiv0,到0x208处看一下:

    8460:   e3500000    cmp r0, #0  ; 0x0
8464: c3e00102 mvngt r0, #-2147483648 ; 0x80000000
8468: b3a00102 movlt r0, #-2147483648 ; 0x80000000
846c: ea000007 b 8490 <__aeabi_idiv0> 00008470 <__aeabi_idivmod>:
8470: e3510000 cmp r1, #0 ; 0x0
8474: 0afffff9 beq 8460 <.divsi3_nodiv0+0x208>
8478: e92d4003 push {r0, r1, lr}
847c: ebffff75 bl 8258 <.divsi3_nodiv0>
8480: e8bd4006 pop {r1, r2, lr}
8484: e0030092 mul r3, r2, r0
8488: e0411003 sub r1, r1, r3
848c: e12fff1e bx lr 00008490 <__aeabi_idiv0>:
8490: e92d4002 push {r1, lr}
8494: e3a00008 mov r0, #8 ; 0x8
8498: eb0007d8 bl a400 <raise>
849c: e8bd8002 pop {r1, pc}

调用__aeabi_idiv0,而__aeabi_idv0则调用raise,參数为8

Crazy!原来是这里调用的raise函数!

这也就解释了为什么在源代码中找不到调用raise的代码,由于调用raise不是代码本身,而是eabi的arm工具链给出的数学运算函数。

arm工具链的__aeabi_div除数为0则会调用raise,发SIGFPE信号。这样整个的解释就通了!

uboot是裸机程序,是非GNU/linux的程序。

linux内核也是满足eabi的GNU/linux程序,符合eabi的arm工具链是GNU/linux工具链,eabi接口规定了内核的系统调用 信号规范,arm工具链编译linux内核,内核实现中会去实现raise函数。

也就是说arm工具链的__aeabi_idiv与linux内核的raise出现了交叉调用,有了依赖关系!



这样裸机程序如uboot可就受苦了,只是uboot中实现了如上的raise空函数,来让arm工具链的__aeabi_div来调用,仅仅是打印一下,而不做处理,

就是我们看到的raise:Signal #8 caught!

这也算是提个醒,以后做裸机程序,用eabi的arm工具链,假设代码中有除操作,记得链接libgcc.so和实现raise函数。

uboot中raise:Signal #8 caught的根本原因的更多相关文章

  1. uboot中添加FIQ中断及相关问题

    本文主要说明了在uboot中添加FIQ中断时遇到的问题以及对应的解决办法. 首先交代一下项目的软硬件环境.硬件方面,使用s3c2440作为主控芯片,外接串口.网卡等设备.软件方面,主控芯片上电后运行u ...

  2. u-boot中nandflash初始化流程分析(转)

    u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...

  3. (四)ubuntu学习前传—uboot中对Flash和DDR的管理

    1.uboot阶段Flash的分区 (1)所谓分区,就是说对Flash进行分块管理.(2)PC机等产品中,因为大家都是在操作系统下使用硬盘的,整个硬盘由操作系统统一管理,操作系统会使用文件系统帮我们管 ...

  4. uboot中gd的定义和使用

    近期在做uboot中nand启动相关的工作,遇到一个问题一直纠结着.如今最终明确了这个问题,想想还有好多兄弟在某个黑暗的角落里或者某台电脑前纠结着呢,所以赶紧写下来以供查阅. uboot versio ...

  5. u-boot中分区和内核MTD分区关系

    一.u-boot中环境变量与uImage中MTD的分区关系 分区只是内核的概念,就是说A-B地址放内核,C-D地址放文件系统,(也就是规定哪个地址区间放内核或者文件系统)等等. 一般我们只需要分3-4 ...

  6. 关于Yaffs2在u-boot中的支持

    开发板是一块2G的MLC的NandFlash,页大小8k+512,为其移植u-boot到yaffs2这了.以前在Mini2440上移植过2k+64的slc的NandFlash的Yaffs2支持,当然也 ...

  7. 分析uboot中 make xxx_config过程

    make xxx_config实质上就是调用了 首先看MKCONFIG: [注意]SRCTREE=源文件下的目录 之后的语句: @$(MKCONFIG) $(@:_config=) arm arm92 ...

  8. uboot中的命令体系

    一.uboot的命令体系介绍以及实例分析: U-Boot 的命令实现大多在 common 目录下.在该目录下命令的代码文件都是以“ cmd_”开头的,如下图所示: 其中每一个文件都是一个命令实现的代码 ...

  9. uboot-tiny4412启动流程(下)----如何将自己的裸板测试程序加入uboot中启动测试

    今天在工作上搞了一天高通的芯片uboot程序,目的是希望将一个裸板的程序移植到uboot中,并且开机让它运行.这个芯片是NXP4330,目前是高通的一个芯片,基于ARM-contexA9架构,那么就跟 ...

随机推荐

  1. 【Android进阶】获取Android软件的版本信息

    PackageInfo pinfo = getPackageManager().getPackageInfo("com.andbase", PackageManager.GET_C ...

  2. 谷歌上不去,长期的解决方案。在稳定高速Google和Gmail

    对稳定Google神器 国内Google很不稳定,缓慢并经常上不去,由"我想去Google",安全和稳定的使用Google.Gmail.Google+所以通常需要特殊的手段岗位胜任 ...

  3. Android SharedPreferences复杂的存储

    我们知道SharedPreferences简单类型的数据.比如.String.int等. 假设想用SharedPreferences存取更复杂的数据类型(类.图像等),就须要对这些数据进行编码. 我们 ...

  4. Java网络编程注意事项3

    如何使用POST请求和GET请求Web网站发送请求,下面的参考代码: import java.io.BufferedReader; import java.io.InputStream; import ...

  5. Linux管道通信

    1.Linux内部自己实现了管道的同步,但多个读或者多个写之间的互斥,还需要自己实现.

  6. [数字dp] hdu 3565 Bi-peak Number

    意甲冠军: 为了范围[X,Y],的最大位数的范围内的需求高峰和值多少. 双峰是为了满足一些规定数量 你可以切两 /\ /\ 形式. 思维: dp[site][cur][ok]  site地点  面的数 ...

  7. ASP.NET开发规范:OWIN

    ASP.NET开发规范:OWIN 今天投简历 准备面试了... 本节目录: OWIN简介 OWIN规范 Katana Hello World(3种Host) 自定义Middleware OWIN简介 ...

  8. FastJson基本使用

    在发展中Android的过程中.假设我们常与server联系,更新数据等,然后,json它必须是一个良好的数据格公式,但随着json.使用原生的解析也能够,可是非常不高效,所以这里介绍两种json数据 ...

  9. Oracle 多行转多列

    Oracle 多行转多列,列值转为列名   前段时间做调查问卷,客户创建自定义问卷内容,包括题目和选项内容; 之后需要到处问卷明细,,,,麻烦来咯 于是到网上到处搜索,没有直接结果;于是又找各种相似的 ...

  10. Installshield自动安装IIS组件

    原文:Installshield自动安装IIS组件 一. 工程类型:IS2010 installscript 二.原理: 1. XP和 Server2003系统:由于系统默认没有自带IIS组件,一般情 ...