C语言引用连接脚本lds中的符号——清除bss段,c实现方式

时间:2021-11-03 22:36:12

之前我们的启动文件清除bss和拷贝都是通过汇编的方式的实现,但是,我们能够使用C语言,就不使用汇编:

先看连接脚本:

SECTIONS
{
. = 0x30000000; __code_start = .; . = ALIGN();
.text :
{
*(.text)
} . = ALIGN();
.rodata : { *(.rodata) } . = ALIGN();
.data : { *(.data) } . = ALIGN();
__bss_start = .;
.bss : { *(.bss) *(COMMON) }
_end = .;
}

现在我们编写C语言的copy和clean函数,但是在我们的c程序中,需要访问连接脚本中的符号。

先看代码,稍后解释:

void copy2sdram(void)
{
/* 要从lds文件中获得 __code_start, __bss_start
* 然后从0地址把数据复制到__code_start
*/ extern int __code_start, __bss_start; volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
volatile unsigned int *src = (volatile unsigned int *); while (dest < end)
{
*dest++ = *src++;
}
} void clean_bss(void)
{
/* 要从lds文件中获得 __bss_start, _end
*/
extern int _end, __bss_start; volatile unsigned int *start = (volatile unsigned int *)&__bss_start;
volatile unsigned int *end = (volatile unsigned int *)&_end; while (start <= end)
{
*start++ = ;
}
}

start.S部分展示:

C语言引用连接脚本lds中的符号——清除bss段,c实现方式

先不管连接脚本的引用,我们现在讨论一个问题,我们这里使用bl,是相对跳转,我们必须认识到一个问题,此时我们的sdram虽然初始化好了,但是此刻我们sdram里面是没有程序的,需要先进行重定位,把nor flash的内容全部copy到sdram,所以此时(还没有拷贝的时候) copy2sdram是在nor flash里面执行的,那么使用bl也就可以理解了。但是clean_bss这个函数,到底应不应该使用bl呢?似乎好像我们完成了重定位,让text段,rodata段,data段都重定位到了sdram,可是,sdram这上面几个段之后才开始存放bss段,而bss段的变量是为0值得,不会放在bin文件中,如果这里不使用bl clean_bss,而使用 ldr pc,=clean_bss绝对跳转,程序没有输出,因为此时没有保存lr寄存器的值。

但是,如果非要使用ldr绝对跳转呢?可以这样,增加一个标号:

     ldr pc,=m

m:
bl clean_bss
bl main /* 绝对跳转, 跳到SDRAM */

增加标号m,绝对跳转到m处执行,这个时候程序运行于sdram了,后面使用bl指令即可。

为什么想要这样呢?

严格来说,重定位完毕,我们的程序就可以在sdram中运行了,而我们之前的程序采用

bl clean_bss

ldr pc,=main

这样会导致清除bss段的代码还是运行于nor flash上,而并非出于sdram上,所以我们采用绝对跳转的方式。清除bss段。

回到C语言引用连接脚本的方法:

参考连接(请点击)

在编译阶段,会有一个叫做符号表的东西,

C语言引用连接脚本lds中的符号——清除bss段,c实现方式

c程序中不保存lds中的变量,但是万一c程序需要使用lds的文件,就可以通过编译程序时的符号表来引用。

上图已经显示了,C语言是变量名,然后变量地址,而连接脚本lds中,是符号名,然后它的值。

现在,我们这样对比,我们在C语言中要取一个变量的地址,是使用 &符号,就得到符号表中对应的地址了,

同样的道理,在c中引用连接脚本的符号,我们需要的是lds符号的值,也还是通过 &符号,只不过,此时取地址符 & +lds的符号,得到的是符号的值,而不是符号本身的地址了,而这符号的值,却又恰好是某个地址。(请仔细体会这个对比)

所以,我们上面采用外部声明变量的方式。

C函数怎么使用lds文件中的变量abc?
a. 在C函数中声明改变量为extern类型, 比如:
extern int abc;

b. 使用时, 要取址, 比如:
int *p = &abc; // p的值即为lds文件中abc的值

声明的类型可以是int也可以char。此时类型不重要,但是,最好是声明成你想使用的那种类型。

C语言引用连接脚本lds中的符号——清除bss段,c实现方式

这是官方的参考,所以具体声明成什么类型,虽然不影响,但是最好是声明成你想使用的类型。因为在符号表中,连接脚本的符号表是符号和值,直接存储了,没有再开辟地址,在C语言使用extern声明之前,lds中的符号已经被定义在符号表中了,也就意味着,char或int类型修饰对符号的值没有影响,该值早就在符号表确定了,而我们C语言中之所以要声明一个类型,是为了方便后面要使用lds中这个符号的操作,而且C语言语法也要求标识符必须要有类型修饰。

对lds中的符号取地址,得到就是这个符号的值,而这个值,正好代表了某个地址。

C语言引用连接脚本lds中的符号——清除bss段,c实现方式的更多相关文章

  1. gcc连接脚本lds详解

    转载:blog.chinaunix.net/uid-28685940-id-3889918.html 我们对每个c或者汇编文件进行单独编译,但是不去连接,生成很多.o 的文件,这些.o文件首先是分散的 ...

  2. C语言引用另一个源文件中定义的数组

    C语言中是可以引用另外一个源文件的全局数组的,但是不能引用局部数组. 引用方式举例如下: 设a.c文件有有数据定义 int  array1[10]; 现有b.c文件中想访问a.c中的array1数组 ...

  3. 第2阶段——编写uboot之硬件初始化和制作链接脚本lds&lpar;1&rpar;

    目标: 1.关看门狗 2.设置时钟 3.初始化SDRAM (初始化寄存器以及清除bss段) 4.重定位 (将nand/nor中代码COPY到链接地址上,需要初始化nandflash,读flash) 5 ...

  4. 嵌入式Linux-linux连接脚本

    嵌入式Linux-linux连接脚本 介绍 每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出 ...

  5. u-boot中bss段的使用

    近日在调试uboot时,发现了一个现象,即在relocate_code前如果给未初始化或者初始化值为0的变量赋值的话,则在relocate_code后程序无法正常执行.经过学习,恶补如下知识 BSS段 ...

  6. Linux的nm查看动态和静态库中的符号

    功能 列出.o .a .so中的符号信息,包括诸如符号的值,符号类型及符号名称等.所谓符号,通常指定义出的函数,全局变量等等. 使用 nm [option(s)] [file(s)] 有用的optio ...

  7. 让C&num;语言充当自身脚本!——&period;NET中的动态编译

    原文:让C#语言充当自身脚本!--.NET中的动态编译 代码的动态编译并执行是.NET平台提供给我们的很强大的一个工具,用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码 ...

  8. shell中引用其他脚本的方法

    在Shell中引用其他脚本的方法是source   filename.sh 或    .    filename.sh 注意:      .   和   filename.sh之间必须有空格

  9. C语言学习笔记 &lpar;002&rpar; - C&plus;&plus;中引用和指针的区别&lpar;转载&rpar;

    下面用通俗易懂的话来概述一下: 指针-对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,如const.volatile等等.见下图 ...

随机推荐

  1. My安卓知识2--使用listview绑定sqlite中的数据

    我想在我的安卓项目中实现一个这样的功能,读取sqlite数据库中的数据并显示到某个页面的listview控件中. 首先,我建立了一个Service类,来实现对数据库的各种操作,然后在这个类中添加对数据 ...

  2. 创建XML文档结构

    static void CreateXML(string outputPath) { XmlDocument _xmlDoc = new XmlDocument(); string _xmlNode ...

  3. socket发送和接收数据

    1)sendBuf(),sendText(),sendStream() 几乎所有的通信控件都会提供上面的3个方法.首先看看SendBuf(). function TCustomWinSocket.Se ...

  4. mysql 5&period;5 win7安装最后一步总是停止响应

    今天刚开始安装了64位版本的mysql5.5 ,安装很顺利,后来发现库不兼容的问题,于是卸载,安装mysql-5.5.27-win32的32位版本,奇怪了,怎么安装,怎么卸载都不行,就是到最 后一步停 ...

  5. VMProtect使用小计【一】

    文章列表 VMProtect使用小计[一] – 初次使用VMProtect使用小计[二] – 加壳查看VMProtect使用小计[三] – 权限管理 说明 VMProtect的功能我就不说了,详情大家 ...

  6. 50条规则提高PHP开发提高效率技巧

    0.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作 参数的“函数”(译注:PHP手 ...

  7. 解决org&period;hibernate&period;exception&period;SQLGrammarException&colon;could not insert

    今天在使用hibernate搭建项目时碰到了这个错误,找了好半天没能发现错误的原因, 上网求助了一下 发现出现这个bug的原因是因为数据表使用了数据库的关键字(保留字) 然后检查了一下,发现字段名称都 ...

  8. npm run dev时报错&OpenCurlyDoubleQuote;events&period;js&colon;160 throw er&semi; &sol;&sol; Unhandled &&num;39&semi;error&&num;39&semi; event”

    经查,此问题由端口占用导致,node服务器默认端口8080已被其他程序占用,关闭占用端口的程序或者修改node服务器的默认端口即可解决此问题

  9. QuantLib 金融计算——基本组件之 DayCounter 类

    目录 QuantLib 金融计算--基本组件之 DayCounter 类 DayCounter 对象的构造 一些常用的成员函数 如果未做特别说明,文中的程序都是 Python3 代码. QuantLi ...

  10. POJ 1456 Supermarket(贪心&plus;并查集)

    题目链接:http://poj.org/problem?id=1456 题目大意:有n件商品,每件商品都有它的价值和截止售卖日期(超过这个日期就不能再卖了).卖一件商品消耗一个单位时间,售卖顺序是可以 ...