Inspect GCC and LTO Objects with gcc-nm-13
You will use the GCC 13 gcc-nm wrapper to list symbols from an object file, then see why it is useful for objects produced with link-time optimisation (LTO). The wrapper passes the appropriate GCC plugin option to nm; it is not a different symbol format or a compiler replacement.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need GCC 13, GNU Binutils and a shell. The commands below only read object files or create disposable build artefacts in a working directory. They do not need sudo. Do not run them in a source tree with uncommitted generated files unless you know where the output will go.
The examples were checked with package versions gcc-13 13.3.0-6ubuntu2~24.04.1, gcc-13-x86-64-linux-gnu 13.3.0-6ubuntu2~24.04.1 and binutils 2.42-4ubuntu2.10. The installed wrapper reports the GNU nm implementation supplied by the local toolchain, so your version string and diagnostic paths may differ.
1. Check the wrapper you will run
The four names in this guide are aliases for the same GCC 13 wrapper on the tested system. Check the command selected by your PATH and read its version:
$ command -v x86_64-linux-gnu-gcc-nm-13
/usr/bin/x86_64-linux-gnu-gcc-nm-13
$ x86_64-linux-gnu-gcc-nm-13 --version
GNU nm (GNU Binutils) 2.47.20260726
That version line describes nm, because gcc-nm is a wrapper. If the command is missing, install or repair the GCC and Binutils packages through your normal system-management process. Do not substitute an unrelated nm binary just to make the example continue.
Checkpoint
You have a GCC-targeted wrapper and know which executable will receive your options.
2. Build a small ordinary object
Use a disposable source file with one global function, one global variable and one private variable. Replace the path if you already have an object you want to inspect:
$ cat > /tmp/gcc-nm-demo.c <<'EOF'
int exported_value = 42;
static int hidden_value = 7;
int add_values(int left, int right) { return left + right + hidden_value; }
EOF
$ gcc-13 -c -O0 -o /tmp/gcc-nm-demo.o /tmp/gcc-nm-demo.c
The redirection creates or replaces /tmp/gcc-nm-demo.c. This is deliberate, but check the path before pasting it. The compiler writes the object named by -o; it does not alter the source.
Now list the symbols:
$ x86_64-linux-gnu-gcc-nm-13 /tmp/gcc-nm-demo.o
0000000000000000 T add_values
0000000000000000 D exported_value
0000000000000004 d hidden_value
The exact addresses can change with compiler options and target details. The useful parts here are the names and type letters. Uppercase T and D identify global text and data symbols; lowercase d identifies a local data symbol. gcc-nm accepts the normal nm options, so -g limits the display to external symbols and -P selects a portable, one-line format:
$ x86_64-linux-gnu-gcc-nm-13 -P -g /tmp/gcc-nm-demo.o
add_values T 0 20
exported_value D 0 4
Checkpoint
The wrapper can inspect an ordinary ELF object and the output changes when you apply a documented nm option.
3. Create an LTO object
GCC's -flto stores compiler information for link-time optimisation in the object. It is still an object file, but a plain symbol reader may not know how to interpret it. Compile the same source with LTO:
$ gcc-13 -flto -c -o /tmp/gcc-nm-demo-lto.o /tmp/gcc-nm-demo.c
$ file /tmp/gcc-nm-demo-lto.o
/tmp/gcc-nm-demo-lto.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
The wording from file can vary. The important check is that the command identifies a relocatable ELF object and exits successfully. The file is not an executable and cannot be run directly.
Inspect it with the wrapper:
$ x86_64-linux-gnu-gcc-nm-13 /tmp/gcc-nm-demo-lto.o
00000000 T add_values
00000000 D exported_value
This is the practical reason to use the GCC wrapper. It supplies the appropriate --plugin option to nm, allowing the Binutils reader to understand GCC's LTO object. The command lists the symbols relevant to the object; do not expect the output to match an unoptimised object byte for byte.
4. Reproduce and interpret the plain-nm failure
Run an unwrapped nm against the LTO object only as a diagnostic comparison:
$ nm /tmp/gcc-nm-demo-lto.o
nm: /tmp/gcc-nm-demo-lto.o: plugin needed to handle lto object
0000000000000001 C __gnu_lto_slim
The exact path and ordering may differ. The useful signal is plugin needed to handle lto object. The small __gnu_lto_slim entry is not a substitute for the source symbols. It shows that plain nm has not interpreted the LTO payload correctly.
Do not fix this by copying a random plugin into a system directory. GNU Binutils documents --plugin as the mechanism for loading support for extra object types. gcc-nm packages the compiler-specific choice so that normal inspection does not depend on a hand-maintained global plugin directory.
Checkpoint
If ordinary objects work but an LTO object reports that a plugin is needed, switch to the matching GCC wrapper before investigating the object itself.
5. Narrow the output for a real inspection
Start broad, then narrow the result for the question you are answering. These read-only commands do not change the object:
$ x86_64-linux-gnu-gcc-nm-13 -g /path/to/object.o
$ x86_64-linux-gnu-gcc-nm-13 -u /path/to/object.o
$ x86_64-linux-gnu-gcc-nm-13 -C /path/to/object.o
$ x86_64-linux-gnu-gcc-nm-13 -A /path/to/one.o /path/to/two.o
-gshows external symbols only.-ushows undefined symbols, useful for spotting unresolved dependencies.-Cdemangles supported low-level names, which helps with C++ output.-Aprefixes each symbol with its input file when comparing several objects.
Pass paths as separate arguments and quote paths containing spaces. Keep shell options before the object names. If you need machine-readable processing, use -P and treat names as data rather than parsing the human-oriented default layout.
6. Clean up without touching source
The demo created three temporary files. Remove only those exact files after you have finished checking the output:
$ rm -f /tmp/gcc-nm-demo.c /tmp/gcc-nm-demo.o /tmp/gcc-nm-demo-lto.o
$ test ! -e /tmp/gcc-nm-demo.c && echo "demo files removed"
demo files removed
This cleanup is irreversible for those temporary files, so do not broaden the command to a directory or replace the paths with a wildcard. If you used an existing object instead, there is nothing to undo: gcc-nm only reads its input.
Done means
- You confirmed the installed GCC 13 wrapper and its Binutils version.
- You used normal
nmoptions throughgcc-nm-13on an ordinary object. - You inspected a GCC LTO object successfully with the wrapper.
- You can recognise the plain-
nmplugin diagnostic. - You kept inspection unprivileged and removed only the disposable demo files.