Linker Scripts3--简单的链接脚本命令2-Assigning Values to Symbols
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的更多相关文章
- Linker Scripts3--简单的链接脚本命令1
1.前言 这个部分我们描述了简单的链接脚本命令 2.设置entry point 程序中第一条运行的指令被称为入口点entry point,可以使用ENTRY链接脚本命令设置entry point,参数 ...
- Linux链接脚本学习--lds
一.概论 ld: GNU的链接器. 用来把一定量的目标文件跟档案文件链接在一起,并重新定位它们的数据,链接符号引用. 一般编译一个程序时,最后一步就是运行ld进行链接 每一个链接都被一个链接脚本所控制 ...
- Linux链接脚本学习--lds(转)
Linux链接脚本学习--lds 一.概论 ld: GNU的链接器. 用来把一定量的目标文件跟档案文件链接在一起,并重新定位它们的数据,链接符号引用. 一般编译一个程序时,最后一步就是运行ld进行链接 ...
- arm链接脚本
一. 为什么需要链接脚本 1.1. 从源码到可执行程序(主要有三个步骤:预编译.编译.链接) 1.1.1. 预编译 a. 预编译器执行.譬如C中的宏定义就是由预编译器处理,注释等也是由预编译器处理的. ...
- win7 cmd终端连接android手机运行adb shell脚本命令
win7 cmd终端连接android手机运行adb shell脚本命令 (2013-03-22 20:13:57) 转载▼ 标签: android it shell 连接 linux 分类: 嵌入式 ...
- 链接脚本(Linker Script)用法解析(二) clear_table & copy_table
可执行文件中的.bss段和.data段分别存放未赋初值的全局变量和已赋初值的全局变量,两者的特点分别为: (1).bss段:①无初值,所以不占ROM空间:②运行时存储于RAM:③默认初值为0 (2). ...
- GNU linker script,ld script,GNU链接脚本
https://blog.csdn.net/itxiebo/article/details/50937412 https://blog.csdn.net/itxiebo/article/details ...
- 链接脚本(Linker Script)用法解析(一) 关键字SECTIONS与MEMORY
1.MEMORY关键字用于描述一个MCU ROM和RAM的内存地址分布(Memory Map),MEMORY中所做的内存描述主要用于SECTIONS中LMA和VMA的定义. 2.SECTIONS关键字 ...
- 链接脚本(Linker Script)应用实例(一)使用copy table将函数载入到RAM中运行
将函数载入到RAM中运行需要以下三个步骤: (1)用编译器命令#pragma section "<section name>" <user functions&g ...
随机推荐
- nginx中间件
Nginx简介 Nginx是一个开源且高性能.可靠的HTTP中间件.代理服务.其特点是占有内存少,并发能力强. Nginx优势:IO多路复用epoll 1.什么是IO复用 它是内核提供的一种同时监控多 ...
- OpenDayLight——HelloWorld
OpenDayLight——HelloWorld 既然学习OpenDayLight编程这个鬼,就得像学语言一样来一个HelloWorld来试试水,经过几天的折腾,总算成功输出HelloWorld了,这 ...
- shipyard 管理swarm集群(附etcd发现服务方法)
docker swarm集群 第一步:安装swarm集群 192.168.132.131----->(manger1,node) 192.168.132.132----->(manger2 ...
- centos 7.4 安装gitlab
centos 7.4 安装gitlab #curl -s https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/scrip ...
- 15、JDBC-CallableStatement
一.存储过程 创建 CREATE DEFINER=CURRENT_USER PROCEDURE `adder`(IN a int, IN b int, OUT sum int) BEGIN DECLA ...
- jenkins笔记
java -jar jenkins.war -httpPort=9090 以9090端口启动jekins的web应用(内置jetty)
- Enum入门【原】
package com.bobo.www.cxf.impl; public enum Traffic { Red(1), Green(2), Yellow(3);//必须最前面 private int ...
- golang 缓冲区的终端输入
bufio包实现了有缓冲的I/O.它包装一个io.Reader或io.Writer接口对象,os.stdin就是实现了这个接口 package main import ( "bufio&qu ...
- TCP简介(一)
1. TCP如何利用IP达到自己目的 1.1 IP特点 无连接 不可靠 1.2 TCP将应用程序的传输数据分割成合适的数据块 1.3 定时器 1.4 延迟确认 1.5 检验和 1.6 流量控制 2. ...
- Linux 命令详解(十二)Systemd 入门教程:使用定时器发送邮件(mail)
Systemd 定时器教程:http://www.ruanyifeng.com/blog/2018/03/systemd-timer.html 一.定时任务 Systemd 定时任务:每小时发送一封电 ...