C语言 为函数参数指定寄存器?



旧的gcc或egcs说,一些编译器对单个文件中的静态函数应用abi破坏优化,如传递参数或使用任意寄存器返回结果。

考虑如下源代码:

// Original foobar.c
// This example targets MIPS o32 ABI.
// Shared subroutine
// Compiler decided to use $16, $17 to pass a0 and a1 to minimize stack usage and move between registers.
static void __bar(int a0, int a1) {
// Something very complicated
}
// ...
void foo(int a0, int a1) {
// ...
/*
This call was compiled to something like:
ori $16, $0, 0x1
jal __bar
ori $17, $0, 0x1
*/
__bar(1, 1); 
// ...
}
// ...

假设有人想要从编译后的程序集中恢复/重新实现foobar.c,而不需要访问原始源代码。
人们可能想要先反编译/重写一些部分,比如从foo()或其他标准函数开始。然而,为了测试实现的正确性,必须处理对非标准ABI例程的调用。
一个简单的方法是使用gcc/clang提供的全局寄存器变量:
// Restoration of foobar.c
// void __bar(int asm("s0"), int asm("s1"))
// External function in assembly, says foobar.s, which is from compiled original foobar.c.
void __bar();
volatile register int s0 asm ("s0"); // $16 = s0
volatile register int s1 asm ("s1"); // $17 = s1
// ...
void foo(int a0, int a1) {
// ...
// __bar(1, 1);
s0 = 1; s1 = 1;
__bar();
// ...
}
// ...

问题是:

  1. gcc/clang是否支持自定义某些特定函数的调用约定?
  2. 有没有更优雅地处理非标准ABI调用的方法?
  1. gcc/clang是否支持某些特定函数的自定义调用约定?

你能做的最好的事情是选择一个特定的支持调用约定,例如x86的其中一个。如果所讨论的static函数不符合其中任何一个,那么您就被卡住了。

  1. 是否有更优雅地处理非标准ABI调用的方法?

没有真正的优雅。如果所有支持的调用约定都不适用,则只能使用以下两种:

  1. 扭转,重新构建整个东西(这样它就可以正常编译而不依赖于原始二进制文件),或者至少足够的,你完全替换ABI兼容的函数和它们的所有依赖,或者
  2. 从汇编程序调用它,根据编译函数的非标准调用约定显式传递参数。

#2是最优雅的解决方案的基础,它基本上是用汇编编写一个包装器函数,它根据ABI接收参数并返回值,除此之外什么都不做,只是重新排列它们以传递给它包装的非标准函数(如果它没有根据正常规则返回,则可能修复返回值)。只需编写一次包装器,现在剩下的代码就可以用C编写了,调用遵循ABI的包装器函数,并且完全不知道背后的奇怪之处。

同样,如果你想用另一个函数替换现有的非标准函数,你可以用汇编语言编写不符合标准的包装器,然后用纯C语言编写替换函数,并让包装器调用它,然后在原始二进制代码和新代码的混合中交换包装器。

最新更新