Linux下的lds链接脚本基础(4)

2019-03-10 15:57

6 我的参考 0. 前提

– 只限于ELF文件格式 – 以下讨论用gcc

1. 带版本号的符号的定义(共享库内) 文件b.c内容如下,

int old_true() {

return 1; }

int new_true() {

return 2; }

写连接器的版本控制脚本,本例中为b.lds,内容如下

VER1.0{ new_true; };

VER2.0{ };

$gcc -c b.c

$gcc -shared -Wl,–version-script=b.lds -o libb.so b.o

可以在{}内填入要绑定的符号,本例中new_true符号就与VER1.0绑定了。

那么如果有一个应用程序连接到该库的new_true符号,那么它连接的就是VER1.0版本的new_true符号 如果把b.lds更改为,

VER1.0{ };

VER2.0{ new_true; };

然后在生成libb.so文件,在运行那个连接到VER1.0版本的new_true符号的应用程序,可以发现该应用程序不能运行了,

因为库内没有VER1.0版本的new_true,只有VER2.0版本的new_true。 2. 连接到带版本的符号

写一个简单的应用(名为app)连接到libb.so,应用符号new_true 假设libb.so的版本控制文件为, VER1.0{ };

VER2.0{ new_true; };

$ nm app | grep new_true U new_true@@VER1.0

$

用nm命令发现app连接到VER1.0版本的new_true 3. GNU的扩充

它允许在程序文件内绑定 *符号* 到 *带版本号的别名符号* 文件b.c内容如下,

int old_true() {

return 1; }

int new_true() {

return 2; }

__asm__( ―.symver old_true,true@VER1.0″ ); __asm__( ―.symver new_true,true@@VER2.0″ );

其中,带版本号的别名符号是true,其默认的版本号为VER2.0 供连接器用的版本控制脚本b.lds内容如下,

VER1.0{ };

VER2.0{ };

版本控制文件内必须包含版本VER1.0和版本VER2.0的定义,因为在b.c文件内有对他们的引用

****** 假定libb.so与app.c在同一目录下 ******** 以下应用程序app.c连接到该库,

int true(); int main() {

printf( ―%d ―, true ); }

$ gcc app.c libb.so

$ LD_LIBRARY_PATH=. ./app 2

$ nm app | grep true U true@@VER2.0 $

很明显,程序app使用的是VER2.0版本的别名符号true,如果在b.c内没有指明别名符号true的默认版本,

那么gcc app.c libb.so将出现连接错误,提示true没有定义。 也可以在程序内指定特定版本的别名符号true,程序如下, __asm__( ―.symver true,true@VER1.0″ ); int true(); int main() {

printf( ―%d ―, true ); }

$ gcc app.c libb.so

$ LD_LIBRARY_PATH=. ./app 1

$ nm app | grep true U true@VER1.0 $

显然,连接到了版本号为VER1.0的别名符号true。其中只有一个@表示,该版本不是默认的版本 我的疑问:

版本控制脚本文件中,各版本号节点之间的依赖关系 英文搜索关键字: .symver

versioned symbol

version a shared library 参考:

info ld, Scripts node

=====================

================================== 11. 表达式

———-

表达式的文法与C语言的表达式文法一致,表达式的值都是整型,如果ld的运行主机和生成文件的目标机都是32位,则表达式是32位数据,否则是64位数据。 能够在表达式内使用符号的值,设置符号的值。 下面看六项表达式相关内容, 常表达式:

_fourk_1 = 4K; /* K、M单位 */ _fourk_2 = 4096; /* 整数 */

_fourk_3 = 0×1000; /* 16 进位 */ _fourk_4 = 01000; /* 8 进位 */

1K=1024 1M=1024*1024 符号名:

没有被引号‖\包围的符号,以字母、下划线或‘.'开头,可包含字母、下划线、‘.'和‘-'。当符号名被引号包围时,符号名可以与关键字相同。如,

―SECTION‖=9

―with a space‖ = ―also with a space‖ + 10; 定位符号‘.':

只在SECTIONS命令内有效,代表一个程序地址空间内的地址。

注意:当定位符用在SECTIONS命令的输出section描述内时,它代表的是该section的当前**偏移**,而不是程序地址空间的绝对地址。 先看个例子, SECTIONS {

output : {

file1(.text) . = . + 1000; file2(.text) . += 1000; file3(.text) } = 0×1234; }

其中由于对定位符的赋值而产生的空隙由0×1234填充。其他的内容应该容易理解吧。 再看个例子,

SECTIONS {

. = 0×100 .text: { *(.text) . = 0×200 }

. = 0×500 .data: { *(.data) . += 0×600 }

} .text section在程序地址空间的开始位置是0x 表达式的操作符: 与C语言一致。

优先级 结合顺序 操作符

1 left ! – ~ (1) 2 left * / % 3 left + - 4 left >> = 6 left & 7 left | 8 left && 9 left || 10 right ? :

11 right &= += -= *= /= (2) (1)表示前缀符,(2)表示赋值符。 表达式的计算:

连接器延迟计算大部分表达式的值。 但是,对待与连接过程紧密相关的表达式,连接器会立即计算表达式,如果不能计算则报错。比如,对于section的VMA地址、内存区域块的开始地址和大小,与其相关的表达式应该立即被计算。 例子,

SECTIONS {

.text 9+this_isnt_constant : { *(.text) } }

这个例子中,9+this_isnt_constant表达式的值用于设置.text section的VMA地址,因此需要立即运算,但是由于this_isnt_constant变量的值不确定,所以此时连接器无法确立表达式的值,此时连接器会报错。 相对值与绝对值:

在输出section描述内的表达式,连接器取其相对值,相对与该section的开始位置的偏移

在SECTIONS命令内且非输出section描述内的表达式,连接器取其绝对值 通过ABSOLUTE关键字可以将相对值转化成绝对值,即在原来值的基础上加上表达式所在section的VMA值。 例子,

SECTIONS {

.data : { *(.data) _edata = ABSOLUTE(.); } }

该例子中,_edata符号的值是.data section的末尾位置(绝对值,在程序地址空间内)。 内建函数:

ABSOLUTE(EXP) :转换成绝对值

ADDR(SECTION) :返回某section的VMA值。

ALIGN(EXP) :返回定位符‘.'的修调值,对齐后的值,(. + EXP – 1) & ~(EXP – 1) BLOCK(EXP) :如同ALIGN(EXP),为了向前兼容。

DEFINED(SYMBOL) :如果符号SYMBOL在全局符号表内,且被定义了,那么返回1,否则返回0。例子,

SECTIONS { … .text : {

begin = DEFINED(begin) ? begin : . ; … } … }

LOADADDR(SECTION) :返回三SECTION的LMA MAX(EXP1,EXP2) :返回大者 MIN(EXP1,EXP2) :返回小者 NEXT(EXP) :返回下一个能被使用的地址,该地址是EXP的倍数,类似于ALIGN(EXP)。除非使用了MEMORY命令定义了一些非连续的内存块,否则NEXT(EXP)与ALIGH(EXP)一定相同。

SIZEOF(SECTION) :返回SECTION的大小。当SECTION没有被分配时,即此时SECTION的大小还不能确定时,连接器会报错。 SIZEOF_HEADERS :

sizeof_headers :返回输出文件的文件头大小(还是程序头大小),用以确定第一个

section的开始地址(在文件内)。??? 12. 暗含的连接脚本

输入文件可以是目标文件,也可以是连接脚本,此时的连接脚本被称为 暗含的连接脚本 如果连接器不认识某个输入文件,那么该文件被当作连接脚本被解析。更进一步,如果发现它的格式又不是连接脚本的格式,那么连接器报错。

一个暗含的连接脚本不会替换默认的连接脚本,仅仅是增加新的连接而已。

一般来说,暗含的连接脚本符号分配命令,或INPUT、GROUP、VERSION命令。 在连接命令行中,每个输入文件的顺序都被固定好了,暗含的连接脚本在连接命令行内占住一个位置,这个位置决定了由该连接脚本指定的输入文件在连接过程中的顺序。

典型的暗含的连接脚本是libc.so文件,在GNU/linux内一般存在/usr/lib目录下。


Linux下的lds链接脚本基础(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:9月24-27日-新税收环境下的税收规划与风险防范

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: