我应该如何编写我的 C 代码以使生成的程序集使用额外的 dsll32 和 dsra32 指令?

问题描述 投票:0回答:1

我正在反编译作为调试版本提供的 PS2 游戏。我已经进行了足够的反编译,能够使用最初使用的编译器(Metrowerks CodeWarrior)编译 ELF 文件。

现在我正在对原始ELF文件的反汇编和我编译的ELF文件的反汇编进行比较。原始程序集有一种重复出现的模式,但我的程序集没有:使用 dsll32 和 dsra32 指令进行定期转换。

原始组装:

dsll32  v1,s1,16
dsra32  v1,v1,16
subu    v1,$0,v1
dsll32  v1,v1,16
dsra32  v1,v1,16
dsll32  s1,v1,16
dsra32  s1,s1,16

这被反编译为以下 C 代码:

d1 = -d1;

并被编译为以下程序集:

subu    v1,$0,s1
dsll32  s1,v1,16
dsra32  s1,s1,16

请注意,缺少一对移位指令。到目前为止,我还未能复制这一点。我尝试过添加各种类型转换,将其更改为

d1 = 0 - d1
,有人建议我添加 64 位类型转换,但没有达到预期的结果。

这是另一个例子:

原始组装:

lh      v0,80(sp)
dsll32  v1,v0,16
dsra32  v1,v1,16
lw      v0,128(sp)
lb      v0,0(v0)
dsll32  v0,v0,24
dsra32  v0,v0,24
dsll32  v0,v0,16
dsra32  v0,v0,16
addu    v0,v1,v0
dsll32  v0,v0,16
dsra32  v0,v0,16
dsll32  s3,v0,16
dsra32  s3,s3,16

C代码:

x = xposi + sprdat->xoff;

编译为:

lh      v1,80(sp)
lw      v0,128(sp)
lb      v0,0(v0)
dsll32  v0,v0,24
dsra32  v0,v0,24
addu    v0,v1,v0
dsll32  s3,v0,16
dsra32  s3,s3,16

有谁知道什么 C 代码会对此负责吗?

c mips codewarrior metrowerks
1个回答
0
投票

TL;DR:别担心,一切都很好。

  1. 您正在反编译 debug 版本。因此,优化[可能]已关闭。
  2. 原始程序集看起来像是未优化的代码。
  3. 反编译后的代码是
    d1 = -d1;
  4. 重新编译代码的最终程序集经过了优化,并增加了一个附加因素(见下文)。
  5. 在 x86 上,这将产生一个指令。如果
    d1
    位于(例如)
    %ecx
    ,则装配将是:
    neg %ecx
  6. 在没有该功能的架构上(例如
    mips
    ),我们从 0 中减去该值。

以下是一些粗略的解释。

在原始汇编中,前两条指令将 16 位整数转换为 32 位整数:

dsll32  v1,s1,16            # move 16 bit int's sign bit into bit 31
dsra32  v1,v1,16            # shift back with sign extension

算术右移会将第31位移入左侧的数字中。

因此,

short
中的-1将具有0xFFFF的位模式(即0x0000FFFF)。我们将其转换为 32 位(即 0xFFFFFFFF)

这使用了一个额外的

tmp
变量,该变量被放入寄存器
v1
中。
tmp
可能[隐式]定义为:

int tmp;

这意味着

d1
(位于寄存器
s1
中)被定义为 16 位整数:

short d1;

现在,要实际对数字求负,我们用零减去它:

subu    v1,$0,v1                # perform negation

等效的 C 代码是:

tmp = 0 - tmp;

接下来的两条指令将 32 位

tmp
中的符号位放入 16 位数字的符号位中:

dsll32  v1,v1,16                # put sign bit from 16 bit part into bit 31
dsra32  v1,v1,16                # shift back to get sign bit for 16 bit number

其余两条指令正在执行类似的[且不必要的]转换,以获得

d1
的最终 16 位值:

dsll32  s1,v1,16                # d1 = tmp << 16
dsra32  s1,s1,16                # d1 = d1 >> 16

重建的程序集已假定

s1
已符号扩展至 32 位。因此,不需要原始程序集的前两条指令:

# assume s1 has _already_ been sign extended to 32 bits here in _prior_
# instructions
subu    v1,$0,s1

# move sign bit from 32 bits back to 16 bits
dsll32  s1,v1,16
dsra32  s1,s1,16

并且,额外不必要的最后两条指令被省略。

总结一下:重新编译的代码与原始代码的功能完全相同,但经过优化,可以用更少的指令来完成。

快乐重新编译!

© www.soinside.com 2019 - 2024. All rights reserved.