ARM 基础学习记录 / ARM 裸机编程

news2024/11/24 19:39:30

汇编程序调用 C 程序详情

在 C 程序和 ARM 汇编程序之间相互调用时必须遵守 ATPCS 规则,其是基于 ARM 指令集和 THUMB 指令集过程调用的规范,规定了调用函数如何传递参数,被调用函数如何获取参数,以何种方式传递函数返回值。

  1. 寄存器 R0~R15 在 ATPCS 规则的使用

    • 在函数中,通过寄存器 R0~R3 来传递参数,被调用的函数在返回前无需恢复寄存器 R0~R3 的内容。

    • 在函数中,通过寄存器 R4~R11 来保存局部变量。

    • 寄存器 R12 用作函数间 scratch 寄存器。

    • 寄存器 R13 用作栈指针,记作 SP ,在函数中寄存器 R13 不能用做其他用途,寄存器 SP 在进入函数时的值和退出函数时的值必须相等。

    • 寄存器 R14 用作链接寄存器,记作 LR ,它用于保存函数的返回地址,如果在函数中保存了返回地址,则 R14 可用作其它的用途。

    • 寄存器 R15 是程序计数器,记作 PC ,它不能用作其他用途。

  2. 汇编程序向 C 程序函数传递参数

    • 当参数小于等于 4 个时,使用寄存器 R0~R3 来进行参数传递。

    • 当参数大于 4 个时,前四个参数按照上面方法传递,剩余参数传送到栈中,入栈的顺序与参数顺序相反,即最后一个参数先入栈。

  3. C 程序函数返回结果给汇编程序

    • 结果为一个 32 位的整数时,通过寄存器 R0 返回。

    • 结果为一个 64 位整数时,通过 R0 和 R1 返回,依此类推。

    • 结果为一个浮点数时,通过浮点运算部件的寄存器 f0,d0 或 s0 返回。

    • 结果为一个复合的浮点数时,通过寄存器 f0-fN 或者 d0~dN 返回。

    • 对于位数更多的结果,通过调用内存来传递。

  4. 当 C 程序从一个函数跳转到另一个函数时,会先把源函数的 CPU 的寄存器和函数内的局部变量都入栈,当跳回时再出栈,这一过程的汇编代码是当 C 程序编译成汇编时被编译器自动添加。


imx6ull 裸机编程相关

这里是处理器启动流程等的介绍,属于科普环节,有个印象,会加深对于处理器如何运行的理解,非必要记住,而是为以后的操作说明每一个步骤都在做什么事情。此部分理解为主。

裸机映像文件合成详情

先说原理,看 imx6ull 芯片手册可知,芯片上电时内部的 boot ROM 固化的程序会通过外部引脚确定启动方式(USB\NAND\EMMC\SD等),将应用的二进制数据(app.bin)从存储区(NAND\EMMC\SD等)搬运到内存区(DDR2\3等),然后跳转到内存区的程序处开始执行程序。这个过程是这个芯片自动完成的,但是需要根据规定合成烧录到存储区的映像文件, 在编译得到应用的二进制文件 app.bin(这个就是比如 裸机应用固件 或 Linux 固件等)之后,再用 mkimage 工具(gcc-arm-linux-gnueabihf-6.2.1 编译器自带的)根据 imximage.cfg.cfgtmp 这个文件的信息,合成头部信息,再与 app.bin 组合生成 .imx 文件, .imx 的头部再添加 1KB 的数据(可以全为0,也可包含分区表等数据) 组合生成 .img 文件,具体如下:

  • .imx 文件 = 头部信息( IVT + Boot data + DCD) + app.bin -> 用于在烧写工具中烧写到 EMMC 中,烧写工具会自动将其烧写到 1KB 偏移处。

  • .img 文件 = 1k.bin + .imx 文件 = 1k.bin + 头部信息( IVT + Boot data + DCD) + app.bin -> 用于在烧写工具中烧写到 SD 中,烧写工具会将其烧写到 0 位置处(对与 SD 的烧写,此工具不会自动加 1KB 偏移...)。

头部信息包含了指示 boot ROM 程序要把 app.bin 数据搬运到内存的何处,其大小,以及包含了配置 DDR 的寄存器、引脚等数据等待,具体如下:

  • IVT:Image vector table,含 header(含 tag、length、version,这 3 项,length 表示 IVT 的大小)、entry(指示 app.bin 在内存中的位置,即程序数据被复制到内存哪里)、dcd(指示 DCD 数据 在内存中的位置)、boot_data(指示 Boot data 在内存中的位置)、self(指示 IVT 在内存中的位置)等,共占 32*8bit 大小,entry 为 app.bin 要在内存中的目的地址。

  • Boot data:start(映像文件在内存中的地址,为 IVT 在内存中的绝对地址减去 1024 偏移)、length(整个映像文件的长度,含 1k.bin)、plugin,共占 32*3bit 大小。

  • DCD:配 imx6ull 芯片的寄存器,如 DDR 的配置等,可自定,复杂,mkimage 根据 imximage.cfg.cfgtmp 这个文件的信息合成。

    其中,entry(指示 app.bin 在内存中的位置,即程序数据被复制到内存哪里)的地址在 Makefile 中调用 mkimage 工具时是可以指定的,在"重定位"章节会细说。

具体分布:

  • 头部数据和偏移区使用 mkimage 工具生成,官方都会提供的。

  • 最前面的灰色部分就是偏移数据区,对于EMMC/SD存储区设备是 1KB,对于 NAND 是256B,具体看手册。

最终生成的 .img 文件结构:

imx6ull 上电启动过程分析:

  1. boot Rom 会把 EMMC 或 SD 卡的前 4K 数据(涵盖了头部信息( IVT + Boot data + DCD)这些等)读入到芯片内部 RAM 运行。

  2. boot Rom 根据 DCD 进行初始化 DDR。

  3. boot Rom 根据 IVT,从 EMMC 或 SD 卡中将 app.bin 读到 DDR 的 0x80100000 地址(IVT 的 entry,如上图所示)。

  4. 跳转到 DDR 的 0x80100000 地址执行,即 CPU 开始从内存 0x80100000 地址开始执行机器码。

    以上步骤执行完之后的 DDR 内存图示:(这是反汇编 应用固件 产生的 机器码-汇编码 相互对应的内容)

重定位、启动和编译

各段数据重排序

每一个汇编成机器码的 .o 文件都会分为这几个数据段:

  • 代码段(.text):存放代码指令;

  • 只读数据段(.rodata):存放有初始值并且 const 修饰的全局类变量;

  • 数据段(.data):存放有初始值的全局类变量(有非零初始值的变量,如 char A = 'A';);

  • 零初始化段(.bss):存放没有初始值或初始值为0的全局类变量(如 int g_intA = 0;int g_intB;,这些存放在 .bss 段);

  • 注释段(.comment):存放注释。

在 Makefile 文件中,在链接步骤,通过 LD 工具,把各个 .o 文件的各个数据段,按照 imx6ull.lds 定义的顺序安放,即各段数据重排序,最后合成一个二进制文件 app.bin,其中的代码段(.text)、只读数据段(.rodata)和数据段(.data)等都来自于前面各个 .o 文件,每个段 的顺序按照 imx6ull.lds 安放。

链接脚本 imx6ull.lds 解析(一体式链接脚本格式):

SECTIONS {
    . = 0x80100000;                      //设定链接地址为0x80100000
​
    . = ALIGN(4);                        //将当前地址以4字节为标准对齐
    .text      :                         //创建段,其名称为 .text
    {                                    //.text包含的内容为所有链接文件的数据段
        *(.text)                         // *:表示所有文件
    }
​
    . = ALIGN(4);                        //将当前地址以4字节为标准对齐
    .rodata : { *(.rodata) }             //.rodata存放在.text之后,包含所有链接文件的只读数据段
​
    . = ALIGN(4);
    .data : { *(.data) }                 //.data存放在.rodata之后,包含所有链接文件的只读数据段
​
    . = ALIGN(4);
    __bss_start = .;                     //将当前地址的值存储为变量__bss_start
    .bss : { *(.bss) *(.COMMON) }        //.bss存放在.data段之后, 包含所有文件的bss段和注释段
    __bss_end = .;                       //将当前地址的值存储为变量__bss_end
}
​

可见 imx6ull.lds 文件给出 .bss 段的头、尾地址标识:__ bss_start__ bss_end

启动文件程序

以最简单的裸机点灯程序的启动文件 start.S 为例。仅为示例,过于简单,完整示例可看 下面 “ARM异常处理 & 启动文件的示例” 一节。

.text
.global  _start
_start:                 
    /* 设置栈地址 */
    ldr  sp,=0x80200000
    bl main
​
halt:
    b  halt
Makefile 文件解析

以最简单的裸机点灯程序的 makefile 为例。

PREFIX=arm-linux-gnueabihf-
CC=$(PREFIX)gcc
LD=$(PREFIX)ld
AR=$(PREFIX)ar
OBJCOPY=$(PREFIX)objcopy
OBJDUMP=$(PREFIX)objdump
​
led.img : start.S  led.c main.c
    $(CC) -nostdlib -g -c -o start.o start.S                 # 把启动文件 .s 和各个 .c 文件都汇编为机器码文件 .o
    $(CC) -nostdlib -g -c -o led.o led.c    
    $(CC) -nostdlib -g -c -o main.o main.c  
    
    $(LD) -T imx6ull.lds -g start.o led.o main.o -o led.elf  # 链接,按照 imx6ull.lds 定义的格式,各段数据重排序,把各个 .o 文件组成 .elf 文件
    
    $(OBJCOPY) -O binary -S led.elf  led.bin                 # .elf 转为 .bin 二进制文件,应用二进制文件
    $(OBJDUMP) -D -m arm  led.elf  > led.dis    
    mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80100000 -d led.bin led.imx
                                                             # 使用 mkimage 生成 头部数据,并与 .bin 组合,产生 .imx 文件
    dd if=/dev/zero of=1k.bin bs=1024 count=1                # 创建一个 1KB 的空文件 1k.bin
    cat 1k.bin led.imx > led.img                             # 把 1k.bin 放在 .imx 前头,组合成 .img 文件
​
clean:
    rm -f led.dis  led.bin led.elf led.imx led.img *.o
​
清零 bss 段

在 启动文件 汇编程序中,根据 .bss 段的头、尾地址(__ bss_start__ bss_end)来对此区域清零,让 C 程序中未定义初始值或零初始值的变量在初始化时都为零值,而非随机值。

附程序:

clean_bss:
    ldr r1, =__bss_start    @ 将链接脚本变量__bss_start变量保存于r1
    ldr r2, =__bss_end      @ 将链接脚本变量__bss_end变量保存于r2
    mov r3, #0
clean:
    strb r3, [r1]           @ 将当前地址下的数据清零
    add r1, r1, #1          @ 将r1内存储的地址+1
    cmp r1, r2              @ 相等:清零操作结束;否则继续执行clean函数清零bss段
    bne clean
    
    mov pc, lr

并在进入主函数前调用 bl clean_bss /* 清零bss段 */

数据段再单独重定位

事出有因,想要把 .data 段的数据放到 片内内存中以加快访问速度,参考芯片手册得到片内RAM的地址为:0x900000 ~ 0x91FFFF,共128KB(当然不会很大,也就裸机下的编一编、学一学行,Linux 系统等的大型工程就不适合了),所以我们将 .data 段重定位后的地址设置为0x900000。

第一步:把链接脚本 imx6ull.lds 中的 .data : { *(.data) }换成下面的:

     data_load_addr = .;                    
     .data 0x900000 : AT(data_load_addr) 
     {
       data_start = . ;                  //addr = 0x900000
       *(.data)
       data_end = . ;                    //addr = 0x900000 + SIZEOF(.data)
     }

第二步:在启动文件中,复制 data 段数据到片内内存 data_start

 copy_data:
      /* 重定位data段 */
      ldr r1, =data_load_addr     /* data段的加载地址, 从链接脚本中得到, 0x8010xxxx */
      ldr r2, =data_start        /* data段重定位地址, 从链接脚本中得到, 0x900000 */
      ldr r3, =data_end          /* data段结束地址, 从链接脚本中得到,0x90xxxx */
 cpy:
      ldr r4, [r1]              /* 从r1读到r4 */
      str r4, [r2]              /* r4存放到r2 */
      add r1, r1, #4           /* r1+1 */
      add r2, r2, #4           /* r2+1 */
      cmp r2, r3               /* r2 r3比较 */
      bne cpy                  /* 如果不等则继续拷贝 */
​
      mov pc, lr               /* 跳转回调用copy_data函数之前的地址 */

并在进入主函数前调用 bl copy_data /* 复制 data 段数据到片内内存 data_start */

100ask imx6ull 的 《IMX6ULL裸机开发完全手册》中的 "第13篇 IMX6ULL裸机开发 - 9.4.3 总结:如何在C函数中使用链接脚本变量" 章节讲了如何在 C 程序中调用链接脚本中的表示地址的变量,从而可以在 C 程序中实现 "清零 bss 段"和"数据段搬运到片内内存",而不用在启动代码里完成这些操作。

100ask imx6ull 的 《IMX6ULL裸机开发完全手册》中的 "第13篇 IMX6ULL裸机开发 - 9.5 重定位全部代码" 章节讲了将全部应用的二进制数据搬到芯片的内部内存(128KB),并在其内运行,并且使用 C 程序实现 bss 段清零。其步骤是:第一步,修改链接脚本,段顶位置加上 . = 0x900000;,并加上头、尾的地址标识字符;第二步,在 C 程序中利用头、尾的地址标识字符将其间的数据搬运到芯片内部内存地址;第三步,修改启动文件汇编程序,跳转到内部内存的应用数据处执行。

修改应用在内存中的存放地址

IVT 中的 entry(指示 app.bin 在内存中的位置,即程序数据被复制到内存哪里)的地址在 Makefile 中调用 mkimage 工具时是可以指定,需要改相关联的几个地方如下:

假设应用的二进制数据(app.bin)原来是要存放在内存的 0x80100000 位置,现在要改为 app_address 处。

  1. Makefile 文件中修改 -e 选项后的地址 mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80100000 -d relocate.bin relocate.imx

  2. 链接脚本 imx6ull.lds 中 SECTIONS { . = 0x80100000;... 此处改为 app_address 。

  3. 启动文件 start.S 内,要修改栈地址 sp,ldr sp,=0x80200000 此处根据 app_address 与 0x80100000 的偏移相应修改,对于小的裸机程序,可以至少比 app_address 大 0x00100000。

100ASK IMX6ULL Flashing Tool 工具使用

  • 通过 USB 运行裸机程序(不需要烧写,通过u-boot直接在内存中运行):

    板子设到 USB 启动,在 100ask_imx6ull_flashing_tool 工具中的“专业版”界面,打开 .imx 文件,直接点运行。

  • 通过 USB 烧写裸机程序:

    板子设到 USB 启动,在 Tool 中的“基础版”界面,若选 EMMC ,则用 .imx 文件,若选 SD ,则用 .img 文件。成功后,断电,切到 EMMC 或 SD 启动模式,再上电。

    或者在 win 上,用 win disk imager 工具,把 .img 文件写到 SD 卡。

  • 基础版界面详情:

按钮作用
烧写整个系统“选择设备”为EMMC时,把emmc.img烧到EMMC上; “选择设备”为SD/TF时,把sdcard.img烧到SD/TF卡上; “选择设备”为NAND时,把rootfs.ubi烧到Nand Flash上; 并且会烧写对应的U-Boot,请看下面的“更新Uboot”按钮说明。
更新内核把zImage上传到根文件系统的/boot目录 (对于Nand,是直接烧到内核分区)
更新设备树把100ask_imx6ull-14x14.dtb上传到根文件系统的/boot目录 (对于Nand,是直接烧到设备树分区)
更新Uboot对于IMX6ULL全功能版: ①“选择设备”为EMMC时,把u-boot-dtb.imx烧写到EMMC ②“选择设备”为SD/TF时,把u-boot-dtb.imx烧写到SD/TF卡 对于IMX6ULL mini nand版: ①“选择设备”为NAND时,把u-boot-dtb_nand.imx烧写到Nand Flash ②“选择设备”为SD/TF时,把u-boot-dtb_nandsd.imx烧写到SD/TF卡
烧写裸机把所选裸机文件,烧写到EMMC、SD/TF卡或Nand Flash
上传文件把所选用户文件,上传到根文件系统的/目录 对于imx6ull mini nand版,无法上传文件(只支持ext4文件系统,而它不是)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1194684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux内核有什么之内存管理子系统有什么第六回 —— 小内存分配(4)

接前一篇文章:Linux内核有什么之内存管理子系统有什么第五回 —— 小内存分配(3) 本文内容参考: linux进程虚拟地址空间 《趣谈Linux操作系统 核心原理篇:第四部分 内存管理—— 刘超》 特此致谢! 二、小…

SpringData、SparkStreaming和Flink集成Elasticsearch

本文代码链接:https://download.csdn.net/download/shangjg03/88522188 1 Spring Data框架集成 1.1 Spring Data框架介绍 Spring Data是一个用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快…

vue项目pdf文件的预览

1.下载 您可以在以下网址下载pdfjsLib&#xff1a;https://github.com/mozilla/pdf.js pdfjsLib是一个开源项目&#xff0c;您可以在GitHub上找到其源代码和相关资源。 2.放置文件位置 3.进入 在index.html引入 <script src"<% BASE_URL %>static/pdfjs-dist/b…

赛宁网安入选国家工业信息安全漏洞库(CICSVD)2023年度技术组成员单

近日&#xff0c;由国家工业信息安全发展研究中心、工业信息安全产业发展联盟主办的“2023工业信息安全大会”在北京成功举行。 会上&#xff0c;国家工业信息安全发展研究中心对为国家工业信息安全漏洞库&#xff08;CICSVD&#xff09;提供技术支持的单位授牌表彰。北京赛宁…

WPF ToggleButton 主题切换动画按钮

WPF ToggleButton 主题切换动画按钮 仿造最近看到的html中的一个效果&#xff0c;大致思路是文章这样&#xff0c;感觉还可以再雕琢一下。 代码如下 XAML: <UserControl x:Class"WPFSwitch.AnimationSwitch"xmlns"http://schemas.microsoft.com/winfx/200…

8255 boot介绍及bring up经验分享

这篇文章会简单的介绍8255的启动流程&#xff0c;然后着重介绍8255在实际项目中新硬件上的bring up工作&#xff0c;可以给大家做些参考。 8255 boot介绍 下面这些信息来自文档&#xff1a;《QAM8255P IVI Boot and CoreBSP Architecture Technical Overview》 80-42847-11 R…

Django debug page XSS漏洞复现_(CVE-2017-12794)

Django debug page XSS漏洞复现_(CVE-2017-12794) 复现过程 首先进入靶场环境 按照他写的&#xff0c;需要给这个变量赋值 创建一个用户&#xff0c;用弹窗做用户名 http://10.4.7.137:8000/create_user/?username<script>alert(1)</script>返回&#xff0c;然…

Unity中Shader的间接光的产生Meta Pass

文章目录 前言Unity中Shader的间接光的产生Meta Pass&#xff0c;这也是属于全局光照 GI 的内容。主要实现像现实生活中&#xff0c;光线照到有颜色的物体后&#xff0c;该物体有反射出该颜色的光的效果。 一、我们先使用Unity自带的Shader看看间接光效果1、先按照如下设置搭建…

C++ | 继承和多态

目录 继承 继承的概念及用法 继承的作用域 向上转型和向下转型 继承过程中的默认生成函数 菱形继承及其解决方案 - 虚继承 虚继承的原理 - 虚基类表 继承和组合 多态 虚函数 多态的定义及使用 纯虚函数与抽象类 多态的原理 小点补充 虚表的位置 父类指针new一个…

聊聊测试左移到需求阶段

这是鼎叔的第七十九篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。本人新书《无测试组织-测试团队的敏捷转型》​​​​​​​已出版&#xff08;机械工业出版…

单链表的基本操作代码

#include<stdio.h> #include<stdlib.h> typedef int Status;//函数类型&#xff08;当return值为函数结果状态时用&#xff09;需要自定义&#xff01; typedef int ElemType;//相当于给 int起个别名 typedef struct LinkList{ ElemType data; struct L…

JMeter实现持续压测websocket

1、安装插件&#xff1a;JMeter WebSocket Samplers pjtr / JMeter WebSocket Samplers / Downloads — Bitbuckethttps://bitbucket.org/pjtr/jmeter-websocket-samplers/downloads/ 将下载的Jar包放在安装jmeter的/lib/ext路径下&#xff0c;重启生效 查看测试计划--》配置…

python爬虫怎么翻页

爬虫程序的代码实现如下&#xff1a; #include <iostream> #include <string> #include <curl/curl.h>int main() {CURL *curl;CURLcode res;std::string readBuffer;curl_global_init(CURL_GLOBAL_DEFAULT);curl curl_easy_init();if(curl) {curl_easy_se…

ChatGPT、GPT-4 Turbo接口调用(stream模式)

接口地址 https://chat.xutongbao.top/api/light/chat/createChatCompletion 请求方式 post 请求参数 model可选值&#xff1a; “gpt-3.5-turbo-1106”、 “gpt-3.5-turbo-16k” 、 “gpt-4”、“gpt-4-1106-preview”。 默认值为&#xff1a; “gpt-3.5-turbo-1106” to…

LeetCode 17. 电话号码的字母组合 中等

题目 - 点击直达 1. 17. 电话号码的字母组合 中等1. 题目详情1. 原题链接2. 题目要求3. 基础框架 2. 解题思路1. 思路分析2. 时间复杂度3. 代码实现 3. 知识与收获 1. 17. 电话号码的字母组合 中等 1. 题目详情 1. 原题链接 LeetCode 17. 电话号码的字母组合 中等 2. 题目要…

后端架构选择:构建安全强大的知识付费小程序平台

构建知识付费小程序平台需要考虑后端架构&#xff0c;确保系统安全性、性能和可扩展性。以下是一些常见的后端技术和最佳实践&#xff0c;能帮助您构建强大且安全的知识付费小程序平台。 1. 服务器端语言和框架选择 选择流行、成熟的后端语言和框架&#xff0c;如Node.js、P…

基于php js+mysql+laravel技术架构的手术麻醉管理系统源码 手麻系统源码

PHP手术麻醉管理系统源码 手麻系统源码 手术麻醉管理系统定义&#xff1a; 手术麻醉系统主要是由麻醉信息管理和监护设备数据采集系统两个部分组成&#xff0c;主要是将麻醉信息和从监护仪器上采集到的数据以及手术信息进行统计。 手术麻醉系统是指专用于住院患者手术与麻醉…

AR工业眼镜:智能化生产新时代的引领者!!

科技飞速发展&#xff0c;人工智能与增强现实&#xff08;AR&#xff09;技术结合正在改变生活工作方式。AR工业眼镜在生产领域应用广泛&#xff0c;具有实时信息展示、智能导航定位、远程协作培训、智能安全监测等功能&#xff0c;提高生产效率、降低操作风险&#xff0c;为企…

网络通信TCP、UDP详解

目录 IP 和端口 网络传输中的 2 个对象&#xff1a;server 和 client 两种传输方式&#xff1a;TCP/UDP TCP 和 UDP 原理上的区别 为何存在 UDP 协议 TCP/UDP 网络通信大概交互图 IP 和端口 所有的数据传输&#xff0c;都有三个要素 &#xff1a;源、目的、长度。 怎么表…

开机自启动笔记本的小键盘

虽然电脑开机次数不多&#xff0c;但每次开机都要摁下小键盘的开关&#xff0c;好烦 终于忍不住了&#xff1a; 将下面文件命名为 XXX.bat echo off rem 禁用批处理文件中的命令回显&#xff0c;以使输出更整洁rem 查询注册表中 "InitialKeyboardIndicators" 的值 r…