Home / Alt manpages / x86_64-w64-mingw32-c-filt(1)

  • x86_64-w64-mingw32-c-filt(1)
  • User command
  • linux

Demangle MinGW C++ Symbols with x86_64-w64-mingw32-c++filt

An encoded symbol like _Z3fooi means nothing to a human; x86_64-w64-mingw32-c++filt turns it back into foo(int). The same command can filter a symbol list or assembler-like text through standard input without touching the source file.

Allow about ten minutes for a first pass. You need a Linux shell and the binutils-mingw-w64-x86-64 package. These examples use GNU Binutils 2.41.90.20240122, installed as package version 2.41.90.20240122-1ubuntu1+11.4. The UCRT alias installs alongside the primary command and has the same local manpage behaviour.

1. Check the installed command

Start by confirming which executable your shell will actually run, and record its version. This is read-only and needs no elevated privileges:

$ command -v x86_64-w64-mingw32-c++filt
/usr/bin/x86_64-w64-mingw32-c++filt
$ x86_64-w64-mingw32-c++filt --version
GNU c++filt (GNU Binutils) 2.41.90.20240122
$ dpkg-query -W -f='${Package} ${Version}\n' binutils-mingw-w64-x86-64
binutils-mingw-w64-x86-64 2.41.90.20240122-1ubuntu1+11.4

Use the exact target-prefixed command when examining MinGW output. Do not silently swap in an unprefixed c++filt from a different toolchain: its demangler may default to a different format entirely.

Checkpoint

You know the executable and version before comparing output from a build machine or a bug report.

2. Demangle one symbol

Pass a bare mangled name as a command-line argument. -n means do not strip a leading underscore, it is the installed default and just makes the input handling explicit:

$ x86_64-w64-mingw32-c++filt -n _Z3fooi
foo(int)
$ x86_64-w64-mingw32-c++filt -n _Z1fv
f()

Command-line arguments are treated as complete names, so punctuation stuck to a name makes that argument invalid. That is a common trap:

$ x86_64-w64-mingw32-c++filt -n '_Z1fv,'
_Z1fv,

The comma survives because the whole argument is not a valid mangled symbol. When punctuation is in the way, use standard input instead.

3. Filter a symbol list or source text

With no symbol arguments, c++filt reads standard input and writes the transformed text to standard output. It spots candidate words inside the input, so surrounding punctuation stays put:

$ printf '%s\n' '_Z1fv,' | x86_64-w64-mingw32-c++filt -n
f(),
$ printf '%s\n' '.type _Z1fv, @function' | x86_64-w64-mingw32-c++filt -n
.type f(), @function

For a file, write to a new destination first:

$ x86_64-w64-mingw32-c++filt -n < symbols.s > symbols-demangled.s
$ cmp --silent symbols.s symbols-demangled.s; printf 'cmp status: %s\n' "$?"
cmp status: 1

A status of 1 here is expected when the output differs, and the input stays untouched. Decide to replace the original later? Keep a backup and inspect the new file first, since shell redirection with > truncates an existing destination before the command even starts.

Checkpoint

Use an argument for one bare symbol, and standard input when the symbol sits inside assembler text or trails punctuation.

4. Control the displayed name

Normal output includes function parameter types. Add -p when you need shorter names for a report or a quick comparison:

$ x86_64-w64-mingw32-c++filt -n _Z3fooi
foo(int)
$ x86_64-w64-mingw32-c++filt -n -p _Z3fooi
foo

Use -t carefully, it also attempts to demangle type encodings, which is off by default because an ordinary short name can be mistaken for a type. For implementation details, -i asks for less verbose output where the demangler has any to show.

-s selects a mangling format. The default is automatic, and the installed help lists auto, gnu-v3, java, gnat, dlang and rust among others this build supports. Check the local help rather than copying a format name from a different Binutils release:

$ x86_64-w64-mingw32-c++filt --help | sed -n '1,14p'
Usage: x86_64-w64-mingw32-c++filt [options] [mangled names]
Options are:
  [-_|--strip-underscore]     Ignore first leading underscore
  [-n|--no-strip-underscore]  Do not ignore a leading underscore (default)
  [-p|--no-params]            Do not display function arguments
  [-i|--no-verbose]           Do not show implementation details (if any)

5. Keep the recursion guard enabled

Binutils mangling can nest deeply. The installed manpage puts the default recursion limit at 2048 levels. Keep that protection on with -R when processing untrusted symbol text, or just leave both recursion options alone.

Security boundary

-r and --no-recurse-limit disable the guard. The manual warns a sufficiently complicated name can exhaust stack space and crash the process. Only reach for that switch on controlled input, when the limit itself blocks a known legitimate symbol, and run it in an isolated worker if the input is not fully trusted. No sudo needed here.

$ x86_64-w64-mingw32-c++filt -R -n _Z3fooi
foo(int)
$ x86_64-w64-mingw32-c++filt -r -n _Z3fooi
foo(int)

Those two outputs match for this small symbol. That does not make disabling the limit safe for arbitrary input.

6. Diagnose an unchanged name

Output identical to the input can mean the word is not a supported mangled name, the wrong format is selected, or punctuation got swallowed into a command-line argument. Compare these cases:

$ x86_64-w64-mingw32-c++filt -n not_a_mangled_symbol
not_a_mangled_symbol
$ x86_64-w64-mingw32-c++filt -s gnu-v3 -n _Z3fooi
foo(int)

An unchanged name is not an error by itself, the program simply echoes names it cannot demangle. Check the input source, target toolchain and exact version before reaching for more flags. If an option gets rejected, run --help on that installed binary, the local manpage and a newer upstream manual can describe different format lists.

Done means

  • Verified the executable. You confirmed the target-prefixed binary and installed Binutils version.
  • Demangled a symbol. A bare MinGW symbol comes back as the expected readable name.
  • Used standard input for messy cases. You reach for standard input when punctuation or surrounding assembler text matters.
  • Know the display flags. -p removes displayed parameter types, and -t broadens type detection.
  • Kept the recursion limit on. The guard stays enabled for untrusted input.
  • Preserved the originals. Your source file and previous output remain available after any redirected conversion.