Inspect GCC and LTO Objects with gcc-nm-13
Plain nm on a link-time-optimised object gives you a plugin error and a near-useless symbol list, which is a bad moment to discover the difference. x86_64-linux-gnu-gcc-nm-13 is the fix: a GCC 13 wrapper that passes the right plugin option to nm so LTO objects read the same as ordinary ones.
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.
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. 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
That redirection creates or replaces /tmp/gcc-nm-demo.c, deliberately, 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
Exact addresses can change with compiler options and target details. The names and type letters are what matter: uppercase T and D identify global text and data symbols, lowercase d 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 inside 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. What matters 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, letting the Binutils reader understand GCC's LTO 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 signal to look for 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 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 any containing spaces. Keep shell options before the object names. For 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 once 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
Warning
This cleanup is irreversible for those temporary files, so do not broaden the command to a directory or swap in a wildcard. If you used an existing object instead, there is nothing to undo: gcc-nm only reads its input.
Done means
- Wrapper confirmed: you checked the installed GCC 13 wrapper and its Binutils version.
- Ordinary object read: normal
nmoptions worked throughgcc-nm-13. - LTO object read: you inspected a GCC LTO object successfully with the wrapper.
- Failure recognised: you can spot the plain-
nmplugin diagnostic. - Cleaned up: inspection stayed unprivileged, and only the disposable demo files were removed.