• 链接脚本和可执行文件


    几个重要的概念

    摘取自知乎内容:

    链接器与链接脚本 - 知乎

    linker 链接器

    链接器(linker) 是一个程序,这个程序主要的作用就是将目标文件(包括用到的标准库函数目标文件)的代码段、数据段以及符号表等内容搜集起来并按照 ELF或者EXE 等格式组合成一个可执行的二进制文件的过程。

    链接脚本

    链接器在链接过程中需要使用链接脚本。如果没有通过 “-T” 参数指定链接脚本时,链接器会使用内置的链接脚本。链接脚本的作用: 将输入文件的段按照指定的地址空间布局合并到输出文件的段中。输出的文件具有可执行性。

    可执行程序

    任何一个可执行程序,不论是exe还是elf,都是由代码段,数据段,未初始化的数据段等组成。

    section

     

    一个输出段有两个地址 - 虚拟地址(Virtual Memory Address,VMA):运行时段所在的地址,即运行地址 - 加载地址(Load Memory Address,LMA): 加载时段所在的地址

    有个地方需要注意的是,如果没有通过 “AT” 来指定LMA,那么 LMA=VMA,即加载地址等于运行地址。

    一般嵌入式系统中,经常遇到加载地址和运行地址不一致的情况。比如image 存放在Flash中,运行时复制到RAM中。


    分析s32k144的工程

    分析S32K1xx_flash_debug.ld

    1,memory map ,非常重要的一个概念,一个处理器的存储布局,存储器地址空间,cpu指令集直接可以寻址的外存空间。对应于地址空间的实体可以是flash,nandflash,sram,ddr等,如norflash,emmc等使用spi或者mdio控制器才能访问的存储器,则不在这个地址空间内。

    1. /* Specify the memory areas */
    2. MEMORY
    3. {
    4.   /* Flash */
    5.   m_interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400
    6.   m_flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010
    7.   m_text                (RX)  : ORIGIN = 0x00000410, LENGTH = 0x0007FBF0
    8.   /* SRAM_L */
    9.   m_data                (RW)  : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000
    10.   /* SRAM_U */
    11.   m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00007000
    12. }

    2,目标可执行文件是由输入文件的各个段填充过来的,具体填充规则,以及虚拟地址和加载地址的定义均在链接文件中定义。

    中断向量,os的异常向量表表存储在m_interrupts中,m_interrupts段定义在地址空间的RIGIN = 0x00000000, LENGTH = 0x00000400。

    1. .interrupts :
    2.   {
    3.     __VECTOR_TABLE = .;
    4.     . = ALIGN(4);
    5.     "*(.isr_vector)"
    6.     "*(Os_ExceptionVectors)" /* Startup code */
    7.     . = ALIGN(4);
    8.   } > m_interrupts

    代码段(.code)虚拟地址(运行)在m_text (RX) : ORIGIN = 0x00000410, LENGTH = 0x0007FBF0,数据段虚拟地址(运行)在m_data,bss段虚拟地址(运行)在m_data_2中。注意段的虚拟地址和存储地址是两回事。下面会解释。

    3,需要注意链接脚本的地址信息是会被程序引用的,也就是说连接脚本也属于程序的一部分,可以理解为既是程序的一个头文件,又是链接器的脚本程序。

    1. __etext = .;    /* Define a global symbol at end of code. */
    2.   __DATA_ROM = .; /* Symbol is used by startup for data initialization. */
    3.   .data : AT(__DATA_ROM)
    4.   {
    5.     . = ALIGN(4);
    6.     __DATA_RAM = .;
    7.     __data_start__ = .;      /* Create a global symbol at data start. */
    8.     "*(.data)"               /* .data sections */
    9.     "*(.data*)"              /* .data* sections */
    10.     "*(.os_data)"
    11.     "*(.mcal_data)"
    12.     "*(.jcr*)"
    13.     . = ALIGN(4);
    14.     __data_end__ = .;        /* Define a global symbol at data end. */
    15.   } > m_data

    虚拟地址是程序运行的地址,加载地址是程序存储的地址,或者是程序从哪里加载的地方。

    数据段一开始定义了两个宏地址,这两个宏就是会被用到c代码中的。

    __DATA_ROM就是近接着代码段之后的一个地址,AT(__DATA_ROM)表示将.data段加载地址设置为__DATA_ROM。

    在startup_init_bss.c引用了这个地址:

    1. void init_data_bss(void)
    2. data_rom        = (uint8_t *)__DATA_ROM;
    3. /* Copy initialized data from ROM to RAM */
    4.     while (data_rom_end != data_rom)
    5.     {
    6.         *data_ram = *data_rom;
    7.         data_ram++;
    8.         data_rom++;
    9.     }

    startup.s中使用init_data_bss,.data从加载地址拷贝到运行地址(实际上是从flash拷贝到sram中,不拷贝其实也能执行,只要cpu可以寻址就行,flash和sram都在地址空间内)

    1. ; Init .data and .bss sections
    2.     LDR     R0,=init_data_bss
    3.     BLX     R0
    4.     ;cpsie   i               ; Unmask interrupts
    5.     BL      main

    从规格书上找一下memorymap

     

     

    总结:

    1,可执行程序都是由数据段,代码段,bss段等组成。

    2,每个段对应有两个地址:虚拟地址(运行地址),加载地址(存储地址),这个地址都是cpu可以寻址的地址空间。

    3,链接脚本中的地址信息会被C代码引用,链接脚本可以是程序一部分(类似头文件)。

    4,CPU的memory map 对应的存储器可以是flash 也可以是sarm,ddr等,如果加载地址和运行地址不一样,那么程序在运行的时候(startup.s)需要把加载地址放到对应运行的地址中。加载地址和运行地址,是程序员自定义的,受到存储器的特点进行调整。

  • 相关阅读:
    关于方法重写/方法覆盖
    R语言偏相关和典型相关
    974. 和可被 K 整除的子数组
    一文玩转Java 泛型知识
    【批处理DOS-CMD命令-汇总和小结】-环境变量、路径变量、搜索文件位置相关指令——set、path、where
    STM32物联网项目-双极性步进电机
    【Java基础】程序流程控制
    供应商绩效管理:如何衡量以及衡量什么指标?
    Vue自定义组件学习笔记
    图像识别技术:提升零售行业的顾客体验与运营效率
  • 原文地址:https://blog.csdn.net/seek_0380/article/details/127718250