Home / Alt manpages / mono-cil-strip(1)

  • mono-cil-strip(1)
  • User command
  • linux

Strip CIL from a Mono Assembly After AOT Compilation

You will finish with a smaller-risk way to create a second Mono assembly whose method bodies have been emptied while its metadata remains. That is useful after Mono Ahead Of Time (AOT) compilation, when native code is available and the original Common Intermediate Language (CIL) is no longer needed at runtime.

Allow about fifteen minutes. You need the mono-devel package, a readable managed assembly, enough free space for a separate output file, and a backup of the original. The examples use mono-cil-strip from Mono 6.8.0.105+dfsg-3.6ubuntu2 on this machine. The tool does not perform AOT compilation itself and does not replace the original unless you explicitly make it do so later.

1. Check the installed tool

Confirm which executable will run and record the package version. These are ordinary, read-only commands and do not need elevated privileges:

$ command -v mono-cil-strip
/usr/bin/mono-cil-strip
$ dpkg-query -W -f='${Package} ${Version}\n' mono-devel
mono-devel 6.8.0.105+dfsg-3.6ubuntu2

The command has a deliberately small interface:

$ mono-cil-strip [options] assembly [output-assembly]
Usage: mono-cil-strip [options] file [output]
    -q         Only output errors.

There is no useful --help mode in this installed version. It treats an unrecognised first argument as an input filename, so mono-cil-strip --help attempts to open a file named --help. Use the installed manpage and the usage text above instead.

Checkpoint

If command -v finds nothing, stop here. Install or repair the package through your normal system-management process before changing any assembly.

2. Keep the original assembly safe

Do not use the input file as the output while you are learning the workflow. The safest pattern is to choose a new destination and keep the source untouched:

$ INPUT='/path/to/application.exe'
$ OUTPUT='/path/to/application-stripped.exe'
$ test -r "$INPUT" || printf 'input is not readable\n' >&2
$ test ! -e "$OUTPUT" || printf 'output already exists; choose another name\n' >&2

The two test commands only report a problem. They do not create files. Replace both placeholders with real paths, and do not proceed if the destination already contains something valuable. The tool's second positional argument is the output assembly, not an AOT image or a directory.

Safety warning

Do not overwrite the only copy of a production assembly. If you have already written a replacement and need to undo it, restore the original from your verified backup or deployment artefact. There is no reverse operation that reconstructs emptied method bodies.

3. Strip into a new assembly

Pass the input first and the new output second:

$ mono-cil-strip "$INPUT" "$OUTPUT"
Mono CIL Stripper

Assembly /path/to/application.exe stripped out into /path/to/application-stripped.exe

A successful run prints the source and destination and exits with status zero. The output is still a managed assembly containing metadata. Its method bodies have been emptied, so it is not a drop-in substitute for the original CIL assembly until the native AOT code and the rest of your deployment have been tested together.

For scripts, check the status immediately rather than relying on the printed message:

$ mono-cil-strip "$INPUT" "$OUTPUT"
$ status=$?
$ test "$status" -eq 0 || { printf 'strip failed with status %s\n' "$status" >&2; exit "$status"; }

Use -q when normal progress output would distract a batch job. It means only output errors; it does not make a bad input safe, and it does not change what is removed.

4. Verify the result before deployment

First check that the destination exists and is a managed file. The exact file wording can vary, so treat it as a useful indication rather than the complete test:

$ test -s "$OUTPUT"
$ file "$OUTPUT"
/path/to/application-stripped.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows

Compare the output with the source, but do not expect a fixed size reduction. In a small test assembly, stripping can leave the file the same size because metadata and file layout remain. The meaningful change is in the method bodies, not a guaranteed byte count.

If you have Mono available, run a harmless smoke test in a disposable test environment:

$ mono "$OUTPUT"
$ printf 'exit status: %s\n' "$?"
exit status: 0

An empty method body may produce no output, even when the original program printed text, and it may still return status zero. That is expected for a stripped sample. It is not evidence that every application will behave correctly with its AOT image. Test the exact AOT deployment path, including native libraries, startup arguments and any reflection or dynamic-code features your application uses.

5. Handle failures without guessing

A missing input is an input or path problem. Reproduce the diagnostic safely without using sudo:

$ mono-cil-strip -q /path/to/missing-assembly.exe
Error: System.IO.FileNotFoundException: File '/path/to/missing-assembly.exe' not found.

The full exception includes a stack trace in this Mono build, and it is written as diagnostic output. Check the path, spelling and read permission. Elevated privileges do not repair a wrong path, and running the stripper as root can leave the resulting file owned by root.

If the input is not a valid assembly, stop and identify the file's producer. Do not feed a native executable, an AOT image or an arbitrary archive to this tool and infer anything from a partial output. Keep the original until a clean, end-to-end test has passed.

6. Decide where stripping belongs

Use this utility as a packaging step after compilation and AOT processing, not as a general optimiser. It removes CIL method bodies because the native AOT code is expected to supply execution. Metadata remains valuable for type information and runtime operations, but code that depends on loading or compiling method bodies dynamically needs a separate compatibility test.

For a service or shipped application, build the stripped file in a staging directory, test it there, then promote it using your normal atomic deployment process. That keeps rollback simple: point the service back at the previous untouched assembly. Do not edit a live file underneath a running process, and do not delete the original merely because the stripped file exists.

Done means

  • mono-cil-strip and its mono-devel version were checked.
  • The original assembly remains available as a backup or deployment artefact.
  • A new output path was supplied explicitly and the command returned status zero.
  • The output exists, is non-empty and is recognised as a managed assembly.
  • A disposable smoke test and the real AOT deployment path have been checked.
  • Rollback is still possible without reconstructing emptied method bodies.