Chinaunix首页 | 论坛 | 博客
  • 博客访问: 62450
  • 博文数量: 14
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 20
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-09 16:41
文章分类
文章存档

2014年(14)

我的朋友

分类: LINUX

2014-08-15 16:02:43

原文地址:LD用法 作者:xlpang

                                                           LD用法

LD连接脚本(linker script)主要是用来描述输入文件中的节(section)是如何映射到输出文件的,并且控制输出文件的内存布局(memory layout);源代码会编译成为目标对象文件(object file),每个对象文件中包含一系列的段(section),为LD的输入文件。

官方资料:  info ld

环境:

X86_64

[xlpang@server ld]$ ld -v

GNU ld version 2.20.51.0.2-5.11.el6 20091009

 

test.c

int a = 1; /* data section */

int main(void)

{

    return 0;

}

 

gcc test.c –c –o test.o

 

[xlpang@server ld]$ file test.o

test.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

 

[xlpang@server ld]$ nm test.o

0000000000000000 D a

0000000000000000 T main

 

[xlpang@server ld]$ readelf -S test.o

There are 11 section headers, starting at offset 0x110:

 

Section Headers:

  [Nr] Name              Type             Address           Offset

       Size              EntSize          Flags  Link  Info  Align

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .text             PROGBITS         0000000000000000  00000040

       000000000000000b  0000000000000000  AX       0     0     4

  [ 2] .data             PROGBITS         0000000000000000  0000004c

       0000000000000004  0000000000000000  WA       0     0     4

  [ 3] .bss              NOBITS           0000000000000000  00000050

       0000000000000000  0000000000000000  WA       0     0     4

  [ 4] .comment          PROGBITS         0000000000000000  00000050

       000000000000002e  0000000000000001  MS       0     0     1

  [ 5] .note.GNU-stack   PROGBITS         0000000000000000  0000007e

       0000000000000000  0000000000000000           0     0     1

  [ 6] .eh_frame         PROGBITS         0000000000000000  00000080

       0000000000000038  0000000000000000   A       0     0     8

  [ 7] .rela.eh_frame    RELA             0000000000000000  000004d0

       0000000000000018  0000000000000018           9     6     8

  [ 8] .shstrtab         STRTAB           0000000000000000  000000b8

       0000000000000054  0000000000000000           0     0     1

  [ 9] .symtab           SYMTAB           0000000000000000  000003d0

       00000000000000f0  0000000000000018          10     8     8

  [10] .strtab           STRTAB           0000000000000000  000004c0

       000000000000000f  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

 

1 SECTION

3.6.1 Output Section Description

      section [address] [(type)] :

       [AT(lma)]

       [ALIGN(section_align)]

       [SUBALIGN(subsection_align)]

       [constraint]

       {

         output-section-command

         output-section-command

         ...

       } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp]

称为节。

 

示例

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000:

    {

        *(.text)

    }

 

    .data :

    {

        *(.data)

    }

}

 

结果:

[xlpang@server ld]$ ld test.o -o test  -T pxl.lds ;ls -lh;readelf -S test

total 20K

-rw-rw-r-- 1 xlpang xlpang  196 Oct 27 15:33 pxl.lds

-rwxrwxr-x 1 xlpang xlpang 1.1M Oct 27 15:33 test

-rw-rw-r-- 1 xlpang xlpang   45 Oct 27 15:21 test.c

-rw-rw-r-- 1 xlpang xlpang 1.3K Oct 27 15:21 test.o

There are 8 section headers, starting at offset 0x1000b8:

 

Section Headers:

  [Nr] Name              Type             Address           Offset

       Size              EntSize          Flags  Link  Info  Align

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .text             PROGBITS         0000000000100000  00100000

       000000000000000b  0000000000000000  AX       0     0     4

  [ 2] .eh_frame         PROGBITS         0000000000100010  00100010

       0000000000000038  0000000000000000   A       0     0     8

  [ 3] .data             PROGBITS         0000000000100048  00100048

       0000000000000004  0000000000000000  WA       0     0     4

  [ 4] .comment          PROGBITS         0000000000000000  0010004c

       000000000000002d  0000000000000001  MS       0     0     1

  [ 5] .shstrtab         STRTAB           0000000000000000  00100079

       000000000000003a  0000000000000000           0     0     1

  [ 6] .symtab           SYMTAB           0000000000000000  001002b8

       00000000000000c0  0000000000000018           7     6     8

  [ 7] .strtab           STRTAB           0000000000000000  00100378

       000000000000000f  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

 

[xlpang@server ld]$ file test.o

test.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

[xlpang@server ld]$ file test

test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped.

 

说明:

a) .text.data表示定义的2个输出节,名字后面需要有空格;

b) .text 0x100000,表示将.text节放在0x100000开始处;

c) 输入文件test.o中包含的11个节为输入节;其中.bss.note.GNU-stack节内容为0,输出

文件test中后就被删除掉了;.rela.eh_frame输入节为重定向节,输出文件test链接后也

被删除掉;

d) 输出的.text.data节,按照链接脚本中定义的格式来生成

e) 输入文件中存在但输出链接脚本中没没定义的节,被称为孤儿节(Orhpan Section);此类

节会被LD智能地放在输出文件中合适的位置。

 

2 Location Counter

The special linker variable dot `.' always contains the current output location counter. Since the . always refers to a location in an output section, it may only appear in an expression within a SECTIONS command. The . symbol may appear anywhere that an ordinary symbol is allowed in an expression.

Assigning a value to . will cause the location counter to be moved. This may be used to create holes in the output section. The location counter may not be moved backwards inside an output section, and may not be moved backwards outside of an output section if so doing creates areas with overlapping LMAs.

SECTIONS     {      
    output :
    {          
       file1(.text)          
       . = . + 1000;           
       file2(.text)          
       . += 1000;          
       file3(.text)        
     } = 0x12345678;    
}

In the previous example, the `.text' section from file1 is located at the beginning of the output section `output'. It is followed by a 1000 byte gap. Then the `.text' section from file2 appears, also with a 1000 byte gap following before the `.text' section from file3. The notation `= 0x12345678' specifies what data to write in the gaps.

Note: . actually refers to the byte offset from the start of the current containing object. Normally this is the SECTIONS statement, whose start address is 0, hence . can be used as an absolute address. If . is used inside a section description however, it refers to the byte offset from the start of that section, not an absolute address. Thus in a script like this:

     SECTIONS    
     {        
       . = 0x100;        
       .text:
       {          
             *(.text)          
             . = 0x200;        
       }        
       . = 0x500;        
       .data:
       {          
             *(.data)          
             . += 0x600;        
       }    
     }

The `.text' section will be assigned a starting address of 0x100 and a size of exactly 0x200 bytes, even if there is not enough data in the `.text' input sections to fill this area. (If there is too much data, an error will be produced because this would be an attempt to move . backwards). The `.data' section will start at 0x500 and it will have an extra 0x600 bytes worth of space after the end of the values from the `.data' input sections and before the end of the `.data' output section itself.

Setting symbols to the value of the location counter outside of an output section statement can result in unexpected values if the linker needs to place orphan sections. For example, given the following:

     SECTIONS    
    {        
         start_of_text = . ;        
         .text: { *(.text) }        
         end_of_text = . ;              
         start_of_data = . ;        
         .data: { *(.data) }        
         end_of_data = . ;    
     }

If the linker needs to place some input section, e.g. .rodata, not mentioned in the script, it might choose to place that section between .text and .data. You might think the linker should place .rodata on the blank line in the above script, but blank lines are of no particular significance to the linker. As well, the linker doesn't associate the above symbol names with their sections. Instead, it assumes that all assignments or other statements belong to the previous output section, except for the special case of an assignment to .. I.e., the linker will place the orphan .rodata section as if the script was written as follows:

    SECTIONS    
    {        
         start_of_text = . ;        
         .text: { *(.text) }        
         end_of_text = . ;              
         start_of_data = . ;        
         .rodata: { *(.rodata) }        
         .data: { *(.data) }        
         end_of_data = . ;    
    }

This may or may not be the script author's intention for the value of start_of_data. One way to influence the orphan section placement is to assign the location counter to itself, as the linker assumes that an assignment to . is setting the start address of a following output section and thus should be grouped with that section. So you could write:

     SECTIONS    
    {        
         start_of_text = . ;        
         .text: { *(.text) }        
         end_of_text = . ;              
         . = . ;        
         start_of_data = . ;         
         .data: { *(.data) }        
         end_of_data = . ;    
     }

Now, the orphan .rodata section will be placed between end_of_text and start_of_data.

3 ALIGN命令

a) ALIGN()命令用于SECTION定义中,会以2的幂对齐,如ALIGN(0x300000)会以

ALIGN(0x400000)来对齐;

b) 用于其它定义中(如变量),不会自动对齐。

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 :

    {

        *(.text)

    }

 

    a = ALIGN(0x300000);

    .data : ALIGN(0x300000)

    {

        *(.data)

    }

}

结果:

[xlpang@server ld]$ ld test.o -o test  -T pxl.lds ;ls -lh;readelf -S test;nm test

total 24K

-rw-rw-r-- 1 xlpang xlpang  238 Oct 27 15:21 pxl.lds

-rwxrwxr-x 1 xlpang xlpang 4.1M Oct 27 15:21 test

-rw-rw-r-- 1 xlpang xlpang   45 Oct 27 15:21 test.c

-rw-rw-r-- 1 xlpang xlpang 1.3K Oct 27 15:21 test.o

There are 8 section headers, starting at offset 0x400070:

 

Section Headers:

  [Nr] Name              Type             Address           Offset

       Size              EntSize          Flags  Link  Info  Align

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .text             PROGBITS         0000000000100000  00100000

       000000000000000b  0000000000000000  AX       0     0     4

  [ 2] .eh_frame         PROGBITS         0000000000100010  00100010

       0000000000000038  0000000000000000   A       0     0     8

  [ 3] .data             PROGBITS         0000000000400000  00400000

       0000000000000004  0000000000000000  WA       0     0     4194304

  [ 4] .comment          PROGBITS         0000000000000000  00400004

       000000000000002d  0000000000000001  MS       0     0     1

  [ 5] .shstrtab         STRTAB           0000000000000000  00400031

       000000000000003a  0000000000000000           0     0     1

  [ 6] .symtab           SYMTAB           0000000000000000  00400270

       00000000000000c0  0000000000000018           7     6     8

  [ 7] .strtab           STRTAB           0000000000000000  00400330

       000000000000000f  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

0000000000300000 A a

0000000000100000 T main

说明:

a) .data节被放在了0x400000处,.data : ALIGN(0x300000)的对齐地址以2的幂对齐;

b) 链接脚本中的符号a覆盖了test.c中定义的符号a

c) 符号a的最终链接地址为0x300000a = ALIGN(0x300000)的对齐地址为0x300000

 
4符号变量

链接脚本文件中定义符号变量时:

1) 没有使用PROVIDE/PROVIDE_HIDDEN,当输入文件中已定义此变量,链接文件中的定义会覆盖输入文件中的定义;

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 :

    {

        *(.text)

    }

 

    a = ALIGN(0x300000);

    .data : ALIGN(0x300000)

    {

        *(.data)

    }

}

结果:

[xlpang@server ld]$ ld test.o -o test  -T pxl.lds ; readelf -S test;nm test

Section Headers:

  [Nr] Name              Type             Address           Offset

       Size              EntSize          Flags  Link  Info  Align

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .text             PROGBITS         0000000000100000  00100000

       000000000000000b  0000000000000000  AX       0     0     4

  [ 2] .eh_frame         PROGBITS         0000000000100010  00100010

       0000000000000038  0000000000000000   A       0     0     8

  [ 3] .data             PROGBITS         0000000000400000  00400000

       0000000000000004  0000000000000000  WA       0     0     4194304

  [ 4] .comment          PROGBITS         0000000000000000  00400004

       000000000000002d  0000000000000001  MS       0     0     1

  [ 5] .shstrtab         STRTAB           0000000000000000  00400031

       000000000000003a  0000000000000000           0     0     1

  [ 6] .symtab           SYMTAB           0000000000000000  00400270

       00000000000000c0  0000000000000018           7     6     8

  [ 7] .strtab           STRTAB           0000000000000000  00400330

       000000000000000f  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

0000000000300000 A a

0000000000100000 T main

说明:

链接脚本中的符号a覆盖了test.c中定义的符号a

 

2) 使用了PROVIDE/PROVIDE_HIDDEN,输入文件中已定义此变量,将以文件中为准;

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 :

    {

        *(.text)

    }

 

    PROVIDE(a = ALIGN(0x300000));

    .data : ALIGN(0x300000)

    {

        *(.data)

    }

}

结果:

[xlpang@server ld]$ ld test.o -o test  -T pxl.lds ;ls -lh;readelf -S test;nm test

total 24K

-rw-rw-r-- 1 xlpang xlpang  245 Oct 27 15:45 pxl.lds

-rwxrwxr-x 1 xlpang xlpang 4.1M Oct 27 15:46 test

-rw-rw-r-- 1 xlpang xlpang   45 Oct 27 15:46 test.c

-rw-rw-r-- 1 xlpang xlpang 1.3K Oct 27 15:46 test.o

There are 8 section headers, starting at offset 0x400070:

 

Section Headers:

  [Nr] Name              Type             Address           Offset

       Size              EntSize          Flags  Link  Info  Align

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .text             PROGBITS         0000000000100000  00100000

       000000000000000b  0000000000000000  AX       0     0     4

  [ 2] .eh_frame         PROGBITS         0000000000100010  00100010

       0000000000000038  0000000000000000   A       0     0     8

  [ 3] .data             PROGBITS         0000000000400000  00400000

       0000000000000004  0000000000000000  WA       0     0     4194304

  [ 4] .comment          PROGBITS         0000000000000000  00400004

       000000000000002d  0000000000000001  MS       0     0     1

  [ 5] .shstrtab         STRTAB           0000000000000000  00400031

       000000000000003a  0000000000000000           0     0     1

  [ 6] .symtab           SYMTAB           0000000000000000  00400270

       00000000000000c0  0000000000000018           7     6     8

  [ 7] .strtab           STRTAB           0000000000000000  00400330

       000000000000000f  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

0000000000400000 D a

0000000000100000 T main

说明:

符号a的地址变成了0x400000,即使用test.c中定义的(.data节中)

 

3) 使用了PROVIDE/PROVIDE_HIDDEN定义变量,输入文件中却没有定义或引用此变量,此变量将被忽略;

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 :

    {

        *(.text)

    }

 

PROVIDE(a = ALIGN(0x300000));

PROVIDE(b = ALIGN(0x300000));

    .data : ALIGN(0x300000)

    {

        *(.data)

    }

}

结果:

[xlpang@server ld]$ ld test.o -o test  -T pxl.lds ;nm test

0000000000400000 D a

0000000000100000 T main

说明:

符号b被忽略。

 

4) 链接脚本中没有使用PROVIDE/PROVIDE_HIDDEN定义变量,输入文件中也没有定义此变量,此变量不会被忽略;

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 :

    {

        *(.text)

    }

 

PROVIDE(a = ALIGN(0x300000));

b = ALIGN(0x300000);

    .data : ALIGN(0x300000)

    {

        *(.data)

    }

}

结果:

[xlpang@server ld]$ ld test.o -o test  -T pxl.lds ;ls -lh;readelf -S test;nm test

total 24K

-rw-rw-r-- 1 xlpang xlpang  270 Oct 27 15:52 pxl.lds

-rwxrwxr-x 1 xlpang xlpang 4.1M Oct 27 15:52 test

-rw-rw-r-- 1 xlpang xlpang   45 Oct 27 15:46 test.c

-rw-rw-r-- 1 xlpang xlpang 1.3K Oct 27 15:46 test.o

There are 8 section headers, starting at offset 0x400070:

 

Section Headers:

  [Nr] Name              Type             Address           Offset

       Size              EntSize          Flags  Link  Info  Align

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .text             PROGBITS         0000000000100000  00100000

       000000000000000b  0000000000000000  AX       0     0     4

  [ 2] .eh_frame         PROGBITS         0000000000100010  00100010

       0000000000000038  0000000000000000   A       0     0     8

  [ 3] .data             PROGBITS         0000000000400000  00400000

       0000000000000004  0000000000000000  WA       0     0     4194304

  [ 4] .comment          PROGBITS         0000000000000000  00400004

       000000000000002d  0000000000000001  MS       0     0     1

  [ 5] .shstrtab         STRTAB           0000000000000000  00400031

       000000000000003a  0000000000000000           0     0     1

  [ 6] .symtab           SYMTAB           0000000000000000  00400270

       00000000000000d8  0000000000000018           7     6     8

  [ 7] .strtab           STRTAB           0000000000000000  00400348

       0000000000000011  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

0000000000400000 D a

0000000000300000 A b

0000000000100000 T main

说明:

符号b不会被忽略,地址为0x300000

注:objdump –t filenm file都可以查看符号

 

5) 链接脚本中定义的变量符号,是没有数值的,赋值表示的只是地址,引用的时候请务

必使用地址&;

  示例:

  _data_start  =  0x100000;  /* _data_start表示地址0x100000 */

  C程序中引用:

  extern char _data_start;

  char *start  =  &_data_start;  /* 指针start指向地址0x100000 */

 

6) 许多编译器在生成符号表的时候,通常会将源代码(如C代码)中定义的变量符号前面

或后面添加一个下划线“_”(不会改变链接脚本中定义的变量);因此,如果存在这种情

况,在源代码中引用链接脚本中定义的变量时,请注意去掉下划线(在链接脚本中定义

变量时,也请注意变量的起名)。

示例:

链接脚本中的符号定义:

_foo = 1000;

在源代码中(如C代码)引用时,应该书写如下:

extern int foo;

这样编译过后,就是链接脚本中定义的_foo符号地址了。

 

7) 符号分相对(定义在节内并且没有使用ABSOLUTE命令字)和绝对(定义在节外或

使用ABSOLUTE命令字)2种。相对符号可以通过命令选项”-r”来做进一步链接,但

最终符号代表的地址值是一样的。

示例一

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 (NOLOAD):

    {

        *(.text)

    }

 

    .data 0x200000:

    {

        y = .;

        *(.data)

    }

}

 

结果

[xlpang@server ld]$ ld test.o -o test -T pxl.lds ;nm test

0000000000200000 D a

0000000000100000 T main

0000000000200000 D y

 

示例二

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 (NOLOAD):

    {

        *(.text)

    }

 

    .data 0x200000:

    {

        y = ABSOLUTE(.);

        *(.data)

    }

}

 

结果

[xlpang@server ld]$ ld test.o -o test -T pxl.lds ;nm test

0000000000200000 D a

0000000000100000 T main

0000000000200000 A y  /* A表示Absolute,即绝对符号 */

 

8) 在节内操作Location Counter,也属于相对值(即使使用ABSOLUTE命令字,

ABSOLUTE只会影响符号是绝对的还是相对的类型,不影响符号代表的地址值);

示例

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 (NOLOAD):

    {

        *(.text)

    }

 

    .data 0x200000:

    {

        . = 0x300000; /* 表示相对节偏移:位置变成了0x500010,而不是0x30 0000 */

        *(.data)

    }

}

 

结果

[xlpang@server ld]$ ld test.o -o test -T pxl.lds ;readelf -S test

There are 8 section headers, starting at offset 0x700070:

 

Section Headers:

  [Nr] Name              Type             Address           Offset

       Size              EntSize          Flags  Link  Info  Align

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .eh_frame         PROGBITS         0000000000000000  00200000

       0000000000000038  0000000000000000   A       0     0     8

  [ 2] .text             NOBITS           0000000000100000  00200038

       000000000000000b  0000000000000000  AX       0     0     4

  [ 3] .data             PROGBITS         0000000000200000  00400000

       0000000000300004  0000000000000000  WA       0     0     4

  [ 4] .comment          PROGBITS         0000000000000000  00700004

       000000000000002d  0000000000000001  MS       0     0     1

  [ 5] .shstrtab         STRTAB           0000000000000000  00700031

       000000000000003a  0000000000000000           0     0     1

  [ 6] .symtab           SYMTAB           0000000000000000  00700270

       00000000000000c0  0000000000000018           7     6     8

  [ 7] .strtab           STRTAB           0000000000000000  00700330

       000000000000000f  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

说明

.data节的大小,由原来的0x4变成了0x300004

 

5入口

有多种方法控制程序的入口点,连接器会依次按照以下步骤进行查找,符合条件即停止:

1) 使用-e entry命令行选项;

2) linker script中使用ENTRY(symbol)命令;

3) 标号start的地址(如果有的话);

4) .text段第一个字节的地址(如果有的话);

5) 地址0

 

6 VMALMA

1) VMA表示程序的运行地址;

2) LMA表示程序的装载地址(如烧至ROM中的二进制文件中的存储位置);

3) 一般情况下,LMAVMA是相同的;但有些时候必须不同。

示例一

放在ROM中直接执行的二进制,ROM首地址0x100000,大小为16KBRAM首地址0x300000,大小为4KB

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000 :

    {

        *(.text)

    }

 

. = 0x300000;

    .data :

    {

        *(.data)

    }

}

结果:

[xlpang@server ld]$ ld test.o -o test  -T pxl.lds;objcopy -O binary test; ls test -l

-rwxrwxr-x 1 xlpang xlpang 2097156 Oct 27 16:19 test

 

[xlpang@server ld]$ readelf -S test

There are 8 section headers, starting at offset 0x300070:

 

Section Headers:

  [Nr] Name              Type             Address           Offset

       Size              EntSize          Flags  Link  Info  Align

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .text             PROGBITS         0000000000100000  00100000

       000000000000000b  0000000000000000  AX       0     0     4

  [ 2] .eh_frame         PROGBITS         0000000000100010  00100010

       0000000000000038  0000000000000000   A       0     0     8

  [ 3] .data             PROGBITS         0000000000300000  00300000

       0000000000000004  0000000000000000  WA       0     0     4

  [ 4] .comment          PROGBITS         0000000000000000  00300004

       000000000000002d  0000000000000001  MS       0     0     1

  [ 5] .shstrtab         STRTAB           0000000000000000  00300031

       000000000000003a  0000000000000000           0     0     1

  [ 6] .symtab           SYMTAB           0000000000000000  00300270

       00000000000000c0  0000000000000018           7     6     8

  [ 7] .strtab           STRTAB           0000000000000000  00300330

       000000000000000f  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

说明:

a) objcopyelf64格式转成binary

b)生成的二进制文件大小竟然有2097156B之大;如果没有定义LMA,默认装载地址LMA等于VMA,即.data节被装载在了0x300000地址处,与第一个.text节有2MB之差导致。

ROM只有16KB,这样将此2MB多的二进制程序烧至ROM中时,就会损失掉数据段。

 

示例二

pxl.lds

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

OUTPUT_ARCH(i386:x86-64)

SECTIONS

{

    .text 0x100000:

    {

        *(.text)

    }

 

    data_load_addr = .;

    . = 0x300000;

    .data : AT(ALIGN(data_load_addr, 0x10) + SIZEOF(.eh_frame))

    {

        *(.data)

    }

}

结果:

[xlpang@server ld]$ ld test.o -T pxl.lds -o test; objdump -h test; objcopy -O binary test;ls -l

test:     file format elf64-x86-64

 

Sections:

Idx Name          Size      VMA               LMA               File off  Algn

  0 .text         0000000b  0000000000100000  0000000000100000  00100000  2**2

                  CONTENTS, ALLOC, LOAD, READONLY, CODE

  1 .eh_frame     00000038  0000000000100010  0000000000100010  00100010  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  2 .data         00000004  0000000000300000  0000000000100048  00300000  2**2

                  CONTENTS, ALLOC, LOAD, DATA

  3 .comment      0000002d  0000000000000000  0000000000000000  00300004  2**0

                  CONTENTS, READONLY

total 16

-rw-rw-r-- 1 xlpang xlpang  289 Oct 27 17:29 pxl.lds

-rwxrwxr-x 1 xlpang xlpang   76 Oct 27 17:30 test

-rw-rw-r-- 1 xlpang xlpang   45 Oct 27 16:34 test.c

-rw-rw-r-- 1 xlpang xlpang 1256 Oct 27 16:34 test.o

说明:

a) AT()显示指定装载位置后,这样.data节被装载在.text节和.eh_frame节(孤儿节)结束位

置,并以16B对齐处,这样生成的二进制文件只有76B了,可以完全放在ROM了;

b) LMA主要针对放在ROM中的二进制来讲的(生成的elf格式的文件照样很大);

[xlpang@server ld]$ od -tx4 -Ax test (蓝色为数据节的数据,即int a=1;)

000000 e5894855 000000b8 00c3c900 00000000

000010 00000014 00000000 00527a01 01107801

000020 08070c1b 00000190 0000001c 0000001c

000030 ffffffd0 0000000b 100e4100 0d028643

000040 070c4606 00000008 00000001

c) .text节中通常有一段代码,用来将.data节中位于LMA处的数据搬运至其定义的VMA处;

下面是一个典型的做法:

定义节:

SECTIONS   
{      
      .text 0x1000 :
      {
          *(.text)
          _etext = . ;
       }
 
      .data 0x2000 : AT ( ADDR (.text) + SIZEOF (.text) )                     
      {
             _data = .;
             *(.data)
             _edata = .;
       }    
}

拷贝.data

extern char _etext, _data, _edata;    
char *src = &_etext; /* .data start LMA */    
char *dst = &_data;  /* .data start VMA */    
 
/* ROM has data at end of text; copy it.  */    
while (dst < &_edata)       *dst++ = *src++; /* LMA -> VMA */
 
7 OVERLAY

语法:

OVERLAY [start] : [NOCROSSREFS] [AT ( ldaddr )]      
{        
        secname1          
        {            
             output-section-command            
             output-section-command            
              ...          
         } [:phdr...] [=fill]        
        
         secname2          
        {            
              output-section-command            
              output-section-command            
              ...          
         } [:phdr...] [=fill]        
         
         ...      
} [>region] [:phdr...] [=fill] 
 

引入原因

在某些嵌入式系统中,存在着一些内存速度比较快,但大小有限;多个节都想使用这块速度快的内存,便可以将这些节定义在一个OVERLAY中,管理程序在使用的时候将即将运行的节拷至速度快的内存中,当另一个节要使用时,管理程序将此节重新拷贝至速度快的内存中覆盖掉原来的节即可;OVERLAY必须定义在SECTIONS{}中。

示例:

       OVERLAY 0x1000 : AT (0x4000)       
       {         
            .text0 { o1/*.o(.text) }         
            .text1 { o2/*.o(.text) }       
        }

定义了两个节都将在0x1000起始处(快速内存)运行,节.text0被装载在0x4000,.text1被装载在紧临.text0节的后面。

The following symbols will be defined if referenced: __load_start_text0, __load_stop_text0, __load_start_text1, __load_stop_text1.

C code to copy overlay .text1 into the overlay area might look like the following.

       extern char __load_start_text1, __load_stop_text1;      
       memcpy ((char *) 0x1000, &__load_start_text1,&__load_stop_text1 - &__load_start_text1);

Note that the OVERLAY command is just syntactic sugar, since everything it does can be done using the more basic commands. The above example could have been written identically as follows.

       .text0 0x1000 : AT (0x4000) { o1/*.o(.text) }      
       PROVIDE (__load_start_text0 = LOADADDR (.text0));      
       PROVIDE (__load_stop_text0 = LOADADDR (.text0) + SIZEOF (.text0));      
       .text1 0x1000 : AT (0x4000 + SIZEOF (.text0)) { o2/*.o(.text) }      
       PROVIDE (__load_start_text1 = LOADADDR (.text1));      
       PROVIDE (__load_stop_text1 = LOADADDR (.text1) + SIZEOF (.text1));      
       . = 0x1000 + MAX (SIZEOF (.text0), SIZEOF (.text1));
由此也可以看出来,OVERLAY是针对VMA的。

8 PHDRS

ELF格式专用命令,定义了ELF格式的程序头(Program Headers),又称作段(Segments);

objdump –p elf_file可以打印出程序头的内容。

 

格式:

PHDRS    
{      
     name type [ FILEHDR ] [ PHDRS ] [ AT ( address ) ]   
               [ FLAGS ( flags ) ] ;    
}
输出SECTION的定义中可以指定本节所在的程序头(即段),可以指定多个程序头,后续定义的节如果没有显示指定也将默认使用本节指定的程序头。 

示例:

PHDRS    
{      
     headers PT_PHDR PHDRS ;      
     interp PT_INTERP ;      
     text PT_LOAD FILEHDR PHDRS FLAGS(5) /* 5 means R+X */;      
     data PT_LOAD FLAGS(6) /* 6 means R+W */;      
     dynamic PT_DYNAMIC ;    
}          
 
SECTIONS    
{      
    . = SIZEOF_HEADERS;      
    .interp : { *(.interp) } :text :interp      
    .text : { *(.text) } :text      
    .rodata : { *(.rodata) } /* defaults to :text */      
    ...      
    . = . + 0x1000; /* move to a new page in memory */      
    .data : { *(.data) } :data      
    .dynamic : { *(.dynamic) } :data :dynamic      
    ...    
} 

objdump –f elf_file可以打印出elf文件头的内容(包括elf文件的架构)。

 

PHDRS定义的AT将覆盖其包含节的AT地址。

 

9 MEMORY/REGION_ALIAS

语法:

a)     MEMORY      
      {        
           name [(attr)] : ORIGIN = origin, LENGTH = len        
           ...      
       }

The attr string must consist only of the following characters:

`R'

Read-only section

`W'

Read/write section

`X'

Executable section

`A'

Allocatable section

`I'

Initialized section

`L'

Same as `I'

`!'

Invert the sense of any of the attributes that follow

 

b) REGION_ALIAS(alias, region)

 

示例:

Suppose we have an application for embedded systems which come with various memory storage devices. All have a general purpose, volatile memory RAM that allows code execution or data storage. Some may have a read-only, non-volatile memory ROM that allows code execution and read-only data access. The last variant is a read-only, non-volatile memory ROM2 with read-only data access and no code execution capability. We have four output sections:

  • .text program code;
  • .rodata read-only data;
  • .data read-write initialized data;
  • .bss read-write zero initialized data.

The goal is to provide a linker command file that contains a system independent part defining the output sections and a system dependent part mapping the output sections to the memory regions available on the system. Our embedded systems come with three different memory setups A, B and C:

Section

Variant A

Variant B

Variant C

.text

RAM

ROM

ROM

.rodata

RAM

ROM

ROM2

.data

RAM

RAM/ROM

RAM/ROM2

.bss

RAM

RAM

RAM

The notation RAM/ROM or RAM/ROM2 means that this section is loaded into region ROM or ROM2 respectively. Please note that the load address of the .data section starts in all three variants at the end of the .rodata section.

The base linker script that deals with the output sections follows. It includes the system dependent linkcmds.memory file that describes the memory layout:

     INCLUDE linkcmds.memory

    

     SECTIONS

     {

         .text :

         {

             *(.text)

         } > REGION_TEXT

         .rodata :

         {

             *(.rodata)

             rodata_end = .;

           } > REGION_RODATA

         .data : AT (rodata_end)

         {

             data_start = .;

             *(.data)

         } > REGION_DATA

         data_size = SIZEOF(.data);

         data_load_start = LOADADDR(.data);

         .bss :

         {

             *(.bss)

         } > REGION_BSS

     }

Now we need three different linkcmds.memory files to define memory regions and alias names. The content of linkcmds.memory for the three variants A, B and C:

A

Here everything goes into the RAM.

          MEMORY

          {

              RAM : ORIGIN = 0, LENGTH = 4M

          }

         

          REGION_ALIAS("REGION_TEXT", RAM);

          REGION_ALIAS("REGION_RODATA", RAM);

          REGION_ALIAS("REGION_DATA", RAM);

          REGION_ALIAS("REGION_BSS", RAM);

B

Program code and read-only data go into the ROM. Read-write data goes into the RAM. An image of the initialized data is loaded into the ROM and will be copied during system start into the RAM.

          MEMORY

          {

              ROM : ORIGIN = 0, LENGTH = 3M

              RAM : ORIGIN = 0x10000000, LENGTH = 1M

          }

         

          REGION_ALIAS("REGION_TEXT", ROM);

          REGION_ALIAS("REGION_RODATA", ROM);

          REGION_ALIAS("REGION_DATA", RAM);

          REGION_ALIAS("REGION_BSS", RAM);    

C

Program code goes into the ROM. Read-only data goes into the ROM2. Read-write data goes into the RAM. An image of the initialized data is loaded into the ROM2 and will be copied during system start into the RAM.

          MEMORY

          {

              ROM : ORIGIN = 0, LENGTH = 2M

              ROM2 : ORIGIN = 0x10000000, LENGTH = 1M

              RAM : ORIGIN = 0x20000000, LENGTH = 1M

          }

         

          REGION_ALIAS("REGION_TEXT", ROM);

          REGION_ALIAS("REGION_RODATA", ROM2);

          REGION_ALIAS("REGION_DATA", RAM);

          REGION_ALIAS("REGION_BSS", RAM);    

It is possible to write a common system initialization routine to copy the .data section from ROM or ROM2 into the RAM if necessary:

     #include

    

     extern char data_start [];

     extern char data_size [];

     extern char data_load_start [];

    

     void copy_data(void)

     {

       if (data_start != data_load_start)

         {

           memcpy(data_start, data_load_start, (size_t) data_size);

         }

     }

 

10 ASSERT

ASSERT(exp, message)

Ensure that exp is non-zero. If it is zero, then exit the linker with an error code, and print message.

示例:

arch/arm/kernel/vmlinux.lds.S中:

/*

 * These must never be empty

 * If you have to comment these two assert statements out, your

 * binutils is too old (for other reasons as well)

 */

ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")

ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")

 

阅读(1750) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~