将二进制浮点表示 BEE赣H000000H 转换成十进制数?

什么是Oops?从语言学的角度说,Oops应该是一个拟声词。当出了点小事故,或者做了比较尴尬的事之后,你可以说"Oops",翻译成中国话就叫做“哎呦”。“哎呦,对不起,对不起,我真不是故意打碎您的杯子的”。看,Oops就是这个意思。

在Linux内核开发中的Oops是什么呢?其实,它和上面的解释也没什么本质的差别,只不过说话的主角变成了Linux。当某些比较致命的问题出现时,我们的Linux内核也会抱歉的对我们说:“哎呦(Oops),对不起,我把事情搞砸了”。Linux内核在发生kernel panic时会打印出Oops信息,把目前的寄存器状态、堆栈内容、以及完整的Call trace都show给我们看,这样就可以帮助我们定位错误。

下面,我们来看一个实例。为了突出本文的主角--Oops,这个例子唯一的作用就是造一个空指针引用错误。

很明显,错误的地方就是第8行。

接下来,我们把这个模块编译出来,再用insmod来插入到内核空间,正如我们预期的那样,Oops出现了。

在这里,我们需要用到一个辅助工具objdump来帮助分析问题。objdump可以用来反汇编,命令格式如下:

下面是hello.o反汇编的结果,而且是和C代码混排的,非常的直观。

对照Oops的提示,我们可以很清楚的看到,出错的位置hello_init+0x5的汇编代码是:

这句代码的作用是把数值1存入0这个地址,这个操作当然是非法的。

我们还能看到它对应的c代码是:

Bingo!在Oops的帮助下我们很快就解决了问题。

我们再回过头来检查一下上面的Oops,看看Linux内核还有没有给我们留下其他的有用信息。

这里面,0002表示Oops的错误代码(写错误,发生在内核空间),#1表示这个错误发生一次。

Oops的错误代码根据错误的原因会有不同的定义,本文中的例子可以参考下面的定义(如果发现自己遇到的Oops和下面无法对应的话,最好去内核代码里查找):

有时候,Oops还会打印出Tainted信息。这个信息用来指出内核是因何种原因被tainted(直译为“玷污”)。具体的定义如下:

基本上,这个Tainted信息是留给内核开发者看的。用户在使用Linux的过程中如果遇到Oops,可以把Oops的内容发送给内核开发者去debug,内核开发者根据这个Tainted信息大概可以判断出kernel panic时内核运行的环境。如果我们只是debug自己的驱动,这个信息就没什么意义了。

本文的这个例子非常简单,Oops发生以后没有造成宕机,这样我们就可以从dmesg中查看到完整的信息。但更多的情况是Oops发生的同时系统也会宕机,此时这些出错信息是来不及存入文件中的,关掉电源后就无法再看到了。我们只能通过其他的方式来记录:手抄或者拍照。

还有更坏的情况,如果Oops信息过多的话,一页屏幕显示不全,我们怎么来查看完整的内容呢?第一种方法,在grub里用vga参数指定更高的分辨率以使屏幕可以显示更多的内容。很明显,这个方法其实解决不了太多的问题;第二种方法,使用两台机器,把调试机的Oops信息通过串口打印到宿主机的屏幕上。但现在大部分的笔记本电脑是没有串口的,这个解决方法也有很大的局限性;第三种方法,使用内核转储工具把发生Oops时的内存和CPU寄存器的内容dump到一个文件里,之后我们再用gdb来分析问题。

开发内核驱动的过程中可能遇到的问题是千奇百怪的,调试的方法也是多种多样,Oops是Linux内核给我们的提示,我们要用好它。

objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍:

显示test的文件头信息

反汇编test中的需要执行指令的那些section

除了显示test的全部Header信息,还显示他们对应的十六进制文件代码

将C源代码和反汇编出来的指令对照:

编译成目标文件(要加-g选项)

输出C源代码和反汇编出来的指令对照的格式

如何对任意一个二进制文件进行反汇编?

-D表示对全部文件进行反汇编,-b表示二进制,-m表示指令集架构,a.bin就是我们要反汇编的二进制文件

同时我们也可以指定big-endian或little-endian(-EB或-EL),我们可以指定从某一个位置开始反汇编等。所以objdump命令是非常强大的!

6.-S 不从源文件中复制重定位信息和符号信息到目标文件中

7.-g 不从源文件中复制调试符号到目标文件中

-l用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求编译时使用了-g之类的调试编译选项。

1. 内核处理UBOOT传入的参数 2. 硬件驱动的入口函数里: // 先把输出信息放入临时BUFFER // 可以用dmesg命令把log_buf里的数据打印出来重现内核的输出信息 // 调用硬件的write函数输出 // 从log_buf得到数据,算出打印级别 // 如果可以级别够格打印 二. 根据内核打印的段错误信息分析 1. 根据pc值确定该指令属于内核还是外加的模块 pc=0xbf000018 它属于什么的地址?是内核还是通过insmod加载的驱动程序? 如果不属于System.map里的范围,则它属于insmod加载的驱动程序 2. 假设它是加载的驱动程序引入的错误,怎么确定是哪一个驱动程序? 先看看加载的驱动程序的函数的地址范围 从这些信息里找到一个相近的地址, 这个地址<=0xbf000018 内核使用来访问时发生了错误 PC就是发生错误的指令的地址 大多时候,PC值只会给出一个地址,不到指示说是在哪个函数里 执行这条导致错误的指令时各个寄存器的值 发生错误时当前进程的名称是firstdrvtest 1. 根据pc值确定该指令属于内核还是外加的模块 3. 根据栈信息分析函数调用过程 3.1 根据PC确定出错位置 3.2 确定它属于哪个函数 四. 修改内核来定位系统僵死问题

本篇内容基于和 ,只是进行简单的融合并附上自己的理解而已,有需要的可以自己浏览上面两篇优秀的博客。

      课本原话:填充一个 ‘1’ 和若干个 ‘0’ 使其长度模 512 与 448 同余,然后再将消息的真实长度以 64bit 表示附加在填充结果后面,从而使得消息长度恰好为 512bit 的整数倍。

。因为消息长度为 56,所以用 64 位二进制表示为 00~00(56个零)。到目前为止 512 位全部填充完整了~

      课本原话:MD5 中有 A、B、C、D 4 个 32 位寄存器,最开始存放 4 个固定的 32 位的整数参数,即初始链接变量,这些参数用于第 1 轮运算。(第 1 轮之后值就会变化)

     课本原话:MD5 算法的分组处理(压缩函数)与分组密码的分组处理相似。它由4大轮组成,512bit 的消息分组 M[i] 被分成 16 个子分组(每个子分组为 32bit )参与每大轮中16步函数运算,即每大轮包括 16 个小步骤。

每步的输入是 4 个 32bit 的链接变量(也就是A、B、C、D)和一个32bit的消息分组(就是 M[i] ),输出为 32 位值。经过 4 轮共 64 步后,得到的 4 个寄存器值分别与输入链接变量(也就是初始的A、B、C、D)进行模加,即是当前消息的中间散列值。

与第几大轮第几小轮有关),然后继续与 T[i] 模加(这里的 i 从 1 取到 64 ),然后进行循环左移 s 位(左移的 2 位数也与第几大轮第几小轮有关),结果再与 B 模加,最后得到的结果终值赋值给 A 。

    一小轮结束后将 A 赋值给 B,B 赋值给 C,C 赋值给 D,原本的 D 赋值给 A,赋值完的 A、B、C、D 便可以进入下一轮。

1. MD5 加密算法里面的所有模加都是模 2 的 32 次加,而不是模 2 加。

举例说明下模 2 的 32 次加:(就是简单的二进制加法取前 32 位而已)

2.初始的A、B、C、D 4个链接变量与 M[j] 在进入步函数前要先经过大小端处理,T[i]不需要。

3.位数填充时(64 bit),若长度的二进制位数不足,需要在二进制前补齐至 8 的整数倍而不是 4 的整数倍。如400=(补齐 8 的整数倍,共 16 位)

4.大小端处理不是单纯指 -> ,之所以有前面这种变换,是因为 12、34、56、78 分别表示 4 个十进制数,也就是说表示第 1 个十进制数的十六进制数经过转换放在最后,第 2 个放在第 1 个前。当 1234 表示第 1 个十进制数、5678 表示第 2 个十进制数时, -> 而不是

源文件如下:md5.h

只引用一次头文件:(头文件名转为大写并用 '_' 代替 '.' ,举例:md5.h写为MD5_H ) md5 加密算法的第四步: 分组处理中步函数课本里的非线性函数, 包含F、G、H、I这四种函数。 这里定义 4 大轮里的所有小轮(16 轮)的同一个步函数: 上面的 '\' 是续行符 这里声明了下面要用到的函数 * 功 能: 将要加密的信息传递给初始化过的MD5结构体,无返回值 input:需要加密的信息,可以任意长度 #第一次调用:context->count[0] = 56,这里左移 3 位要看成乘 8,这里是计算输入的字符的 bits 数,用于给后面加密第一步附加填充位来参考 #第一次调用:由于我们传入的 iscbupt (7字符,56bits)不足 64 位的 512bits 所以不进入该循环。 #第二次调用:由于我们传入的 MD5Final 中的 8 足够新 partlen = 8 ,所以进入该循环,开始加密。 #如果传入的参数的长度直接就满足 64 位的 512bits 就先把这部分加密,因为每次每次只能加密 512bits ,后面补位上去的 64bits 长度什么的等补齐到 512bits 的整数倍后再继续加密。 * 功 能: 转换成32位的16进制字符串。 #这里 padlen = 49 ,这是判断传入参数是否小于 56,因为最后的 8 位 64bits 是用来补充真实长度的,如果传入参数大于 56 位,那总长度就以 1024bits 来算。 * 功 能: 将 32 位的 int 类型拆分成 8 位的 char 类型,存放到 4 个数组中,要注意的是存放的值是 ASCII 码的形式。 input:要拆分的 int 类型的字符串 len :字符串的长度 output:作为拆分后的承接载体 * 功 能: 将 8 位的 char 类型拼凑成成 32 位的 int 类型,存放到 1 个数组中,要注意的是存放的值是数字型。 len :字符串的长度 output:作为拼凑后的承接载体 一小轮结束后将 A 赋值给 B,B 赋值给 C,C 赋值给 D,原本的 D 赋值给 A, 赋值完的 A、B、C、D 便可以进入下一轮。 经过 4 轮共 64 步后,得到的 4 个寄存器值分别与输入链接变量(也就是初始的A、B、C、D)进行模加, 即是当前消息的中间散列值,这里是 16 位的,要转成 32 位 16 进制的密文。

字符串和文件加密实例:

什么是Oops?从语言学的角度说,Oops应该是一个拟声词。当出了点小事故,或者做了比较尴尬的事之后,你可以说"Oops",翻译成中国话就叫做“哎呦”。“哎呦,对不起,对不起,我真不是故意打碎您的杯子的”。看,Oops就是这个意思。

在Linux内核开发中的Oops是什么呢?其实,它和上面的解释也没什么本质的差别,只不过说话的主角变成了Linux。当某些比较致命的问题出现时,我们的Linux内核也会抱歉的对我们说:“哎呦(Oops),对不起,我把事情搞砸了”。Linux内核在发生kernel panic时会打印出Oops信息,把目前的寄存器状态、堆栈内容、以及完整的Call trace都show给我们看,这样就可以帮助我们定位错误。

下面,我们来看一个实例。为了突出本文的主角--Oops,这个例子唯一的作用就是造一个空指针引用错误。

很明显,错误的地方就是第8行。

接下来,我们把这个模块编译出来,再用insmod来插入到内核空间,正如我们预期的那样,Oops出现了。

在这里,我们需要用到一个辅助工具objdump来帮助分析问题。objdump可以用来反汇编,命令格式如下:

下面是hello.o反汇编的结果,而且是和C代码混排的,非常的直观。

对照Oops的提示,我们可以很清楚的看到,出错的位置hello_init+0x5的汇编代码是:

这句代码的作用是把数值1存入0这个地址,这个操作当然是非法的。

我们还能看到它对应的c代码是:

Bingo!在Oops的帮助下我们很快就解决了问题。

我们再回过头来检查一下上面的Oops,看看Linux内核还有没有给我们留下其他的有用信息。

这里面,0002表示Oops的错误代码(写错误,发生在内核空间),#1表示这个错误发生一次。

Oops的错误代码根据错误的原因会有不同的定义,本文中的例子可以参考下面的定义(如果发现自己遇到的Oops和下面无法对应的话,最好去内核代码里查找):

有时候,Oops还会打印出Tainted信息。这个信息用来指出内核是因何种原因被tainted(直译为“玷污”)。具体的定义如下:

基本上,这个Tainted信息是留给内核开发者看的。用户在使用Linux的过程中如果遇到Oops,可以把Oops的内容发送给内核开发者去debug,内核开发者根据这个Tainted信息大概可以判断出kernel panic时内核运行的环境。如果我们只是debug自己的驱动,这个信息就没什么意义了。

本文的这个例子非常简单,Oops发生以后没有造成宕机,这样我们就可以从dmesg中查看到完整的信息。但更多的情况是Oops发生的同时系统也会宕机,此时这些出错信息是来不及存入文件中的,关掉电源后就无法再看到了。我们只能通过其他的方式来记录:手抄或者拍照。

还有更坏的情况,如果Oops信息过多的话,一页屏幕显示不全,我们怎么来查看完整的内容呢?第一种方法,在grub里用vga参数指定更高的分辨率以使屏幕可以显示更多的内容。很明显,这个方法其实解决不了太多的问题;第二种方法,使用两台机器,把调试机的Oops信息通过串口打印到宿主机的屏幕上。但现在大部分的笔记本电脑是没有串口的,这个解决方法也有很大的局限性;第三种方法,使用内核转储工具把发生Oops时的内存和CPU寄存器的内容dump到一个文件里,之后我们再用gdb来分析问题。

开发内核驱动的过程中可能遇到的问题是千奇百怪的,调试的方法也是多种多样,Oops是Linux内核给我们的提示,我们要用好它。

objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍:

显示test的文件头信息

反汇编test中的需要执行指令的那些section

除了显示test的全部Header信息,还显示他们对应的十六进制文件代码

将C源代码和反汇编出来的指令对照:

编译成目标文件(要加-g选项)

输出C源代码和反汇编出来的指令对照的格式

如何对任意一个二进制文件进行反汇编?

-D表示对全部文件进行反汇编,-b表示二进制,-m表示指令集架构,a.bin就是我们要反汇编的二进制文件

同时我们也可以指定big-endian或little-endian(-EB或-EL),我们可以指定从某一个位置开始反汇编等。所以objdump命令是非常强大的!

6.-S 不从源文件中复制重定位信息和符号信息到目标文件中

7.-g 不从源文件中复制调试符号到目标文件中

-l用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求编译时使用了-g之类的调试编译选项。

1. 内核处理UBOOT传入的参数 2. 硬件驱动的入口函数里: // 先把输出信息放入临时BUFFER // 可以用dmesg命令把log_buf里的数据打印出来重现内核的输出信息 // 调用硬件的write函数输出 // 从log_buf得到数据,算出打印级别 // 如果可以级别够格打印 二. 根据内核打印的段错误信息分析 1. 根据pc值确定该指令属于内核还是外加的模块 pc=0xbf000018 它属于什么的地址?是内核还是通过insmod加载的驱动程序? 如果不属于System.map里的范围,则它属于insmod加载的驱动程序 2. 假设它是加载的驱动程序引入的错误,怎么确定是哪一个驱动程序? 先看看加载的驱动程序的函数的地址范围 从这些信息里找到一个相近的地址, 这个地址<=0xbf000018 内核使用来访问时发生了错误 PC就是发生错误的指令的地址 大多时候,PC值只会给出一个地址,不到指示说是在哪个函数里 执行这条导致错误的指令时各个寄存器的值 发生错误时当前进程的名称是firstdrvtest 1. 根据pc值确定该指令属于内核还是外加的模块 3. 根据栈信息分析函数调用过程 3.1 根据PC确定出错位置 3.2 确定它属于哪个函数 四. 修改内核来定位系统僵死问题

我要回帖

更多关于 赣H00000 的文章

 

随机推荐