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



如果我编译这个程序:

#include <stdio.h>
int main(int argc, char** argv) {
    printf("hello world!n");
    return 0;
}

对于x86-64,asm输出使用movl $.LC0, %edi/call puts。(请参阅godbolt上的完整asm输出/编译选项。)

我的问题是:GCC如何知道字符串的地址可以放在32位立即数操作数中?为什么不需要使用movabs $.LC0, %rdi(即mov r64, imm64,而不是零或符号扩展的imm32)。

AFAIK,并没有说加载程序必须决定在任何特定地址加载数据段。如果字符串存储在1ULL << 32以上的某个地址,则movl将忽略较高的位。我对clang有类似的行为,所以我不认为这是GCC独有的。


我关心的原因是我想创建自己的数据段,该数据段存在于我选择的任何任意地址(可能在2^32以上)的内存中。

在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=small

为小代码模型生成代码:程序及其符号必须链接到地址空间的较低2 GB中。指针为64位。程序可以静态链接,也可以动态链接这是默认代码模型

-mcmodel=kernel为内核代码模型生成代码。内核在负2GB的地址空间中运行。此模型必须用于Linux内核代码。

-mcmodel=介质

生成介质模型的代码:程序链接在下2 GB的地址空间。小符号也被放置在那里。将大小大于-mlarge数据阈值的符号放入大数据或bss部分,并且可以位于2GB以上。程序可以可以静态或动态链接。

-mcmodel=大型

为大型模型生成代码:此模型不做任何假设关于节的地址和大小。


https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html

3.18.1 AArch64选项

-mcmodel=微小

为微小代码模型生成代码。程序及其静态定义的符号之间的距离必须在1GB以内。指针是64位。程序可以静态链接,也可以动态链接。这该模型并没有完全实现,大多被视为"小"模型。

-mcmodel=small

为小代码模型生成代码。程序及其静态定义的符号之间的距离必须在4GB以内。指针是64位。程序可以静态链接,也可以动态链接这是默认代码模型

-mcmodel=大型

为大型代码模型生成代码。这对部分的地址和大小没有任何假设。指针为64位。程序只能静态链接。

我可以确认这发生在64位编译中:

gcc -O1 foo.c

然后是objdump -d a.out(还要注意,printf("%sn")可以优化为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位地址。

最新更新