/*********************************************************************************************************包含配置文件
*********************************************************************************************************/INCLUDE "config.lds"/*********************************************************************************************************链接配置
*********************************************************************************************************/OUTPUT_FORMAT(elf64-littleaarch64)                                      /*  binary file format          */
OUTPUT_ARCH(aarch64)                                                    /*  target select               */

在allwinner h6 链接脚本里首先引用了 config.lds 文件 ,以下是config.lds文件的内容:

MEMORY
{TEXT (rx) : ORIGIN = (0x40000000 + (1 * 1024 * 1024)), LENGTH = (6 * 1024 * 1024)DATA (rw) : ORIGIN = (0x40000000 + (1 * 1024 * 1024)) + ((6 * 1024 * 1024)), LENGTH = (122 * 1024 * 1024)
}
BOOT_STACK_SIZE = (2 * 128 * 1024);

这里指定了TEXT 和DATA位置和大小 还有系统启动时的堆栈大小。这些值其实是从bsp中的config.ld文件转化过来的。config.ld:

MEMORY
{TEXT (rx) : ORIGIN = BSP_CFG_RAM_BASE, LENGTH = BSP_CFG_TEXT_SIZEDATA (rw) : ORIGIN = BSP_CFG_RAM_BASE + (BSP_CFG_TEXT_SIZE), LENGTH = BSP_CFG_DATA_SIZE
}BOOT_STACK_SIZE = BSP_CFG_BOOT_STACK_SIZE;

而config.ld文件里的这些BSP_RAM_BASE 等宏定义来自于bsp中的config.h文件,因为每个板子的内存,内存映射不一样,所以需要配置text,data段起始地址和大小。

链接脚本中的OUTPUT_FORMAT 指定了输出问题格式,OUTPUT_FORMAT(elf64-littleaarch64)   代表输出的是小段64位的elf文件。 OUTPUT_ARCH(aarch64)  OUTPUT_ARCH代表的是 体系结构。 因为h6是armv8 64位体系结构。 在全志h3中 输出格式配置如下:

OUTPUT_FORMAT(elf32-littlearm)                                          /*  binary file format          */
OUTPUT_ARCH(ARM)                                                        /*  target select               */

OUTPUT_FORMAT 指定了是32位elf文件小端模式。

配置完输出格式,需要对段定义

/*********************************************************************************************************代码段  LMA == VMA == ORIGIN(TEXT)
*********************************************************************************************************/.text : {*(.vector)*(.text)*(.text.*)                                                      /*  cpp namespace function      */*(.romrun)                                                      /*  ROM 中必须的函数            */*(.rodata)                                                      /*  read-only data (constants)  */*(.rodata*)*(.glue_7)*(.glue_7t)} > TEXT

LMA和VMA

LMA(Load Memory Address):我的理解是程序原来在硬盘中,LMA是加载到内存中的某个位置。 这里的>TEXT 将真个.text内容添加到内存到>TEXT 为其实的地址。

VMA(Virtual Memory Address):我的理解是当程序从硬盘加载到内存后,由于有MMU的存在,程序执行是使用虚拟地址,使用VMA可以让LMA处的代码段拷贝到VMA指定的地址处运行。

在MCU中LMA一般被指定为flash地址,VMA指定为内存中地址,当使用代码段内容时搬运到内存中执行。

在SylixOS arm 中.(vector) 段在arm 32体系结构下使用,当使用arm64 时 .(vector) 无效。直接进入到.(text)段开始执行。无论在vector和text 最开始进入系统定义的异常复位。在uboot中会指定起系统的地址,这个地址正是在config.lds指定的TEXT地址,也就是uboot直接启动系统的第一行执行代码就是系统异常入口。

在h3 和 h6 异常复位入口都在startup.S文件中,在h3中如下

;/*********************************************************************************************************
;  异常向量表
;*********************************************************************************************************/.code 32.section .vector, "ax".balign 4FUNC_DEF(vector)B       resetLDR     PC, undefineEntryLDR     PC, swiEntryLDR     PC, prefetchEntryLDR     PC, abortEntryLDR     PC, reserveEntryLDR     PC, irqEntryLDR     PC, fiqEntryFUNC_END()

.code 32 定义代码是32位的 .section .vector 定义了 vector这个段 ,也就是在链接脚本中看到的.(vector)  . 后面 ax是 allocation  execute的缩写 意思可分配和执行。 .balign 4 就是4字节对齐 FUNC_DEF 这个宏是sylixos中对汇编定义一层封装。不同体系结构,gnu汇编可能存在不同,通过这次宏的封装让写代码时风格7一致。函数vector 就是启动系统执行的第一个函数,首先 B reset 跳转到 reset函数,看reset函数内容

UNC_DEF(reset);/*********************************************************************************************************
;  进入 SVC32 模式(关中断)
;*********************************************************************************************************/MSR     CPSR_c, #(SVC32_MODE | DIS_INT);/*********************************************************************************************************
;  初始化堆栈
;*********************************************************************************************************/BL      archMpCurLDR     R1 , =__stack_end                                          ;/*  栈区顶端地址                 */
LINE_LABEL(0)CMP     R0, #0SUBNE   R1, R1, #CPU_STACKS_SIZESUBNE   R0, R0, #1BNE     0bMOV     R0, R1

reset开始观众段初始化堆栈等系统启动过程。

h6 与h3 类似,不过h6 是在.text段

;  复位入口
;*********************************************************************************************************/SECTION(.text)FUNC_DEF(reset);/*********************************************************************************************************
;  关闭 D-Cache(回写并无效)
;  关闭 I-Cache(无效)
;  无效并关闭分支预测
;  关闭 MMU(无效 TLB)
;*********************************************************************************************************/BL      arm64DCacheClearAllBL      arm64DCacheDisableBL      arm64ICacheInvalidateAllBL      arm64ICacheDisableBL      arm64MmuDisable

可以看到系统启动第一行代码就是执行reset函数,然后BL 跳转到arm清cache, 关cache等。

回到链接脚本

  GOT
*********************************************************************************************************/.got : { *(.got.plt) *(.got) } > TEXT/*********************************************************************************************************.ARM.exidx is sorted, so has to go in its own output section.
*********************************************************************************************************/.ARM.exidx : {. = ALIGN(8); PROVIDE(__exidx_start = .); *(.ARM.exidx* .gnu.linkonce.armexidx.*). = ALIGN(8); PROVIDE(__exidx_end = .); } > TEXT/*********************************************************************************************************C++ 全局对象构造与析构函数表 这里放在 .text 和 .ARM.exidx 之后, .data 之前, 这里的  LMA 和 VMA 相同, 如果放在 .data 之后, LMA 与 VMA 不同, 则需要启动程序从装载区搬运到运行区
*********************************************************************************************************/.ctors : {. = ALIGN(8);KEEP (*cppRtBegin*.o(.ctors))KEEP (*(.preinit_array))KEEP (*(.init_array))KEEP (*(SORT(.ctors.*)))KEEP (*(.ctors))KEEP (*cppRtEnd*.o(.ctors))} > TEXT.dtors : {. = ALIGN(8);KEEP (*cppRtBegin*.o(.dtors))KEEP (*(.fini_array))KEEP (*(SORT(.dtors.*)))KEEP (*(.dtors))KEEP (*cppRtEnd*.o(.dtors))} > TEXT/*********************************************************************************************************.data 段数据初始化内容放在这里
*********************************************************************************************************/. = ALIGN(8);PROVIDE (_etext = .);

got全局符号表, .ARM.exidx 没找到详细的资料。

`.ARM.exidx` is the section containing information for unwinding the stack. If your C program has functions that print out a stack backtrace, the functions will likely depend on this section being present.

_etext = . 这里代码段就结束了。

/*********************************************************************************************************数据段.data 段运行地址 VMA 为 ORIGIN(DATA), 装载地址 LMA 为 _etext,连接器会将 .data 的初始化数据放在 _etext 的地方, 然后启动程序必须将 _etext 的内容搬运到 VMA ORIGIN(DATA) 中. 大小等于 SIZEOF(.data)
*********************************************************************************************************/.data ORIGIN(DATA) : AT (_etext) {. = ALIGN(8);PROVIDE (_data = .);*(.data). = ALIGN(8);PROVIDE (_edata = .);} > DATA/*********************************************************************************************************清零段NOLOAD 表示不装载, 启动程序只需要借助 __bss_start 和 __bss_end 两个符号指定的起始地址和结束地址将 .bss 区域清零即可. (注意 *.noinit 可以不进行清零)
*********************************************************************************************************/.bss (NOLOAD) : {. = ALIGN(8);*(.noinit). = ALIGN(8);PROVIDE (__bss_start = .);*(.bss). = ALIGN(8);*(COMMON). = ALIGN(8);PROVIDE (__bss_end = .);} > DATA/*********************************************************************************************************栈段SylixOS 启动时使用,异常入口也使用
*********************************************************************************************************/.stack (NOLOAD) : {. = ALIGN(16);PROVIDE (__stack_start = .);. += BOOT_STACK_SIZE;. = ALIGN(16);PROVIDE (__stack_end = .);} > DATA/*********************************************************************************************************内核堆段
*********************************************************************************************************/.heap (NOLOAD) : {. = ALIGN(16);PROVIDE (__heap_start = .);PROVIDE (__heap_end = (ORIGIN(DATA) + LENGTH(DATA) - 128));} > DATA

data段 LMA和VMA并不相同, 注释里已经有介绍。 bss段和堆栈,异常堆,NOLOAD 也就是不加载,告诉加载器程序启动后不需要搬运到VMA区域。

PROVIDE  一定定义,当 PROVIDE括号内的符号已经在c语言中有定义时这个符号将被取代。__bss_start = . 是将当前的地址赋值给__bss_start  ,c语言中能够访问链接脚本中的符号,这样就能够知道bss段的起始位置。__stack_start 就能知道堆栈的起始位置。PROVIDE等语法具体可参考蜗窝科技 中计算机科学基础知识(三):静态库和静态链接的介绍。

SylixOS allwinner h6 链接脚本相关推荐

  1. makefile使用.lds链接脚本以及 $@ ,$^, $, 解析

    先来分析一个简单的.lds链接脚本 例1,假如现在有head.c init.c nand.c main.c这4个文件: 1.1 首先创建链接脚本nand.lds: 1 SECTIONS { 2 fir ...

  2. 软件构建之链接应用--链接脚本

    链接脚本的基本概念 链接脚本用于描述链接器如何将输入文件格式化为可执行的输出文件,所谓输入文件既为汇编后的目标文件(.o或.obj等结尾). 基本命令: ENTRY (Symbol) 用于设置程序第一 ...

  3. 重写了GD32VF103的启动脚本和链接脚本

    官方的startup和链接脚本有点乱七八糟的,像是调试早期写的东西.我重新整理了下. 启动脚本去掉了中断向量表表部分,能节省一部分代码空间,后续可以自己定义,只要对齐到512字节(这样支持最多128个 ...

  4. Linker Scripts3--简单的链接脚本命令2-Assigning Values to Symbols

    1.前言 本章继续讲述简单脚本命令的后半部分 2.Assigning Values to Symbols 你可以给一个符号(symbol)赋值,它会把这些定义的符号放入全局符号表(symbols ta ...

  5. 【软件开发底层知识修炼】十一 链接器-链接脚本

    上一篇文章学习了链接器之-main函数不是第一个执行的函数:main函数不是第一个执行的函数 今天继续学习链接器,学习链接是如何动作的,从而引入链接脚本的概念.本文就学习链接脚本的概念. 1.链接脚本 ...

  6. S3C2440 lds链接脚本解析

    1.  SECTIONS到底意味着什么 在一个裸版程序里面含有*.lds文件,而lds文件意味着如果你的程序烧录在nandflash,那在nandflash的内存将根据lds文件指定偏移来分布,下面从 ...

  7. linux 链接脚本,Linux下的lds链接脚本简介(一)

    每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空 ...

  8. 链接脚本文件(*.lds)

    链接脚本 SECTIONS {. = 0x87800000;.text : {start.o *(.text)}.rodata ALIGN(4) : {*(.rodata)}.data ALIGN(4 ...

  9. [GNU LD系列 3.1]一些基本的链接脚本概念

    我们需要定义一些基本的概念和词汇来描述链接脚本语言. 链接器(linker)将一系列输入文件(input files)组合起来变成一个单一的输出文件(output file).输出文件和每一个输入文件 ...

  10. [GNU LD系列 3.3] 简单的链接脚本例程

    许多链接脚本是相当简单的. 可能最简单的链接脚本只包含一个命令:'SECTIONS'.我们使用'SECTIONS'命令来描述输出文件的内存布局. 'SECTIONS'命令是一个很强大的命令.在这里我们 ...

最新文章

  1. LabVIEW轮廓分析与比较(基础篇—8)
  2. Windows下Rtools环境安装
  3. 《 Java并发编程从入门到精通》Thread安全与不安全
  4. day11 - 15(装饰器、生成器、迭代器、内置函数、推导式)
  5. 关于ubuntu的root密码问题
  6. matlab调用c++生成dll32位与64位对应关系
  7. 第 2 章:初出茅庐【初级篇 - 2.2 贪心算法】
  8. jsp java 分离,java与jsp页面的字符串拼接和拆分
  9. boost::hana::detail::create用法的测试程序
  10. 设计时晶振的问题库(z)
  11. Java注释教程– ULTIMATE指南(PDF下载)
  12. Juniper SSG 550M HA配置文档
  13. Qt创建ui界面/界面关联类和LNK2001
  14. 大连IT产业解析(2人才篇)
  15. 非线性回归-最小二乘法
  16. 全志V3S H.264 对视频进行编码的过程
  17. Cepton宣布与美国底特律顶级汽车制造商合作,赢得业内最大ADAS激光雷达量产订单
  18. Android Couldn‘t find meta-data for provider with authority
  19. oracle中update语句更新字段
  20. 湘潭大学计算机科学与技术录取分数线,计算机科学与技术专业分数线各大学排名(湖南)...

热门文章

  1. Mysql数据库乱码
  2. c语言 extern_C语言入门笔记(三)
  3. RDD、DataFrame和DataSet的区别
  4. Hadoop在运行几个T数据量时报堆内存溢出
  5. Python零基础学习笔记(三十九)—— time
  6. MySQL之Lock探索(二)
  7. linux定位异常前后日志信息
  8. j$(function() j$(document).ready 区别
  9. PHP 命令行之-F (--process-file) 对每个输入行都执行 PHP 文件 (PHP 5 新加)
  10. windows终止处理程序( __try __finally) 简单解析