“jal”到 MIPS 中另一个函数中间的原因可能是什么

Riz*_*Riz 5 mips function-calls disassembly

我正在查看 C 应用程序的一个非常可疑的反汇编 MIPS 代码

80019B90                 jal     loc_80032EB4
Run Code Online (Sandbox Code Playgroud)

loc_80032EB4 在另一个函数体的中间,我特别检查了在运行时没有在这个地址加载其他代码,并且以这种方式调用该函数(在开始时传递一些代码)可能很有用。但是在 C 中怎么做呢?它不是 goto,因为您不能转到另一个函数,并且正常的函数调用将始终“jal”到开头。这可以是一些手动优化吗?

更新:

两个函数的简化布局,被调用者:

sub_80032E88 (lz77_decode)
... save registers ...
80032E90                 addiu   $sp, -8
... allocate memory for decompressed data ...
80032EB0                 move    DECOMPRESSED_DATA_POINTER_A1, $v0
loc_80032EB4:
80032EB4                 lw      $t7, 0(PACKED_DATA_POINTER_A0)
... actual data decompression ...
80032F4C                 jr      $ra
Run Code Online (Sandbox Code Playgroud)

呼叫者:

80019ACC                 addiu   $sp, -0x30
... some not related code ...
80019B88                 lw      $a1, off_80018084   // A predefined buffer is used instead of allocating it for decompressed data
80019B90                 jal     loc_80032EB4
80019B94                 move    $a0, $s0
... some other code and function epilogue ...
Run Code Online (Sandbox Code Playgroud)

更新 2: 我已经检查过这是否可能是 setjmp/longjmp 使用的情况,但在我的测试中,我总是可以在反汇编代码中看到对 setjmp 和 longjmp 函数的调用,而不是直接跳转。

更新 3: 我尝试使用特定于 GCC 的能力来获取标签指针并将此指针转换为函数,结果接近我想要的但反汇编代码仍然不同,因为它没有使用带有 exaxct 地址的 jal 它计算它运行时,也许由于范围问题,我无法强制编译器将此值视为常量。

mar*_*kgz 2

由于它是来自游戏系统的数据解压函数,因此该函数很可能是具有多个入口点的手工优化组装。多个入口点并不常用,因此很难找到公开可用的示例,但这里有一个来自 gcc 邮件列表的旧线程,建议使用此技术。

要点是,如果您有两个函数,其中一个函数F1的代码是另一个函数F2代码的子集,那么F2的代码可以落入F1的代码中。在您的情况下,F2为解压缩的数据分配内存,F1假设内存分配已经完成。我很确定 GCC 2.9x 无法生成这样的代码。

无法直接将此构造从汇编程序转换为标准 C,因为您无法使用gotoC 中的另一个函数,但这在汇编程序代码中是完全合法的。gcc 邮件列表线程建议了一些解决方法来在 C 中表达相同的想法。

如果您查看解压缩的反汇编代码,它可能与编译器生成的代码具有不同的风格。甚至可能会使用一些操作码,例如查找编译器无法从 C 生成的第一个设置位。