加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Linux > 正文

2019-8-10-linux

发布时间:2020-12-14 00:12:37 所属栏目:Linux 来源:网络整理
导读:============================================= title:2019-8-10-linux ============================================= title:2019-8-10-linux 程序的组成 代码重定位 基本概念 链接脚本的作用 链接脚本介绍 链接脚本代码实例分析 C函数怎么使用lds文件中

=============================================

title:2019-8-10-linux

=============================================

  • title:2019-8-10-linux
    • 程序的组成
    • 代码重定位
      • 基本概念
    • 链接脚本的作用
    • 链接脚本介绍
      • 链接脚本代码实例分析
    • C函数怎么使用lds文件中的变量
    • 如何编写位置无关码
    • 重定位代码实例分析
      • 方式一
        • 重点: #F44336
      • 方式二
  • title:2019-8-13-linux
      • ATPCS中各寄存器的使用规则及其名称
      • ARM——THUMB子程序调用规则 ATPCS
        • 寄存器的使用规则总结 #F44336
        • 数据栈使用规则 #F44336
  • title:2019-8-19-linux
    • MakeFile中的符号
    • 汇编中标签的含义
    • 中断发生时CPU的处理过程
      • CPU处理中断程序
      • CPU从中断服务程序返回到主程序
    • ARM的七种工作模式
      • 七种模式的概述
      • 各模式之间的切换逻辑

程序的组成

  1. 代码段:用于存放数据
  2. 数据段:用于存放全变量(有初始值,且不为零)
  3. 只读数据段:用于存放只读数据,如:const 变量
  4. bss段:用于存放未初始化的全局变量,或初始化为零的全局变量
  5. comment段:用于存放代码的一些注释信息
  • 其中bss段和comment的内容不存放在bin文件中。

代码重定位

  1. 为什么需要代码的重定位
  • 2440的启动方式有两种,nor flash启动和nand flash启动。(1) 首先讲解norflash启动的特点,noflash启动时,norflash中的内容可以像内存一样写,但是不能像内存一样读,因此会出现程序中变量进行修改无效的情况,其内部深层次的原因是,程序中的全局变量和静态变量都存放在bin上,写在norflash中,直接修改无效,因此需要重定位。(2)若采用nand flash启动,2440默认会将nand flash的前4k内容拷贝到sram中,此时sram的地址是从0开始的。但是,当代码的大小超过4k时,我们必须采用代码重定位,前4k的代码需要将整个程序拷贝到,sdram中运行。
  1. 重定位的两种方式

基本概念

  • 链接器把一个或多个输入文件合并成一个输出文件,目标文件的每个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
    1. secname:段名,用以命名此段。
    2. contents:决定那些内容放在本段,可以是整个目标文件(.o),也可以是目标文件中的某段(代码段、数据段等)。
    3. start:是段的重定位地址,即 本段运行的地址。如果代码中有位置无关指令,程序运行时这个段必须放在这个地址上。start可以用任意一种描述地址的符号来描述。
    4. BLOCK(align)指定块对齐。比如,前一个段从0x30000000到0x300003F1,此处标记为ALIGN(4),表示此处最小占用4Bytes。
    5. NOLOAD:告述加载器程序运行时不加载这段到内存。
    6. AT(ldadr):定义本段存储的地址,如果不使用这个选项,则加载地址等于运行地址,通过这个选项可以控制各段分别保存在输出文件中的不同位置。

链接脚本代码实例分析

SECTIONS
{
    . = 0x30000000;		//设置运行时地址

    . = ALIGN(4);		 //设置内存对齐方式
    .text      :			   //设置secname
    {
      *(.text)				//存放所有文件的代码段
    }						  //未设置存储地址,默认存储地址与运行地址相同

    . = ALIGN(4);    //同上
    .rodata : { *(.rodata) }//同上(只读数据段)

    . = ALIGN(4);	//同上
    .data : { *(.data) }//同上(数据段)

    . = ALIGN(4);
    __bss_start = .;//将当前地址赋值给_bss_start变量
    .bss : { *(.bss) *(.COMMON) }//同上
    _end = .; //将当前地址赋值给_end变量
}

C函数怎么使用lds文件中的变量

如何编写位置无关码

  • 使用相对跳转命令 b或bl

  • 重定位之间,不可使用绝对地址,不可访问全局变量/静态变量,也不可访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问);

  • 重定位之后,使用ldr pc = xxx,跳转到从定位之后的地址;

  • 写位置无关码,其实就是不使用绝对地址,判断有没有使用绝对地址,除了前面的几条规则,最根本的方法是查看反汇编代码

  • 反汇编文件里面,B或者BL某个值,只是起到方便查看的作用,并不是真的跳转

重定位代码实例分析

方式一

void copy2sdram(volatile unsigned int *src,volatile unsigned int *dest,unsigned int len) /* src,dest,len */ {
        unsigned int i = 0;

        while (i < len)
        {
            *dest++ = *src++;			//++的优先级要高于*
            i += 4;
        }
    }


    void clean_bss(volatile unsigned int *start,volatile unsigned int *end) /* start,end */ {
        while (start <= end)
        {
            *start++ = 0;			//++的优先级要高于*
        }
    }
--------------------- 

    /* 重定位text,rodata,data段整个程序 */
    mov r0,#0
    ldr r1,=_start         /* 第1条指令运行时的地址 */
    ldr r2,=__bss_start    /* bss段的起始地址 */
    sub r2,r2,r1          /*长度*/


    bl copy2sdram  /* src,len */

    /* 清除BSS段 */
    ldr r0,=__bss_start
    ldr r1,=_end

    bl clean_bss  /* start,end */
---------------------

重点:

  • 汇编中,为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 r2,_armboot_start
  • 这样做的目的是因为LDR指令的格式为:LDR{条件} RD,<地址>。

中断发生时CPU的处理过程

CPU处理中断程序


enter description here

  1. 首先保存中断发生时,下一条要执行的指令的地址。
  2. 拷贝CPSR中的值到SPAR中。
  3. 改变CPSR中CPU的模式设置位,进入异常模式。
  4. 从中断向量表中,拷贝中断处理程序的地址到PC中。

CPU从中断服务程序返回到主程序


enter description here

  1. 将LR寄存器中的值,拷贝到PC寄存器中。
  2. 将SPSR中内容,拷贝到CPSR寄存器中。
  3. 清除相关中断标志位。

ARM的七种工作模式

七种模式的概述

  1. 用户模式(USR):属于正常的用户模式,ARM处理器正常的程序执行状态
  2. 快速中断模式(FIQ):用于处理快速中断,对高速数据传输或通道处理
  3. 外部中断模式(IRQ):对于一般情况下的中断进行处理。
  4. 管理模式(SVC):属于操作系统使用的保护模式,处理软件中断SWI RESET
  5. 数据访问终止模式(ABT):当数据或指令预取终止式进入该模式,可用于处理处理器故障、实现虚拟存储器和存储保护。
  6. 系统模式(SYS):运行具有特权的操作系统任务。
  7. 未定义指令终止模式(UND):处理未定义的指令陷阱,当未定义的指令执行式进入该模式,可用于支持硬件协处理器的仿真。

各模式之间的切换逻辑

  • 除了用户模式之外,其他六种均为特权模式。处理器模式可以通过控制进行切换,也可以通过外部中断或异常处理过程进行切换。大多数的用户程序运行在用户模式下,这时,应用程序不能访问一些受操作系统保护的系统,应用程序也不能直接进行处理器模式的切换。当需要进行处理器模式的切换时,应用程序可以产生异常中断,在异常处理中进行处理器模式的切换。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读