Home / Alt manpages / llvm-nm-20(1)

  • llvm-nm-20(1)
  • User command
  • linux

Inspect LLVM Symbols Safely with llvm-nm-20

You will use llvm-nm-20 to answer a practical binary question: which symbols does an object, archive, executable or LLVM bitcode file provide, require or hide behind a filter? The command only reads its inputs. It does not modify binaries, so ordinary user privileges are enough for files you can read. Allow about 10 minutes for a first inspection.

Before you start

This guide targets Ubuntu's llvm-20 package. The installed executable here reports LLVM 20.1.8. You need a readable object, archive or bitcode file. The examples create temporary input with clang-20; substitute your own path when inspecting a build artefact. No command below needs sudo.

Checkpoint

Confirm that the versioned executable is the one on your path.

$ llvm-nm-20 --version
Ubuntu LLVM version 20.1.8

1. Inspect an object file

An object file is the clearest starting point. Compile a small source file into an object, then ask for its default symbol table. This test creates files under /tmp, not in your project.

$ work=$(mktemp -d /tmp/llvm-nm.XXXXXX)
$ cat > "$work/sample.c" <<'EOF'
static int local_value = 7;
int exported_value = 11;
int add_value(int x) { return x + local_value + exported_value; }
EOF
$ clang-20 -g -c "$work/sample.c" -o "$work/sample.o"
$ llvm-nm-20 "$work/sample.o"
0000000000000000 T add_value
0000000000000000 D exported_value
0000000000000004 d local_value

The default is BSD format. Each record contains an address when one is meaningful, a type letter and a name. Uppercase letters normally identify global or external symbols; lowercase letters identify local symbols. Here T is code, D is writable data and d is local writable data. Addresses in an object file are not final load addresses.

2. Narrow the list for a specific question

Filtering is more reliable than scanning a long table by eye. Use --defined-only when checking what the file supplies, and --undefined-only when looking for unresolved references. The sample has no undefined symbols, so the second command prints nothing and still completes successfully.

$ llvm-nm-20 --defined-only "$work/sample.o"
0000000000000000 T add_value
0000000000000000 D exported_value
0000000000000004 d local_value
$ llvm-nm-20 --undefined-only "$work/sample.o"
$ echo $?
0

For scripts or a quick membership check, print names only. This avoids treating addresses as stable data.

$ llvm-nm-20 --format=just-symbols "$work/sample.o"
add_value
exported_value
local_value

Use --extern-only for symbols accessible from other files. Use --no-sort when the input order matters, or --numeric-sort when address order is the useful view. Do not assume a missing name proves that code is absent until you have checked whether one of these filters changed the view.

3. Make output useful in a build log

When several inputs are involved, --print-file-name puts the source filename before each record. This is usually the least confusing form for a CI log or a directory-wide comparison.

$ llvm-nm-20 --defined-only --print-file-name "$work/sample.o"
/tmp/llvm-nm.XXXXXX/sample.o: 0000000000000000 T add_value
/tmp/llvm-nm.XXXXXX/sample.o: 0000000000000000 D exported_value
/tmp/llvm-nm.XXXXXX/sample.o: 0000000000000004 d local_value

The directory suffix is generated, so your path will differ. For C++ or other mangled names, add --demangle to show human-readable names. Demangling changes presentation only. It does not prove that two differently named source-level functions have compatible types.

If addresses are not the right unit, --print-size adds symbol sizes where the object format supports them. --radix=d, --radix=x and --radix=o select decimal, hexadecimal or octal addresses. These options are reporting choices, not transformations of the input.

4. Inspect archives without confusing the index

Static libraries are archives of object files. Create one from the sample, then ask for both its archive map and its member symbols.

$ llvm-ar-20 rcs "$work/libsample.a" "$work/sample.o"
$ llvm-nm-20 --print-armap "$work/libsample.a"
Archive map
add_value in sample.o
exported_value in sample.o


sample.o:
0000000000000000 T add_value
0000000000000000 D exported_value
0000000000000004 d local_value

The archive map is the index used to find members for externally requested symbols. It is separate from the member records below it. If you only need the symbols, omit --print-armap. If a link cannot find a symbol in a library, this view helps distinguish an absent member from an index or link-order problem.

5. Read LLVM bitcode and standard input

LLVM bitcode is accepted directly. Bitcode symbols do not have final addresses, so this version prints a row of dashes in the address column even for definitions.

$ clang-20 -emit-llvm -c "$work/sample.c" -o "$work/sample.bc"
$ llvm-nm-20 "$work/sample.bc"
---------------- T add_value
---------------- D exported_value
---------------- d local_value

A dash is therefore expected here, not evidence that the symbol is undefined. You can also use - as the filename to read one input from standard input.

$ llvm-nm-20 - < "$work/sample.o" | head -n 3
0000000000000000 T add_value
0000000000000000 D exported_value
0000000000000004 d local_value

The no-argument default is different: without a filename, llvm-nm-20 attempts to inspect a.out. Pass the filename explicitly in repeatable commands so an old executable or a missing file does not distract you.

Common traps and safe recovery

Do not mistake a quiet command for a failed inspection. An empty result from an intentional filter can be valid. Re-run without the filter, then check the exit status. Conversely, a missing or unreadable input is an error and returns a non-zero status.

$ llvm-nm-20 "$work/no-such-file"
llvm-nm-20: error: ...: No such file or directory
$ echo $?
1

Do not pass GNU nm options by habit. LLVM documents that it does not support the full GNU option set. Check llvm-nm-20 --help before copying a command from a different toolchain. The versioned command also supports response files with @FILE when a generated argument list is easier to manage.

Clean up only the temporary test data. The inspection commands change nothing. If you created the sample directory, remove that exact directory after checking its contents. Never apply a recursive removal command to a project path just because the examples used mktemp.

Done means

  • You confirmed the installed llvm-nm-20 version.
  • You can read BSD records and distinguish global from local type letters.
  • You can filter defined, undefined, external, sorted and name-only output.
  • You can identify archive maps and understand dash-filled bitcode addresses.
  • You checked the exit status when output was empty or an input was missing.