Smallest .exe possible...

Started by
30 comments, last by reaction 20 years, 2 months ago
There is an article on HUGI diskmag 21 [http://www.hugi.scene.org/hugiidh.htm] that explains methods to make a smaller windows executable. Some of the optimisations are OS version specific though. Does anyone know of any other tricks? Regards, R
optionalreaction.net
"I will, even if I try, always be second; and because of that, I know nothing is a lesson." - me
Advertisement
did you mean issue #27, with darkblade? if so, there is a second aritlce in the series at ukscene.org.
there are other methods to reduce the exe size, by changing the size of the alignments, but you then loose win98 portability, and, since in that article he demonstrates how to compress the exe header the size difference is typically only a few bytes : basically not worth it.
if you want to give it a shot, set align and filealign to 16 each.

if you want to make a really small exe, use assembly. there are examples floating around.
http://www.coldcity.com/code_tinyexes.asp

This even works in vc7.1

..Then, after compilation, you could use UPX on the executable to make it even smaller.

http://upx.sourceforge.net/

[edited by - shrt on January 7, 2004 1:34:44 AM]
Jacob H. Hansen - My Website
Thanks!

I compressed one of my programs with UPX. It was 86Kb, and it is now 36Kb!

I have tried it on my W2K install, and it works no problems... if anyone has a W9X or XP install that is unbreakable...?

Download from here (SendToSync 4.6 - compressed).

SendToSync homepage

R





[edited by - reaction on January 7, 2004 2:44:56 AM]
optionalreaction.net
"I will, even if I try, always be second; and because of that, I know nothing is a lesson." - me
Actually the alignment tweak won''t remove compatibility with Windows 98. The tweak controls the section alignment; under Windows 98 it is optimal to align sections on a 4k boundary to improve load time but this obviously results in larger executables. If you don''t do this (specify /opt:nowin98 in VS or change Properties->Linker->Optimisation->Optimise for Windows98) then it''ll just load a little slower under Win98.

cheers
sam
I wrote a tutorial a while back.. maybe it can help.
http://dr-code.com/modules/Top/Tutorial_C+/rse.html
Thanks Eddycharly,

Those ''#pragma'' statements will be in my next source.

How bad would the performance penalty be for something like a game?

R

optionalreaction.net
"I will, even if I try, always be second; and because of that, I know nothing is a lesson." - me
I don''t think you''ll notice a change..
Maybe it will be a bit slower to start, but once it''s started, it should run at the same speed than an uncompressed exe.
Would DWORD (32 bit) alignment be affected at all?

quote:
In fact, you cannot reduce them more than the size of the biggest structure used in your program.


If a structure is aligned in the .exe in a particular way, are all structures created in the memory heap by the .exe aligned the same way, or are they re-aligned (DWORD) for efficiency there?

R

(That is the best article I have read on the subject BTW)





[edited by - reaction on January 7, 2004 7:18:07 AM]
optionalreaction.net
"I will, even if I try, always be second; and because of that, I know nothing is a lesson." - me
Eddycharly: nice article, thanks! I was on a similar 'squeeze the hell out of my first demo' trip a while back That's an interesting idea with RLE for the header. My approach was to interleave the headers, and fit as much data in there as possible; apack would hopefully compress the remaining zeros effectively. I'll have to try your scheme (when I have time ).

You can save quite a bit in the dropper (52 -> 27!) - here's mine:
; exe dropper; Jan Wassenberg 2003; free mem	mov		ah, 0x49	int		0x21; fopen	mov		ah, 0x3c	pop		cx				; cx = 0 (attrib)	mov		dx, 0x100+exe			; filename == "MZ."	int		0x21; fwrite	xchg		ax, bx	mov		ah, 0x40	dec		cx				; 64 kb	int		0x21; fclose	mov		ah, 0x3e	int		0x21; exec	mov		ax, 0x4b00	int		0x21exe:incbin "w.exe" 

(Note: the filename seems to require a '.').

And the crazy header:
		db	'MZ'				; e_magic		db	'.',0				;   dropped file name		dd	'PE'				; Signature		dw	0x014c				; Machine = x86		dw	1				; NumberOfSections		dd	0,0,0		dw	0x0070				; SizeOfOptionalHeader		dw	0x010f				; Characteristics = 32 bit exe		dw	0x010b				; Magic		db	'glu32.dll',0			;   Name (RVA 30)		dd	0		dd	rva(start)			; AddressOfEntryPoint		dd	0,0		dd	0x400000			; ImageBase		dd	4				; SectionAlignment, e_lfanew		dd	4				; FileAlignment		dd	0,0		dw	4				; MajorSubsystemVersion		db	'EDIT',0			;   wnd class name (RVA 78)		db	0		dd	0x10000				; SizeOfImage (virtual)		dd	0xa4				; SizeOfHeaders		dd	0		dw	2				; Subsystem = WIN32_GUI		dw	0		dd	0x400000			; SizeOfStackReserve		dd	0x400000			; SizeOfStackCommit		dd	30				;   Name (RVA 108)		dd	rva(imp_tbl)			;   FirstThunk		dd	0		dd	2				; NumberOfRvaAndSizes (# data dirs)		dd	0				; Export VirtualAddress		dd	0				; Export Size, End Import		dd	96				; Import VirtualAddress		dd	40				; Import Size		dd	0,0		dd	0				; VirtualSize		dd	0				; VirtualAddress		dd	0x10000				; SizeOfRawData				FIXME?		dd	0				; PointerToRawData 

(Notes: I just saw it won't run on 9x at all - section alignment is set to 4. Total size = 160 bytes, including embedded import table; there's plenty of room for more data, but I didn't have any)

Another worthwhile thing to squeeze is OpenGL imports in the exe.
Specify the ordinals of functions you need (unfortunately, some idiot at MS removed the DllInitialize function in opengl32.dll for WinXP, so the ordinals are decreased by one. *sigh*); the ORD macro builds a table of deltas to the next value (1 byte). If there's a large gap, specify a dummy value to bridge it.
ORD glBegin, 12
ORD glClear, 18
...

now the import code:
; import gl functions	mov		esi, ord_tbl	mov		edi, ogl_funcs	push		byte num_ords	pop		ebp	xor		ebx, ebx			; (xchg eax, ebx if eax == 0)?.1:	xor		eax, eax	lodsb						; delta to next ordinal	add		ebx, eax			; ebx = current ordinal	push		ebx	push		opengl32	call		GetProcAddress	stosd	dec		ebp	jnz		.1 

ogl_funcs is our import table, so to speak. call glClear translates to call [ebp-4] - 3 bytes! (Note: ebp points to my BSS; the table comes before it).

Great fun

[edited by - Jan Wassenberg on January 7, 2004 7:20:29 AM]
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3

This topic is closed to new replies.

Advertisement