链接脚本和可执行文件

Posted Eliot_shao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链接脚本和可执行文件相关的知识,希望对你有一定的参考价值。

几个重要的概念

摘取自知乎内容:

链接器与链接脚本 - 知乎

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控制器才能访问的存储器,则不在这个地址空间内。

/* Specify the memory areas */

MEMORY



  /* Flash */

  m_interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400

  m_flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010

  m_text                (RX)  : ORIGIN = 0x00000410, LENGTH = 0x0007FBF0

  /* SRAM_L */

  m_data                (RW)  : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000

  /* SRAM_U */

  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00007000

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

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

.interrupts :

  

    __VECTOR_TABLE = .;

    . = ALIGN(4);

    "*(.isr_vector)"

    "*(Os_ExceptionVectors)" /* Startup code */

    . = ALIGN(4);

   > m_interrupts

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

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

__etext = .;    /* Define a global symbol at end of code. */

  __DATA_ROM = .; /* Symbol is used by startup for data initialization. */

  .data : AT(__DATA_ROM)

  

    . = ALIGN(4);

    __DATA_RAM = .;

    __data_start__ = .;      /* Create a global symbol at data start. */

    "*(.data)"               /* .data sections */

    "*(.data*)"              /* .data* sections */

    "*(.os_data)"

    "*(.mcal_data)"

    "*(.jcr*)"

    . = ALIGN(4);

    __data_end__ = .;        /* Define a global symbol at data end. */

   > m_data

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

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

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

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

void init_data_bss(void)

data_rom        = (uint8_t *)__DATA_ROM;

/* Copy initialized data from ROM to RAM */

    while (data_rom_end != data_rom)

    

        *data_ram = *data_rom;

        data_ram++;

        data_rom++;

    

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

; Init .data and .bss sections

    LDR     R0,=init_data_bss

    BLX     R0

    ;cpsie   i               ; Unmask interrupts

    BL      main

从规格书上找一下memorymap

 

 

总结:

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

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

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

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

以上是关于链接脚本和可执行文件的主要内容,如果未能解决你的问题,请参考以下文章

同时为静态库和可执行文件创建makefile

sqlserver 执行一段脚本报错,逐句执行就能成功,怎么回事?

具有多个命令和可重入的 BASH 变量

Linux文件权限符号含义

视频文件中嵌入URL地址是怎么回事?可以去掉吗?

存储可选管道多行字符串和可选参数的批处理脚本