The linker cannot find a symbol that is obviously in your static library, and the fix is often a stale index: llvm-ranlib-18 rebuilds it in seconds. This guide uses Ubuntu's llvm-18 package, version 1:18.1.3-1ubuntu1, and the installed program reports LLVM 18.1.3.
Allow about ten minutes. You need a shell, an existing Unix archive such as libexample.a, and write permission for its directory. The examples use llvm-ar-18 and clang-18 only to create a small test archive. In a normal build, your compiler and archiver will already have produced the archive.
Check the executable and package before putting it into a build script. These are ordinary read-only commands and do not need elevated privileges:
$ command -v llvm-ranlib-18
/usr/bin/llvm-ranlib-18
$ llvm-ranlib-18 --version
Ubuntu LLVM version 18.1.3
Optimized build.
$ dpkg-query -W -f='${Package} ${Version}\n' llvm-18
llvm-18 1:18.1.3-1ubuntu1
Checkpoint: if the command is missing, stop here and use your normal package-management process. Do not replace it with an unversioned ranlib until you have checked that the toolchain expects that program.
An archive is a collection of object files, commonly named libexample.a. Its symbol index lets a linker find an object member that supplies a needed function without scanning every member in turn. llvm-ranlib-18 generates that index in the archive supplied as its positional argument:
$ llvm-ranlib-18 /path/to/libexample.a
Warning: the command updates the archive in place. It does not compile source files, add object members, or choose a library name. Treat the archive as the state that will change. Before running the command on an important build artefact, keep the source objects or a reproducible build available. If you need a reversible trial, copy the archive to a scratch path first:
$ cp --preserve=all /path/to/libexample.a /tmp/libexample-ranlib-test.a
$ llvm-ranlib-18 /tmp/libexample-ranlib-test.a
The copy is the one changed by that test. There is no separate undo option; restore the original from a known-good copy or rebuild the archive if a later build step has changed more than its index.
If you do not already have an archive to inspect, create one in a temporary directory. The following commands do not need root privileges:
$ workdir=$(mktemp -d /tmp/llvm-ranlib-example.XXXXXX)
$ printf '%s\n' 'int ranlib_demo(void) { return 7; }' > "$workdir/demo.c"
$ clang-18 -c "$workdir/demo.c" -o "$workdir/demo.o"
$ llvm-ar-18 rc "$workdir/libdemo.a" "$workdir/demo.o"
$ llvm-ranlib-18 "$workdir/libdemo.a"
llvm-ar-18 rc creates an archive containing the object file. The final command then creates or refreshes its symbol table. If your system has a different compiler command, use that command to produce an object file and keep the llvm-ranlib-18 step unchanged.
Checkpoint: confirm that the archive contains the object member:
$ llvm-ar-18 t "$workdir/libdemo.a"
demo.o
Use an archive inspection tool to check the result. llvm-ranlib-18 is quiet on success, so silence alone is not a useful inspection result:
$ nm -s "$workdir/libdemo.a"
Archive index:
ranlib_demo in demo.o
demo.o:
0000000000000000 T ranlib_demo
The address and symbol type can vary with the object format, but the archive index should name ranlib_demo and point to demo.o. You can also ask the archiver to print the index:
$ llvm-ar-18 s "$workdir/libdemo.a"
That command is a separate archiver operation. Use it as an inspection or repair step only when it matches your toolchain's workflow; the command documented here for generating the index is llvm-ranlib-18.
LLVM 18 defaults to -D, which uses zero for archive member timestamps and user and group IDs. That default helps make build outputs reproducible. Make it explicit in a build command when reproducibility is part of the requirement:
$ llvm-ranlib-18 -D /path/to/libexample.a
-U selects actual timestamps and user and group IDs instead:
$ llvm-ranlib-18 -U /path/to/libexample.a
Do not switch to -U merely to make an archive look newer. It introduces machine- and time-dependent metadata. Conversely, do not assume that -D makes the whole build reproducible: object contents, archive member order and compiler inputs still matter.
A missing or unreadable archive normally fails before any useful index is written. Check the path and permissions without using sudo:
$ ls -l /path/to/libexample.a
$ test -r /path/to/libexample.a && echo readable
$ test -w /path/to/libexample.a && echo writable
$ llvm-ranlib-18 /path/to/libexample.a
$ printf 'exit status: %s\n' "$?"
A zero status means the command completed successfully. It does not prove that the archive contains the symbol your application needs, so repeat the nm -s check when that distinction matters.
Recovery: if the archive is in a protected build directory, ask the build owner to fix ownership or permissions rather than routinely running the command as root. Elevated privileges can hide a deployment mistake and may leave root-owned output behind. If a build process has already replaced the archive, recover by rebuilding from its object files or restoring the last known-good copy.
The installed command accepts an archive path, -h/--help, -v/--version, -D, -U, and the AIX-only -X choice. -X accepts 32, 64, 32_64 or any to specify which archive symbol tables should be generated when they do not already exist. It is not a general Linux compatibility switch, so leave it out on an ordinary Linux build unless an AIX archive workflow specifically requires it.
Do not add source files or linker flags to this command. If a symbol is missing, inspect the archive members and the build command that produced them first. Rebuilding an index cannot create a symbol that no object file defines.
llvm-ranlib-18 --version reports the expected LLVM 18 installation.nm -s or an equivalent inspection shows the expected archive index and symbol.-D or -U was chosen deliberately, rather than inherited as a mystery default.