Home / Alt manpages / mono(1)

  • mono(1)
  • User command
  • linux

Run, Inspect and AOT-Compile .NET Assemblies with Mono

You will finish with a small .NET assembly running under Mono, a reliable way to select its runtime configuration, and an optional ahead-of-time (AOT) image you can inspect and remove. The examples use Mono 6.8.0.105 from Ubuntu's mono-runtime-common package. The cli command name is an alias for the same runtime on this installation.

Allow about twenty minutes. You need a shell, the installed mono command, and mcs if you want to compile the smoke-test program. The normal run and inspection commands are unprivileged. Do not use sudo for them.

1. Check the installed runtime

Start by confirming which binary and package version will handle the assembly:

$ command -v mono
/usr/bin/mono
$ mono --version
Mono JIT compiler version 6.8.0.105 (...)
$ dpkg-query -W -f='${Package} ${Version}\n' mono-runtime-common
mono-runtime-common 6.8.0.105+dfsg-3.6ubuntu2

The exact build suffix and copyright line can differ between package updates. The useful checks are the command path, the Mono release, and the fact that this binary uses the JIT by default. The --version=number form prints only the version number if you need output suitable for a script.

Checkpoint

If command -v finds nothing, stop here and install or repair Mono through your normal package-management process. Do not work around a missing runtime by downloading an unrelated binary into the application directory.

2. Build a harmless assembly

This creates files below a temporary directory and prints one line. It does not alter system configuration:

$ workdir="$(mktemp -d /tmp/mono-demo.XXXXXX)"
$ cat > "$workdir/hello.cs" <<'EOF'
using System;
class Hello
{
    static void Main(string[] args)
    {
        Console.WriteLine("mono-ok");
    }
}
EOF
$ mcs -out:"$workdir/hello.exe" "$workdir/hello.cs"
$ mono "$workdir/hello.exe"
mono-ok

mono loads an ECMA assembly, commonly a file ending in .exe or .dll, and passes the remaining words to that program. Keep the assembly path separate from its arguments. A missing file, a malformed assembly, or a missing referenced assembly is an application or deployment problem, not a reason to run the command as root.

Checkpoint

Save the value of workdir in the shell if you want to run the later examples. Check the result with test -s "$workdir/hello.exe". A zero exit status and the line mono-ok confirm this small assembly completed.

3. Separate Mono options from program arguments

Put runtime options before the assembly. Put program arguments after it:

$ mono --verbose "$workdir/hello.exe" first-argument
mono-ok

This example enables extra runtime output, so the exact diagnostic lines vary. The program still receives first-argument. Use mono --help to see the options present in this installed build. Useful read-only choices include --debug for line information in stack traces, --trace for runtime tracing, --gc=sgen or --gc=boehm for the selected collector, and --interpreter where runtime code generation is unavailable.

Do not put an untrusted string into the options area. Quote paths and values, and keep data that belongs to the managed program after the assembly name. If the program uses a file called hello.exe.config, Mono also looks for that assembly-specific configuration beside the assembly.

4. Select configuration without changing the host

Mono normally reads /etc/mono/config, then the user's ~/.mono/config, and also loads an assembly-specific .config file where applicable. The MONO_CONFIG environment variable can select another runtime configuration file; --config takes precedence over that variable.

For a safe selection test, create an empty configuration document in the temporary directory and explicitly pass it:

$ printf '%s\n' '<configuration></configuration>' > "$workdir/empty.config"
$ mono --config "$workdir/empty.config" "$workdir/hello.exe"
mono-ok
$ MONO_CONFIG="$workdir/empty.config" mono "$workdir/hello.exe"
mono-ok

The file is XML-like and must have a configuration root. One practical use of mono-config(5) is a dllmap: it can map a library name used by a Windows-oriented P/Invoke declaration to a Unix shared library, and can select mappings by operating system, CPU or word size. Later matching entries override earlier ones. Test such a mapping with the real application and its native dependencies before deploying it.

Use --config for a one-command experiment or a wrapper under your control. Do not edit /etc/mono/config merely to fix one application. A system-wide edit needs elevated privileges, affects other Mono processes, and should be backed up and reviewed first. To undo a temporary selection, stop passing --config and unset MONO_CONFIG; the host files have not changed.

5. Diagnose the first useful failure

Make the runtime report its version and usage before changing settings:

$ mono --version=number
6.8.0.105
$ mono --help | sed -n '1,20p'
Usage is: mono [options] program [program-options]

For an assembly failure, rerun the exact command with --verbose and record the first missing file or assembly name. Check the paths without changing them:

$ test -r "$workdir/hello.exe" && echo assembly-readable
assembly-readable
$ file "$workdir/hello.exe"
... Mono/.Net assembly ...

MONO_PATH adds directories to the assembly search path, separated by colons on Unix. The manual describes it as a debugging convenience and warns that it can break assembly loading in subtle ways. Prefer installing dependent libraries correctly or placing them beside the main assembly for deployment. If you use MONO_PATH to isolate a test, set it for that command only:

$ MONO_PATH="$workdir/lib" mono "$workdir/hello.exe"
mono-ok

If the command fails because $workdir/lib does not exist, that is expected for this minimal program. Do not leave a broad, inherited MONO_PATH in a service environment.

6. Produce and verify an AOT image

AOT precompiles methods into a shared object beside the assembly. It can reduce startup JIT work, but it does not replace the original assembly: Mono still needs its metadata and exception information. This is a build artefact, not a portable binary. CPU-specific choices can make it unsuitable for another machine.

Warning

This command writes hello.exe.so next to the assembly. Do it in a disposable build directory, not beside a production deployment, until you have measured the result:

$ mono --aot "$workdir/hello.exe"
Mono Ahead of Time compiler - compiling assembly ...
Compiled: 2/2
$ test -s "$workdir/hello.exe.so" && echo aot-image-created
aot-image-created
$ mono "$workdir/hello.exe"
mono-ok

Mono picks up the matching AOT image automatically when it runs the assembly. A successful AOT command does not prove that every future code path works without JIT compilation. Options such as --full-aot require a separately prepared full-AOT image and can abort when code needs dynamic generation, so test the complete application before considering that mode.

To recover the temporary build directory after checking it, remove only the exact directory you created:

$ rm -rf -- "$workdir"
$ test ! -e "$workdir" && echo temporary-files-removed
temporary-files-removed

This deletion is irreversible. Confirm that workdir contains only the disposable files from this guide before running it. Never substitute a broad path such as your home directory.

Done means

  • The installed mono path and version are known.
  • A small assembly ran with the expected mono-ok output.
  • Runtime options and program arguments were kept on their respective sides of the assembly name.
  • A temporary configuration file was selected without editing host configuration.
  • MONO_PATH was treated as a narrow diagnostic aid, not a deployment default.
  • Any AOT image was tested beside its original assembly and the disposable files were removed when no longer needed.