uxmal / reko

Reko is a binary decompiler.

Home Page:https://uxmal.github.io/reko

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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;
}