=============================================
title:2019-8-10-linux
=============================================
-
title:2019-8-10-linux
- 程序的组成
-
代码重定位
- 链接脚本的作用
-
链接脚本介绍
- C函数怎么使用lds文件中的变量
- 如何编写位置无关码
-
重定位代码实例分析
-
title:2019-8-13-linux
-
- ATPCS中各寄存器的使用规则及其名称
-
ARM——THUMB子程序调用规则 ATPCS
- 寄存器的使用规则总结 #F44336
- 数据栈使用规则 #F44336
-
title:2019-8-19-linux
- MakeFile中的符号
- 汇编中标签的含义
-
中断发生时CPU的处理过程
- CPU处理中断程序
- CPU从中断服务程序返回到主程序
-
ARM的七种工作模式
程序的组成
- 代码段:用于存放数据
- 数据段:用于存放全变量(有初始值,且不为零)
- 只读数据段:用于存放只读数据,如:const 变量
- bss段:用于存放未初始化的全局变量,或初始化为零的全局变量
- comment段:用于存放代码的一些注释信息
- 其中bss段和comment的内容不存放在bin文件中。
代码重定位
- 为什么需要代码的重定位
- 2440的启动方式有两种,nor flash启动和nand flash启动。(1) 首先讲解norflash启动的特点,noflash启动时,norflash中的内容可以像内存一样写,但是不能像内存一样读,因此会出现程序中变量进行修改无效的情况,其内部深层次的原因是,程序中的全局变量和静态变量都存放在bin上,写在norflash中,直接修改无效,因此需要重定位。(2)若采用nand flash启动,2440默认会将nand flash的前4k内容拷贝到sram中,此时sram的地址是从0开始的。但是,当代码的大小超过4k时,我们必须采用代码重定位,前4k的代码需要将整个程序拷贝到,sdram中运行。
- 重定位的两种方式

基本概念
- 链接器把一个或多个输入文件合并成一个输出文件,目标文件的每个section至少包含两个信息:名字和大小,大部分section还包括与它关联的一块数据块,一个section可被标记为“loadable”或“allocatable”。loadable section: 在输出文件运行时,相应的section内容将被加载到进程地址空间中。allocatable section:内容为空的section可被标记为可分配的,在输出文件运行时,在进程地址空间中空出大小同section指定大小的部分,在某些情况下,这块内存必须被置零。
- 每一个“loadable”或“allocatable”输出section通常包含两个地址:VMA(virtual memory address)和LMA(load memory address),通常VMA和LMA是相同的。但在嵌入式系统中,经常存在加载地址和执行地址不相同的情况:比如将输出文件加载到开发板的flash中(由LMA决定),而在运行时将位于flash中的输出文件复制到SDRAM中(VMA决定)。
链接脚本的作用
- NORFLASH启动时,NORFLASH中的内容可以向内存一样读,但是不能想内存一样写,因此makefile时,我们需要将data数据段的内容写到SDRAM中,即将数据段的内容指定地址到0x30000000地址处。
- makefile的作用主要为预处理、编译、汇编和链接四个部分,链接的其中一个作用为符号的解析和地址的重定位,我们可以指定代码的起始地址和数据的起始地址。
- 当数据段和代码段之间的间隔较大时,为避免程序链接时,出现较大的空洞,造成程序文件过大,可以运用链接脚本。
链接脚本介绍
GNU链接脚本
链接脚本介绍

-
两个必须的(secname,contents),其他optional
- secname:段名,用以命名此段。
- contents:决定那些内容放在本段,可以是整个目标文件(.o),也可以是目标文件中的某段(代码段、数据段等)。
- start:是段的重定位地址,即 本段运行的地址。如果代码中有位置无关指令,程序运行时这个段必须放在这个地址上。start可以用任意一种描述地址的符号来描述。
- BLOCK(align)指定块对齐。比如,前一个段从0x30000000到0x300003F1,此处标记为ALIGN(4),表示此处最小占用4Bytes。
- NOLOAD:告述加载器程序运行时不加载这段到内存。
- AT(ldadr):定义本段存储的地址,如果不使用这个选项,则加载地址等于运行地址,通过这个选项可以控制各段分别保存在输出文件中的不同位置。
链接脚本代码实例分析
SECTIONS
{
. = 0x30000000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}
C函数怎么使用lds文件中的变量
如何编写位置无关码
-
使用相对跳转命令 b或bl
-
重定位之间,不可使用绝对地址,不可访问全局变量/静态变量,也不可访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问);
-
重定位之后,使用ldr pc = xxx,跳转到从定位之后的地址;
-
写位置无关码,其实就是不使用绝对地址,判断有没有使用绝对地址,除了前面的几条规则,最根本的方法是查看反汇编代码
-
反汇编文件里面,B或者BL某个值,只是起到方便查看的作用,并不是真的跳转
重定位代码实例分析
方式一
void copy2sdram(volatile unsigned int *src,volatile unsigned int *dest,unsigned int len) {
unsigned int i = 0;
while (i < len)
{
*dest++ = *src++;
i += 4;
}
}
void clean_bss(volatile unsigned int *start,volatile unsigned int *end) {
while (start <= end)
{
*start++ = 0;
}
}
---------------------
mov r0,#0
ldr r1,=_start
ldr r2,=__bss_start
sub r2,r2,r1
bl copy2sdram
ldr r0,=__bss_start
ldr r1,=_end
bl clean_bss
---------------------
重点:
- 汇编中,为C语言传入的参数,依次就是R1、R2、R3
方式二
假设我们不想通过汇编传入参数,而是通过C语言直接取参数。
=============================================
title:2019-8-13-linux
=============================================
ATPCS中各寄存器的使用规则及其名称
寄存器 |
别名 |
使用规则 |
r15 |
pc |
程序计数器 |
r14 |
lr |
连接寄存器 |
r13 |
sp |
数据栈指针 |
r12 |
ip |
子程序内部调用的scratch寄存器 |
r11 |
v8 |
ARM状态局部变量寄存器8 |
r10 |
v7sl |
ARM状态局部变量寄存器7、在支持数据栈检查的ATPCS中为数据栈限制指针 |
r9 |
v6sl |
ARM状态局部变量寄存器6、在支持RWPI的ATPCS中为静态基址寄存器 |
r8 |
v5 |
ARM状态局部变量寄存器5 |
r7 |
v4wr |
ARM状态局部变量寄存器4、Thumb状态工作寄存器 |
r6 |
v3 |
ARM状态局部变量寄存器3 |
r5 |
v2 |
ARM状态局部变量寄存器2 |
r4 |
v1 |
ARM状态局部变量寄存器1 |
r3 |
a4 |
参数/结果/scratch寄存器4 |
r2 |
a3 |
参数/结果/scratch寄存器3 |
r1 |
a2 |
参数/结果/scratch寄存器2 |
r0 |
a1 |
参数/结果/scratch寄存器1 |
ARM——THUMB子程序调用规则 ATPCS
- 基本的ATPCS规则包括寄存器使用规则、数据栈使用规则、参数传递规则。
寄存器的使用规则总结
- 子程序间通过寄存器r0-r3来传递参数,这时可以使用他们的别名a0-a3。被调用的子程序返回前无需恢复r0~r3的内容
- 在子程序中,使用r4-r11来保存局部变量,这时可以使用它们的别名v1-v8。如果在子程序中使用了它们的某些寄存器,子程序进入时要保存这些寄存器的值,在返回前恢复它们;对于子程序中没有使用的寄存器则不必进行这些操作。在Thumb程序中,通常只能使用寄存器r4~r7来保存局部变量。(保存r4-r11寄存器中的值是CPU自动运行的,不需要我们写程序来操作)
- 寄存器r12作为子程序间scratch寄存器,别名为ip。
- 寄存器r13作为数据栈指针,别名为sp。在子程序中寄存器r13不能用作其他用途。它的值在进入和退出子程序时必须相等。
- 寄存器r14被称为连接寄存器,别名为lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址(比如将lr的值保存到数据栈中),r14可以用作其他用途。
- 寄存器r15是程序计数器,别名为pc。它不能用做其他用途。
数据栈使用规则
- 数据栈有两个增长方向:向内存地址减少的方向增长时,称为DESCENDING栈;向内存地址增加的方向增长时,称为ASCENDING栈。
- 所谓数据栈的增长就是移动指针。但栈指针指向栈顶元素时,称为FULL栈;当栈指针指向栈顶元素相邻的一个空的数据单元时,称为EMPTY栈。
- 综合这两个特点,数据栈可以分为一下4种:
数据栈类型 |
|
|
FD |
FULL DESCENDING |
满减栈 |
ED |
EMPTY DESCENDING |
空减栈 |
FA |
FULL ASCENDING |
满增栈 |
EA |
EMPTY ASCENDING |
空增栈 |
- ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐。
=============================================
title:2019-8-19-linux
=============================================
MakeFile中的符号
汇编中标签的含义
.global _start
_start:b reset
- 标签就是在某一行程序代码前作一个标记,标签代表的就是这行代码的地址
.global _armboot_start
_armboot_start:
.word _start
- .word expression 就是在当前位置放置一个word类型的值,这个值就是expression,此处的含义就是建立一个全局标签_armboot_start,在这个位置上放置_start的值,则下面的语句会把地址_armboot_start处的内容(_ start)装载到r2中
- 这样做的目的是因为LDR指令的格式为:LDR{条件} RD,<地址>。
中断发生时CPU的处理过程
CPU处理中断程序

enter description here
- 首先保存中断发生时,下一条要执行的指令的地址。
- 拷贝CPSR中的值到SPAR中。
- 改变CPSR中CPU的模式设置位,进入异常模式。
- 从中断向量表中,拷贝中断处理程序的地址到PC中。
CPU从中断服务程序返回到主程序

enter description here
- 将LR寄存器中的值,拷贝到PC寄存器中。
- 将SPSR中内容,拷贝到CPSR寄存器中。
- 清除相关中断标志位。
ARM的七种工作模式
七种模式的概述
- 用户模式(USR):属于正常的用户模式,ARM处理器正常的程序执行状态
- 快速中断模式(FIQ):用于处理快速中断,对高速数据传输或通道处理
- 外部中断模式(IRQ):对于一般情况下的中断进行处理。
- 管理模式(SVC):属于操作系统使用的保护模式,处理软件中断SWI RESET
- 数据访问终止模式(ABT):当数据或指令预取终止式进入该模式,可用于处理处理器故障、实现虚拟存储器和存储保护。
- 系统模式(SYS):运行具有特权的操作系统任务。
- 未定义指令终止模式(UND):处理未定义的指令陷阱,当未定义的指令执行式进入该模式,可用于支持硬件协处理器的仿真。
各模式之间的切换逻辑
- 除了用户模式之外,其他六种均为特权模式。处理器模式可以通过控制进行切换,也可以通过外部中断或异常处理过程进行切换。大多数的用户程序运行在用户模式下,这时,应用程序不能访问一些受操作系统保护的系统,应用程序也不能直接进行处理器模式的切换。当需要进行处理器模式的切换时,应用程序可以产生异常中断,在异常处理中进行处理器模式的切换。