在这个文件中。 它说文本段从0x400000开始。 为什么select这个特定的地址? 这有什么理由吗? 在Linux
GNU ld
select了相同的地址:
$ ld -verbose | grep -i text-segment PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
令人惊讶的是,这个地址在32位x86可执行文件中更大 :
$ ld -verbose | grep -i text-segment PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;
我读到这个问题 ,讨论为什么0x080xxxxx地址被选为i386,但它不解释在x86_64中的变化。 这件事很难find任何解释。 有人有线索吗?
底线: amd64
使用大地址的一些技术限制,建议将较低的2GiB
地址空间2GiB
于代码和数据以提高效率。 因此,堆栈已经被重新定位在这个范围之外。
在i386
ABI 1
0x8048000
以下0x8048000
增长。 它提供了“堆栈大约128 MB,文本和数据大约2 GB” (p。3-22)。 0x80000000
(2GiB)开始, 1GiB
,至少从0xC0000000
(第3-21页)( 这就是通常的做法 )。 128MiB
),但是期望128MiB
( 288KiB
)以上的某些栈空间将被保留用于这个目的是288KiB
。 amd64
( 其ABI是作为对i386
一个修正(第9页)的修改)具有非常大的(48位)地址空间,但大多数指令只接受32位立即操作数(包括跳转指令中的直接地址和偏移量),需要更多的工作和更低效率的代码(特别是在考虑指令相互依赖性时)来处理更大的值。 作者通过介绍一些他们推荐用来“允许编译器生成更好的代码”的 “代码模型”来总结解决这些限制的措施。 (第33页)
0x00000000
到0x7effffff
” ,这允许一些非常有效的相对引用和数组迭代。 这是1.98GiB
,这对许多程序来说已经足够了。 movabs
指令,就像在中等代码模型中一样,即使是处理文本部分的内部地址也是如此,另外,在分支到地址时需要间接分支其当前指令指针的偏移量是未知的。“ 他们继续建议将代码库分割为多个共享库,因为这些措施不适用于已知在边界内的偏移的相对引用(如“小型位置独立代码模型”中所述)。 因此,堆栈被移动到共享库空间( 0x80000000000
)下,因为它的地址永远不会是立即操作数,总是被间接地引用,或者被来自另一个引用的lea
/ mov
引用,因此只会应用相对偏移限制。
上面解释了为什么加载地址被移动到一个较低的地址。 现在,为什么它移到了0x400000
( 4MiB
)? 在这里,我空了,所以总结了我在ABI规范中读到的内容,我只能猜测它感觉“恰到好处”:
amd64
运行的更大的数据单元,但又足够小,不会浪费许多有价值的起始2GiB
地址空间。 1 请注意,随着时间的推移,实际的x32 Linuxes已经越来越偏离这种布局。 但是我们在这里讨论的是ABI规范,因为amd64
是正式的,而不是任何派生的布局(请参阅引用的段落)。