1.前言

本章继续讲述简单脚本命令的后半部分

2.Assigning Values to Symbols

你可以给一个符号(symbol)赋值,它会把这些定义的符号放入全局符号表(symbols table)中

2.1 Assigning Values to Symbols

可以使用下面的C操作为一个符号指定值:

symbol = expression ;
symbol += expression ;
symbol -= expression ;
symbol *= expression ;
symbol /= expression ;
symbol <<= expression ;
symbol >>= expression ;
symbol &= expression ;
symbol |= expression ;

注:[1]第一条语句为symbol赋值,其它情况symbol必需提前定义。

[2]特别的符号名:"."代表位置计数符,只能在SECTIONS命令中使用

[3]赋值语句后面必需跟一个;

[4]可以把符号赋值作为一个命令写在它的右侧;也可以作为声明写在SECTIONS命令里面;或者作为SECTIONS命令中输出section的一部分

如下示例显示了赋值语句位于不同的位置:

floating_point = ;
SECTIONS
{
.text :
{ The GNU linker
*(.text)
_etext = .;
}
_bdata = (. + ) & ~ ;
.data : { *(.data) }
}

在这个例子中,‘floating_point’符合被定义为0。‘_etext’符号被定义为‘.text’输入段最后的地址。‘_bata’符号被定义为‘.text’输出段最后按4字节对齐的地址

2.2 PROVIDE

很多情况下,脚本中定义的符号任何对象只能对其引用而不能定义它,比如传统链接器定义的'etext'符号。

然而,ANSI C要求用户使用‘etext’符号作为函数名而不能报错,在程序仅仅是引用某个符号而非定义时,RPOVIDE关键字可以定义一个符号,比如‘etext’,语法是PROVIDE(symbol = expression)

下面是一个‘etext’使用的例子:

SECTIONS
{
.text :
{
*(.text)
_etext = .;
PROVIDE(etext = .);
}
}

在这个例子中,如果程序定义‘_etext’(带有前导下划线),链接器会报很多重定义错误。

另一方面,如果程序定义‘etext’(没有前导下划线),那么链接器会默默地使用程序中的定义,如果程序中只是引用了‘etext’而没有定义它,链接器则使用脚本中的定义

2.3 PROVIDE_HIDDEN

类似PROVIDE的用法。对于ELF目标文件,定义的符号会被隐藏不会被导出

补充:如果你查看链接后的文件的符号表,会发现它的符号绑定信息是LOCAL,即内部可见的

2.4 Source Code Reference

从源代码来访问链接脚本中定义的变量其实并不直观,脚本中的符号和高级语言中定义的变量不能等同,它是一个符号而不是一个值

在进一步讨论之前,需要注意的是当源代码中的符号名字被放入符号表(symbol table)中时,编译器通常会把它们进行转换。

举例来说,Fortran语言编译器通常会加前导或后接一个下划线,C++编译器会进行扩展的‘name mangling’。

因此在源代码中的使用的变量和在链接脚本中定义的变量是可能有差异的。举例来说,C语言中引用脚本中的变量像这样:

extern int foo;  

但是在链接脚本中它被定义为这样:

_foo = ; 

不过,在下面的例子中我们假设没有名称转换

当一个符号被声明,比如用C语言,那么会有两件事情发生,第一件事情是编译器会保留一块内存空间来存储这个符号的值,

第二件事情是编译器会在符号表中创建一个符号并存储好该符号的地址,符号表中包含的是存储该符号的值的地址,比如在文件中这样一个C语言的声明:

int foo = ;

会做如下两件事情:

(1)在内存中留出4字节空间,存储1000

(2)在符号表中创建一个‘foo’的符号,符号的地址为整数型数字1000存储在内存中的地址

当一个程序要引用一个符号时,编译器首先会生成一段去符号表中找到该符号内存块地址的代码,然后在生成一段读取内存块值的代码,比如:

foo = ;

去符号表中查询‘foo’符号的地址值,然后往这个地址里写值1,然而:

int * a = & foo; 

去符号表中查询‘foo’符号的地址值,然后将地址直接拷贝到变量‘a’的内存块里面

对比一下链接脚本中的符号声明,它会在符号表中创建一个符号,但是不会给它分配任何内存,所以它们仅仅是一个地址没有值,比如脚本中这样定义:

foo = ;  

在符号表中创建一个‘foo’的符号,然后存储它的地址值为1000,但地址1000指向的存储位置没有任何意义。

这就意味着你不能访问链接脚本中定义的符号的值(它没有值),你唯一能做的就是访问符号的地址

注:foo并不是一个变量,它只是一个值,并不需要在内存中留出一段空间来保存它;
在C语言中,符号表中会有一个名为foo的项,这个项目中的值(地址值)是1000;
注意,这个1000并没有实际存在的内存

因此,当你在源代码中使用一个链接脚本定义的符号时,你应该使用它的地址,而不去尝试使用它的值。

举例,假设你想拷贝一个叫.ROM段的内容到.FLASH段中,链接脚本这样声明的:

start_of_ROM   = .ROM;
end_of_ROM = .ROM + sizeof (.ROM) - ;
start_of_FLASH = .FLASH;

你的C语言代码应该这样写:

extern char start_of_ROM, end_of_ROM, start_of_FLASH;  

memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);  
 

注意,使用‘&’操作也是完全正确的,使用取址符号&去得到它在符号表中的值

Linker Scripts3--简单的链接脚本命令2-Assigning Values to Symbols的更多相关文章

  1. Linker Scripts3--简单的链接脚本命令1

    1.前言 这个部分我们描述了简单的链接脚本命令 2.设置entry point 程序中第一条运行的指令被称为入口点entry point,可以使用ENTRY链接脚本命令设置entry point,参数 ...

  2. Linux链接脚本学习--lds

    一.概论 ld: GNU的链接器. 用来把一定量的目标文件跟档案文件链接在一起,并重新定位它们的数据,链接符号引用. 一般编译一个程序时,最后一步就是运行ld进行链接 每一个链接都被一个链接脚本所控制 ...

  3. Linux链接脚本学习--lds(转)

    Linux链接脚本学习--lds 一.概论 ld: GNU的链接器. 用来把一定量的目标文件跟档案文件链接在一起,并重新定位它们的数据,链接符号引用. 一般编译一个程序时,最后一步就是运行ld进行链接 ...

  4. arm链接脚本

    一. 为什么需要链接脚本 1.1. 从源码到可执行程序(主要有三个步骤:预编译.编译.链接) 1.1.1. 预编译 a. 预编译器执行.譬如C中的宏定义就是由预编译器处理,注释等也是由预编译器处理的. ...

  5. win7 cmd终端连接android手机运行adb shell脚本命令

    win7 cmd终端连接android手机运行adb shell脚本命令 (2013-03-22 20:13:57) 转载▼ 标签: android it shell 连接 linux 分类: 嵌入式 ...

  6. 链接脚本(Linker Script)用法解析(二) clear_table & copy_table

    可执行文件中的.bss段和.data段分别存放未赋初值的全局变量和已赋初值的全局变量,两者的特点分别为: (1).bss段:①无初值,所以不占ROM空间:②运行时存储于RAM:③默认初值为0 (2). ...

  7. GNU linker script,ld script,GNU链接脚本

    https://blog.csdn.net/itxiebo/article/details/50937412 https://blog.csdn.net/itxiebo/article/details ...

  8. 链接脚本(Linker Script)用法解析(一) 关键字SECTIONS与MEMORY

    1.MEMORY关键字用于描述一个MCU ROM和RAM的内存地址分布(Memory Map),MEMORY中所做的内存描述主要用于SECTIONS中LMA和VMA的定义. 2.SECTIONS关键字 ...

  9. 链接脚本(Linker Script)应用实例(一)使用copy table将函数载入到RAM中运行

    将函数载入到RAM中运行需要以下三个步骤: (1)用编译器命令#pragma section "<section name>" <user functions&g ...

随机推荐

  1. UVA11527Unique Snowflakes(滑动窗口 + set判重 | | map)

    题意:输入长度为n的序列a,找到一个尽量长的连续子序列a[l] - a[r],使该序列中没有相同的元素 紫薯P239 序列元素从0开始编号,l 和 r 分别表示子序列左右端点,初始化为0,固定 l,判 ...

  2. 机器学习算法 Python&R 速查表

    sklearn实战-乳腺癌细胞数据挖掘( 博主亲自录制) https://study.163.com/course/introduction.htm?courseId=1005269003&u ...

  3. 使用git遇到的一些问题

    上传github时忽略.DS_Store方法 这个文件在mac中是管理文件夹的位置之类的信息,所以并没有必要上传到git中,这个时候就需要用git.gitignore文件来忽略此类文件. 在默认情况下 ...

  4. Shuttle 学习

    见  http://blog.csdn.net/liu765023051/article/details/38521039

  5. Bootstrap模态框修改出现的位置和大小

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  6. vs code解决golang开发环境问题 dial tcp 216.239.37.1:443: connectex: A connection attempt failed

    安装插件是出现 如下错误提示, https fetch failed: Get https://golang.org/x/tools/cmd/gorename?go-get=1: dial tcp 2 ...

  7. windows安装mysql5.7.xx解压版

    解压后修改配置文件 my.ini [mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] #设置3306端口 port = 3306 ...

  8. ACM-ICPC 2018 焦作赛区网络预赛 G Give Candies(高精度求余)

    https://nanti.jisuanke.com/t/31716 题意 n颗糖果n个人,按顺序给每个人任意数目(至少一个)糖果,问分配方案有多少. 分析 插板法或者暴力打表后发现答案就为2^(n- ...

  9. Python复习笔记(六)网络编程(udp/tcp)

    一.网络-udp(用户数据报协议) 用户数据报协议 类似写信,不安全,数据有可能丢 1.1 ip地址 注意: IP地址127.0.0.1 ~ 127.255.255.255 用于回路测试 私有ip地址 ...

  10. vue 组件数据传递

    vue组件化开发 主要为了把一个大功能拆分成若干个小的功能,解决高耦合问题,同时也方便开发人员维护.   从功能上组件可以分为木偶组件和功能组件. 木偶组件(为了接收数据,渲染数据,基本上是没有逻辑的 ...