gpt4 book ai didi

c - gcc 参数寄存器在 x86-64 上溢出

转载 作者:太空狗 更新时间:2023-10-29 17:18:04 24 4
gpt4 key购买 nike

我正在对 x86-64 汇编进行一些试验。编译了这个虚拟函数:

long myfunc(long a, long b, long c, long d,
long e, long f, long g, long h)
{
long xx = a * b * c * d * e * f * g * h;
long yy = a + b + c + d + e + f + g + h;
long zz = utilfunc(xx, yy, xx % yy);
return zz + 20;
}

使用 gcc -O0 -g 我惊讶地发现函数汇编的开头如下:

0000000000400520 <myfunc>:
400520: 55 push rbp
400521: 48 89 e5 mov rbp,rsp
400524: 48 83 ec 50 sub rsp,0x50
400528: 48 89 7d d8 mov QWORD PTR [rbp-0x28],rdi
40052c: 48 89 75 d0 mov QWORD PTR [rbp-0x30],rsi
400530: 48 89 55 c8 mov QWORD PTR [rbp-0x38],rdx
400534: 48 89 4d c0 mov QWORD PTR [rbp-0x40],rcx
400538: 4c 89 45 b8 mov QWORD PTR [rbp-0x48],r8
40053c: 4c 89 4d b0 mov QWORD PTR [rbp-0x50],r9
400540: 48 8b 45 d8 mov rax,QWORD PTR [rbp-0x28]
400544: 48 0f af 45 d0 imul rax,QWORD PTR [rbp-0x30]
400549: 48 0f af 45 c8 imul rax,QWORD PTR [rbp-0x38]
40054e: 48 0f af 45 c0 imul rax,QWORD PTR [rbp-0x40]
400553: 48 0f af 45 b8 imul rax,QWORD PTR [rbp-0x48]
400558: 48 0f af 45 b0 imul rax,QWORD PTR [rbp-0x50]
40055d: 48 0f af 45 10 imul rax,QWORD PTR [rbp+0x10]
400562: 48 0f af 45 18 imul rax,QWORD PTR [rbp+0x18]

gcc 非常奇怪地将所有参数寄存器溢出到堆栈上,然后从内存中取出它们以进行进一步操作。

这只发生在 -O0 上(-O1 没有问题),但是,为什么?这对我来说似乎是反优化 - 为什么 gcc 会那样做?

最佳答案

我绝不是 GCC 内部专家,但我会试一试。不幸的是,关于 GCC 寄存器分配和溢出的大部分信息似乎都已过时(引用的文件如 local-alloc.c 已不存在)。

我正在查看 gcc-4.5-20110825 的源代码.

GNU C Compiler Internals提到初始函数代码是由expand_function_start生成的在gcc/function.c .在那里我们找到以下用于处理参数的内容:

4462   /* Initialize rtx for parameters and local variables.
4463 In some cases this requires emitting insns. */
4464 assign_parms (subr);

assign_parms处理每个参数存储位置的代码如下:

3207       if (assign_parm_setup_block_p (&data))
3208 assign_parm_setup_block (&all, parm, &data);
3209 else if (data.passed_pointer || use_register_for_decl (parm))
3210 assign_parm_setup_reg (&all, parm, &data);
3211 else
3212 assign_parm_setup_stack (&all, parm, &data);

assign_parm_setup_block_p处理聚合数据类型,在这种情况下不适用,因为数据不是作为指针传递 GCC 检查 use_register_for_decl .

这里相关的部分是:

1972   if (optimize)
1973 return true;
1974
1975 if (!DECL_REGISTER (decl))
1976 return false;

DECL_REGISTER测试变量是否用 register 声明关键词。现在我们有了答案:当未启用优化时,大多数参数都存在于堆栈中,然后由 assign_parm_setup_stack 处理。 .在最终溢出值之前通过源代码所采用的路径对于指针参数来说稍微复杂一些,但如果您好奇的话,可以在同一个文件中进行跟踪。

为什么 GCC 在禁用优化的情况下溢出所有参数和局部变量?帮助调试。考虑这个简单的函数:

1 extern int bar(int);
2 int foo(int a) {
3 int b = bar(a | 1);
4 b += 42;
5 return b;
6 }

编译自gcc -O1 -c这会在我的机器上生成以下内容:

 0: 48 83 ec 08             sub    $0x8,%rsp
4: 83 cf 01 or $0x1,%edi
7: e8 00 00 00 00 callq c <foo+0xc>
c: 83 c0 2a add $0x2a,%eax
f: 48 83 c4 08 add $0x8,%rsp
13: c3 retq

这很好,除非你在第 5 行中断并尝试打印 a 的值,你会得到

(gdb) print a
$1 = <value optimized out>

由于参数在调用 bar 后未被使用而被覆盖.

关于c - gcc 参数寄存器在 x86-64 上溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7201763/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com