The ecm fork of LZEXE v0.91 is based on the 2025 May free software release under the MIT license. Refer to the LICENSE file for full attribution and usage conditions.
Hardware: PC and compatibles, 80286 or 80386 microprocessor recommended for greater execution speed. Memory required: 128 KiB minimum to run LZEXE.
This document has been compiled on 2025-07-12.
This software compresses EXE files, that is, EXEcutable files from the 8086 DOS PC world. But you could tell me that a lot of software compresses EXE files better than this one, such as the excellent PKZIP or LHARC. But the advantage of this program is that your EXE files once compressed can be launched! And the depacking is so fast that for virtually all files, this depacking time is negligible! In addition, the depacker does not use any additional disk space or RAM on a virtual disk: it only uses the RAM normally reserved for the unpacked EXE file. In addition, I have greatly optimized my depack algorithm in speed but also in efficiency: EXE files are almost as small as the corresponding ZIP files and much more compact than the old ARC files.
It's very simple: just type in DOS:
LZEXE [switches] filename[.EXE]
where ‘filename
’ is the name of the EXE file you want to compress. The .EXE
extension is added by default. The compressed file is created in the default directory. The ‘switches
’ part is optional and may hold any number of switches. Switches are indicated by a leading slash. Refer to section 2.2 for the meaning of the switches.
Warning! Some files are EXE only by their name: in fact, for DOS, it is not the .EXE
extension that characterizes this type of file, it is the fact that at the beginning there are the letters ‘MZ
’ or ‘ZM
’ followed by a few bytes that indicate the length of the file, the memory size that it occupies, etc. So some people do not hesitate to rename COM files to EXE, and this explains why LZEXE refuses certain EXE files that are just renamed COM files.
But there is a method to make LZEXE accept COM files: just use the LZEXE companion tool COMTOEXE which converts COM files to EXE files.
For added security, LZEXE does not erase your old EXE file: it renames it to *.OLD. In addition, it creates the temporary file LZEXE.TMP which is only renamed to *.EXE after packing is complete.
The following switches are supported:
/1
/2
/I
/J
/S
/O
/L
/-
//
/-
/#XX
/1
#Chooses to pack with the old v0.91 stub format. This generates a file largely compatible with the output of LZEXE v0.91 albeit not identicalised to it. The length of the depacker stub and the placement of its variables do exactly match.
/2
#Chooses to pack with the new stub format. (This is the default.) The current format is known as LZX0, and comes in 8 variants. This format is not compatible with any older depackers expecting the LZEXE v0.90 or v0.91 formats.
/I
#Inline getbit code alike old stub. Inlining the getbit code makes depacking faster at the expense of some code size. In the old format, the code was inlined.
/J
#Do not inline getbit code. This is slower than inlining but saves some space. (This is the default.)
/S
#Stop optimisation, always use LZE3 equivalent. This disables the dropping of the relocation table if empty, and of the segment change code if the uncompressed image size is below 40 KiB.
/O
#Optimise to drop relocs or segment change. Different variants of the LZX0 depacker stub can be used to optimise stub size. This switch enables two different optimisations. The LZEXE application will detect if either or both of these optimisations can be used. (This is the default.)
/L
#Allow output file that is not smaller than input. Usually LZEXE will delete its temporary output file if it is at least as large as the input file. With this switch, it will only warn about the condition but still write to the destination file.
/-
#No more switches, filename follows. This switch indicates that the next input on the command line is a filename, even if it starts with a dash or slash. This avoids misdetecting such names as switches.
//
#
Same as /-
.
/#XX
#
Force LZ signature letters to XX
(for debugging). The XX
can be replaced by any two printable ASCII bytes. The specified bytes are appended to the LZ signature. This overrides the LZ91 or LZX0 signatures usually chosen by the current LZEXE. This switch should not be used lightly and is intended only for debugging LZEXE. It can lead to data corruption if misused.
For some files, compression may not work for several reasons:
Note that another file packer exists: EXEPACK.EXE from Microsoft. But it is far less efficient than mine, and even if your EXE file is already packed with this program, LZEXE will still be able to pack a lot. But in this case, you'll be presented with a warning message, because there's another LZEXE companion tool: UPACKEXE, which allows you to unpack these files, and thus the gains are even greater.
/L
switch was specified.
/1
switch is used, indicating to use LZ91 format, the new LZEXE will detect executables with these and reject them rather than producing a depacker that crashes or corrupts random memory. If the LZX0 format is used these unusual relocations are fully supported.
More serious is that some compressed EXE files will "crash" the machine:
(This list is not exhaustive.)
Less serious: Some programs have configuration options that modify the EXE file (Turbo Pascal for example). In this case, you must first configure the program, then compress it and keep an uncompressed version so you can edit it.
The companion tool unlzexe can reverse the LZEXE compression and generate an unpacked executable. It can handle all file formats generated by this version of LZEXE, except if the header signature was changed using the /#
switch (refer to section 2.2.10).
Note that the exact header size and the order and addressing in the relocation entries may differ as compared to the original file before LZEXE compression. Additional data such as overlays or lDOS iniload's entries cannot be restored either, as they are not retained by LZEXE. The memory allocation and image size should be reproduced exactly. The relocation entries should lead to the same result, but as mentioned may be re-ordered and use different segmented addresses.
Compressing the unlzexe output using LZEXE (same version with the same switches) should produce exactly the same compressed file as the unlzexe input.
The unlzexe tool accepts flags from the DEBUG environment variable. (%DEBUG%
on DOS, $DEBUG
on Linux.) The following flags are accepted in a mask:
This program converts a COM (or BIN) file into an EXE file. It is the ideal complement to LZEXE since, thanks to COMTOEXE, LZEXE can also compress COM files.
Syntax:
COMTOEXE [switches] filename[.COM] [filename2[.EXE]]
where ‘filename
’ is the name of the COM file to be converted. The COM
extension is automatically added. The COM file is not deleted for added security.
By specifying ‘filename2
’, you can specify another name for the EXE file.
The ‘switches
’ part is optional and may hold any number of switches. Switches are indicated by a leading slash. Refer to section 5.1 for the meaning of the switches.
Some additional notes:
RETN
to terminate.
The following switches are supported:
/0
/1
/2
/A=num
num
bytes after image
/P=num
/0
#Choose old-style no stub operation. This is the closest to the reverse of exe2bin. It prepends the executable image with a 32-byte MZ EXE header, followed by the literal flat-format image. No stub is appended. The initial CS:IP is equal to PSP:100h.
The minimum allocation is set so that at least 64 KiB are allocated to the process memory block, and the initial SS:SP is equal to PSP:FFFEh. However, this stack element is not initialised to a zero word.
/1
#Choose new-style stub pushing zero (default). This option causes COMTOEXE to prepend the 32-byte MZ EXE header and append an 8-byte stub behind the flat-format image. The initial CS is equal to PSP while the initial IP points to the stub.
The minimum allocation is set so that at least 64 KiB are allocated to the process memory block, and the initial SS:SP upon running the original entrypoint is equal to PSP:FFFEh. This stack element is initialised to a zero word by the stub.
/2
#
Choose stub that expands SP dynamically. A 32-byte stub is appended behind the image. This stub reads the actual size of the process memory block from the word [PSP:2]
and sets up the SP so that SS:SP points either at PSP:FFFEh or at the last word of the process memory block, whichever is smaller. The stack element pointed to is also initialised to a zero word by this stub.
The minimum allocation as well as initial SP in the MZ EXE header can be set up to allow a process memory block smaller than 64 KiB. Due to the stub, the initial SS:SP when running the image at PSP:100h will be adjusted to be as large as PSP:FFFEh, without requiring the program always be allocated a full 64 KiB.
/A=num
#
For /2 stub: Allocate at least num
bytes after image. The num
parameter can be a decimal number or a hexadecimal number. Hex numbers are indicated by leading ‘0x
’ or trailing ‘h
’. The specified size indicates how much space is allocated to the process memory block at least, excluding the size of the PSP, the image, and a 128-byte reservation for the stack.
If the switch /2 stub is not in use, this switch does not take effect.
/P=num
#
For /2 stub: Set minimum SP offset before stub runs. The num
parameter can be a decimal number or a hexadecimal number. Hex numbers are indicated by leading ‘0x
’ or trailing ‘h
’. This switch indicates the lowest value that the EXE header's SP should take. The header's minimum allocation is set so as to allocate the stack space.
If this switch is absent or set to 0, COMTOEXE will use a heuristic to scan up to 128 bytes of the executable image. The scan detects an initial JMP NEAR
or JMP SHORT
and will follow them, as long as they point to within the first 32 KiB of the input file. The scan searches for a CMP SP,imm16
instruction followed by a conditional short jump (JA
, JB
, JAE
, or JBE
). If found, the comparison's immediate is used to determine the minimum SP.
If the calculated minimum SP is smaller than the size indicated by the /A=
switch plus PSP size plus image size plus 128, the effective minimum SP is adjusted to be at least this large.
If the switch /2 stub is not in use, this switch does not take effect.
This program fills a major gap in the field of EXE file compressors/decompressors: It allows you to decompress EXE files compressed with Microsoft's EXEPACK.EXE and then recompress them with LZEXE, so that the gains are much greater. Programs reduced with EXEPACK are very common: practically all programs created with MSC (Microsoft C) are compacted with it, surely to hide its lack of optimisation! But the algorithm used, although very fast, cannot be compared with that of LZEXE, which performs much better.
Additionally, and this is why I created this unpacker, EXEPACK compacts the EXE file's relocation table and makes it inaccessible to LZEXE, which can no longer compress it to its full capacity. Thus, LZEXE's performance is slightly slower.
Syntax:
UPACKEXE filename[.EXE]
where ‘filename
’ is the name of the file to unpack. It is renamed *.OLD. The new EXE file is created in the current directory under the name UPACKEXE.TMP and is renamed at the end.
The compression algorithm I made is based on the famous Lempel Ziv method using a "circular" buffer (ring buffer) and a method of finding repetitions of byte sequences by trees. The coding of the position and length of the strings that repeat is optimized by an additional algorithm inspired by the Huffman coding method. Unpacked literal bytes are sent as is in the file. An additional compression algorithm (like "Adaptive Huffman" (see LHARC) or with Shanon-Fano trees (see PKZIP)) would have required a longer depacking time and above all a more efficient depacker that is more complex and longer, which could actually make the compressed EXE file longer.
The depacker is located at the end of the EXE file and is 395 bytes in size long for version 0.90 and 330 bytes for version 0.91. In the ecm fork the depacker is between 208 and 305 bytes in size. The depacker must:
CRC Error
’. This option was removed in LZEXE v0.91 because it unnecessarily lengthens the file EXE and the depack time. Furthermore, the CRC check was only performed on the depacker.
In the LZX0 format of the ecm fork, the segment change handling can be omitted in case the original executable image was smaller than 40 KiB, saving some space for otherwise unused code in the depacker.
In the LZX0 format of the ecm fork, the relocation table changed slightly. This supports a wider range of relocation entries.
Further, LZX0 format can be used with the relocation table handling omitted in case the original EXE file had an empty relocation table.
That's all!!!
This depacker is a small masterpiece of 8086 assembler programming in itself: needless to say, it took quite a long time to develop. But the packer also posed quite a few problems for me, particularly when it came to updating all the pointers that the depacker uses later.
I optimised the depacker some. In addition to the universal optimisations, LZX0 format can select one of eight variants of the depacker:
The /O
switch enables the optimisation of the relocation table and segment change code, whereas the /S
switch stops these optimisations. The /J
switch disables inlining getbit whereas the /I
switch enables it.
PKARC (latest version): LZEXE performs much better, as "crunching" (aka shrinking for PKZIP) is an outdated algorithm...
PKZIP v0.92: LZEXE does better in almost all cases.
PKZIP v1.02: On large files, LZEXE does better. otherwise, the the difference is quite small.
LHARC v1.01: It does better than LZEXE with "freezing" on small files.
LARC: LZEXE does better.
Important Notes:
UPX: upx-ucl --8086 --lzma
does about 6% better than LZEXE. Like LZEXE it produces files with an online depacker stub in the compressed executable file.
I hope that LZEXE and the EXE files compressed by it will be widely broadcast which will encourage me to make other versions more quickly...
I decline all responsibility in the event of loss of information caused by LZEXE. But rest assured, the algorithms are reliable and I don't think there are many bugs.
Warning! I do not recommend compressing and distributing commercial software protected by copyright: the authors may be displeased...
But if you're making a FREEWARE, SHAREWARE, or even a commercial program, nothing's stopping you from compressing it with LZEXE, and I even recommend it:
There you go, hoping that this software will be useful to you and that it does not have too many bugs!
-
’ was not accepted in file names, this has been corrected.
hg 6687bfb4830c, from commit on at 2025-07-12 09:54:16 +0200
If this is in ecm's repository, you can find it at https://hg.pushbx.org/ecm/lzexe/rev/6687bfb4830c