0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Cortex-M反编译入门

嵌入式大杂烩 来源:嵌入式大杂烩 作者:嵌入式大杂烩 2023-05-01 01:22 次阅读

我们在写单片机裸机程序时,在主函数之前,会有一段启动代码,而启动代码是用汇编写的,有些朋友可能看到汇编头都大了,当时要想深入研究底层架构,这快硬骨头就必须去啃。

汇编 :汇编文件转换为目标文件(里面是机器码)。

反汇编 :可执行文件(目标文件,里面是机器码),转换为汇编文件。

关于汇编的基础知识,请看笔者以前的文章。

今天笔者以STM32F1的点灯程序为例,带领大家进行反汇编,并阅读反汇编后的代码。

1 新建LED裸机程序

关于STM32裸机程序的创建,请看笔者博文:

https://bruceou.blog.csdn.net/article/details/78244735

但是今天这个程序非常简单,不应那么复杂。

1.新建文件夹

新建文件夹“STM32F1”,当然名字也可以另取,在 STM32F1文件夹下,我们新建五个文件夹,分别为CMSIS、Listing、Output、Project、User。

1682861784044ce1xngvdb1

其中CMSIS文件夹放启动文件:

1682861784745y77tys7nql

笔者的开发板芯片是STM32F103ZE,这个文件是根据STM32的固件库startup_stm32f10x_md.s文件修改而来。

2.新建工程

打开Keil,在工具栏 Project->New μVision Project…新建我们的工程文件。

1682861785045yci29v8yep

输入工程名,保存即可。

1682861785347ejt3txvtdg

窗口是让我们选择公司跟芯片的型号,我们用的芯片是 ST 公司的STM32F103ZE,有64K SRAM,512K Flash,属于高集成度的芯片。按如下选择即可。

1682861785701fybynmt941

然后点击项目管理。

16828617860678xirkyf7u2

最后修改后的内容如下:

168286178659146c097dxaa

并添加相应的文件。

1682861786890i3ruwx50xk

其中main.c的内容如下所示:

/**
  * @brief  延时函数
  * @param  d
  * @retval None
  */
void delay(int d)
{
    while(d--);
}

/**
  * @brief  main
  * @param  None
  * @retval int
  */
int main(void)
{
    unsigned int *pReg;
    
    /* 使能GPIOB */
    pReg = (unsigned int *)(0x40021000 + 0x18);
    *pReg |= (1<<3);
    
    /* 设置GPIOB0为输出引脚 */
    pReg = (unsigned int *)(0x40010C00 + 0x00);
    *pReg |= (1<<0);

    pReg = (unsigned int *)(0x40010C00 + 0x0C);
    
    while (1)
    {
        /* 设置GPIOB0输出1 */
        *pReg |= (1<<0);
        
        delay(1000000);

        /* 设置GPIOB0输出0 */
        *pReg &= ~(1<<0);
        
        delay(1000000);
    }
}

startup.s文件的内容如下:

;************************************ STM32F1 ************************************
;* File Name          : startup.s
;* Author             : BruceOu
;* Version            : V1.0
;* Date               : 2021-06-27
;* Description        : STM32F10x Medium Density Devices vector table for MDK-ARM 
;*                      toolchain.  
;*                      This module performs:
;*                      - Set the initial SP
;*                      - Set the initial PC == Reset_Handler
;*                      - Set the vector table entries with the exceptions ISR address
;*                      - Configure the clock system
;*                      - Branches to __main in the C library (which eventually
;*                        calls main()).
;*                      After Reset the CortexM3 processor is in Thread mode,
;*                      priority is Privileged, and the Stack is set to Main.
;*******************************************************************************
                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
				EXPORT  __Vectors
					
__Vectors       DCD     0                  
                DCD     Reset_Handler              ; Reset Handler

				AREA    |.text|, CODE, READONLY

; Reset handler
Reset_Handler   PROC
				EXPORT  Reset_Handler             [WEAK]
                IMPORT  main

				LDR SP, =(0x20000000+0x10000)
				BL main

                ENDP 
                
                END

接下来还有配置工程。

16828617871720kp1099zbj

选择Output文件夹。

1682861787441yb2iav40ku

选择Listing文件夹。

168286178774709w1fbwxlj

基本配置就这些,接下来编译工程。

1682861788115kc6kgvwaat

只要没有错误就可以了,最后就是下载程序,笔者使用的是J-Link,最后的现象如下:

1682861788425ht484axtbw

LED会不停闪烁。

2 Keil反汇编

接下来才是今天正题,反汇编。

在KEIL的User选项中,如下图添加这两项:

fromelf --bin --output=STM32F1.bin ../Output/STM32F1.axf

fromelf --text -a -c --output=STM32F1.dis ../Output/STM32F1.axf

然后重新编译,即可得到二进制文件STM32F1.bin(以后会分析)、反汇编文件STM32F1.dis。

如下图所示:

1682861788915ugsuzllsu3

正常编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编 (Assembly)和链接(Linking)。

但是反编译是讲为二进制文件反编译成汇编文件,因此反汇编的流程如下:

1682861789194tjjboqezsx

3 反汇编代码解析

接下来就是查看反编译代码,打开反编译文件Project/STM32F1.dis。这里只截取一段查看,因为格式都是一样的,知识每条内容不同罢了。

C:\\Users\\BruceOu\\Desktop\\20210628222656.png

第一列是链接地址,第二列是机器码,第三列是汇编指令。

根本汇编指令,我们找到ARM®v7-M Architecture Reference Manual_DDI 0403E.d (ID070218)中的LDR指令。

1682861789789h0yw0lt7wp

我们将F8DFD004变成二进制。

1682861790181wc7gex8qa6

这个使用的32位的Thumb2指令集。

16828617907087x3urvsswf

其中b0~b11是立即数,这里是4,对应的汇编代码的也是4,这里要注意的是,ARM指令采用流水线机制,当前执行地址A的指令,同时已经在对下一条指令进行译码同时已经在读取下下一条指令:PC = A +4 (Thumb/Thumb2指令集)。

1682861791012zlh0i8hbkk

B12~b15是寄存器,这段大小是0XC,对应的寄存器就是sp;

1682861791382rgl1c89djy

后面16bit除了23位意外,全是固定的,其中‘U’表示无条件执行,这里置为1。

其他的汇编指令对应的机器码也是类似的,值得注意的是,不同的架构对应的机器码也是不同的,这也就回答了为了不同的处理器架构会对应不同的指令集。

有兴趣的可以对比Cortex-M系列和Cortex-A系列的的指令集。请参考以下手册:

ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition.pdf

ARM®v7-M Architecture Reference Manual.pdf

4 反汇编代码全解析

进入debug模式,在View下选择disassembly window。

1682861791732udl87y2hkk

这样就可将机器码和对应的代码对应起来。当程序运行起来了,也就从异常向量表中跳转到Reset_Handler中,然后跳转到main函数中,而main函数是在栈中,因此需要设置占空间的起始位置。根据STM32的参考手册,SRAM的其起始地址和大小如下:

1682861792161laj05ypw17

因此栈顶为起始位置加上栈的大小即可,只要不超过SRAM即可。

1682861792494kvv8rmggr9

值得注意的是,栈是__向低地址扩展的数据结构__,是一块连续的内存区域,栈顶的地址和栈的最大容量是在通过LDR设置,因此需要根据应用需求合理分配栈空间。

接下来往下走,如果在汇编中不打断点,会默认进入main函数的一条指令,就从这里分析。为了分析方便,这里还有使用上一节方便出来的文件。

1682861792854yx7buaxbnh

168286179328181dgh7q884

【C代码33行】

从内存地址0x0800 017c拷贝数据0x40021018到r3中,也就是

r3 = * 0x0800 017c

也就是将pReg指针保存到r3中。

1682861793680n54j8yfcae

【C代码34行】

这里对应3条指令

168286179399049x6zh3xs2

首先将r3拷贝到r0中,然后将r0或上1左移3位,也就是

ORR r0,r0,#8

最后将r0的值写入r3所指地址中。

【C代码37行】

同33行,从内存地址0x0800 0180拷贝数据40010c00到r3中

1682861794278a3l8unid93

【C代码38行】

同34行,这里也对应3条指令:

1682861794643xsiexuccee

【C代码40行】

和33行不同的是,这里分了两条指令:

1682861794920c8o1lnkcmn

笔者认为前面是编译器优化了。根据ARM指令采用流水线机制,当前执行地址A的指令,同时已经在对下一条指令进行译码同时已经在读取下下一条指令:PC = A +4 (Thumb/Thumb2指令集)。因此前面类似的代码被优化了。

接下来就进入循环中。

1682861795209ylxlhden5n

后面就移植在死循环中,不断操作GPIO的亮灭。

【C代码45行】

这里是将B0设置为1,和34行类似。

1682861795497jylbjwsdjc

【C代码47行】

这里将进入延时函数。

1682861795758qdjan7453d

进入延时函数:

1682861796046vsclfk8ad3

NOP是字节对齐,减少指令的内存访问次数。首先将变量d保存到r0,然后将r0赋给r1,接着是r0自减1,紧接着是r1与0比较,如果r1等于0,则会返回,否则,又从头开始,值得注意的是,这里先比较,然后r0才自减的。

为了进一步说明,可以看--d的汇编代码。

1682861796329nl965tt4jm

这里就是相当于r1先减1,然后再比较的。

【C代码50行】

这行代码对应一下指令,很简单。

1682861796601o6xh8txu3q

5 总结

在前面使用Keil进行了反汇编,也对相应的C代码进行了分析。我们看到的反汇编代码如下:

C:\\Users\\BruceOu\\Desktop\\20210630233351.png

根据反汇编的代码,可将其对应到Flash,在Flash上的内容如下表所示:

地址 Flash****内容
0x08000000 00000000
0x08000004 08000009
0x08000008 f8dfd004
0x0800000c f000f80c

最后总结下点灯的流程:

第一步:设置栈CPU会从0x08000000读取值,用来设置SP。

第二步:跳转 :CPU从0x08000004得到地址值,根据它的BIT0切换为ARM状态或Thumb状态,然后跳转。

__第三步:__对于cortex M3/M4,它只支持Thumb状态,所以0x08000004上的值bit0必定是1,0x08000004上的值 = Reset_Handler + 1。从Reset_Handler继续执行。

第四步 :然后进入到主函数中执行相应C代码。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6001

    文章

    43978

    浏览量

    620866
  • 汇编
    +关注

    关注

    2

    文章

    214

    浏览量

    25735
  • 编译
    +关注

    关注

    0

    文章

    615

    浏览量

    32397
  • 反编译
    +关注

    关注

    1

    文章

    12

    浏览量

    8469
  • Cortex-M
    +关注

    关注

    2

    文章

    224

    浏览量

    29574
收藏 人收藏

    评论

    相关推荐

    如何选择正确的Cortex-M处理器?

    本文将通过比较Cortex-M系列处理器之间的产品特性,告诉你如何根据产品应用选择正确的Cortex-M处理器。同时也会详细地进行Cortex-M系列处理器的指令集和高级中断处理能力,以及 SoC
    发表于 10-22 08:16

    基于Cortex-M原型系统建立的Cortex-M3 DesignStart原型

    采用Cortex-M原型系统建立Cortex-M3 DesignStart原型为什么选择Cortex-M原型系统?
    发表于 02-01 06:56

    Cortex-M入门资料和书籍分享

    Cortex-M入门在网上看博客逛论坛也是能学到些东西的,但通常是知识点,不能构成知识面。书籍通常会系统性地讲述,通过书籍可以建立起知识面,只有建立起了知识面才算是掌握。推荐两本书:《ARM
    发表于 07-01 09:38

    ARM Cortex-M处理器详解 精选资料分享

    ARM Cortex-M处理器家族现在有8款处理器成员。在本文中,我们会比较Cortex-M系列处理器之间的产品特性,重点讲述如何根据产品应用选择正确的Cortex-M处理器。本文中会详细的对照
    发表于 07-16 07:57

    Cortex-M内核的GCC编译

    下载ARM官方对应Cortex-M内核的GCC编译
    发表于 08-24 06:44

    cortex-m下各种微架构的区别是什么?

    cortex-m单片机在arm产品中的位置是哪里?cortex-m 单片机的类别有哪些?cortex-m下各种微架构的区别是什么?
    发表于 11-04 06:00

    Cortex-M处理器优化的代码

    生成针对Cortex-M处理器优化的代码。嵌入式编码®Support Package的ARM®的Cortex®-M处理器可以生成使用CMSIS库数学运算的优化代码。将此生成的代码用于ARM
    发表于 12-14 09:10

    ARM Cortex-M堆栈机制介绍

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是ARM Cortex-M堆栈机制。  今天给大家分享的这篇依旧是2016年之前痞子衡写的技术文档,花了点时间重新编排了一下
    发表于 12-16 06:26

    ARM Cortex-M内核的相关资料推荐

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是ARM Cortex-M功能模块,不过侧重点是三款安全特性处理器。  ARM Cortex-M处理器家族发展至今(2020),已有
    发表于 12-27 07:21

    ARM Cortex-M 开发实战指南入门篇(二)

    1、集成开发环境和非集成开发环境介绍嵌入式开发的第一步就是搭建开发环境,不同的硬件平台可能所需的环境还不太一样,而且还有可能出现千奇百怪的错误,本讲将讲解ARM Cortex-M开发的常用工具
    发表于 04-19 17:24

    Arm Cortex-M处理器—Cortex-M85介绍

    Arm发布了新一代的Cortex-M处理器,Cortex-M85。简单粗暴的打个比方:Cortex-M85 ≈ Cortex-M7TrustZoneHelium(
    发表于 07-15 14:59

    ARM Cortex-M处理器对比表

    Cortex-M处理器系列针对低成本、高能效的微控制器进行了优化。 这些处理器可以在各种应用中找到,包括物联网、工业和日常消费设备。 该处理器系列基于M-Profile架构,可为深度嵌入式系统提供低
    发表于 08-29 07:00

    Android反编译工具

    Android反编译工具
    发表于 12-17 15:59 21次下载

    java反编译常用的保护技术

    由于Java字节码的抽象级别较高,因此它们较容易被反编译。本节介绍了几种常用的方法,用于保护Java字节码不被反编译。通常,这些方法不能够绝对防止程序被反编译,而是加大反编译的难度而已
    发表于 07-29 16:00 591次阅读

    【专栏精选】Cortex-M反编译入门

    做电子发烧友技术探索官,分享你的原创电子行业文章! 本期为大家推荐一篇嵌入式相关技术文章,作者主要从事嵌入式领域的开发,感兴趣的小伙伴可以关注学习哦~ 本期推 荐 专栏作者 : 嵌入式大杂烩 (点击查看作者主页) 介绍:该专栏主要介绍嵌入式开发相关的知识,与大家一起交流学习。    我们在写 单片机 裸机程序时,在主函数之前,会有一段启动代码,而启动代码是用 汇编 写的,有些朋友可能看到汇编头都大了,当时要想深入研究底层
    的头像 发表于 05-16 09:35 903次阅读