给符号赋值
可以给脚本中的符号赋予一个值。这样会定义一个符号,并且将这个符号放入全局符号表中,并且具有全局符号作用域。
简单赋值
可以使用c中的任何赋值运算符,进行赋值:
symbol = expression ;
symbol += expression ;
symbol -= expression ;
symbol *= expression ;
symbol /= expression ;
symbol <<= expression ;
symbol >>= expression ;
symbol &= expression ;
symbol |= expression ;
-
第一个定义一个符号,然后赋值。
-
而后续的赋值之前,这个符号必须已经定义,则值会根据相应的表达式进行相应的调整。
-
特殊符号’.'表示,位置计数器。你可能只会在SECTION命令中使用它。
-
表达式后面的分号是必须的。
表达式的定义见后续部分。
您可以将符号赋值作为命令单独编写,也可以作为SECTIONS命令中的语句编写,或者作为SECTIONS命令中输出节描述的一部分编写。
下面是一个示例,显示了可以使用符号赋值的三个不同位置:
floating_point = 0;
SECTIONS
{
.text:
{
*(.text)
_etext = .;
}
_bdata = (.+3)&~3;
.data:{*(.data)}
}
上面例子中,floating_point被定义为0.
_etext定义为.text section之后的地址。
_bdata将被定义为.text输出sectoin之后的地址,并且4字节对齐。
PROVIDE
某些情况下,我们希望,链接器提供符号的引用,而不提供任何定义。例如:传统的链接器定义了一个etext的符号。但是,ANSI C要求用户能够使用’etext’作为函数名而不会遇到错误。PROVIDE关键字可用来定义符号,例如etext,但是只会有对他的引用,不会有对他的定义。语法如下:
PROVIDE(symbol = expression)
下面是PROVIDE定义etext的一个例子:
SECTIONS{
.text:
{
*(.text)
_etext = .;
PROVIDE(etext = .);
}
}
在这个例子中,如果程序定义了_etext,链接器将报一个多个定义的错误。另外,如果,程序定义了etext,链接器将使用程序中的定义。如果程序没有定义etext,链接器将使用脚本中的定义。
PROVIDE_HIDDEN
跟PROVIDE相似。对于ELF目标端口,这个符号将被隐藏并且不会被导出。
源代码引用
从源代码中访问脚本中定义的变量不太直观。尤其是链接器脚本符号不等同于高级语言中的变量声明,而是一个没有值的符号。
在进一步讨论之前,应该注意到,编译器将源码中的名字放进符号表时,名字会有不同。例如,Fortran编译器常常在前面或者后面加上一个下划线。c++编译器执行一个扩展的名称修改。
因此,源代码中使用的变量名与链接器脚本中定义的变量名之间可能存在差异
例如,在c中,链接器脚本变量可以被称为:
extern int foo;
但是在链接器脚本中,它可能被定义成如下:
_foo = 1000;
但是,在其余的例子中,假设没有发生名称转换.
当用高级语言(如c)声明符号时,会发生两件事 .首先,编译器在程序内存中保留足够的空间来保存符号的值。第二个是编译器在程序的符号表中创建一个包含符号地址的条目。即符号表包含保存符号值的内存块的地址。
例如下面的c声明:
int foo = 1000;
在符号表中创建名为“foo”的项。此项保存“int”大小的内存块的地址,其中数字1000最初存储在该内存块中
当程序引用一个符号时,编译器生成一段代码:这段代码首先访问符号表以找到该符号的内存块的地址,从该块内存中读取该值。因此:
foo = 1;
在符号表中寻找foo项,然后获取相应的地址,然后向这个地址中写入1.反之:
int * a = & foo;
符号表中查找foo,获取到它的地址,然后复制这个地址到一个内存中,这个内存的符号为a
相反,链接器脚本符号声明在符号表中创建一个条目,但不为它们分配任何内存。
因此,它们是一个没有值的地址。例如,链接器脚本定义:
foo = 1000;
在符号表中创建一个名为“foo”的条目,该条目保存内存位置1000的地址.这意味着您无法访问链接器脚本定义的符号的值(它没有值)您只能访问链接器脚本定义的符号的地址
因此,当您在源代码中使用链接器脚本定义的符号时,您应该始终获取该符号的地址,而不要试图使用其值。
例如,假设要将名为.rom的内存的内容复制到名为.flash内存中,并且链接器脚本包含以下声明:
start_of_ROM = .ROM;
end_of_ROM = .ROM + sizeof (.ROM) - 1;
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);
注意&运算符的用法。这是正确的
下面将介绍SECTIONS命令的细节