LV04-02-GPIO-05-根据dis文件分析代码运行
本文主要是根据反汇编出来的dis文件,分析代码的运行的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows版本 | windows11 |
Ubuntu版本 | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) |
Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
Win32DiskImager | Win32DiskImager v1.0 |
点击查看本文参考资料
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.nxp.com.cn/ | NXP官方网站 | |
https://www.nxpic.org.cn/ | NXP 官方社区 | |
https://u-boot.readthedocs.io/en/latest/ | u-boot官网 | |
https://www.kernel.org/ | linux内核官网 |
点击查看相关文件下载
分类 | 网址 | 说明 |
NXP | https://github.com/nxp-imx | NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库 |
https://elixir.bootlin.com/linux/latest/source | 在线阅读linux kernel源码 | |
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
I.MX6ULL | i.MX 6ULL Applications Processors for Industrial Products | I.MX6ULL 芯片手册(datasheet,可以在线查看) |
i.MX 6ULL Applications ProcessorReference Manual | I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网) |
我们分析的时候,以韦东山教程中的文件为例进行分析,我们前边的Makefile文件中已经可以生成反汇编文件,我们可以直接查看。
一、文件准备
1. start.s
点击查看详情
1 | .text |
2. 链接文件
点击查看 imx6ull.lds
1 | SECTIONS { |
3. C程序文件
3.1 led.c
点击查看 led.c
1 |
|
3.2 led.h
点击查看 led.h
1 |
|
3.3 main.c
点击查看 main.c
1 |
|
4. 工具准备
还有一个生成DCD数据的工具,需要去u-boot的源码中拷贝,所需文件如下:
5. makefile
点击查看makefile文件
1 | PREFIX=arm-linux-gnueabihf- |
二、上电后在做啥?
如下图, imx6ull 芯片一上电后,会先执行 bootRom 程序,此程序是芯片出厂时已经固定的程序,除了芯片原厂,我们是无法修改的。
(1)bootRom 会把 EMMC 或 TF 卡的前 4K 数据读入到芯片内部 RAM 运行。
(2)bootRom 根据 DCD 进行初始化 DDR。
(3)bootRom 根据 IVT,从 EMMC 或 TF 卡中将 led.bin 读到 DDR 的0x80100000 地址
(4)跳转到 DDR 的 0x80100000 地址执行 。
目前 led.bin 程序已经复制到内存中, CPU 开始从内存 0x80100000 地址开始执行机器码,每一条机器码是 32 位/4 字节,此处的机器码就是 led.bin 中的
机器码,我们可以打开led.bin看一下机器码是不是上边图中画的:
前面介绍过大/小端模式,从这里就可以看出来啦。此处可以看到机器码 e59fd028(指令: ldr sp,=0x80200000)的存储形式:
1 | 地址 机器码 |
可以看到imx6ull 的存储方式是小端模式,其实, ARM 处理器中存储方式一般都是小端模式。
三、运行过程分析
这一部分主要是分析led.dis文件。
1. start.s的反汇编
(1)CPU 执行的第一条机器码就是内存地址 0x80100000 存储的 e59fd028 机器码对应的指令是“ ldr sp, [pc, #40] ”,相当于 start.s 文件的 “ ldr sp,=0x80200000 ” 指令。执行完后,寄存器 SP 的值等于 0x80200000。
1 | 80100000: e59fd028 ldr sp, [pc, #40] ; 80100030 <clean+0x14> |
(2)每执行完一条机器码,会自动执行下一个内存地址 0x80100004 存储的eb000001 机器码对应的指令是“ bl 80100010”,相当于 Start.S 文件的“ bl clean_bss”指令。
1 | 80100004: eb000001 bl 80100010 <clean_bss> |
(3)跳转到内存地址 0x80100010 执行 e59f101c 机器码,对应的指令是 ldr r1, [pc, #28] 。相当于 start.s 文件的“ ldr r1, =__bss_start”指令。
(4)此处 clean_bss 相当于一个函数, CPU 会逐条执行指令,clean为循环体,直到执行“ mov pc, lr”指令后,才返回。
(5)最后返回哪里?返回内存地址0x80100008 处执行 fa000057 机器码,对应的指令是“blx 8010016c”。对应 start.s 文件的“ bl main”指令 。
2. 进入main函数
我们简单看一下main函数的反汇编文件对应情况:
(1)进入 main()函数后,先将寄存器 R7、 LR 入栈,保存现场/上下文,方便main()函数执行完毕后返回,并且将当前栈指向的内存地址赋值给寄存器R7。
(2)调用 led_init()函数,因为没有参数传递,所以直接调用 BL 指令进行跳转,即“bl 8010003c”指令。
(3)调用 led_ctl(1)函数,此处只有一个参数,通过寄存器 R0 进行传递,即“movs r0, #1”指令,然后通过 BL 指令进行跳转,即“bl 801000f8”指令,关于参数传递问题,可以参考前面《01嵌入式开发/01HQ课程体系/LV08-ARM基础/LV08-01-ARM体系-03-ARM汇编基础.md》的笔记。
(4)调用 delay(1000000)函数,此处只有一个参数,通过寄存器 R0 进行传递,然后通过 BL 指令进行跳转。
(5)调用 led_ctl(0)函数,此处只有一个参数,通过寄存器 R0 进行传递,然后通过 BL 指令进行跳转。
(6)调用 delay(1000000)函数,此处只有一个参数,通过寄存器 R0 进行传递,然后通过 BL 指令进行跳转。
(7)while(1)循环体到此已经结束,但是需要循环执行循环体的内容,通过 B指令进行跳转到循环体开头,即“b.n 80100174”指令,执行内存地址0x80100174 处的指令,也就是 led_ctl(1)函数对应的汇编指令“movs r0,#1”。