gcc / clang如何假设字符串常量的地址是32位?

时间:2016-03-23 16:22:55

标签: c linux linker x86-64 elf

如果我编译这个程序:

#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)的内存中。

2 个答案:

答案 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位地址。