分类: LINUX
2011-11-05 20:37:14
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)
3.6.1 Output Section Description
[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智能地放在输出文件中合适的位置。
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.
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:
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:
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:
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:
Now, the orphan .rodata section will be placed between end_of_text and start_of_data.
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的最终链接地址为0x300000,a = ALIGN(0x300000)的对齐地址为0x300000。
链接脚本文件中定义符号变量时:
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 file和nm 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。
有多种方法控制程序的入口点,连接器会依次按照以下步骤进行查找,符合条件即停止:
1) 使用-e entry命令行选项;
2) 在linker script中使用ENTRY(symbol)命令;
3) 标号start的地址(如果有的话);
4) .text段第一个字节的地址(如果有的话);
5) 地址0。
1) VMA表示程序的运行地址;
2) LMA表示程序的装载地址(如烧至ROM中的二进制文件中的存储位置);
3) 一般情况下,LMA和VMA是相同的;但有些时候必须不同。
示例一
放在ROM中直接执行的二进制,ROM首地址0x100000,大小为16KB;RAM首地址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) objcopy将elf64格式转成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处;
下面是一个典型的做法:
定义节:
拷贝.data:
语法:
引入原因
在某些嵌入式系统中,存在着一些内存速度比较快,但大小有限;多个节都想使用这块速度快的内存,便可以将这些节定义在一个OVERLAY中,管理程序在使用的时候将即将运行的节拷至速度快的内存中,当另一个节要使用时,管理程序将此节重新拷贝至速度快的内存中覆盖掉原来的节即可;OVERLAY必须定义在SECTIONS{}中。
示例:
定义了两个节都将在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.
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.
ELF格式专用命令,定义了ELF格式的程序头(Program Headers),又称作段(Segments);
objdump –p elf_file可以打印出程序头的内容。
格式:
示例:
objdump –f elf_file可以打印出elf文件头的内容(包括elf文件的架构)。
PHDRS定义的AT将覆盖其包含节的AT地址。
语法:
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
示例:
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:
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);
}
}
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")