Refresh AArch64 Archive Indexes with gcc-ranlib-13

aarch64-linux-gnu-gcc-ranlib-13 rebuilds the symbol index inside an AArch64 static archive, the step a stale library silently skips. You'll refresh the index and confirm the expected symbol shows up afterwards. It's the GCC 13 cross-toolchain version of ranlib: it adds the right GCC 13 plugin option for archive processing.

Allow about ten minutes. You need the gcc-13-aarch64-linux-gnu and gcc-aarch64-linux-gnu packages, an archive such as libexample.a, and write access to its directory. These commands change the archive index, so work on a copy or keep a backup when the library matters. Nothing here normally needs sudo.

1. Confirm the wrapper and installed versions

Check the exact executable before you touch a library:

$ command -v aarch64-linux-gnu-gcc-ranlib-13
/usr/bin/aarch64-linux-gnu-gcc-ranlib-13
$ aarch64-linux-gnu-gcc-ranlib-13 --version
GNU ranlib (GNU Binutils for Ubuntu) 2.42
$ dpkg-query -W -f='${Package} ${Version}\n' gcc-13-aarch64-linux-gnu gcc-aarch64-linux-gnu
gcc-13-aarch64-linux-gnu 13.3.0-6ubuntu2~24.04.1cross1
gcc-aarch64-linux-gnu 4:13.2.0-7ubuntu1

The installed wrapper reports the GNU Binutils 2.42 implementation and comes from the GCC 13 cross package. The unnumbered alias, aarch64-linux-gnu-gcc-ranlib, is also installed here and points at the same toolchain family, but use the numbered command when a build specifically targets GCC 13.

Checkpoint: stop if command -v resolves to a different executable, or the package query shows a toolchain version you didn't mean to use.

2. Inspect the archive before you touch it

List it and ask nm for its current index. Swap in your own path:

$ ARCHIVE='/path/to/libexample.a'
$ ls -l -- "$ARCHIVE"
$ aarch64-linux-gnu-nm -s -- "$ARCHIVE"

Archive index:
example_function in example.o

example.o:
0000000000000000 T example_function

Your symbol list will depend on the object files. If nm -s shows no archive index, or a stale one, that's exactly what this wrapper is for. The archive has to contain relocatable object files: don't hand it an executable, shared object or source file and expect it to be indexed.

3. Back up the library before writing to it

gcc-ranlib updates the archive in place. Make an explicit backup when the current file is worth keeping:

$ cp --preserve=all -- "$ARCHIVE" "$ARCHIVE.before-gcc-ranlib"
$ ls -l -- "$ARCHIVE" "$ARCHIVE.before-gcc-ranlib"

That's an ordinary file copy. If the archive belongs to another account or lives in a protected build directory, ask the build owner to run the update rather than reaching for elevation casually. A backup doesn't protect you from a later command that overwrites both paths, so keep the destination name visible in your history.

4. Rebuild the index

Run the wrapper with the archive as its final argument:

$ aarch64-linux-gnu-gcc-ranlib-13 -- "$ARCHIVE"
$ printf 'exit status: %s\n' "$?"
exit status: 0

A successful run normally prints nothing. The wrapper supplies the plugin option its manpage describes, so you don't need to hunt down liblto_plugin.so or build a separate --plugin argument for a normal GCC 13 cross-toolchain layout. The archive's contents aren't reordered, only its symbol map is refreshed.

Checkpoint: a non-zero status means the index wasn't refreshed. Don't delete the backup or move on to a release step until you understand why.

5. Verify symbols after the update

Read the index again and check it against what the library is actually supposed to provide:

$ aarch64-linux-gnu-nm -s -- "$ARCHIVE"

Archive index:
example_function in example.o

example.o:
0000000000000000 T example_function
$ aarch64-linux-gnu-nm -s -- "$ARCHIVE" | grep -F -- 'example_function'
example_function in example.o

Use a symbol name that genuinely belongs to your library. A successful ranlib exit status only confirms the archive update completed, it says nothing about whether the intended object or symbol actually made it in. If the symbol is missing, check the object list and rebuild the archive with your normal ar command.

6. Handle LTO archives deliberately

For an archive holding GCC link-time optimisation objects, compile and archive with the same target toolchain, then run the numbered wrapper:

$ aarch64-linux-gnu-gcc-13 -flto -c example.c -o example.o
$ aarch64-linux-gnu-ar cr libexample.a example.o
$ aarch64-linux-gnu-gcc-ranlib-13 libexample.a
$ aarch64-linux-gnu-nm -s -- libexample.a

Archive index:
example_function in example.o

The plugin is the entire reason this GCC-specific wrapper exists. Don't swap in a host ranlib just because its name is shorter, and don't mix host and AArch64 archive tools in a cross build without checking the resulting format. The LTO example changes example.o and libexample.a, so use a scratch directory or your build system's disposable output tree while testing.

Common traps

If the command fails, keep the diagnostic and check the path, file type and write permission:

$ file -- "$ARCHIVE"
$ test -r "$ARCHIVE" && printf '%s\n' readable
$ test -w "$ARCHIVE" && printf '%s\n' writable

Recovery: if you need to undo the update and made the backup in step 3, restore it only after checking both paths:

$ ls -l -- "$ARCHIVE" "$ARCHIVE.before-gcc-ranlib"
$ cp --preserve=all -- "$ARCHIVE.before-gcc-ranlib" "$ARCHIVE"
$ aarch64-linux-gnu-nm -s -- "$ARCHIVE"

That restore replaces the current archive, so it's a state-changing action in its own right. If the archive comes from a build system, the durable fix is usually correcting the toolchain rule and rebuilding, not hand-editing a generated library. Never run the wrapper as root just to hide a permissions error.

Done means