连接脚本(linker script)介绍

摘要:一些连接脚本示例。 
关键字: 连接脚本   linker script    lds文件

连接脚本的详细介绍可以参考:http://sourceware.org/binutils/docs/ld/Scripts.html#Scripts,一个简单的lds文件如下所示:

 1:  OUTPUT_FORMAT("elf32-i386", "elf32-i386","elf32-i386")
 2:  OUTPUT_ARCH(i386)
 3:  ENTRY(_start)
 4:  SECTIONS
 5:  {
 6:      .text :
 7:      {
 8:          * (.text)
 9:      }   
10:      
11:      .data :
12:      {
13:          * (.data)
14:      }   
15:      
16:      _bss_start = . ;
17:      .bss :
18:      {
19:          * (.bss)
20:      }   
21:      _bss_end = . ;
22:  } 

但是如果在PC平台,使用了标准库的话,这个lds还不够完整。

通常情况下,ld使用默认的连接脚本,这个脚本内嵌到ld文件中了。可以使用下面的命令查看:

ld –verbose
或者使用输出重定向,将其导出到一个lds文件中(当然需要稍作修改,把文件前面几句额外的说删除)。
ld –verbose > default.lds
可以使用下面的命令来使用这个default.lds
gcc –o hello hello.c –Wl,-Tdefault.lds
“-Wl,”后面不要有空格。当然也可以直接使用ld命令然后将-Tdefault.lds作为参数传入,但是那样比较麻烦,如果程序中使用了标准库,必须手动配置一些库文件,不然ld连接不成功,具体可以看这篇文章 《gcc编译,ld连接》。 如果仅仅是为了测试连接脚本的使用,还是上面的方式比较简单。

即使一个简单的hello.c程序,如果使用gcc直接编译,也会默认的增加一起初始化和标准库的代码,所以默认的default.lds相对来说并不简单,做测试的话,建议直接在上面增加自己的代码段。

PROVIDE (etext = .);
/* 对齐 */
. = ALIGN(4) ;
__m_section_init = .;
/* 测试下面一个语句会不会占用空间,测试结果是不占用空间,那这句话到底有什么作用呢? */
. = . ;
__m_section_start = . ;
.my_section :
{   
 *(.my_section)
    *(.my_section1) 
 __m_section_mid = . ;
    /* 测试通配符在段名中的使用 */
 *(.my_section.a*) 
}
__m_section_end = . ;
 
  
.rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1        : { *(.rodata1) }

测试结果是__m_section_start = . ;是不占用空间的;“.= . ;” 也不占用空间,但是为什么有的lds文件中会用这句话,有什么意义? 
通配符是有效果的,所以在源程序中可以使用满足通配符的段。

上面代码是我自己测试时用的,主要是测试C语言调用连接脚本中的变量,测试代码如下(hello.c):

 1:  #include <stdio.h>
 2:  
 3:  extern char  __m_section_init[] ;
 4:  extern char  __m_section_start ;
 5:  extern char  __m_section_mid  ;
 6:  extern char* __m_section_end ; 
 7:  
 8:  int var_standard = 0x1234 ;
 9:  
10:  #define DPRINT(x) printf( #x " is 0x%x/n", (unsigned int)x)
11:  
12:  int var_a __attribute__((section(".my_section"))) = 1 ;
13:  int var_b __attribute__((section(".my_section1"))) = 2 ;
14:  int var_c __attribute__((section(".my_section.a_1"))) = 3 ;
15:  
16:  int main(void)
17:  {
18:      DPRINT(&var_standard);
19:      DPRINT(__m_section_init);
20:      DPRINT(&__m_section_start);
21:      DPRINT(&var_a);
22:      DPRINT(&var_b);
23:      DPRINT(&__m_section_mid);
24:      DPRINT(&var_c);
25:      
26:      DPRINT(&__m_section_end);
27:      
28:      return 0;
29:  }

这段代码的运行结果是:

1:  &var_standard is 0x80495c4
2:  __m_section_init is 0x8048418
3:  &__m_section_start is 0x8048418
4:  &var_a is 0x8048418
5:  &var_b is 0x804841c
6:  &__m_section_mid is 0x8048420
7:  &var_c is 0x8048420
8:  &__m_section_end is 0x8048424

注意C语言中调用连接脚本的方法,特别是__m_section_end声明为了一个指针,但是实际上在这里相当于一个int类型而已,与数组名的意义并不一样,所以后面还是得使用&符号获取连接脚本里面的值。不过推荐的做法是声明为相关长度的数组,比如unsigned char或者unsigned int类型的数组。

你可能感兴趣的:(连接脚本(linker script)介绍)