Home / Alt manpages / upx-ucl(1)

  • upx-ucl(1)
  • User command
  • linux

Compress a Linux executable safely with UPX

UPX can shrink a Linux binary to a fraction of its size in seconds, but pack the wrong file and you may not get the original back. This walkthrough inspects an executable, compresses a disposable copy, tests it, and restores the original bytes. The installed manpage documents UPX 4.2.2, and the commands below target that behaviour.

Allow about 10 minutes for one file. You need the upx-ucl package, a trusted executable that you are allowed to modify, enough free storage for a temporary copy, and a shell account that can read and write the working directory. No root access is needed unless the file itself is protected; do not change permissions just to make this experiment work.

1. Confirm the tool and make a safe copy

UPX treats an executable as input and, by default, compresses it in place. Make a copy before you run anything that changes bytes. Substitute a real path for PATH/TO/program; do not type the placeholder literally.

upx --version
cp --reflink=auto -- PATH/TO/program /tmp/program.upx-test
chmod --reference=PATH/TO/program /tmp/program.upx-test

On this installation, the manpage identifies the release as upx 4.2.2. The copy should now exist with the source file's mode bits:

ls -l -- /tmp/program.upx-test
file -- /tmp/program.upx-test

Warning

UPX inherits the security context of every file it handles. Listing and testing are not a substitute for malware scanning, and compressing an untrusted executable can still expose you to whatever that file was carrying. Work on trusted files only.

2. Inspect the file before you touch it

Use -l to list the packed and unpacked sizes. It is read-only with respect to the executable, which makes it a good first check.

upx -l -- /tmp/program.upx-test

For an already packed file, the listing shows compressed and uncompressed sizes and a compression ratio. For a normal executable, it tells you whether UPX recognises the format at all. Keep the output if you plan to compare it against a later build.

  • linux/elf386. The specialised format Linux ELF executables use automatically; decompresses straight into memory.
  • linux/sh386. Used for recognised shell scripts.
  • linux/386. The generic fallback, which needs temporary space in /tmp and support for /proc at runtime instead of decompressing purely in memory.

3. Compress the disposable copy

Compression changes the target file in place. Start with the default level rather than tuning a release build before you even know whether the packed executable still runs.

upx -- /tmp/program.upx-test

UPX uses level -8 by default for files smaller than 512 KiB and -7 otherwise. Levels -1 to -3 favour speed, -4 to -6 balance speed and ratio, and -7 to -9 favour a smaller result. --best can take a long time, so save it for a measured release workflow rather than a first test.

Check what happened and run the integrity test:

upx -l -- /tmp/program.upx-test
upx -t -- /tmp/program.upx-test

Checkpoint

A successful test exits with status 0. It checks the compressed and uncompressed data that will be used during execution, not every byte of the file, so do not treat it as virus checking. The documented statuses are 0 for success, 1 for an error and 2 for a warning.

Runtime boundaries to check before deployment

Run the packed copy in a test environment with the same arguments and account that the released file will use. A compressed ELF program can need more RAM or swap, because its pages are not shared in the same way as an ordinary file-backed executable. The generic Linux format also needs free /tmp space for the uncompressed program while it runs. Programs launched many times in parallel, such as shells and build tools, are poor candidates for casual packing.

  • Never pack set-user-ID, set-group-ID or sticky-bit programs. The manpage says UPX rejects these because of the security implications.
  • Do not assume a packed script stays portable. The Linux script format can lose the portability you had when the script was plain text.
  • Watch for self-reading programs. They can fail once the file no longer has its original layout.

Overlays are extra data appended after an executable's logical end. UPX copies them by default, but an application may not read copied overlay data correctly afterwards. If the file must have no overlay, --overlay=skip refuses to compress one. Avoid --overlay=strip unless you have a tested recovery copy: stripping it can make the program unusable.

4. Restore or publish

The safest undo is to throw away the disposable copy and start again from the untouched source:

rm -- /tmp/program.upx-test

If you genuinely need to unpack the copy, use -d. This is another state-changing operation, so test the result and compare it against a checksum captured before compression.

sha256sum -- PATH/TO/program > /tmp/program.sha256
upx -d -- /tmp/program.upx-test
upx -t -- /tmp/program.upx-test
sha256sum --check --status /tmp/program.sha256 --ignore-missing

For a release, take the checksum before packing and keep the original build artefact. UPX documents byte-identical decompression for the Linux formats, but --exact is still work in progress and is not supported for every format. The checksum comparison is the proof that actually matters for your project.

Defaults that can quietly change a run

The UPX environment variable supplies options before anything on the command line does. An old setting can therefore alter a command that looks simple. Check it before a reproducible build:

env | grep '^UPX=' || true
upx --no-env -t -- /tmp/program.upx-test

Use --no-env when you need the command line to ignore that variable. Explicit command-line options override the environment default anyway, but making the choice visible in a build script is easier to audit later.

Done means

  • Version and trust confirmed. The tool reports the expected version and the input file is one you trust.
  • Copy inspected first. You checked the file before changing it and worked on a copy throughout.
  • Integrity test passed. upx -t returned status 0 after compression.
  • Runtime checked. You checked RAM, swap, /tmp, /proc, overlays and self-reading behaviour for the target.
  • Original preserved. The original build artefact is still available, or a checksum proves the restored copy matches it.