Nawiązując do artykułu o kompilatorze Go, nie mogę ominąć kompilatora PureBasic w wersji od 4.xx do 5.30. Jest to kompilator dla kolejnej odmiany języka BASIC, której developerzy są głusi na wszelkie maile dotyczące nieprawidłowości w kodzie i w ogóle „nie rozumieją o co chodzi”.
Więc o co tym razem mi chodzi? Analizowałem plik kompilatora PureBasic.exe w wersji 5.30. Może zacznijmy od optymalizacji.
1. Optymalizacja
Optymalizacja to słowo, które jest obce twórcom kompilatora PureBasic. W poprzednim artykule ktoś sugerował, że biedna optymalizacja kompilatora Go być może ma jakiś „sens”, na pewno jakiś gimbus, który nie rozróżnia assemblera od akumulatora, no więc być może i tutaj znajdziesz jakiś sens (czekam na komentarz):
.code:004F165E mov ebp, [esp+10h+arg_70] .code:004F1665 push ebp .code:004F1666 pop ebp .code:004F1667 mov ebx, [ebp+684h] .code:004F166D cmp ebx, [esp+10h+var_10] .code:004F1670 jnz short loc_4F16A9 .code:004F1672 push 0 .code:004F1677 mov ebp, [esp+14h+arg_70] .code:004F167E push ebp .code:004F167F pop ebp .code:004F1680 push dword ptr [ebp+688h] .code:004F1686 call sub_5743CC
Nie wiem jak można by i to zaklasyfikować? push ebp + pop ebp = zero efektu, wyrównanie kolejnych instrukcji też nie.
Kolejny genialny fragment kodu:
.code:004F1FBE sub_4F1FBE proc near ; DATA XREF: start+50C7o .code:004F1FBE push dword_703918 .code:004F1FC4 pop eax .code:004F1FC5 mov dword_70411C, eax .code:004F1FCA push dword_7039CC .code:004F1FD0 pop eax .code:004F1FD1 mov dword_703EE4, eax .code:004F1FD6 push dword_703F08 .code:004F1FDC pop eax .code:004F1FDD mov dword_703CC0, eax .code:004F1FE2 xor eax, eax .code:004F1FE4 retn 4 .code:004F1FE4 sub_4F1FBE endp
Widać ktoś nie doczytał, że instrukcją mov można również odczytać dane z pamięci, pomijając dodatkowo stos.
Teraz trochę dłuższy fragment, w którym być może zauważycie pewien powtarzający się wzorzec:
.code:00401B0E mov ebp, dword_705314 .code:00401B14 mov dword ptr [ebp+0Ch], 31h .code:00401B1B mov dword ptr [ebp+10h], 5Bh .code:00401B22 add ebp, 8 .code:00401B25 push dword ptr [ebp+0Dh] .code:00401B28 call sub_56E513 .code:00401B2D mov ebp, dword_705314 .code:00401B33 add ebp, 8 .code:00401B36 mov ebp, [ebp+11h] .code:00401B39 mov edx, offset off_696024 ; Str .code:00401B3E lea ecx, [ebp+8] ; int .code:00401B41 call sub_540558 .code:00401B46 mov ebp, dword_705314 .code:00401B4C add ebp, 8 .code:00401B4F mov ebp, [ebp+11h] .code:00401B52 mov dword ptr [ebp+0Ch], 0C80000h .code:00401B59 mov ebp, dword_705314 .code:00401B5F add ebp, 8 .code:00401B62 mov ebp, [ebp+11h] .code:00401B65 mov dword ptr [ebp+10h], 1 .code:00401B6C mov ebp, dword_705314 .code:00401B72 add ebp, 8 .code:00401B75 push dword ptr [ebp+0Dh] .code:00401B78 call sub_56E513 .code:00401B7D mov ebp, dword_705314 .code:00401B83 add ebp, 8 .code:00401B86 mov ebp, [ebp+11h] .code:00401B89 mov edx, offset off_6953F2 ; Str .code:00401B8E lea ecx, [ebp+8] ; int .code:00401B91 call sub_540558 .code:00401B96 mov ebp, dword_705314 .code:00401B9C add ebp, 8 .code:00401B9F mov ebp, [ebp+11h] .code:00401BA2 mov dword ptr [ebp+0Ch], 0CA0000h .code:00401BA9 mov ebp, dword_705314 .code:00401BAF add ebp, 8 .code:00401BB2 mov ebp, [ebp+11h] .code:00401BB5 mov dword ptr [ebp+10h], 2 .code:00401BBC mov ebp, dword_705314 .code:00401BC2 add ebp, 8 .code:00401BC5 push dword ptr [ebp+0Dh] .code:00401BC8 call sub_56E513 .code:00401BCD mov ebp, dword_705314 .code:00401BD3 add ebp, 8 .code:00401BD6 mov ebp, [ebp+11h] .code:00401BD9 mov edx, offset off_691FE6 ; Str .code:00401BDE lea ecx, [ebp+8] ; int .code:00401BE1 call sub_540558 .code:00401BE6 mov ebp, dword_705314 .code:00401BEC add ebp, 8 .code:00401BEF mov ebp, [ebp+11h] .code:00401BF2 mov dword ptr [ebp+0Ch], 0C90000h .code:00401BF9 mov ebp, dword_705314 .code:00401BFF add ebp, 8 .code:00401C02 mov ebp, [ebp+11h] .code:00401C05 mov dword ptr [ebp+10h], 4 .code:00401C0C mov ebp, dword_705314 .code:00401C12 add ebp, 8 .code:00401C15 push dword ptr [ebp+0Dh] .code:00401C18 call sub_56E513 .code:00401C1D mov ebp, dword_705314 .code:00401C23 add ebp, 8 .code:00401C26 mov ebp, [ebp+11h] .code:00401C29 mov edx, offset off_691198 ; Str .code:00401C2E lea ecx, [ebp+8] ; int .code:00401C31 call sub_540558 .code:00401C36 mov ebp, dword_705314 .code:00401C3C add ebp, 8 .code:00401C3F mov ebp, [ebp+11h] .code:00401C42 mov dword ptr [ebp+0Ch], 0C40000h .code:00401C49 mov ebp, dword_705314 .code:00401C4F add ebp, 8 .code:00401C52 mov ebp, [ebp+11h] .code:00401C55 mov dword ptr [ebp+10h], 8 .code:00401C5C mov ebp, dword_705314 .code:00401C62 add ebp, 8 .code:00401C65 push dword ptr [ebp+0Dh] .code:00401C68 call sub_56E513 .code:00401C6D mov ebp, dword_705314 .code:00401C73 add ebp, 8 .code:00401C76 mov ebp, [ebp+11h] .code:00401C79 mov edx, offset off_69AAA0 ; Str .code:00401C7E lea ecx, [ebp+8] ; int .code:00401C81 call sub_540558 .code:00401C86 mov ebp, dword_705314 .code:00401C8C add ebp, 8 .code:00401C8F mov ebp, [ebp+11h] .code:00401C92 mov dword ptr [ebp+0Ch], 10000000h .code:00401C99 mov ebp, dword_705314 .code:00401C9F add ebp, 8 .code:00401CA2 mov ebp, [ebp+11h] .code:00401CA5 mov dword ptr [ebp+10h], 10h .code:00401CAC mov ebp, dword_705314 .code:00401CB2 add ebp, 8 .code:00401CB5 push dword ptr [ebp+0Dh] .code:00401CB8 call sub_56E513 .code:00401CBD mov ebp, dword_705314 .code:00401CC3 add ebp, 8 .code:00401CC6 mov ebp, [ebp+11h] .code:00401CC9 mov edx, offset off_695A52 ; Str .code:00401CCE lea ecx, [ebp+8] ; int .code:00401CD1 call sub_540558 .code:00401CD6 mov ebp, dword_705314 .code:00401CDC add ebp, 8 .code:00401CDF mov ebp, [ebp+11h] .code:00401CE2 mov dword ptr [ebp+0Ch], 0C00000h .code:00401CE9 mov ebp, dword_705314 .code:00401CEF add ebp, 8 .code:00401CF2 mov ebp, [ebp+11h] .code:00401CF5 mov dword ptr [ebp+10h], 20h .code:00401CFC mov ebp, dword_705314 .code:00401D02 add ebp, 8 .code:00401D05 push dword ptr [ebp+0Dh] .code:00401D08 call sub_56E513 .code:00401D0D mov ebp, dword_705314 .code:00401D13 add ebp, 8 .code:00401D16 mov ebp, [ebp+11h] .code:00401D19 mov edx, offset off_68ED54 ; Str .code:00401D1E lea ecx, [ebp+8] ; int .code:00401D21 call sub_540558 .code:00401D26 mov ebp, dword_705314 .code:00401D2C add ebp, 8 .code:00401D2F mov ebp, [ebp+11h] .code:00401D32 mov dword ptr [ebp+0Ch], 4 .code:00401D39 mov ebp, dword_705314 .code:00401D3F add ebp, 8 .code:00401D42 mov ebp, [ebp+11h] .code:00401D45 mov dword ptr [ebp+10h], 40h .code:00401D4C mov ebp, dword_705314 .code:00401D52 add ebp, 8 .code:00401D55 push dword ptr [ebp+0Dh] .code:00401D58 call sub_56E513 .code:00401D5D mov ebp, dword_705314 .code:00401D63 add ebp, 8 .code:00401D66 mov ebp, [ebp+11h] .code:00401D69 mov edx, offset off_69312E ; Str .code:00401D6E lea ecx, [ebp+8] ; int .code:00401D71 call sub_540558 .code:00401D76 mov ebp, dword_705314 .code:00401D7C add ebp, 8 .code:00401D7F mov ebp, [ebp+11h] .code:00401D82 mov dword ptr [ebp+0Ch], 80000000h .code:00401D89 mov ebp, dword_705314 .code:00401D8F add ebp, 8 .code:00401D92 mov ebp, [ebp+11h] .code:00401D95 mov dword ptr [ebp+10h], 80h .code:00401D9C mov ebp, dword_705314 .code:00401DA2 add ebp, 8 .code:00401DA5 push dword ptr [ebp+0Dh] .code:00401DA8 call sub_56E513 .code:00401DAD mov ebp, dword_705314 .code:00401DB3 add ebp, 8 .code:00401DB6 mov ebp, [ebp+11h] .code:00401DB9 mov edx, offset off_6916E6 ; Str .code:00401DBE lea ecx, [ebp+8] ; int .code:00401DC1 call sub_540558 .code:00401DC6 mov ebp, dword_705314 .code:00401DCC add ebp, 8 .code:00401DCF mov ebp, [ebp+11h] .code:00401DD2 mov dword ptr [ebp+0Ch], 1 .code:00401DD9 mov ebp, dword_705314 .code:00401DDF add ebp, 8 .code:00401DE2 mov ebp, [ebp+11h] .code:00401DE5 mov dword ptr [ebp+10h], 100h .code:00401DEC mov ebp, dword_705314 .code:00401DF2 add ebp, 8 .code:00401DF5 push dword ptr [ebp+0Dh] .code:00401DF8 call sub_56E513 .code:00401DFD mov ebp, dword_705314 .code:00401E03 add ebp, 8 .code:00401E06 mov ebp, [ebp+11h] .code:00401E09 mov edx, offset off_68C802 ; Str .code:00401E0E lea ecx, [ebp+8] ; int .code:00401E11 call sub_540558 .code:00401E16 mov ebp, dword_705314 .code:00401E1C add ebp, 8 .code:00401E1F mov ebp, [ebp+11h] .code:00401E22 mov dword ptr [ebp+0Ch], 2 .code:00401E29 mov ebp, dword_705314 .code:00401E2F add ebp, 8 .code:00401E32 mov ebp, [ebp+11h] .code:00401E35 mov dword ptr [ebp+10h], 200h .code:00401E3C mov ebp, dword_705314 .code:00401E42 add ebp, 8 .code:00401E45 push dword ptr [ebp+0Dh] .code:00401E48 call sub_56E513 .code:00401E4D mov ebp, dword_705314 .code:00401E53 add ebp, 8 .code:00401E56 mov ebp, [ebp+11h] .code:00401E59 mov edx, offset off_69776C ; Str .code:00401E5E lea ecx, [ebp+8] ; int .code:00401E61 call sub_540558 .code:00401E66 mov ebp, dword_705314 .code:00401E6C add ebp, 8 .code:00401E6F mov ebp, [ebp+11h] .code:00401E72 mov dword ptr [ebp+0Ch], 1000000h .code:00401E79 mov ebp, dword_705314 .code:00401E7F add ebp, 8 .code:00401E82 mov ebp, [ebp+11h] .code:00401E85 mov dword ptr [ebp+10h], 400h .code:00401E8C mov ebp, dword_705314 .code:00401E92 add ebp, 8 .code:00401E95 push dword ptr [ebp+0Dh] .code:00401E98 call sub_56E513 .code:00401E9D mov ebp, dword_705314 .code:00401EA3 add ebp, 8 .code:00401EA6 mov ebp, [ebp+11h] .code:00401EA9 mov edx, offset off_694F68 ; Str .code:00401EAE lea ecx, [ebp+8] ; int .code:00401EB1 call sub_540558 .code:00401EB6 mov ebp, dword_705314 .code:00401EBC add ebp, 8 .code:00401EBF mov ebp, [ebp+11h] .code:00401EC2 mov dword ptr [ebp+0Ch], 20000000h .code:00401EC9 mov ebp, dword_705314 .code:00401ECF add ebp, 8 .code:00401ED2 mov ebp, [ebp+11h] .code:00401ED5 mov dword ptr [ebp+10h], 800h .code:00401EDC mov ebp, dword_705314 .code:00401EE2 add ebp, 8 .code:00401EE5 push dword ptr [ebp+0Dh] .code:00401EE8 call sub_56E513 .code:00401EED mov ebp, dword_705314 .code:00401EF3 add ebp, 8 .code:00401EF6 mov ebp, [ebp+11h] .code:00401EF9 mov edx, offset off_690ADC ; Str .code:00401EFE lea ecx, [ebp+8] ; int .code:00401F01 call sub_540558 .code:00401F06 mov ebp, dword_705314 .code:00401F0C add ebp, 8 .code:00401F0F mov ebp, [ebp+11h] .code:00401F12 mov dword ptr [ebp+0Ch], 8 .code:00401F19 mov ebp, dword_705314 .code:00401F1F add ebp, 8 .code:00401F22 mov ebp, [ebp+11h] .code:00401F25 mov dword ptr [ebp+10h], 1000h .code:00401F2C mov ebp, dword_705314 .code:00401F32 add ebp, 8 .code:00401F35 push dword ptr [ebp+15h] .code:00401F38 call sub_56E513 .code:00401F3D mov ebp, dword_705314 .code:00401F43 add ebp, 8 .code:00401F46 mov ebp, [ebp+19h] .code:00401F49 mov edx, offset off_697A38 ; Str .code:00401F4E lea ecx, [ebp+8] ; int .code:00401F51 call sub_540558 .code:00401F56 mov ebp, dword_705314 .code:00401F5C add ebp, 8 .code:00401F5F push dword ptr [ebp+15h] .code:00401F62 call sub_56E513 .code:00401F67 mov ebp, dword_705314 .code:00401F6D add ebp, 8 .code:00401F70 mov ebp, [ebp+19h] .code:00401F73 mov edx, offset off_69D178 ; Str .code:00401F78 lea ecx, [ebp+8] ; int .code:00401F7B call sub_540558 .code:00401F80 mov ebp, dword_705314 .code:00401F86 add ebp, 8 .code:00401F89 push dword ptr [ebp+15h] .code:00401F8C call sub_56E513 .code:00401F91 mov ebp, dword_705314 .code:00401F97 add ebp, 8 .code:00401F9A mov ebp, [ebp+19h] .code:00401F9D mov edx, offset off_699E48 ; Str .code:00401FA2 lea ecx, [ebp+8] ; int .code:00401FA5 call sub_540558 .code:00401FAA mov ebp, dword_705314 .code:00401FB0 add ebp, 8 .code:00401FB3 push dword ptr [ebp+15h] .code:00401FB6 call sub_56E513 .code:00401FBB mov ebp, dword_705314 .code:00401FC1 add ebp, 8 .code:00401FC4 mov ebp, [ebp+19h] .code:00401FC7 mov edx, offset off_69770A ; Str .code:00401FCC lea ecx, [ebp+8] ; int .code:00401FCF call sub_540558 .code:00401FD4 mov ebp, dword_705314 .code:00401FDA add ebp, 8 .code:00401FDD push dword ptr [ebp+15h] .code:00401FE0 call sub_56E513 .code:00401FE5 mov ebp, dword_705314 .code:00401FEB add ebp, 8 .code:00401FEE mov ebp, [ebp+19h] .code:00401FF1 mov edx, offset off_692BA4 ; Str .code:00401FF6 lea ecx, [ebp+8] ; int .code:00401FF9 call sub_540558 .code:00401FFE mov ebp, dword_705314 .code:00402004 add ebp, 8 .code:00402007 push dword ptr [ebp+15h] .code:0040200A call sub_56E513 .code:0040200F mov ebp, dword_705314 .code:00402015 add ebp, 8 .code:00402018 mov ebp, [ebp+19h] .code:0040201B mov edx, offset off_68FA1C ; Str .code:00402020 lea ecx, [ebp+8] ; int .code:00402023 call sub_540558 .code:00402028 mov ebp, dword_705314 .code:0040202E add ebp, 8 .code:00402031 push dword ptr [ebp+15h] .code:00402034 call sub_56E513 .code:00402039 mov ebp, dword_705314 .code:0040203F add ebp, 8 .code:00402042 mov ebp, [ebp+19h] .code:00402045 mov edx, offset off_6953C2 ; Str .code:0040204A lea ecx, [ebp+8] ; int .code:0040204D call sub_540558 .code:00402052 mov ebp, dword_705314 .code:00402058 add ebp, 8 .code:0040205B push dword ptr [ebp+15h] .code:0040205E call sub_56E513 .code:00402063 mov ebp, dword_705314 .code:00402069 add ebp, 8 .code:0040206C mov ebp, [ebp+19h] .code:0040206F mov edx, offset off_699AE0 ; Str .code:00402074 lea ecx, [ebp+8] ; int .code:00402077 call sub_540558 .code:0040207C mov ebp, dword_705314 .code:00402082 add ebp, 8 .code:00402085 push dword ptr [ebp+15h] .code:00402088 call sub_56E513 .code:0040208D mov ebp, dword_705314 .code:00402093 add ebp, 8 .code:00402096 mov ebp, [ebp+19h] .code:00402099 mov edx, offset off_695382 ; Str .code:0040209E lea ecx, [ebp+8] ; int .code:004020A1 call sub_540558 .code:004020A6 mov ebp, dword_705314 .code:004020AC add ebp, 8 .code:004020AF push dword ptr [ebp+15h] .code:004020B2 call sub_56E513 .code:004020B7 mov ebp, dword_705314 .code:004020BD add ebp, 8 .code:004020C0 mov ebp, [ebp+19h] .code:004020C3 mov edx, offset off_692706 ; Str .code:004020C8 lea ecx, [ebp+8] ; int .code:004020CB call sub_540558 .code:004020D0 mov ebp, dword_705314 .code:004020D6 add ebp, 8 .code:004020D9 push dword ptr [ebp+15h] .code:004020DC call sub_56E513 .code:004020E1 mov ebp, dword_705314 .code:004020E7 add ebp, 8 .code:004020EA mov ebp, [ebp+19h] .code:004020ED mov edx, offset off_6897C6 ; Str .code:004020F2 lea ecx, [ebp+8] ; int .code:004020F5 call sub_540558 .code:004020FA mov ebp, dword_705314 .code:00402100 add ebp, 8 .code:00402103 push dword ptr [ebp+15h] .code:00402106 call sub_56E513 .code:0040210B mov ebp, dword_705314 .code:00402111 add ebp, 8 .code:00402114 mov ebp, [ebp+19h] .code:00402117 mov edx, offset off_69784A ; Str .code:0040211C lea ecx, [ebp+8] ; int .code:0040211F call sub_540558 .code:00402124 mov ebp, dword_705314 .code:0040212A add ebp, 8 .code:0040212D push dword ptr [ebp+15h] .code:00402130 call sub_56E513 .code:00402135 mov ebp, dword_705314 .code:0040213B add ebp, 8 .code:0040213E mov ebp, [ebp+19h] .code:00402141 mov edx, offset off_69790A ; Str .code:00402146 lea ecx, [ebp+8] ; int .code:00402149 call sub_540558 .code:0040214E mov ebp, dword_705314 .code:00402154 add ebp, 8 .code:00402157 push dword ptr [ebp+15h] .code:0040215A call sub_56E513 .code:0040215F mov ebp, dword_705314 .code:00402165 add ebp, 8 .code:00402168 mov ebp, [ebp+19h] .code:0040216B mov edx, offset off_698C36 ; Str .code:00402170 lea ecx, [ebp+8] ; int .code:00402173 call sub_540558 .code:00402178 push dword_705310 .code:0040217E call sub_56E513 .code:00402183 mov ebp, dword_705314 .code:00402189 mov dword ptr [ebp+0Ch], 32h .code:00402190 mov edx, offset aButton ; "Button" .code:00402195 lea ecx, [ebp+8] ; int .code:00402198 call sub_540558 .code:0040219D mov byte ptr [ebp+14h], 1 .code:004021A1 mov dword ptr [ebp+10h], 59h .code:004021A8 add ebp, 8 .code:004021AB push dword ptr [ebp+0Dh] .code:004021AE call sub_56E513 .code:004021B3 mov ebp, dword_705314 .code:004021B9 add ebp, 8 .code:004021BC mov ebp, [ebp+11h] .code:004021BF mov edx, offset off_696176 ; Str .code:004021C4 lea ecx, [ebp+8] ; int .code:004021C7 call sub_540558 .code:004021CC mov ebp, dword_705314 .code:004021D2 add ebp, 8 .code:004021D5 mov ebp, [ebp+11h] .code:004021D8 mov dword ptr [ebp+0Ch], 200h .code:004021DF mov ebp, dword_705314 .code:004021E5 add ebp, 8 .code:004021E8 mov ebp, [ebp+11h] .code:004021EB mov dword ptr [ebp+10h], 1 .code:004021F2 mov ebp, dword_705314 .code:004021F8 add ebp, 8 .code:004021FB push dword ptr [ebp+0Dh] .code:004021FE call sub_56E513 .code:00402203 mov ebp, dword_705314 .code:00402209 add ebp, 8 .code:0040220C mov ebp, [ebp+11h] .code:0040220F mov edx, offset off_6939FE ; Str .code:00402214 lea ecx, [ebp+8] ; int .code:00402217 call sub_540558 .code:0040221C mov ebp, dword_705314 .code:00402222 add ebp, 8 .code:00402225 mov ebp, [ebp+11h] .code:00402228 mov dword ptr [ebp+0Ch], 100h .code:0040222F mov ebp, dword_705314 .code:00402235 add ebp, 8 .code:00402238 mov ebp, [ebp+11h] .code:0040223B mov dword ptr [ebp+10h], 2 .code:00402242 mov ebp, dword_705314 .code:00402248 add ebp, 8 .code:0040224B push dword ptr [ebp+0Dh] .code:0040224E call sub_56E513 .code:00402253 mov ebp, dword_705314 .code:00402259 add ebp, 8 .code:0040225C mov ebp, [ebp+11h] .code:0040225F mov edx, offset off_68A2BC ; Str .code:00402264 lea ecx, [ebp+8] ; int .code:00402267 call sub_540558 .code:0040226C mov ebp, dword_705314 .code:00402272 add ebp, 8 .code:00402275 mov ebp, [ebp+11h] .code:00402278 mov dword ptr [ebp+0Ch], 1 .code:0040227F mov ebp, dword_705314 .code:00402285 add ebp, 8 .code:00402288 mov ebp, [ebp+11h] .code:0040228B mov dword ptr [ebp+10h], 4 .code:00402292 mov ebp, dword_705314 .code:00402298 add ebp, 8 .code:0040229B push dword ptr [ebp+0Dh] .code:0040229E call sub_56E513 .code:004022A3 mov ebp, dword_705314 .code:004022A9 add ebp, 8 .code:004022AC mov ebp, [ebp+11h] .code:004022AF mov edx, offset off_698054 ; Str .code:004022B4 lea ecx, [ebp+8] ; int .code:004022B7 call sub_540558 .code:004022BC mov ebp, dword_705314 .code:004022C2 add ebp, 8 .code:004022C5 mov ebp, [ebp+11h] .code:004022C8 mov dword ptr [ebp+0Ch], 2000h .code:004022CF mov ebp, dword_705314 .code:004022D5 add ebp, 8 .code:004022D8 mov ebp, [ebp+11h] .code:004022DB mov dword ptr [ebp+10h], 8 .code:004022E2 mov ebp, dword_705314 .code:004022E8 add ebp, 8 .code:004022EB push dword ptr [ebp+0Dh] .code:004022EE call sub_56E513 .code:004022F3 mov ebp, dword_705314 .code:004022F9 add ebp, 8 .code:004022FC mov ebp, [ebp+11h] .code:004022FF mov edx, offset off_69A590 ; Str .code:00402304 lea ecx, [ebp+8] ; int .code:00402307 call sub_540558 .code:0040230C mov ebp, dword_705314 .code:00402312 add ebp, 8 .code:00402315 mov ebp, [ebp+11h] .code:00402318 mov dword ptr [ebp+0Ch], 1003h .code:0040231F mov ebp, dword_705314 .code:00402325 add ebp, 8 .code:00402328 mov ebp, [ebp+11h] .code:0040232B mov dword ptr [ebp+10h], 10h .code:00402332 push dword_705310 .code:00402338 call sub_56E513 .code:0040233D mov ebp, dword_705314 .code:00402343 mov dword ptr [ebp+0Ch], 33h .code:0040234A mov byte ptr [ebp+14h], 1 .code:0040234E mov edx, offset aB_10 ; "B" .code:00402353 lea ecx, [ebp+8] ; int .code:00402356 call sub_540558 .code:0040235B mov dword ptr [ebp+10h], 5Ah .code:00402362 add ebp, 8 .code:00402365 push dword ptr [ebp+0Dh] .code:00402368 call sub_56E513 .code:0040236D mov ebp, dword_705314 .code:00402373 add ebp, 8 .code:00402376 mov ebp, [ebp+11h] .code:00402379 mov edx, offset off_69A590 ; Str .code:0040237E lea ecx, [ebp+8] ; int .code:00402381 call sub_540558 .code:00402386 mov ebp, dword_705314 .code:0040238C add ebp, 8 .code:0040238F mov ebp, [ebp+11h] .code:00402392 mov dword ptr [ebp+0Ch], 1003h .code:00402399 mov ebp, dword_705314 .code:0040239F add ebp, 8 .code:004023A2 mov ebp, [ebp+11h] .code:004023A5 mov dword ptr [ebp+10h], 10h .code:004023AC push dword_705310 .code:004023B2 call sub_56E513 .code:004023B7 mov ebp, dword_705314 .code:004023BD mov dword ptr [ebp+0Ch], 49h .code:004023C4 mov byte ptr [ebp+14h], 1 .code:004023C8 mov dword ptr [ebp+10h], 5Bh .code:004023CF mov edx, offset off_69635E ; Str .code:004023D4 lea ecx, [ebp+8] ; int .code:004023D7 call sub_540558 .code:004023DC add ebp, 8 .code:004023DF push dword ptr [ebp+0Dh] .code:004023E2 call sub_56E513 .code:004023E7 mov ebp, dword_705314
Kolejno ustawiana jest wartość rejestru EBP, nie zważając na to, że z 20 razy jest ta sama operacja wykonywana, być może twórcy PureBasic-a doczytali o loop unrollingu i wdrożyli go w życie z dodatkowymi benefitami…
2. Techniki czyszczenia stosu dla cdecl
Konwencja wywoływania funkcji cdecl bazuje na tym, że parametry są kolejno wrzucane na stos serią instrukcji push, po czym następuje wywołanie funkcji instrukcją call, po której wskaźnik stosu np. ESP jest korygowany o rozmiar wrzuconych wcześniej parametrów np. przez add esp,8 (dla dwóch 4 bajtowych parametrów etc.).
Różne kompilatory różnie radzą sobie z korektą stosu, jedne po każdym wywołaniu funkcji w konwencji cdecl korygują stos, inne potrafią inteligentnie obliczyć rozmiar parametrów i skorygować stos po całej serii wywołań wielu funkcji w konwencji cdecl (np. LCC), a jeszcze inne kompilatory robią to tak:
.code:00445E0D push eax .code:00445E0E push [esp+14h+var_10] ; lpMem .code:00445E12 call sub_59A300 .code:00445E17 pop eax .code:00445E18 add esp, 4 .code:00445E1B xor eax, eax
Mówiąc w skrócie po wykonaniu call-a (2 parametry, czyli 8 bajtów na stosie zostało do tego wykorzystanych) stos korygowany jest przez instrukcję pop eax (która działa jak add esp,4), po czym stos korygowany jest ponownie instrukcją add esp,4, a po niej wartość rejestru EAX, który jak można by się było domyśleć miał być do czegoś wykorzystany – jest zerowany! PureBasic – PureMagic 🙂
3. Sprawdzanie flag
W sumie można by to zaliczyć do optymalizacji, gdyby w ogóle kompilator jakieś stosował:
.code:004B42B2 call sub_50F9BC .code:004B42B7 and eax, eax .code:004B42B9 jz loc_4B43D4
.code:004B42D0 call sub_56E5C6 .code:004B42D5 or eax, eax .code:004B42D7 jz loc_4B43CF
.text:0065062E test eax, eax .text:00650630 jz short loc_65063B
Jak widać kompilator PureBasic w zależności od humoru stosuje 3 metody sprawdzania czy rejestr jest wyzerowany (z czego 2 nie są rekomendowane, gdyż to operacje zapisu czyli and i or, instrukcja test jedynie ustawia flagi).
4. Konwencja „zerocall”
W czasach DOS-u, częstą praktyką była konwencja wywoływania funkcji, w której ustawiano flagę carry (przeniesienia) instrukcją STC lub ją zerowano instrukcją CLC w zależności od wartości zwracanej przez funkcję (true / false). Przypadki użycia takich technik widziałem również w kilku programach na Windows, jednak to taka rzadkość, że można przypuszczać, że to twór programistów mających korzenie programistyczne w DOS-ie.
Wykorzystanie flagi carry miało swoje plusy, gdyż po wywołaniu funkcji nie trzeba było sprawdzać wyniku funkcji np. przez test eax,eax, a jedynie wykonać instrukcję skoku JC lub JNC.
PureBasic stosuje unikalną konwencję, którą nazwałem zerocall:
.text:0059A142 loc_59A142: ; CODE XREF: sub_59A110+22j .text:0059A142 pop ebx .text:0059A143 xor eax, eax .text:0059A145 test eax, eax .text:0059A147 retn .text:0059A147 sub_59A110 endp ... .code:00413667 mov edx, offset ValueName .code:0041366C pop ecx .code:0041366D call sub_59A110 .code:00413672 jz short loc_41367B
Czyli funkcja sama w sobie sprawdza wynik i ustawia flagę zerową, można powiedzieć, że jest to technologia unikalna dla PB, gdyż nigdy wcześniej nie spotkałem się z taką innowacją.
Co ciekawe miałoby to jedynie sens w przypadku optymalizacji pod względem rozmiaru i w przypadku zastosowanie tej techniki do wszystkich wygenerowanych funkcji, a tak niestety nie jest.
5. Zerowanie pustej pamięci
W sumie ten kod zainspirował mnie do kolejnego artykułu po kompilatorze języka Go. Generował go kompilator PB w wersji od 4.xx do 5.30.
.code:00401000 start proc near .code:00401000 .code:00401000 var_C = dword ptr -0Ch .code:00401000 var_8 = dword ptr -8 .code:00401000 .code:00401000 push 1C20h ; Size .code:00401005 push 0 ; Val .code:0040100A push offset hHeap ; Dst .code:0040100F call memset
Krótki opis. Zerowana jest pamięć od zmiennej hHeap, która znajduje się w sekcji danych „.data”. Zerowany bufor pamięci ma rozmiar 0x1C20 bajtów.
Spoglądając na deadlisting można zauważyć, że ten obszar jest pusty! A do tego jego większa część znajduje się w zakresie sekcji „.data”, która jest niezainicjalizowana.
Gwoli wyjaśnienia sekcja „.data” posiada ustawiony rozmiar fizyczny oraz rozmiar wirtualny, który przekracza rozmiar fizyczny co pozwala na zaoszczędzenie miejsca na dysku plikom, które chcą mieć zaalokowane duże obszary pamięci.
System Windows niezainicjalizowane obszary pamięci w sekcjach zawsze wypełnia zerami.
Zerowany obszar w plikach PureBasic jest zatem wypełniony 0x00 od samego początku i nie ma potrzeby dodatkowo go zerować, całej sytuacji smaczku dodaje jeszcze fakt, że obszar ten wykorzystywany jest do trzymania zmiennych globalnych i kompilator zamiast zapisać wartości tych zmiennych od razu do pliku, najpierw generuje kod, który zeruje cały ten obszar, a później dodatkowo generuje instrukcje, które ustawiają te wartości, czyli 2 razy marnowany jest czas procesora po uruchomieniu takiej aplikacji, podczas gdy cały ten proces można zrealizować na etapie kompilacji.
Konkluzje
PureBasic to całkiem ciekawe narzędzie, ale jakość generowanego kodu pozostawia wiele do życzenia i nie zdecydowałbym się na jego używanie do wysokowydajnych aplikacji, poza tym fundamentalne braki w wiedzy twórców kompilatora, w tak podstawowych kwestiach jak alokacja pamięci czy poprawne wykorzystanie instrukcji procesora x86, sprawiają, że można mieć wątpliwości co do samej jakości kodu jak i innych technologii użytych w tym narzędziu.