天天看点

【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )

  • ​​一. 处理器工作模式相关介绍​​
  • ​​1. 处理器模式简介​​
  • ​​(1) 处理器工作模式分类​​
  • ​​(2) 处理器不同工作模式区别​​
  • ​​(3) Linux 系统运行的模式​​
  • ​​(4) 特权模式 说明​​
  • ​​(5) 异常模式​​
  • ​​(6) 系统模式​​
  • ​​2. 处理器模式 改变​​
  • ​​(1) 处理器工作模式 改变 的前提条件​​
  • ​​(2) 处理器工作模式 修改方式 ( 程序状态字寄存器 工作模式修改 )​​
  • ​​(3) 程序状态字寄存器 位 类型​​
  • ​​(4) 程序状态字寄存器修改流程​​
  • ​​二. 处理器工作模式修改 代码示例​​
  • ​​1. 汇编代码编写​​
  • ​​(1) 代码 逻辑 分析​​
  • ​​(2) 汇编代码示例​​
  • ​​2. 链接器脚本​​
  • ​​3. Makefile 编译脚本​​
  • ​​4. 编译输出可执行文件​​

​本博客的参考文章及相关资料下载 : 

一. 处理器工作模式相关介绍

​参考手册 :​ ​​A2.2 Processor modes​​

  • ​1.处理器工作模式位置 :​ ​​ARM Architecture Reference Manual [ A 2.2 ] 章节​​;
  • ​2.参考手册下载地址 : ​​

1. 处理器模式简介

(1) 处理器工作模式分类

​处理器的 七种 工作模式 :​

  • ​1.User ( 用户模式 usr ) :​ ​​普通的应用运行的模式​​ ;
  • ​2.FIQ ( 快速中断模式 fiq ) :​ ​​该模式下支持数据的高速传输​​ ;
  • ​3.IRQ ( 普通中断模式 irq ) :​ ​​该模式常用于处理普通的中断​​ ;
  • ​4.Supervisor ( 管理模式 svc ) :​ ​​操作系统使用的一种保护模式​​ , ​本节 BootLoader 就是需要设置这种 svc 模式​;
  • ​5.Abort ( 终止模式 abt ) :​ ​​实现虚拟内存 和 存储器保护​​ ;
  • ​6.Undefined ( 未定义模式 und ) :​ ​​硬件协处理器 的 软件仿真支持​​, 当执行的指令​​处理器不支持​​, 那么会进入该模式;
  • ​7.System ( 系统模式 ) :​ ​​该模式用于运行具有特权的操作系统任务​​, ARMv4 以上的架构才有;

(2) 处理器不同工作模式区别

​处理器 工作模式 区别 :​

  • ​1.可运行的指令不同 :​ 不同的处理器工作模式下 可 ​​运行的 处理器指令​​ 是不同的;
  • ​2.可访问的寄存器不同 :​ 不同处理器模式下 ​​可访问的 寄存器​​ 也是有区别的;
    【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )
  • ​3.分级别处理​ : 7 种工作模式级别不同, 操作系统 一般在级别较高的模式下运行, 应用程序在级别较低的模式下运行;
  • ​4.用户模式说明 :​
    • ​( 1 ) 应用运行 :​ 绝大多数 ​​应用程序都运行在用户模式 ( User ) 下​​;
    • ​( 2 ) 资源限制 :​ 在 ​​① 用户模式下, 应用无法访问受保护的系统资源​​ , ​​② 系统资源的使用 是在操作系统的控制下​​;
    • ​( 3 ) 无法修改模式 :​ 在 ​​用户模式 下, 应用也无法修改 处理器 的工作模式​​ ;

(3) Linux 系统运行的模式

​Linux 操作系统运行模式 :​

  • ​1.应用程序 :​ Linux 系统的​​应用程序运行在 User 用户模式下​​;
  • ​2.内核 :​ Linux 系统 ​​内核 运行在 Supervisor 管理模式下​​ ;

(4) 特权模式 说明

​特权模式 :​

  • ​1.特权概念 :​ ​​除 用户模式 外, 其它的 6 种模式 都是特权模式​​ ;
  • ​2.特权模式 的 功能 :​ 特权模式 比 用户模式 拥有更大的权限, 可 ​​① 访问系统资源​​, ​​② 修改处理器工作模式​​ ;
    • ​( 1 ) 资源访问 :​ 特权模式 ​​拥有访问系统资源的权限​​ ;
    • ​( 2 ) 模式修改 :​ 特权模式 下 可以 ​​修改 处理器的工作模式​​ ;
  • ​3.五种异常模式 :​ 在 6 种 特权模式中, 有 5 种 是 ​​异常模式 , 当对应的异常发生的时候, 就会进入对应的工作模式​​ ;
  • ​4.异常模式寄存器​ : 每种异常模式都有一些​​附加的寄存器​​, 当异常发生的时候, ​​避免影响用户模式的状态​​;

(5) 异常模式

​五种 处理器工作模式 ( 异常模式 ) 对应的异常类型 :​

  • ​1.FIQ ( 快速中断模式 fiq ) :​ ​​该模式下支持数据的高速传输​​ , ​​对应异常类型 为 快速中断 异常​​;
  • ​2.IRQ ( 普通中断模式 irq ) :​ ​​该模式常用于处理普通的中断​​ , ​​对应异常类型 为 普通中断 异常​​ ;
  • ​3.Supervisor ( 管理模式 svc ) :​ ​​操作系统使用的一种保护模式​​ , ​本节 BootLoader 就是需要设置这种 svc 模式​, ​​对应异常类型 为 Reset 和 软中断 异常​​ ;
  • ​4.Abort ( 终止模式 abt ) :​ ​​实现虚拟内存 和 存储器保护​​ , ​​对应异常类型 为 预取指令失败 和 读取数据失败 异常​​ ;
  • ​5.Undefined ( 未定义模式 und ) :​ ​​硬件协处理器 的 软件仿真支持​​, 当执行的指令​​处理器不支持​​, 那么会进入该模式, ​​对应异常类型 为 无法识别指令 异常​​;
【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )

​七种 异常类型 对应的 处理器工作模式 :​ ARM 架构 支持 七种类型的异常,

  • ​1.Reset​ : 处理器在工作时, 突然 ​​按下重启键, 就会触发该异常​​ , ​​该异常对应的处理器工作模式为 svc 模式​​;
  • ​2.Undefined instructions​ : 处理器​​无法识别指令的异常​​, 处理器执行的指令是有规范的, 如果 尝试执行 不符合要求的指令, 就会进入到该异常指令对应的地址中, ​​该异常对应的处理器工作模式为 und 模式​​;
  • ​3.Software interrupt (SWI)​ : ​​软中断​​, 软件中需要去打断处理器工作, 可以使用软中断来执行 , ​​该异常对应的处理器工作模式为 svc 模式​​;
  • ​4.Prefetch Abort (instruction fetch memory abort)​ : ​​预取指令失败​​, ARM 在执行指令的过程中, 要先去预取指令准备执行, 如果预取指令失败, 就会产生该异常, ​​该异常对应的处理器工作模式为 abt 模式​​;
  • ​5.Data Abort (data access memory abort)​ : ​​读取数据失败​​, ​​该异常对应的处理器工作模式为 abt 模式​​;
  • ​6.IRQ (interrupt)​ : ​​普通中断​​ , ​​该异常对应的处理器工作模式为 irq 模式​​;
  • ​7.FIQ (fast interrupt)​ : ​​快速中断​​, 快速中断要比普通中断响应速度要快一些, ​​该异常对应的处理器工作模式为 fiq 模式​​;

(6) 系统模式

​系统模式 :​

  • ​1.进入方式 :​ ​​任何异常都无法进入​​ 系统 模式 ;
  • ​2.寄存器 :​ 从下面的寄存器截图可以看出, ​​系统模式 可 使用的 寄存器​​, ​​与 用户模式 可访问的寄存器 是一样的​​;
    【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )
  • ​3.拥有特权 :​ 系统模式也是一种特权模式, ​​不受用户模式限制影响​​,
  • ​4.模式存在的目的 :​ 系统模式 主要 ​​服务于 需要访问系统资源的 操作系统任务​​, ​​避免使用 与 异常相关的附加寄存器​​;
    • ​​( 1 ) 避免被异常干扰 :​​ 主要是为了 ​​保证 任务状态, 避免 被 发生的异常 干扰​​ ;

2. 处理器模式 改变

(1) 处理器工作模式 改变 的前提条件

​修改 处理器 工作模式 的 前提条件 :​

  • ​1.软件控制 :​ 在 ​​软件控制下, 可以修改处理器的工作模式​​ ;
  • ​2.外部中断 :​ ​​外部中断也会改变处理器的工作模式​​;
  • ​3.异常处理 :​ 当​​异常发生的时候, 也会修改处理器的工作模式​​ ;
  • ​4.BootLoader 工作模式 :​ ​​BootLoader 工作在 svc 模式 下​​, 该模式比较高级, 可以 ​​访问较多的寄存器资源​​ , ​​执行更多的处理器指令​​ ;
  • ​5.如何修改工作模式 :​ 修改 ​​程序状态 寄存器 ( CPSR ) 中的 0 ~ 4 位 即可改变处理器工作模式​​;
    【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )
  • ​6.修改CPSR值 :​ 修改的 ​​程序状态寄存器 0 ~ 4 位的值​​ 为, 下面表中的 模式代码. 即 ​​每行 第三列的 二进制码​​ ;
    【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )
  • ​7.处理器工作模式 对应的 M 位 ( CPRS 0 : 4 ) 值 以及其对应的 可使用的寄存器 :​
    【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )

(2) 处理器工作模式 修改方式 ( 程序状态字寄存器 工作模式修改 )

​参考手册 :​ ​​A2.5 Program status registers​​

  • ​1.处理器工作模式位置 :​ ​​ARM Architecture Reference Manual [ A 2.5 ] 章节​​;
  • ​2.参考手册下载地址 ​​

​程序状态字寄存器 :​

  • ​1.寄存器内容 :​ 该寄存器 中 包含 ​​① 状态码标志位​​, ​​② 中断标志位​​, ​​③ 当前处理器工作模式​​ 和 其它一些 ​​④ 状态​​ 与 ​​⑤ 控制信息​​ ;
  • ​2.CPSR 寄存器 :​ 全称 Current Program Status Register ( 当前程序状态字寄存器 ), ​​保存的是当前的程序状态​​ ;
  • ​3.SPSR 寄存器 :​ 全称 Saved Program Status Register ( 程序状态保存寄存器 ), ​​每个异常都有对应的独立的 SPSR 寄存器​​, ​​当异常发生的时候, 先将 CPSR 寄存器中的值 保存到 SPSR 寄存器中​​, 以便 异常处理完毕后 再回到原来断点处 继续运行 ;
    【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )
  • ​4.SPSR 寄存器分布 :​ ​​用户模式 和 系统模式 没有 对应的 SPSR 寄存器​​, ​​只有 5 种 异常模式才有对应的 SPSR 寄存器​​ ;
    • ​​( 1 ) SPSR 寄存器读写​​ : 在 用户模式 或 系统模式 读写 SPSR 指令 会出现不可预测的错误或行为 ;

(3) 程序状态字寄存器 位 类型

​参考手册 :​ ​​A2.5 Program status registers​​

  • ​1.处理器工作模式位置 :​ ​​ARM Architecture Reference Manual [ A 2.5 ] 章节​​;
  • ​2.参考手册下载地址 ​​

​程序状态字寄存器 位 类型简介 :​

【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )
  • ​1.特权位​ : 在 ​特权模式 下可进行修改​ 的 位数, ​用户模式下不能修改特权位​, 如 ​A, I , F, 和 M ( 0 :4 )​ 位;
  • ​2.用户可写位 :​ ​任何模式都可以修改的 数据位​, 如 ​N, Z, C, V, Q, GE[ 3 : 0 ], E​ 数据位;
  • ​3.执行状态位 :​ 可以从 ​​任何特权模式修改, 用户模式不能修改​​ ; ​J 和 T 两位 是运行状态位​, 在 ARM 状态下总是 0 ;
    • ​( 1 ) CPSR 运行状态位 :​ 使用 MSR 特权指令 将通用寄存器的值 保存到 CPSR 中, ​​J 和 T 两位必须设置为 0​​ , 否则会出现不可预知错误;
    • ​( 2 ) SPSR 运行状态位 :​ 在 上面 的 限制中, 只针对 CPSR 寄存器, SPSR 没有这个限制,
  • ​4.保留位 :​ ​​为之后的功能扩展保留的位数​​ ;
    • ​​( 1 ) 读取 :​​ 保留位 读取时 都当做 0 值;
    • ​​( 2 ) 写入 :​​ 不能向 保留位 写出实际数据 ;

(4) 程序状态字寄存器修改流程

​参考手册 :​ ​​arm汇编手册(中文版).chm​​

  • ​1.本节使用的汇编指令 :​ ​​BIC, ORR, MSR, MRS​​;
  • ​2.arm汇编手册下载地址 ​​

​修改程序状态字寄存器 使用到的汇编指令 :​

  • ​1.将处理器工作模式位 设置 0​ : ​​将 CPRS 程序状态字 寄存器 中的 0 ~ 4 位 设置为 0​​ , 注意 ​​CPRS 不能直接操作​​ ; 使用 BIC 指令进行设置;
    • ​( 1 ) BIC 汇编指令 语法 :​ bic 语法格式 ​

      ​bic <dest>, <op1>, <op2>​

      ​, dest 存放位清除结果, op1 是被清除的对象, op2 是掩码;
    • ​( 2 ) BIC 指令 示例 :​ ​

      ​bic r0, r0, #0b1011​

      ​, 清除 r0 中的 第0, 1, 3 位, 其余位保持不变, 结果放入 r0 中;
    • ​( 3 ) 使用注意点 :​ dest op1 都不能使用立即数, 必须使用寄存器, op2 可以使用立即数;
    • ​( 4 ) 立即数进制 :​ 此处的的立即数必须使用二进制形式 ;
  • ​2.为 处理器工作模式位 设置 1​ : ​将 CPRS 程序状态字 寄存器 中的 0 ~ 4 位 设置为 指定的二进制数字​ , 注意 CPRS 不能直接操作 ; 使用 orr 指令 进行设置 ;
    • ​( 1 ) ORR 汇编指令 语法 :​ ​

      ​ORR{条件}{S} <dest>, <op 1>, <op 2>​

      ​, ​​dest 结果是 op 1 与 op 2 进行或运算的结果​​;
    • ​( 2 ) ORR 指令 说明 :​ ​​dest 必须是寄存器​​, ​​操作数 1 ( op 1 ) 必须是寄存器​​, ​​操作数 2 ( op 2 ) 可以是 ① 寄存器 ② 被移位寄存器 ③ 立即数​​;
    • ​( 3 ) ORR 示例 :​ ​

      ​ORR R0, R0, #3​

      ​, 将 立即数 3 与 R0 寄存器中的值 进行 或 运算, 然后将运算结果存放到 R0 中;
  • ​3.程序状态字寄存器 ( CPSR 和 SPSR ) 访问指令 :​ 使用 MRS MSR 指令, ​​程序状态字 不能使用 通用寄存器的语句 如 MOV 等访问​​, 必须使用 ​​程序状态寄存器的 专用指令​​ 读写;
    • ​( 1 ) 程序状态字寄存器 访问 流程​ : 程序状态字寄存器不能直接访问, 需要​​先将程序状态字寄存器内容导出到通用寄存器中, 才能进行操作​​ , 不能直接修改 CPSR 和 SPSR 中的值 ;
    • ​( 2 ) MRS 指令​ : ​

      ​MRS R0, CPSR​

      ​ , 将 CPRS 寄存器的值 复制 到 R0 寄存器中;
    • ​( 3 ) MSR 指令​ : ​

      ​MSR CPSR, R0​

      ​, 将 R0 寄存器中的值 设置 到 CPSR 寄存器中 ;
  • ​4.流程总结 :​
    • ​( 1 ) 导出 CPSR 寄存器值 :​ ​​使用 MRS 将 CPSR 寄存器的值导出到通用寄存器中​​ ;
    • ​( 2 ) 将工作模式位置 0​ : ​​将导出的 CPSR 寄存器的值的 0 ~ 4 位 设置为 0​​ ;
    • ​( 3 ) 将工作模式位置 1 :​ ​​将导出的 CPSR 寄存器的值的 0 ~ 4 位 设置 对应 的 模式代码​​ ;
    • ​( 4 ) 将设置好的 CPSR 寄存器值设置到 寄存器中 :​ ​​使用 MSR 指令, 将 在通用寄存器中 设置好的 CPSR 寄存器值 设置回 CPSR 寄存器中​​ ;

二. 处理器工作模式修改 代码示例

1. 汇编代码编写

(1) 代码 逻辑 分析

​代码 逻辑 分析 :​

  • ​1.设置 处理器工作模式 时机 :​ 进行 处理器工作模式 设置 是在 ​​开发板上电后, 对应的 reset 异常向量处​​;
  • ​2.设置 指令标号 :​ 设置一个指令标号, 在标号下定义一组汇编指令, ​​当需要执行这一组指令的时候, 在跳转到该标号即可​​;
    • ​( 1 ) 定义标号 :​ ​

      ​set_svc :​

      ​, 在标号下定义一组汇编指令;
  • ​3.导出 CPSR 寄存器值 :​ 使用 MRS 指令, 即 ​

    ​mrs r0 cpsr​

    ​ 将 CPSR 寄存器中的值导出到 R0 寄存器中;
  • ​4.将 R0 中的 M 位 清 0 :​ 在 R0 中将从 CPSR 中导出的寄存器值 对应的 0 ~ 4 位 清0, 使用 ​

    ​bic r0, r0, #0x1f​

    ​, ​​将 R0 寄存器的值 与 #0x1f 进行 与操作, 即 后5 位都设置成0, 然后将 与 操作的结果保存到 R0 寄存器中​​ ;
  • ​5.将 R0 中的 M 位 设置 模式代码 :​ 在下图中, svc 的模式代码时 0b10011 ( 二进制 ), 即 0x13 ( 十六进制 ), 使用 ​

    ​orr r0, r0, #0xd3​

    ​ 语句设置, ​​将 R0 寄存器中的值 与 0x13 进行 或操作, 将 或操作的结果 存放到 R0 寄存器中​​;
    【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )
  • ​6.将值写回 CPSR 寄存器 :​ 使用 MSR 指令 ​

    ​msr cpsr, r0​

    ​ , 将处理完的 CPSR 寄存器值 设置给 CPSR 寄存器;

(2) 汇编代码示例

​汇编代码示例 :​

@****************************  
@File:start.S  
@  
@BootLoader 初始化代码 
@****************************  

.text                                   @ 宏 指明代码段  
.global _start                          @ 伪指令声明全局开始符号  
_start:                                 @ 程序入口标志  
        b   reset                       @ reset 复位异常  
        ldr pc, _undefined_instruction  @ 未定义异常, 将 _undefined_instruction 值装载到 pc 指针中  
        ldr pc, _software_interrupt     @ 软中断异常  
        ldr pc, _prefetch_abort         @ 预取指令异常  
        ldr pc, _data_abort             @ 数据读取异常  
        ldr pc, _not_used               @ 占用 0x00000014 地址                            
        ldr pc, _irq                    @ 普通中断异常  
        ldr pc, _fiq                    @ 软中断异常  

_undefined_instruction: .word undefined_instruction @ _undefined_instruction 标号存放了一个值, 该值是 32 位地址 undefined_instruction, undefined_instruction 是一个地址  
_software_interrupt:    .word software_interrupt    @ 软中断异常  
_prefetch_abort:    .word prefetch_abort            @ 预取指令异常 处理  
_data_abort:        .word data_abort                @ 数据读取异常  
_not_used:      .word not_used                      @ 空位处理  
_irq:           .word irq                           @ 普通中断处理  
_fiq:           .word fiq                           @ 快速中断处理  

undefined_instruction:                              @ undefined_instruction 地址存放要执行的内容  
        nop  

software_interrupt:                                 @ software_interrupt 地址存放要执行的内容  
        nop  

prefetch_abort:                                     @ prefetch_abort 地址存放要执行的内容  
        nop  

data_abort:                                         @ data_abort 地址存放要执行的内容  
        nop  

not_used:                                           @ not_used 地址存放要执行的内容  
        nop  

irq:                                                @ irq 地址存放要执行的内容  
        nop  

fiq:                                                @ fiq 地址存放要执行的内容  
        nop  

reset:                                              @ reset 地址存放要执行的内容  
        bl set_svc                                  @ 跳转到 set_svc 标号处执行

set_svc:
        mrs r0, cpsr                                @ 将 CPSR 寄存器中的值 导出到 R0 寄存器中
        bic r0, r0, #0x1f                           @ 将 R0 寄存器中的值 与 #0x1f 立即数 进行与操作, 并将结果保存到 R0 寄存器中, 实际是将寄存器的 0 ~ 4 位 置 0
        orr r0, r0, #0xd3                           @ 将 R0 寄存器中的值 与 #0xd3 立即数 进行或操作, 并将结果保存到 R0 寄存器中, 实际是设置 0 ~ 4 位 寄存器值 的处理器工作模式代码
        msr cpsr, r0                                @ 将 R0 寄存器中的值 保存到 CPSR 寄存器中      

2. 链接器脚本

​gboot.lds 链接器脚本 代码解析 :​

  • ​1.指明输出格式 ( 处理器架构 ) :​ 使用 ​

    ​OUTPUT_ARCH(架构名称)​

    ​ 指明​​输出格式, 即处理器的架构​​, 这里是 arm 架构的, ​

    ​OUTPUT_ARCH(arm)​

    ​ ;
  • ​2.指明输出程序的入口 :​ 设置编译输出的程序入口位置, 语法为 ​

    ​ENTRY(入口位置)​

    ​, 在上面的 Start.S 中设置的程序入口是 ​

    ​_start​

    ​, 代码为 ​

    ​ENTRY(_start)​

    ​ ;
  • ​3.设置代码段 :​ 使用 ​

    ​.text :​

    ​ 设置代码段;
  • ​4.设置数据段 :​ 使用 ​

    ​.data :​

    ​ 设置数据段;
  • ​5.设置 BSS 段 :​ 使用 ​

    ​.bss :​

    ​ 设置 BSS 段;
    • ​( 1 ) 记录 BSS 段的起始地址 :​ ​

      ​bss_start = .;​

      ​ ;
    • ​( 2 ) 记录 BSS 段的结束地址 :​ ​

      ​bss_end = .;​

      ​ ;
  • ​6.对齐 :​ 每个段都需要设置内存的对齐格式, 使用 ​

    ​. = ALIGN(4);​

    ​ 设置四字节对齐即可;
  • ​7.代码示例 :​
OUTPUT_ARCH(arm)        /*指明处理器结构*/  
ENTRY(_start)           /*指明程序入口 在 _start 标号处*/  
SECTIONS {                
    . = 0x50008000;     /*整个程序链接的起始位置, 根据开发板确定, 不同开发板地址不一致*/  

    . = ALIGN(4);       /*对齐处理, 每段开始之前进行 4 字节对齐*/  
    .text :             /*代码段*/  
    {  
    start.o (.text)     /*start.S 转化来的代码段*/  
    *(.text)            /*其它代码段*/  
    }  

    . = ALIGN(4);       /*对齐处理, 每段开始之前进行 4 字节对齐*/  
    .data :             /*数据段*/  
    {  
    *(.data)  
    }  

    . = ALIGN(4);       /*对齐处理, 每段开始之前进行 4 字节对齐*/  
    bss_start = .;      /*记录 bss 段起始位置*/  
    .bss :              /*bss 段*/  
    {  
    *(.bss)   
    }  
    bss_end = .;        /*记录 bss 段结束位置*/  
}      

3. Makefile 编译脚本

​makefile 文件编写 :​

  • ​1.通用规则 ( 汇编文件编译规则 ) :​ ​汇编文件 编译 成同名的 .o 文件​, 文件名称相同, 后缀不同, ​

    ​%.o : %.S​

    ​, 产生过程是 ​

    ​arm-linux-gcc -g -c $^​

    ​ , 其中 ​

    ​^​

    ​ 标识是所有的依赖文件, 在该规则下 start.S 会被变异成 start.o ;
  • ​2.通用规则 ( C 文件编译规则 ) :​ C 代码编译成同名的 .o 文件, ​

    ​%.o : %.c​

    ​ , 产生过程是 ​

    ​arm-linux-gcc -g -c $^​

    ​ ;
  • ​3.设置最终目标 :​ 使用 ​

    ​all:​

    ​ 设置最终编译目标;
    • ​( 1 ) 依赖文件 :​ ​​产生最终目标需要依赖 start.o 文件​​, 使用 ​

      ​all: start.o​

      ​ 表示最终目标需要依赖该文件;
    • ​( 2 ) 链接过程 :​ ​

      ​arm-linux-ld -Tgboot.lds -o gboot.elf $^​

      ​, 需要使用链接器脚本进行连接, ​​①链接工具是 arm-linux-ld 工具​​, ​​②使用 ​

      ​-Tgboot.lds​

      ​ 设置链接器脚本 是刚写的 gboot.lds 链接器脚本​​, ​​③输出文件是 gboot.elf 这是个中间文件​​, ​​④ 依赖文件是 ​

      ​$^​

      ​ 代表所有的依赖​​;
    • ​( 3 ) 转换成可执行二进制文件 :​ ​

      ​arm-linux-objcopy -O binary gboot.elf gboot.bin​

      ​, 使用 ​

      ​-O binary​

      ​ 设置输出二进制文件, 依赖文件是 ​

      ​gboot.elf​

      ​, 输出的可执行二进制文件 即 结果是 ​

      ​gboot.bin​

      ​ ;
  • ​4.makefile 文件内容 :​
all: start.o #依赖于 start.o  
    arm-linux-ld -Tgboot.lds -o gboot.elf $^    #使用链接器脚本, 将 start.o 转为 gboot.elf  
    arm-linux-objcopy -O binary gboot.elf gboot.bin #将 gboot.elf 转化为可以直接在板子上执行的 gboot.bin 文件  

%.o : %.S   #通用规则, 如 start.o 是由 start.S 编译来的, -c 是只编译不链接  
    arm-linux-gcc -g -c $^  

%.o : %.c   #通用规则, 如 start.o 是由 start.c 编译来的, -c 是只编译不链接  
    arm-linux-gcc -g -c $^  

.PHONY: clean     
clean:              #清除编译信息  
    rm *.o *.elf *.bin      

4. 编译输出可执行文件

​编译过程 :​

  • ​1.文件准备 : 将 汇编代码 ( start.S ) 链接器脚本 ( gboot.lds ) makefile 文件 拷贝到编译目录​ ;
  • ​2.执行编译命令 :​ ​

    ​make​

    ​ ;
  • ​3.编译结果 :​ 可以看到 生成了 编译目标文件 start.o, 链接文件 gboot.elf, 可执行的二进制文件 gboot.bin ;
    【嵌入式开发】ARM 处理器工作模式 及 修改方法 ( 处理器模式 | 设置处理器模式 | 程序状态字寄存器 CPSR SPSR | 模式设置代码编写 | 设置 svc 模式 )

继续阅读