Rt-thread [三] link.lds链接脚本详解
Rt-thread [三] link.lds文件详解
- 前言
- 程序编译流程
- 什么是链接
- 基本概念
- RT-thread stm32f407 link.lds链接脚本详解
- 资料
前言
开一个专题,记录自己学习Rt-thread的过程。许多资料都是来源于之前的笔记和摘抄,有些文档和知识点具体的出处不记得了。如果有您的心血并未写明出处,请联系我。
邮箱:kyq18852982072@163.com
微信:18852982072
以我自己学习Rt-thread的经历来说,这并不是一个很难的IOT-OS。只要有足够的耐心,是很容易入门的。而且对于一款国产RTOS而言。找资料是相对来说比较容易的。
程序编译流程
程序的编译流程如上图所示,因为使用IDE时 大部分的编译工作都被IDE 给屏蔽了。我们最终只能看到一个rtthread.bin的可执行二进制文件。 今天我们来说下这个链接部分的工作。
什么是链接
由汇编程序生成的目标文件(*.s)并不能立即就被执行,其中可能还有许多没有解决的问题。
例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:
静态链接
在这种链接方式下,函数的代码将从其所在的静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。
动态链接
在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。
基本概念
来自官网的内存基础介绍:
程序运行之前,需要有文件实体被烧录到 STM32 的 Flash 中,一般是 bin 或者 hex 文件,该被烧录文件称为可执行映像文件。如下图左边部分所示,是可执行映像文件烧录到 STM32 后的内存分布,它包含 RO 段和 RW 段两个部分:其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。
STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。
还记得我们上一章说过的启动流程中的程序的data段和bss段相关操作么。
RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空间的大小;
RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小;
ROM Size 包含了 Code、RO-data 以及 RW-data,表示烧写程序所占用的 Flash 空间的大小;
程序内存布局:
栈区(stack):由编译器自动分配与释放,存放为运行时函数分配的局部变量、函数参数、返回数据、返回地址等。其操作类似于数据结构中的栈。
堆区(heap):一般由程序员自动分配,如果程序员没有释放,程序结束时可能有OS回收。其分配类似于链表。
全局区(静态区static):存放全局变量、静态数据、常量。程序结束后由系统释放。全局区分为已初始化全局区(data)和未初始化全局区(bss)。
常量区(文字常量区):存放常量字符串,程序结束后有系统释放。
代码区:存放函数体(类成员函数和全局区)的二进制代码。
程序的内存分段如上图所示: 那么在系统一启动之后 就会把ROM中的RW RO分段的加载到内存中来。也就是我们在上一章看到的 startup_stm32f407xx.S 在entry函数之前所做的那一部分操作。
那么程序是如何分段加载这些文件到内存的各个区域的呢?
这时候我们就要看链接脚本了。
链接脚本的对象是.o 文件。但是其实文件分段的操作在汇编时由一个个.s中就已经做好了。也就是说链接脚本的作用只不过是把已经分好段的程序 打包排序起来放好。
RT-thread stm32f407 link.lds链接脚本详解
/** linker script for STM32F407VG with GNU ld*//* Program Entry, set to mark it as "used" and avoid gc */
//用MEMORY命令让在SECTIONS命令内*未*引用的selection分配在程序地址空间内的某个存储区域内。
MEMORY //内存
{ROM (rx) : ORIGIN = 0x08000000, LENGTH = 1024k /* 1024K flash */RAM (rw) : ORIGIN = 0x20000000, LENGTH = 128k /* 128K sram */
}
//程序的入口地址
ENTRY(Reset_Handler)
//栈地址
_system_stack_size = 0x400;//SECTIONS命令告诉ld如何把输入文件的sections映射到输出文件的各个section:
//如何将输入section合为输出section;
//如何把输出section放入程序地址空间(VMA)和进程地址空间(LMA).
SECTIONS
{.text : //程序代码段{. = ALIGN(4); //. 是代表定位器 ALIGN(4) 4字节对齐方式_stext = .; //_stext 代码段起始地址 这个在.s 文件中有定义KEEP(*(.isr_vector)) /* Startup code */. = ALIGN(4);*(.text) /* remaining code */*(.text.*) /* remaining code */*(.rodata) /* read-only data (constants) */*(.rodata*)*(.glue_7)*(.glue_7t)*(.gnu.linkonce.t*)/* section information for finsh shell */. = ALIGN(4);__fsymtab_start = .;
// 在连接命令行内使用了选项–gc-sections后,连接器可能将某些它认为没用的section过滤掉,
// 此时就有必要强制连接器保留一些特定的 section,可用KEEP()关键字达此目的。KEEP(*(FSymTab))__fsymtab_end = .;. = ALIGN(4);__vsymtab_start = .;KEEP(*(VSymTab))__vsymtab_end = .;/* section information for utest */. = ALIGN(4);__rt_utest_tc_tab_start = .;KEEP(*(UtestTcTab))__rt_utest_tc_tab_end = .;/* section information for at server */. = ALIGN(4);__rtatcmdtab_start = .;KEEP(*(RtAtCmdTab))__rtatcmdtab_end = .;. = ALIGN(4);/* section information for initial. */. = ALIGN(4);__rt_init_start = .;KEEP(*(SORT(.rti_fn*)))__rt_init_end = .;. = ALIGN(4);PROVIDE(__ctors_start__ = .);KEEP (*(SORT(.init_array.*)))KEEP (*(.init_array))PROVIDE(__ctors_end__ = .);. = ALIGN(4);_etext = .;} > ROM = 0 //将这个段放入ROM 中 /* .ARM.exidx is sorted, so has to go in its own output section. */__exidx_start = .;.ARM.exidx :{*(.ARM.exidx* .gnu.linkonce.armexidx.*)/* This is used by the startup in order to initialize the .data secion */_sidata = .;} > ROM //将这个段放入ROM 中 __exidx_end = .;/* .data section which is used for initialized data */.data : AT (_sidata) //数据段{. = ALIGN(4);/* This is used by the startup in order to initialize the .data secion */_sdata = . ;*(.data)*(.data.*)*(.gnu.linkonce.d*)PROVIDE(__dtors_start__ = .);KEEP(*(SORT(.dtors.*)))KEEP(*(.dtors))PROVIDE(__dtors_end__ = .);. = ALIGN(4);/* This is used by the startup in order to initialize the .data secion */_edata = . ;} >RAM //将这个段放入RAM memroy中 .stack : {. = ALIGN(4);_sstack = .; //栈的起始地址. = . + _system_stack_size; //栈大小. = ALIGN(4); //4字节对齐_estack = .; //栈结束地址} >RAM //将这个段放入RAM memroy中 __bss_start = .; //bss 起始起始.bss :{. = ALIGN(4);/* This is used by the startup in order to initialize the .bss secion */_sbss = .; *(.bss)*(.bss.*)*(COMMON). = ALIGN(4);/* This is used by the startup in order to initialize the .bss secion */_ebss = . ;*(.bss.init)} > RAM //将这个段放入RAM memroy中 __bss_end = .;_end = .; //结束地址/* Stabs debugging sections. */.stab 0 : { *(.stab) }.stabstr 0 : { *(.stabstr) }.stab.excl 0 : { *(.stab.excl) }.stab.exclstr 0 : { *(.stab.exclstr) }.stab.index 0 : { *(.stab.index) }.stab.indexstr 0 : { *(.stab.indexstr) }.comment 0 : { *(.comment) }/* DWARF debug sections.* Symbols in the DWARF debugging sections are relative to the beginning* of the section so we begin them at 0. *//* DWARF 1 */.debug 0 : { *(.debug) }.line 0 : { *(.line) }/* GNU DWARF 1 extensions */.debug_srcinfo 0 : { *(.debug_srcinfo) }.debug_sfnames 0 : { *(.debug_sfnames) }/* DWARF 1.1 and DWARF 2 */.debug_aranges 0 : { *(.debug_aranges) }.debug_pubnames 0 : { *(.debug_pubnames) }/* DWARF 2 */.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }.debug_abbrev 0 : { *(.debug_abbrev) }.debug_line 0 : { *(.debug_line) }.debug_frame 0 : { *(.debug_frame) }.debug_str 0 : { *(.debug_str) }.debug_loc 0 : { *(.debug_loc) }.debug_macinfo 0 : { *(.debug_macinfo) }/* SGI/MIPS DWARF 2 extensions */.debug_weaknames 0 : { *(.debug_weaknames) }.debug_funcnames 0 : { *(.debug_funcnames) }.debug_typenames 0 : { *(.debug_typenames) }.debug_varnames 0 : { *(.debug_varnames) }
}
资料
Linux下的lds链接脚本详解
Rt-thread [三] link.lds链接脚本详解相关推荐
- Linux下的lds链接脚本详解
一. 概论 每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分 ...
- linux下lds链接脚本详解
转载自:http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.shtml 一. 概论 每一个链接过程都由链接脚本(linker ...
- (GCC)STM32CubeMX生成的ld链接脚本详解
本文所使用工程由STM32CubeMX生成,使用芯片:STM32F103ZET6,基本只开了时钟. ld连接脚本内容如下: /* *********************************** ...
- Linux下的lds连接脚本详解,Linux链接脚本学习--lds
许多脚本是相当简单的. 可能最简单的脚本只含有一个命令:'SECTIONS'. 你可以使用'SECTIONS'来描述输出文件的内存布局. 'SECTIONS'是一个功能很强大的命令. 假设你的程序只有 ...
- makefile使用.lds链接脚本以及 $@ ,$^, $, 解析
先来分析一个简单的.lds链接脚本 例1,假如现在有head.c init.c nand.c main.c这4个文件: 1.1 首先创建链接脚本nand.lds: 1 SECTIONS { 2 fir ...
- shell脚本详解(三)——循环语句之for循环
shell脚本详解(三)--循环语句之for循环 一.echo命令 – 输出字符串或提取Shell变量的值 1.格式 2.常用参数 3.示例 二.for循环语句 1.for循环结构 2.例题 ①.例题 ...
- linux 的文件软链接隐藏,Linux inode及硬链接软链接详解
Linux inode及硬链接软链接详解 York • 2020 年 09 月 28 日 Loading... 操作系统的文件数据除了实际内容之外,通常含有非常多的属性,例如 `Linux`操作系统的 ...
- sshd系统自带启动脚本详解
SSH 为 Secure Shell 的缩写.sshd服务是linux系统中最经常使用的服务之一.由于其规避了明文传送口令.内容本文及中间人***的安全隐患,因此经常作为远程管理系统的首选方案.虽然各 ...
- QT快速入门、三点求圆心实现详解
在编程中,会经常用到数学计算,所以C++将常用的数学计算,例如求正余弦等,封装成函数(正是我们在3.2 数学计算中学习到的),我们只需要写入简单的语句就可以执行所需要的功能,这正是函数的意义.在这一章 ...
最新文章
- 第十章 Linux下RPM软件的安装与卸载
- Java Timer定时器 使用
- HDU 4539郑厂长系列故事――排兵布阵(状压DP)
- 天使投资家李镇樟:如何培养世界级企业家
- asp.net mvc web api 参数输入多个参数
- C++函数重载的概念
- SpringBoot2.x 官方墙裂推荐的缓存框架,竟然不是Redis!
- 利用过滤器(Filter)统一处理请与响应乱码
- 决策树实现手写体识别
- 应用程序正常初始化(0xc0150002)失败
- elastic APM 深入测试 二 基于spring cloud微服务框架的分布式追踪
- rust启动错误ple_Rust 错误处理
- java多线程厨师做饼,Java多线程之厨师与食客问题
- python安装gensim_python gensim
- boonton 功率测试软件,Power Tester
- RN 封装 Android原生组件
- 【PSO三维路径规划】基于matlab粒子群算法融合鸡群算法多无人机三维路径规划【含Matlab源码 1792期】
- 轮训、长轮训、长连接
- 计算机视觉中transformer的理解
- LPDDR5 之Link ECC2