- 论坛徽章:
- 0
|
本帖最后由 jixuyang 于 2012-04-09 15:02 编辑
近来看<<程序员的自我修养>>的6.4.1小节,看到静态链接的可执行文件sectionmapping.elf的两个LOAD segment的地址
连续,且以0x1000的方式对齐,其segment输入信息如下:
program header:
Type Offset Virtual Address Physical Address FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x709e5 0x709e5 RE 0x1000
LOAD 0x0709e8 0x080b99e8 0x080b99e8 0x00798 0x02280 RW 0x1000
这里第2个Segment的虚拟地址为0x080b99e8,作者的书中已经描述清楚了:为了节省物理内存,OS会在物理页面上连续存放
第一个LOAD segment和第二个LOAD segment,但是在虚拟地址上以0x1000的方式来对齐这两个segment。为此,这就会让两个
不同的虚拟地址映射到相同的物理页面,即系统会在第一个LOAD segment的最后一个物理页面同时存放第一个LOAD segment的尾巴,
同时会存放第二个LOAD segment的开头部分,但是会使用两组虚拟地址[0x80b89e5, 0x80b9000]和[0x80b9000, 0x80ba000]来
映射到这个相同的物理页面。对于第二个映射,因为前面部分已经存放第一个LOAD segment的尾巴,所以其真正的起始地址为0x08048000 + 0x709e5
= 0x80b89e5 => 0x80b89e8(4字节对齐的原则)。
我对这里书中的描述,没有疑问。但是,当我使用新版本GCC来编译这个程序时,发现当前版本产生的可执行文件的segment已经与书中描述的不同,
请看我在ubuntu 11.04 (gcc-4.5.2, gas-2.21.0.20110327, ld-2.21.0.20110327) 上编译相同的源代码所输出的segment布局:
program header:
Type Offset Virtual Address Physical Address FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x86aa1 0x86aa1 RE 0x1000
LOAD 0x0709e8 0x080cff8c 0x080cff8c 0x007d4 0x02384 RW 0x1000
按照书中的描述,第二个LOAD segment的地址应该为0x08048000 + 0x86aa1 = 0x80ceaa1 => 0x80ceaa4,但是上面输出的地址确是
0x080cff8c,确实让人想不通。通过readelf -S sectionmapping.elf,我们能得到下面的结果(看不到0x80ceaa4 ~ 0x080cff8c之间是什么东西):
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.ABI-tag NOTE 080480f4 0000f4 000020 00 A 0 0 4
[ 2] .note.gnu.build-i NOTE 08048114 000114 000024 00 A 0 0 4
[ 3] .rel.plt REL 08048138 000138 000028 08 A 0 5 4
[ 4] .init PROGBITS 08048160 000160 000030 00 AX 0 0 4
[ 5] .plt PROGBITS 08048190 000190 000050 00 AX 0 0 4
[ 6] .text PROGBITS 080481e0 0001e0 0668dc 00 AX 0 0 16
[ 7] __libc_freeres_fn PROGBITS 080aeac0 066ac0 000b21 00 AX 0 0 16
[ 8] .fini PROGBITS 080af5e4 0675e4 00001c 00 AX 0 0 4
[ 9] .rodata PROGBITS 080af600 067600 019000 00 A 0 0 32
[10] __libc_subfreeres PROGBITS 080c8600 080600 00002c 00 A 0 0 4
[11] __libc_atexit PROGBITS 080c862c 08062c 000004 00 A 0 0 4
[12] .eh_frame PROGBITS 080c8630 080630 006300 00 A 0 0 4
[13] .gcc_except_table PROGBITS 080ce930 086930 000171 00 A 0 0 1
[14] .tdata PROGBITS 080cff8c 086f8c 000010 00 WAT 0 0 4
[15] .tbss NOBITS 080cff9c 086f9c 000018 00 WAT 0 0 4
[16] .ctors PROGBITS 080cff9c 086f9c 00000c 00 WA 0 0 4
[17] .dtors PROGBITS 080cffa8 086fa8 00000c 00 WA 0 0 4
[18] .jcr PROGBITS 080cffb4 086fb4 000004 00 WA 0 0 4
[19] .data.rel.ro PROGBITS 080cffb8 086fb8 000030 00 WA 0 0 4
[20] .got PROGBITS 080cffe8 086fe8 00000c 04 WA 0 0 4
[21] .got.plt PROGBITS 080cfff4 086ff4 000020 04 WA 0 0 4
[22] .data PROGBITS 080d0020 087020 000740 00 WA 0 0 32
[23] .bss NOBITS 080d0760 087760 001b98 00 WA 0 0 32
[24] __libc_freeres_pt NOBITS 080d22f8 087760 000018 00 WA 0 0 4
[25] .comment PROGBITS 00000000 087760 00002a 01 MS 0 0 1
[26] .shstrtab STRTAB 00000000 08778a 000110 00 0 0 1
[27] .symtab SYMTAB 00000000 087d24 008170 10 28 929 4
[28] .strtab STRTAB 00000000 08fe94 007566 00 0 0 1
通过objdump -D sectionmapping.elf命令,我们能得到下面的结果,但是看起来0x80ceaa4 ~ 0x080cff8c之间
好像为真空,没有任何的代码或数据。
Disassembly of section .gcc_except_table:
080ce930 <.gcc_except_table>:
80ce930: ff (bad)
80ce931: ff 01 incl (%ecx)
80ce933: 2e cs
===============
忽略中间的一些代码
===============
80cea99: c0 02 00 rolb $0x0,(%edx)
80cea9c: f3 repz
80cea9d: 02 .byte 0x2
80cea9e: 05 .byte 0x5
...
Disassembly of section .tdata:
080cff8c <_nl_current_LC_NUMERIC+0x80cff80>:
80cff8c: a0 06 0d 08 a0 mov 0xa0080d06,%al
80cff91: 06 push %es
80cff92: 0d 08 b0 06 0d or $0xd06b008,%eax
80cff97: 08 .byte 0x8
80cff98: a4 movsb %ds %esi),%es %edi)
80cff99: 06 push %es
80cff9a: 0d .byte 0xd
ld --version输出的链接脚本布局为:
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
为此,这里的疑问是:与老版本GCC相比(4.1.2),新版本GCC(4.5.2)产生的静态版本可执行文件的两个segment地址为何不连续。
从上面的链接脚本来看,alignment会使用MAXPAGESIZE进行对齐,但是怎么会让第二个segment的起始地址为0x80cff8c呢?敬请高手指点指点,谢谢。
|
|