有时函数不使用参数(可能是因为另一个“flags”参数不启用特定功能)。
但是,您必须指定一些内容,因此通常只需输入
0
。但如果您这样做,并且该函数是外部的,gcc
将发出代码以“真正确保”该参数设置为 0
。
有没有办法告诉
gcc
函数的特定参数并不重要,并且它可以忽略参数寄存器中恰好存在的任何值?
更新:有人问关于XY问题。这个问题背后的上下文是我想在
x86_64
中实现一个 varargs 函数,而不使用编译器 varargs 支持。当参数位于堆栈上时,这是最简单的,因此我声明我的函数首先采用 5 或 6 个虚拟参数,以便最后一个非可变参数和所有可变参数最终都位于堆栈上。这工作得很好,只是它显然不是最佳的 - 当查看汇编代码时,很明显 gcc
正在将调用者中的所有这些参数寄存器初始化为零。
请不要认真对待以下答案。这个问题要求破解,所以你就可以了。
GCC 将有效地将未初始化变量的值视为“不关心”,因此我们可以尝试利用这一点:
int foo(int x, int y);
int bar_1(int y) {
int tmp = tmp; // Suppress uninitialized warnings
return foo(tmp, y);
}
不幸的是,我的 GCC 版本仍然懦弱地将
tmp
初始化为零,但你的版本可能更激进:
bar_1:
.LFB0:
.cfi_startproc
movl %edi, %esi
xorl %edi, %edi
jmp foo
.cfi_endproc
另一种选择是 (ab) 使用内联汇编来伪造 GCC,使其认为
tmp
已定义(而实际上并未定义):
int bar_2(int y) {
int tmp;
asm("" : "=r"(tmp));
return foo(tmp, y);
}
通过这个 GCC 成功摆脱了参数初始化:
bar_2:
.LFB1:
.cfi_startproc
movl %edi, %esi
jmp foo
.cfi_endproc
请注意,内联汇编必须紧接在函数调用之前,否则 GCC 会认为它必须保留输出值,这会损害寄存器分配。
大多数(所有?)C 实现的 ABI 允许跳过未使用的参数。您可以使用所需的每组参数为要调用的函数定义任意数量的别名,然后使用正确的别名调用该函数。
仅当您想省略最后 N 个参数时,这才有效。如果您想省略前 N 个参数,那将无济于事。
如果函数位于不同的翻译单元中,则必须在被调用者的翻译单元中定义别名。如果您不想修改正在调用的库,您可能需要对链接器进行更多操作。