博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C语言引用连接脚本lds中的符号——清除bss段,c实现方式
阅读量:5363 次
发布时间:2019-06-15

本文共 2546 字,大约阅读时间需要 8 分钟。

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

先看连接脚本:

SECTIONS{    . = 0x30000000;    __code_start = .;    . = ALIGN(4);    .text      :    {      *(.text)    }    . = ALIGN(4);    .rodata : { *(.rodata) }    . = ALIGN(4);    .data : { *(.data) }    . = ALIGN(4);    __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 *)0;    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++ = 0;    }}

 

start.S部分展示:

先不管连接脚本的引用,我们现在讨论一个问题,我们这里使用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,=mm:     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中的变量,但是万一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语言使用extern声明之前,lds中的符号已经被定义在符号表中了,也就意味着,char或int类型修饰对符号的值没有影响,该值早就在符号表确定了,而我们C语言中之所以要声明一个类型,是为了方便后面要使用lds中这个符号的操作,而且C语言语法也要求标识符必须要有类型修饰。

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

转载于:https://www.cnblogs.com/yangguang-it/p/8068088.html

你可能感兴趣的文章
成员函数对象类的const和非const成员函数的重载
查看>>
机器学习实战-----八大分类器识别树叶带源码
查看>>
eclipse git 新的文件没有add index选项
查看>>
java 泛型
查看>>
VC NetShareAdd的用法
查看>>
java web项目中后台控制层对参数进行自定义验证 类 Pattern
查看>>
图论学习一之basic
查看>>
Java的Array和ArrayList
查看>>
记录Ubuntu 16.04 安装Docker CE
查看>>
安东尼奥·维瓦尔第——巴洛克音乐的奇葩
查看>>
pandas的增删改查
查看>>
HDU 5933/思维
查看>>
字节对齐
查看>>
Design Tic-Tac Toe
查看>>
SQL中的去重操作
查看>>
uva 12097 - Pie(二分,4级)
查看>>
mongodb索引
查看>>
nginx源码学习资源(不断更新)
查看>>
【bzoj2882】工艺 后缀自动机+STL-map
查看>>
[redis] redis
查看>>