如果我编译这个程序:
#include <stdio.h>
int main(int argc, char** argv) {
printf("hello world!\n");
return 0;
}
对于x86-64,asm输出使用movl $.LC0, %edi
/ call puts
。 (See full asm output / compile options on godbolt。)
我的问题是:GCC如何知道字符串的地址可以适合32位立即数操作数?为什么不需要使用movabs $.LC0, %rdi
(即mov r64, imm64
,而不是零或符号扩展imm32
。)
AFAIK,没有人说加载器必须决定在任何特定地址加载数据部分。如果字符串存储在1ULL << 32
以上的某个地址,那么movl将忽略更高的位。我对clang有类似的行为,所以我不认为这是GCC独有的。
我关心的原因是我想创建自己的数据段,它存在于我选择的任意地址(可能超过2 ^ 32)的内存中。
答案 0 :(得分:6)
在GCC手册中:
https://gcc.gnu.org/onlinedocs/gcc-4.5.3/gcc/i386-and-x86_002d64-Options.html
3.17.15 Intel 386和AMD x86-64选项
<强> -mcmodel =小强>
为小代码模型生成代码:程序及其符号 必须链接在地址空间的低2 GB。指针是64 位。程序可以静态或动态链接。 这是 默认代码模型。
-mcmodel = kernel为内核代码模型生成代码。内核运行在负2 GB的地址空间中。这个模型必须是 用于Linux内核代码。
-mcmodel =介质
为中型模型生成代码:程序在下部链接 2 GB的地址空间。小符号也放在那里。 放入大小大于-mlarge-data-threshold的符号 大数据或bss部分,可以位于2GB以上。程序可以 静态或动态链接。
-mcmodel =大
为大型模型生成代码:此模型不做任何假设 关于部分的地址和大小。
https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
3.18.1 AArch64选项
-mcmodel =微小的
为微小的代码模型生成代码。程序及其静态定义的符号必须在1GB之内。指针 是64位。程序可以静态或动态链接。这个 模型没有完全实现,大多被视为“小”。
<强> -mcmodel =小强>
为小代码模型生成代码。程序及其静态定义的符号必须在4GB之内。指针 是64位。程序可以静态或动态链接。 这是 默认代码模型。
-mcmodel =大
为大型代码模型生成代码。这不会对部分的地址和大小做出任何假设。指针是64位。程式 只能静态链接。
答案 1 :(得分:1)
我可以确认这是在64位编译中发生的:
gcc -O1 foo.c
然后objdump -d a.out
(另请注意printf("%s\n")
可以是optimized into puts
!):
0000000000400536 <main>:
400536: 48 83 ec 08 sub $0x8,%rsp
40053a: bf d4 05 40 00 mov $0x4005d4,%edi
40053f: e8 cc fe ff ff callq 400410 <puts@plt>
400544: b8 00 00 00 00 mov $0x0,%eax
400549: 48 83 c4 08 add $0x8,%rsp
40054d: c3 retq
40054e: 66 90 xchg %ax,%ax
原因是GCC默认为-mcmodel=small
,其中静态数据链接在地址空间的底部2G。
请注意,字符串常量不会转到数据段,而是在代码段中,而不是-fwritable-strings
。此外,如果您想在内存中自由重定位目标代码,您可能希望使用-fpic
进行编译以使代码RIP相对而不是在任何地方放置64位地址。