GCC __attribute__ 和 link 脚本控制 section 基地址
网上看的一篇文章,感谢作者,另外加上自己的一点注释。
... ... ... ... .... .... .....
利用 GCC 的 __attribute__ 属性的 section 选项来控制数据区的基地址。
以下例子,主要涉及到两个知识点,一个是 GNU C 扩展中的 attribute section 属性,关于这个知识点的相关信息可以参考: http://www.groad.net/bbs/read.php?tid=1035
另外一个知识点是 ld 连接器所用到的 link 脚本相关知识。
测试代码:
#include
int
localmemory0
__attribute__((
section(
"LOCALmem")))
=
0;
int
localmemory1
__attribute__((
section(
"LOCALmem")))
=
0;
int
globalmemory
__attribute__((
section(
"GLOBALmem")))
=
0;
int
main (
int
argc
,
char
*
argv
[])
{
localmemory0
=
0x456;
localmemory1
=
0x123;
globalmemory
=
localmemory0
+
localmemory1;
}
在上面的代码中,定义了两个非传统的 section : LOCALmem 和 GLOBALmem 。
程序里要求变量 localmemory0 和 localmemory1 存放在 section LOCALmem 中,而 globalmemory 存放在 section GLOBALmem 中。
下面编译程序:
beyes@linux-beyes:~/C/GNU_C_EXT> gcc -c -o elf_section.o elf_section.c
或者
beyes@linux-beyes:~/C/GNU_C_EXT> gcc -c -o elf_section2.o -fdata-sections elf_section.c
-fdata-sections 选项的目的是让编译器为每一个单独申明的数据 section 实际分配一个 section,而不是占用 .bss ,在某些系统上需要显式的使用这一参数。
使用 objdump 命令查看生成的 elf_section.o 文件:
beyes@linux-beyes:~/C/GNU_C_EXT> objdump -S elf_section.o
elf_section.o: file format elf32-i386
Disassembly of section .text:
00000000 :
0: 8d 4c 24 04 lea 0x4(%esp),%ecx
4: 83 e4 f0 and $0xfffffff0,%esp
7: ff 71 fc pushl -0x4(%ecx)
a: 55 push %ebp
b: 89 e5 mov %esp,%ebp
d: 51 push %ecx
e: 83 ec 04 sub $0x4,%esp
11: c7 05 00 00 00 00 56 movl $0x456,0x0
18: 04 00 00
1b: c7 05 00 00 00 00 23 movl $0x123,0x0
22: 01 00 00
25: a1 00 00 00 00 mov 0x0,%eax
2a: 8b 15 00 00 00 00 mov 0x0,%edx
30: 01 d0 add %edx,%eax
32: a3 00 00 00 00 mov %eax,0x0
37: 83 c4 04 add $0x4,%esp
3a: 59 pop %ecx
3b: 5d pop %ebp
3c: 8d 61 fc lea -0x4(%ecx),%esp
3f: c3 ret
上面的 objdump 使用了 -S 选项,这是查看目标文件反汇编代码的一个选项;对此,也可以使用 -d 选项,并建议使用 -d 选项。
上面加蓝色高亮的两条 movl 指令分别将数据存放到 localmemory0 和 localmemroy1 ;加粉红色高亮的 mov 指令将两个变量相加的结构存放到 globalmemory 中。
由于是目标文件,变量符号对应的地址没有经过解析和分配,所以指令变量中对应的地址都是 0x0 。
下面,使用一个 link 脚本来控制连接器 ld 输出 section 的己地址(像linux的内核源码树中的 arch/arm/vmlinux-armv.lds.in 文件也是这样的脚本文件<这里是ARM平台,编译内核后的文件)。
文件内容如下:
SECTIONS
{
.text : {
*(.text)
}
LOCALmem 0x1f0000 : {
*(LOCALmem)
}
GLOBALmem 0xff0000 : {
*(GLOBALmem)
}
}
用下面的命令观察生成的 elf 文件内容:
beyes@linux-beyes:~/C/GNU_C_EXT> objdump -S elf_section.elf
elf_section.elf: file format elf32-i386
Disassembly of section .text:
00000000 :
0: 8d 4c 24 04 lea 0x4(%esp),%ecx
4: 83 e4 f0 and $0xfffffff0,%esp
7: ff 71 fc pushl -0x4(%ecx)
a: 55 push %ebp
b: 89 e5 mov %esp,%ebp
d: 51 push %ecx
e: c7 05 00 00 1f 00 56 movl $0x456,0x1f0000
15: 04 00 00
18: c7 05 04 00 1f 00 23 movl $0x123,0x1f0004
1f: 01 00 00
22: 8b 15 00 00 1f 00 mov 0x1f0000,%edx
28: a1 04 00 1f 00 mov 0x1f0004,%eax
2d: 8d 04 02 lea (%edx,%eax,1),%eax
30: a3 00 00 ff 00 mov %eax,0xff0000
35: b8 00 00 00 00 mov $0x0,%eax
3a: 59 pop %ecx
3b: 5d pop %ebp
3c: 8d 61 fc lea -0x4(%ecx),%esp
3f: c3 ret
说明:
ld 命令的选项 -T ,等同于选项 -c ,这是告诉 ld 从 -c 后面的文件(commandfile) 中读取连接命令。这些命令彻底的覆盖 ld 的缺省连接格式 (不是添加);commandfile 必须详尽的描述目标格式的所有细节。
在两个加了蓝色高亮的语句中,可以看到,0x456 和 0x123 两数已经分别搬往 0x1f0000 与 0x1f0004 两个地址。
使用 objdump -s 来查看一下生成的 elf 文件:
beyes@linux-beyes:~/C/GNU_C_EXT> objdump -s elf_section.elf
elf_section.elf: file format elf32-i386
Contents of section .text:
0000 8d4c2404 83e4f0ff 71fc5589 e551c705 .L$.....q.U..Q..
0010 00001f00 56040000 c7050400 1f002301 ....V.........#.
0020 00008b15 00001f00 a104001f 008d0402 ................
0030 a30000ff 00b80000 0000595d 8d61fcc3 ..........Y].a..
Contents of section LOCALmem:
1f0000 00000000 00000000 ........
Contents of section GLOBALmem:
ff0000 00000000 ....
Contents of section .comment:
0000 00474343 3a202853 55534520 4c696e75 .GCC: (SUSE Linu
0010 78292034 2e332e32 205b6763 632d345f x) 4.3.2 [gcc-4_
0020 332d6272 616e6368 20726576 6973696f 3-branch revisio
0030 6e203134 31323931 5d00 n 141291].
Contents of section .comment.SUSE.OPTs:
0000 6f737077 6700 ospwg.
上面命令中, -s 选项表示显示任何一个 section 的所有内容。在默认情况下,所有非空 section 都要显示。
由上可见,section LOCALmem 从 0x1f0000 开始,而 section GLOBALmem 从 0xff0000 开始,程序正文段 .text 从 0x0 地址开始。