I am compiling an application on macOS 15.4 with Xcode 16.1. Upon examining the object files the compiler produces, some of the symbols have [abi:de180100]
appended to them. This suffix is not in the C++ code. It would seem this suffix is being appended to a handful of other symbols and projects as well.
I would like to know more about the suffix, why it is necessary, and what its implications are for the symbol it qualifies. However, I cannot seem to find any documentation about it anywhere. Can someone point me in the right direction?
The [abi:de180100]
appendage is a libc++
ABI (Application Binary Interface) tag. libc++
is the
LLVM/clang implementation of the Standard C++ library, corresponding to GNU/GCC libstdc++
The tag de180100
is the concatenation of three values defined by preprocessor macros
in the libc++
configuration header file (e.g /usr/lib/llvm-18/include/c++/v1/__config
):
_LIBCPP_HARDENING_SIG
= f
,s
,d
or n
, +_LIBCPP_EXCEPTIONS_SIG
= n
or e
+_LIBCPP_VERSION
= libc++ version serial number._LIBCPP_HARDENING_SIG
encodes the hardening mode of the library. A hardening mode
is a level of enforcement of the library's preconditions, implemented via
assertions. Hardening modes are explained here
_LIBCPP_EXCEPTIONS_SIG
encodes whether library functions may (e
) or may not (n
) signal
failure by throwing an exception.
So you are using libc++
version 180100
, configured with hardening mode d
( = Debug mode
)
and with exception-throwing enabled.
The ABI tag so concatenated is called the _LIBCPP_ODR_SIGNATURE
of the library configuration.
The comments in the __config
header explain this macro:
The symbol is given an ABI tag that encodes the ODR-relevant properties of the library. This ensures that no ODR violation can arise from mixing two TUs compiled with different versions or configurations of libc++ (such as exceptions vs no-exceptions). Indeed, if the program contains two definitions of a function, the ODR requires them to be token-by-token equivalent, and the linker is allowed to pick either definition and discard the other
The representation [abi::de180100]
is the demangled representation. In the
mangled linker symbol, it appears as the suffix B8de180100m
The compiler will apply such a tag to a symbol when the symbol is declared in
the source code with __attribute__((__abi_tag__(_LIBCPP_ODR_SIGNATURE)))
. (You
will not find this string by grepping your libc++
header files, because it
is generated from the expansion of other preprocessor macros.)
You, as a user of the library, will not normally deploy such an ABI-tag in the code you
write (though nothing stops you). The source code in question will be
template code that you have included from one of clang's Standard C++ header
files. A symbol that you see ABI-tagged with _LIBCPP_ODR_SIGNATURE
will define an
inlined instantiation of some C++ library template (member-)function and will have weakly global linkage,
meaning that the linker will ingest multiple definitions of the symbol without
failure and pick one arbitrarily. The symbol is ABI-tagged because such weakly global inlined
definitions are just the ones for which the hazard to ODR integrity arises.
If you wonder why inlined instantiations of template functions are compiled with weakly global linkage (whether by clang or GCC) in the first place you can consult my answer to How does the linker handle identical template instantiations across translation units?, where I deliberately violate the ODR to demonstrate the answer.
And for a deeper dive into the use of ABI tags in policing C++ standard library ABI compatibility, you can see C++ standard library ABI compatibility, one of the Maskray blogs.
Incidentally, you don't have to have this guardrail if you don't want it.
If you compile TUs with -D_LIBCPP_NO_ABI_TAG
then clang will take the hint.