Bug: Missing / Merged Variable in High-Level output
Elthial opened this issue · comments
There is an example of a variable [bp-4h] (es:[400Ch]) either being excluded from the HL or being merged into [bp-6h] (es:[403Eh])
In "fn183B_28DB" of the Btech.exe
This seems like an incorrect rewrite to C.
Eq_31270 fn183B_28DB(Eq_21 ds, union Eq_31270 & siOut)
{
Eq_21 wLoc0E;
Eq_21 wLoc10;
Check_Memory_Size_2FDC(0x14);
Eq_21 ax_15 = (*((word32) ds + 0x000055CC))->tA44B;
Eq_21 ax_18 = (*((word32) ds + 0x000055CE))->tA44D;
<---- PosX?
Eq_21 wLoc08_191 = (*((word32) ds + 0x000055D6))->a403E[0]; <---- PosY
struct Eq_30866 * es_26 = *((word32) ds + 0x000055D4);
if (es_26->aC614[0].b000C != 0x08)
wLoc08_191 = (*((word32) ds + 0x000055D6))->a4036[(int16) es_26->aC614[0].b000C];
CharacterPos_Compare(wLoc08_191, wLoc08_191); <--- Compare(PosX, PosY)?
The variable: (*((word32) ds + 0x000055D2)->a400C[0];
Is lost / merged into a403E which was noticed by the bizarre CharacterPos_Compare(PosY, PosY).
Real output should be something like:
word16 fn183B_28DB(Eq_21 ds)
{
Eq_21 wLoc0E;
Eq_21 wLoc10;
Check_Memory_Size_2FDC(0x14);
unsigned short Character_PosX = (*(ds + 0x55CC))->tA44B; //A44B - Character Position X
unsigned short Character_PosY = (*(ds + 0x55CE))->tA44D; //A44D - Character position Y
unsigned short PosX_400 = (*(ds + 0x55D2))->a400C[0]; //t400C - CharacterPosX_OnFoot <---- PosX
unsigned short PosY_400 = (*(ds + 0x55D6))->a403E[0]; //t403E - CharacterPosY_OnFoot <---- PosY
int* es_26 = *(ds + 0x55D4);
if (es_26->aC620 != Character_OnFoot)
{
PosX_400 = (*(ds + 0x55D2))->a4004[es_26->aC614[0]]; //t4004 - CharacterPosX_Mech
PosY_400 = (*(ds + 0x55D6))->a4036[es_26->aC614[0]]; //C614 Name //w4036 - CharacterPosY_Mech
}
//PosX, PosY args
CharacterPos_Compare(PosX_400, PosY_400); <--- Compare(PosX, PosY)
The original ASM:
183B:28DB 55 push bp
183B:28DC 8B EC mov bp,sp
183B:28DE B8 14 00 mov ax,14h
183B:28E1 9A DC 2F 7F 20 call far 207Fh:2FDCh
183B:28E6 56 push si
183B:28E7 8E 06 CC 55 mov es,[55CCh]
183B:28EB 26 A1 4B A4 mov ax,es:[0A44Bh]
183B:28EF 89 46 EE mov [bp-12h],ax
183B:28F2 8E 06 CE 55 mov es,[55CEh]
183B:28F6 26 A1 4D A4 mov ax,es:[0A44Dh]
183B:28FA 89 46 EC mov [bp-14h],ax
183B:28FD 8E 06 D2 55 mov es,[55D2h]
183B:2901 26 A1 0C 40 mov ax,es:[400Ch] <---- PosX
183B:2905 89 46 FC mov [bp-4h],ax <---- PosX
183B:2908 8E 06 D6 55 mov es,[55D6h]
183B:290C 26 A1 3E 40 mov ax,es:[403Eh] <---- PosY
183B:2910 89 46 FA mov [bp-6h],ax <---- PosY
183B:2913 8E 06 D4 55 mov es,[55D4h]
183B:2917 26 80 3E 20 C6 08 cmp byte ptr es:[0C620h],8h
183B:291D 74 24 jz 2943h
183B:291F 26 A0 20 C6 mov al,es:[0C620h]
183B:2923 98 cbw
183B:2924 89 46 FA mov [bp-6h],ax
183B:2927 8B F0 mov si,ax
183B:2929 D1 E6 shl si,1h
183B:292B 8E 06 D2 55 mov es,[55D2h]
183B:292F 26 8B 84 04 40 mov ax,es:[si+4004h]
183B:2934 89 46 FC mov [bp-4h],ax
183B:2937 8E 06 D6 55 mov es,[55D6h]
183B:293B 26 8B 84 36 40 mov ax,es:[si+4036h]
183B:2940 89 46 FA mov [bp-6h],ax
183B:2943 FF 76 FA push word ptr [bp-6h] <---- PosY
183B:2946 FF 76 FC push word ptr [bp-4h] <---- PosX
183B:2949 9A BB 17 00 08 call far 0800h:17BBh <--- Compare(PosX, PosY)
The issue here is that the call to Check_Memory_Size_2FDC
is adjusting the stack pointer by 0x14
, but Reko doesn't understand this. It considers the stack frame of fn183B_28DB
to be 4 bytes, when in fact it is 4 + 0x14
= 0x18
bytes. After incorrectly setting up a too-shallow stack frame, Reko's analysis stomps over the local variable in bp-6
with arguments being passed to subroutines.
Reko does try to track stack adjustments, but the function Check_Memory_Size_2FDC
-- which really is the stack allocating function alloca
-- is doing some hacky stuff to the stack. Reko does have logic to handle alloca
, but you have to tell it which procedures are alloca
because we currently don't have byte signatures implemented. Furthermore, I just noticed there is no way to indicate a procedure is alloca
in the user interface -- only in the .dcproject
file. Finally, I also discovered that Reko doesn't handle alloca
directives in the case when the called alloca
procedure is statically linked into the binary.
I will work on implementing some of these missing features and bugs. Implementing a signature matcher is a large task, so that won't be covered in the resolution of this issue.
The commit above addresses the issue. What you, @Elthial , need to do is to change the signature of Check_MemorySize_2FDC
to the following (I've taken the liberty of changing its name to the more appropriate alloca
):
[[reko::address("207F:2FDC")]]
[[reko::arg(register,"sp")]] char *alloca([[reko::arg(register,"ax")]] unsigned ax );
When I apply the above changes, the reported error goes away:
word16 fn183B_28DB(Eq_21 ds)
{
Eq_21 wLoc0E;
Eq_21 wLoc10;
Eq_21 ax_15 = (*((word32) ds + 0x000055CC))->tA44B;
Eq_21 ax_18 = (*((word32) ds + 0x000055CE))->tA44D;
Eq_21 wLoc06_227 = (*((word32) ds + 0x000055D2))->t400C;
Eq_21 wLoc08_228 = (*((word32) ds + 0x000055D6))->t403E;
struct Eq_32520 * es_26 = *((word32) ds + 0x000055D4);
if (es_26->bC620 != 0x08)
{
int16 ax_30 = (int16) es_26->bC620;
wLoc06_227 = *((char *) &(*((word32) ds + 0x000055D2))->a4004->t0000 + ax_30 * 0x02);
wLoc08_228 = *((char *) &(*((word32) ds + 0x000055D6))->a4036->t0000 + ax_30 * 0x02);
}
Memory_CMP_17BB(wLoc06_227, wLoc08_228);
fn207F_1314((*((word32) ds + 0x000055CC))->tA44B, (*((word32) ds + 0x000055CE))->tA44D);
fn207F_1DF8();
(*((word32) ds + 22036))->tE486 = (*((word32) ds + 0x000055CC))->tA44B;
(*((word32) ds + 0x00005616))->tE488 = (*((word32) ds + 0x000055CE))->tA44D;
word16 wLoc04_238 = 0x00;
wchar_t wLoc0A_239 = 0x1A;
wchar_t wLoc0C_240 = 0x0C;
int16 wLoc12_241 = 0x0C;
do
{
if (wLoc04_238 == 0x00 && ((*((word32) ds + 0x000055E0))->a406A)[wLoc12_241] != 0x00)
{
wLoc0E = *((char *) &(*((word32) ds + 0x000055D2))->a4004->t0000 + wLoc12_241 * 0x02);
wLoc10 = *((char *) &(*((word32) ds + 0x000055D6))->a4036->t0000 + wLoc12_241 * 0x02);
wLoc04_238 = 0x01;
}
int16 v16_112 = wLoc12_241 + 0x01;
wLoc12_241 = v16_112;
} while (v16_112 < 0x18);
(*((word32) ds + 22042))->t3770.u0 = 0x1E;
if (wLoc04_238 != 0x00)
{
wLoc04_238 = 0x00;
while ((*((word32) ds + 22042))->t3770 > 0x00 && ((*((word32) ds + 22036))->tE486 != wLoc0E || (*((word32) ds + 0x00005616))->tE488 != wLoc10))
{
fn1631_0006(ds, 0x80, wLoc0E, wLoc10, wLoc0A_239, wLoc0C_240, 0x00);
struct Eq_32287 * es_179 = *((word32) ds + 22042);
wLoc0A_239 = (word16) (*((word32) ds + 22044))->t458E.u1 + wLoc0A_239;
wLoc0C_240 = (word16) (*((word32) ds + 22046))->t4590.u1 + wLoc0C_240;
--es_179->t3770;
if ((*((word32) ds + 22036))->tE486 == wLoc0E && (*((word32) ds + 0x00005616))->tE488 == wLoc10)
{
(*((word32) ds + 22042))->t3770.u0 = 0x00;
wLoc04_238 = 0x01;
}
}
}
fn183B_2AA3(ds, ax_15, ax_18);
return wLoc04_238;
}