GNU ld链接脚本学习

转载自:http://blog.chinaunix.net/space.php?uid=13701930&do=blog&id=336529

 

链接器脚本

*********** 

 每次一次链接行为都是被链接器脚本控制,这样的脚本是采用链接器命令语言写成.

   这种脚本主要用途描述如何把输入文件中各段(section)组织到输出文件,并控制输出输出文件的内存布局.大部分链接脚本也就做这些操作.有特殊需要时,链接脚本也可以直接指示链接器使用下面列出的命令执行其它行为.

   链接器总是使用链接脚本.如果没有提供自定义链接脚本则链接器使用默认脚本编译进可执行文件. 查看默认链接器脚本可以使用命令行选项'--verbose':ld --verbose;有些命令行选项例如'-r''-N'的使用会影响到默认脚本.

   如果想使用自己的链接脚本可以使用'-T'命令行选项,指定这个选项后会替换默认脚本.

   你也可以以隐含方式使用链接脚本就像命名的输入文件,好像也被链接一样.Implicit Linker Scripts部分.

*Menu:


** Basic Script Concepts::             链接脚本基本概念

** Script Format::                        链接脚本格式

** Simple Example::                     简单样例

** Simple Commands::                 简单命令

** Assignments::                          分配值给符号(symbols)

** SECTIONS::                             段命令

**MEMORY::                                内存命令

**PHDRS::                                   PHDRS 命令

**VERSION::                               版本命令

**Expressions::                           脚本使用的表达式

**Implicit Linker Scripts::             隐示包含链接脚本



Basic Linker Script Concepts

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


   为更好描述链接器脚本语言,我们要定义些基本概念和名词.

   链接器把所有输入文件组织到单个输出文件.输出文件和输入文件都是以特定数据格式存放,就是众所周知的'目标文件'格式.每个文件都被称作目标文件,输出文件通常称作可执行文件,但是为表达方便也称其为目标文件.每个目标文件(排除特殊情况)都有一个段列表.有时我们把输入文件中的段称作输入段(input section),类似地,也会将输出文件段叫它输出段.

   目标文件中每个段都有一个名字和大小,大部分段也有一个与其关联的数据块,称其为'段注释'.段可是标记为'可装入的(loadable)',意思是输出文件运行时上下文是要被装入内存;没有注释的段标记为'可分配的',意思是在内存的这块区域应该被分配,但是没有任何其它内容装入这里(某些情况下必须被置0).既不是可装入又不是可分配的典型段就是调试信息类的.

   每个可装入的或者可分配的输出段会有两个地址:VMA--虚拟内存地址,LMA--装入内存地址.VMA是输出文件运行是段的地址,LMA是段被装入的位置地址,多数情况下两个地址相同.也有不同的情况,例如一个数据地段被装入ROM,当程序启动时将这个数据段拷贝到RAM(这种技术通常用于初始化ROM系统的全局变量.此时ROM地址是LMARAM地址是VMA.

   你可以使用带有'-h'命令行选项的objdump显示目标文件的段.

   每个目标文件也都有一个符号列表,称作符号表(symbol table),符号可以是已定义或未定义的.每个符号具有名字、地址等其它信息.当编译CC++源程序成目标文件,对于每个定义的函数、全局和静态变量都会产生一个符号;每个未定义函数、全局变量作为输入文件的地址将转变成未定义符号.

   显示目标文件中符号可以使用下面两个命令:

     #nm obj

     #objdump -t obj

Linker Script Format

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

   链接脚本是文本文件.

   链接脚本由命令串构成,每个命令要么是关键字,可能带有参数;要么是指定的符号.使用分号(';')分割命令,空格在处理时被忽略.

   类似文件、格式名字这样的字符串通常可以直接输入,如果文件名包含其它字符像点('.')号等其它字符分割文件名字,可是使用双引号("")包含.没有情况会在文件名中使用双引号.

   链接脚本像C一样可以包含注释,也使用'/*''*/',在C里注释作为空格处理.

Simple Linker Script Example
============================
    不少链接脚本是比较简单明了.
    最简单的链接脚本只有一个命令'SECTIONS',使用'SECTIONS'命令描述输出文件内存布局.
    'SECTIONS'命令是非常强大的,在这我们将解释它的简单用法.现在假设你的程序只是由单一代码段、初始化数段和未初始化数据段,分别对应'.text'、'.data'、'.bss'的段名.再假设输入文件中也只出现这些段.
    在这个例子中,制定代码段装入地址在0x10000,数据段开始在0x8000000,下面脚本即是脚本描述:
     SECTIONS
     {
       . = 0x10000;
       .text : { *(.text) }
       . = 0x8000000;
       .data : { *(.data) }
       .bss : { *(.bss) }
     }
    关键字'SECTIONS'作为命令,后面跟随的是花括弧中包含符号分配和输出描述串.
    在'SECTIONS'里面第一行特殊符号'.'是用作位置计数.如果你没有以其它方式指定输出段地址(其它方式以后在讲解),则地址被设置成位置计数的当前值.位置计数依从输出区大小增长,在'SECTIONS'命令起始处位置计数赋值为0.
    第二行定义输出区'.text',冒号是语法必须的,现在可以忽略.输出区名字后面的花括弧内,列出所有输入区名字,他们将被安置到输出区中.'*'是通配符,匹配任何文件名.表达式'*(.text))意思是所有输入文件的输入区.
    因为位置计数在输出区中定义为0x10000,链接器就会设置输出文件'.text'地址到0x10000.
    剩下行定义输出文件'.data'和'.bss'区,链接器将安排'.data'输出区在地址0x8000000.链接器安排好'.data'输出区后,位置计数的值将是0x8000000加'.data'输出大小,作用就是链接器在内存中紧跟'.data'输出区安排'.bss'输出区.
    链接器确保每个输出区是对齐的,需要时通过增长位置计数.这个例子中指定'.text'和'.data'区地址将满足强制对齐,但是链接器可能会建立间隙在'.data'和'.bss'之间.
    到此为止,这就是一个简单而又完整的链接脚本.
 
Simple Linker Script Commands
=============================
 
    这部分中讲述简单的链接器脚本命令.
 
* Menu:
 
* Entry Point::                 设置入口点
* File Commands::               文件处理命令
* Format Commands::             目标文件格式命令
* Miscellaneous Commands::      其它链接命令
 
Setting the entry point
-----------------------
     程序执行的第一条指令称作'入口点'(entry point).你可以使用链接器脚本命令设置入口点,它的参数是符号名字:
     ENTRY(SYMBOL)
     有几种方式设置入口点,链接器按照顺序设置每个入口点,知道有一个成功:
     *'-e' ENTRY命令行选项
     *'ENTRY(SYMBOL)命令使用在脚本中
     *符号'start'地址,当然要预先定义'start'
     *'.text'区第一个字节地址
     *地址0
 
Commands dealing with files
---------------------------
    几个处理文件的脚本命令.
    'INCLUDE FILENAME':在当前位置包含进脚本文件FILENAME,在当前目录和'-L'指定目录搜索FILENAME,嵌套调用可达10层深度.
  
    `INPUT(FILE, FILE, ...)'
    `INPUT(FILE FILE ...)':输入命令促使链接器在链接时包含命名的文件,就像在命令行指定名字.
    例如,在链接时总是要包含'subr.o',但是有不愿每次链接时设置它,那么就可以在脚本中写'INPUT(subr.o)'.
    其实,如果个人喜欢,你可以在脚本中列出所有输入文件,每次调用链接器不带任何东西除了'-T'选项.
 
    链接器首先尝试打开当前目录下文件,如果没有找到,则链接器搜索库文件路径.查看'-L'描述在*Note Command Line Options: Options.
    如果使用'INPUT(lFILE)',ld将转换名字为'libFILE.a',类似命令行参数'-l'.
    当在隐含脚本文件使用'INPUT'命令,文件需要包含链接脚本也要包含,这可能影响档案文件所搜.
   
    `GROUP(FILE, FILE, ...)'
    `GROUP(FILE FILE ...)':这个命令类似'INPUT',但是所有命名文件完全是归档文件,它们被重复搜索直到不再有未定义的参考(references).请看'-('的描述在*Note Command Line Options: Options.
 
    `OUTPUT(FILENAME)':命名输出文件,使用`OUTPUT(FILENAME)'在脚本文件中和在命令行使用'-o FILENAME'完全相同.如果两者都使用,则命令行优先.
    你也可以使用'output'命令定义默认输出文件名,不同于通常使用的'a.out'.
   
    `SEARCH_DIR(PATH)':添加路径到ld所搜归档库的路径列表中.使用这个命令效果和命令行'-L PATH'完全相同,如果两者都使用链接器将都搜索,命令行指定的路径优先搜索.
    
   
    `STARTUP(FILENAME)':此命令类似'INPUT',除却文件变成第一个被链接的,就像在命令行首先指定,这在使用固定系统很有用,因为入口点永远在第一个文件的开始.

 

你可能感兴趣的:(command,脚本,File,input,output,linker)