旧的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); 

    // ...
}

// ...

Suppose someone want to restore / reimplement foobar.c from the compiled assembly without access to the original source.
One would probably like to decompile / rewrite some part first, says start from foo() or other standard functions. However, in order to test the correctness of the implementation, one must deal with calls to non-standard ABI routines.
A trivial way is to workaround with global register variables provided by 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是否支持为某些特定函数自定义调用约定?

您所能做的最好的事情就是 Select 一个特定的受支持的调用约定e.g. one of these for x86.如果所讨论的static函数不符合其中任何一个,那么您就卡住了.

  1. 有没有办法更优雅地处理非标准ABI呼叫?

没有什么真正优雅的.如果支持的调用约定均不适用,则您将无法满足以下任一要求:

  1. 反转(&A);重建整个过程(这样它就可以正常编译,而不依赖原始二进制文件),或者至少要完全替换符合ABI的函数及其所有依赖项,或者
  2. 从程序集调用它,根据编译函数的非标准调用约定显式传递参数.

#2是最优雅的解决方案的基础,它基本上是在汇编中编写一个包装函数,该函数接收参数并根据ABI返回值,否则只会重新排列它们以传递给它包装的非标准函数(如果它不按照正常规则返回,则可能修复返回值).您只需编写一次包装器,现在您的其余代码就可以用C编写,调用遵循ABI的包装器函数,并且非常高兴地意识到隐藏的奇怪之处.

类似地,如果您试图用另一个非标准函数替换现有的非标准函数,您可以在汇编中编写不一致的包装,然后用普通C编写替换函数,并让包装调用它,然后在原始二进制和新代码的混合代码中交换包装.

C++相关问答推荐

找出文件是否包含给定的文件签名

ATmega328P EEPROM未写入

C是否用0填充多维数组的其余部分?

在C++中通过空指针隐式访问常量变量的值

解决S随机内存分配问题,实现跨进程高效数据共享

OpenSSL:如何将吊销列表与SSL_CTX_LOAD_VERIFY_LOCATIONS一起使用?

C:在编译时构建和使用字符串文字的预处理器宏?

如何在VS 2022中正确安装额外的C头文件

在vfork()之后,链接器如何在不 destruct 父内存的情况下解析execve()?

C语言中的数字指针

为什么电路板被循环删除?

在运行时判断C/C++指针是否指向只读内存(在Linux操作系统中)

具有正确标头的C struct 定义问题

程序对大输入给出错误答案

哪些C++功能可以在外部C块中使用

在分配内存后使用指针是未定义的行为吗?

execve 不给出which命令的输出

多行表达式:C 编译器如何处理换行符?

C 中类型说明符的顺序重要吗?

strlen 可以是[[未排序]]吗?