关的地址模式,这个常数可以很容易地被载入. 因为这个基寄存器相关模式的偏移值是固定的而且很小(比如,16位), 这会限制常量池的最大尺寸. 所以,一个很大的问题是,为了能够定位所有可能的常数,经常需要使用多个全局指针值. 这个选项在这种情况发生时产生一条警告.
`--warn-once'
对于每一个未定义符号只警告一次, 而不是在每一个用到它的模块中警告一次.
`--warn-section-align'
如果输出节的地址因为对齐被改变了,警告. 通常, 对齐会被输入节设置. 如果'SECTION'命令没有指定节的起始地址, 地址就会被隐式改变.
`--whole-archive'
对于每一个在命令行中'--whole-archive'选项后面出现的档案文件, 在连接中包含档案文件中的所有目标文件, 而不是为需要的目标文件搜索档案文件. 这在把一个档案文件转化为一个共享库时使用, 把所有的目标放到最终的共享 库中. 这个选项可以被多次使用.
在GCC中使用这个选项需要注意两点: 首先,GCC不知道这个选项, 所以,你必须使用'-Wl, -whole-archive'.第二, 不要忘了在你的档案文件列表的后面使用'-Wl, -no-whole-archive',因为GCC会把它自己的档案列表加到你的连接后面, 而这可能并不是你所预期的.
`--wrap SYMBOL'
对SYMBOL符号使用包装函数. 任何未定义的对SYMBOL符号的引用会被解析成'_wrap_SYMBOL'. 而任何未定义的对'_real_SYMBOL'的引用会被解析成SYMBOL.
这可以用来为系统函灵敏提供一个包装. 包装函灵敏应当被叫做
'__wrap_SYMBOL'. 如果需要调用这个函数, 那就应该调用'__real_SYMBOL' 这里是一个没什么实用价值的例子: void *
__wrap_malloc (int c) {
printf (\ return __real_malloc (c); }
如果你使用'--wrap malloc'把这节代码跟其他的代码连接, 那所有的对'malloc'的调用都会调用'__wrap_malloc'函数. 而在'__wrap_malloc'中的 '__real_malloc'会调用真正的'malloc'函数.
你有可能也希望提供一个'__real_malloc'函数, 这样,不带有'--wrap'的连接器也会成功连接.如果你这样做了, 你不能把'__real_malloc'的定义跟'__wrap_malloc'放到同一个文件中;如果放在一起汇编器会在连接器之前把调用解析成真正的'malloc'.
`--enable-new-dtags' `--disable-new-dtags'
连接器可以在ELF中创建一个新的动态标签. 但是旧的ELF系统可能不理解这个.如果你指定了'--enable-new-dtags',动态标签会按需要被创建. 如果你指定了'--disable-new-dtags',那不会有新的动态标签被创建. 缺省地,新的动态标签不会被创建. 注意这些选项只在ELF系统中有效.
标 题: GLD中文手册--(四)
i386 PE平台的特定选项.
-----------------------------------
i386 PE连接器支持'-shared'选项, 它使输出文件为一个动态链接库(DLL),而不是一个普通的可执行文件. 在使用这个选项的时候,你应当为输出文件取名'*.dll',另外, 连接器完全支持标准的'*.def'文件, 这类文件可以在连接器命令行上象一个目标文件一样被指定(实际上, 它应当被放在它从中导出符号的那个档案文件前面,以保证它们象一个普通的目标文件一样被连接进去.)
除了对所有平台通用的那些选项外,i386 PE连接器支持一些只对i386平台专有的命令行选面. 带有值的选项应当用空格或等号把它跟值分隔开.
`--add-stdcall-alias'
如果给出这个选项, 带有标准调用后缀(@NN)的符号会被剥掉后缀后导出.
`--base-file FILE'
使用FILE作为文件名,该文件是存放用'dlltool'产生 DLL文件时所需的所有重定位符的基地址的.(这个选面是i386 PE平台所专有的]
`--dll'
创建一个DLL文件而不是一个常规可执行文件. 你可能在一个给出的'.def'文件中使用'-shared'或指定'LIBRARY'.
`--enable-stdcall-fixup' `--disable-stdcall-fixup'
如果连接器发现有符号不能解析, 它会试图进行'失真连接',即寻找另一个定义的符号,它们只是在符号名的格式上不同(cdecl vs stdcall),并把符号解析为找到的这个符号. 比如, 一个未定义的符号'_foo'可能被连接到函数
'_foo@12', 或者一个未定义的符号'_bar@16'可能被连接到函数'_bar'.如果连接器这么做了, 它会打印出一条警告信息, 因为在正常情况下,这会连接失败, 但有时,由第三方库产生的导入库可能需要这个特性. 如果你指定了
'--enable-stdcall-fixup', 这个特性会被完全开启,警告信息也不会打印出来. 如果你指定了'--disable-stdcall-fixup',这个特性被关闭,而且这样的错误匹配会被认为是个错误.
`--export-all-symbols'
如果给出这个选项,目标中所有由DLL建立的全局符号会被DLL导出. 注意这是缺省情况,否则没有任何符号被导出. 如果符号由DEF文件显式地导出,或由函数本身的属性隐式地导出, 缺省情况是除非选项给出,否则不导出任何其他的符号. 注意符号`DllMain@12',`DllEntryPoint@0',
`DllMainCRTStartup@12'和`impure_ptr'不会自动被导出.而且,由其他的DLL导入的符号也不会被再次导出, 还有指定DLL内部布局的符号,比如那些以'_head_'开头,或者以'_iname'结尾的符号也不会被导出.还
有,'libgcc','libstd++','libmingw32'或'crtX.o'中的符号也不会被导出. ......
环境变量
=====================
你可以通过环境变量`GNUTARGET', `LDEMULATION'和`COLLECT_NO_DEMANGLE'改变'ld'的行为.
`GNUTARGET'在你没有使用'-b'(或者它的同义词'--format')的时候,决定输入文件的格式. 它的值应当是BFD中关于输入格式的一个名字. 如果环境中没有'GNUTARGET'变量, 'ld'使用目标平台的缺省格式. 如果'GNUTARGET'被设为'default', 那BFD就会通过检查二进制的输入文件来找到输入格式; 这个方法通常会成功,但会有潜在的不明确性, 因为没有办法保证指定一个目标文件格式的魔数总是唯一的. 但是, 在每一个系统上的BFD配置程序会把这个系统的常规格式放在搜索列表的首位, 所以不明确性可以通过这种惯列来解决.
`LDEMULATION'在你没有使用'-m'选项的时候决定缺省的模拟器. 模拟器可以影响到连接器行为的很多方面,特别是连接器的缺省连接脚本. 你可以通过
'--verbose'或'-V'选项列出所有可用的模拟器. 如果'-m'选项没有使用, 而且`LDEMULATION'环境变量没有定义, 缺省的模拟器跟连接器如何被配置有关.
一般地,连接器缺省状况下会重构符号.但是,如果在环境中设置了
`COLLECT_NO_DEMANGLE', 那缺省状态下就不会重构符号.这个环境变量在GCC的连接包装程序中会以相似的方式被使用. 这个缺省的行为可以被--demangle'或'--no-demangle'选项覆盖.
标 题: GLD中文手册--(五)
连接脚本
**************
每个连接都被一个'连接脚本'所控制. 这个脚本是用连接命令语言书写的. 连接脚本的一个主要目的是描述输入文件中的节如何被映射到输出文件中,并控制输出文件的内存排布. 几乎所有的连接脚本只做这两件事情. 但是,在需要的时候,连接器脚本还可以指示连接器执行很多其他的操作.这通过下面描述的命令实现.
连接器总是使用连接器脚本的.如果你自己不提供, 连接器会使用一个缺省的脚本,这个脚本是被编译进连接器可执行文件的. 你可以使用'--verbose'命令行选项来显示缺省的连接器脚本的内容. 某些命令行选项,比如'-r'或'-N', 会影响缺省的连接脚本.
你可以过使用'-T'命令行选项来提供你自己的连接脚本. 当你这么做的时候, 你的连接脚本会替换缺省的连接脚本.
你也可以通过把连接脚本作为一个连接器的输入文件来隐式地使用它,就象它们是一个被连接的文件一样.
基本的连接脚本的概念
============================
我们需要定义一些基本的概念与词汇以描述连接脚本语言.
连接器把多个输入文件合并成单个输出文件. 输出文件和输入文件都以一种叫做'目标文件格式'的数据格式形式存在. 每一个文件被叫做'目标文件'. 输出文件经常被叫做'可执行文件',但是由于需要,我们也把它叫做目标文件. 每一个目标文件中,在其它东西之间,有一个节列表.我们有时把输入文件的节叫做输入节; 相似的,输出文件中的一个节经常被叫做输出节.
一个目标文件中的每一个节都有一个名字和一个大小尺寸. 大多数节还有一个相关的数据块, 称为节内容. 某一个节可能被标式讵'loadable',含义是在输出文件被执行时,这
个节应当被载入到内存中去. 一个没有内容的节可能是'allocatable', 含义是内存中必须为这个节开辟一块空间,但是没有实际的内容载入到这里(在某些情况下,这块内存必须被标式讵零). 一个既不是loadable也不是allocatable的节一般含有一些调试信息.
每一个loadable或allocatable的输出节有两个地址. 第一个是'VMA'或称为虚拟内存地址. 这是当输出文件运行时节所拥有的地址. 第二个是\或称为载入内存地址. 这个节即将要载入的内存地址. 这大多数情况下这两个地址是相同的. 它们两个有可能不同的一个例子是当一个数据节在ROM中时, 当程序启动时,被拷贝到RAM中(这个技术经常被用在基于ROM的系统中进行全局变量的初始化). 在这种情况下, ROM地址就是LMA, 而RAM地址就是VMA.
你可以通过使用带有'-h'选项的'objdump'来察看目标文件中的节.
每一个目标文件还有一个关于符号的列表, 被称为'符号表'. 一个符号可能是定义过了的,也可能是未定义的.每一个符号有一个名字, 而且每一个定义的符号有一个地址. 如果你把一个C/C++程序编译为一个目标文件,对于每一个定义的函数和全局或静态变量,你为得到一个定义的符号. 每一个在输入文件中只是一个引用而未定义的函数或全局变量会变成一个未定义的符号.
你可以使用'nm'程序来看一个目标文件中的符号, 或者使用'objdump'程序带有'-t'选项.
连接脚本的格式
==================== 连接脚本是文本文件.
你写了一系列的命令作为一个连接脚本. 每一个命令是一个带有参数的关键字,或者是一个对符号的赋值. 你可以用分号分隔命令. 空格一般被忽略.
文件名或格式名之类的字符串一般可以被直接键入. 如果文件名含有特殊字符,比如一般作为分隔文件名用的逗号, 你可以把文件名放到双引号中. 文件名中间无法使用双引号.
你可以象在C语言中一样,在连接脚本中使用注释, 用'/*'和'*/'隔开. 就像在C中,注释在语法上等同于空格.
简单的连接脚本示例
============================ 许多脚本是相当的简单的.
可能的最简单的脚本只含有一个命令: 'SECTIONS'. 你可以使用'SECTIONS'来描述输出文件的内存布局.
'SECTIONS'是一个功能很强大的命令. 这里这们会描述一个很简单的使用. 让我们假设你的程序只有代码节,初始化过的数据节, 和未初始化过的数据节. 这些会存在于'.text','.data'和'.bss'节, 另外, 让我们进一步假设在你的输入文件中只有这些节.
对于这个例子, 我们说代码应当被载入到地址'0x10000'处, 而数据应当从0x8000000处开始. 下面是一个实现这个功能的脚本: SECTIONS {
. = 0x10000;
.text : { *(.text) } . = 0x8000000;
.data : { *(.data) } .bss : { *(.bss) } }
你使用关键字'SECTIONS'写了这个SECTIONS命令, 后面跟有一串放在花括号中的符号赋值和输出节描述的内容.
上例中, 在'SECTIONS'命令中的第一行是对一个特殊的符号'.'赋值, 这是一个定位计数器. 如果你没有以其它的方式指定输出节的地址(其他方式在后面会描述), 那地址值就会被设为定位计数器的现有值. 定位计数器然后被加上输出节的尺寸. 在'SECTIONS'命令的开始处, 定位计数器拥有值'0'.
第二行定义一个输出节,'.text'. 冒号是语法需要,现在可以被忽略. 节名后面的花括号中,你列出所有应当被放入到这个输出节中的输入节的名字. '*'是一个通配符,匹配任何文件名. 表达式'*(.text)'意思是所有的输入文件中的'.text'输入节.
因为当输出节'.text'定义的时候, 定位计数器的值是'0x10000',连接器会把输出文件中的'.text'节的地址设为'0x10000'.
余下的内容定义了输出文件中的'.data'节和'.bss'节. 连接器会把'.data'输出节放到地址'0x8000000'处. 连接器放好'.data'输出节之后, 定位计数器的