[flang-rt] Avoid duplicate definition of std::__libcpp_verbose_abort (#175551)

If a project depends on the Flang runtime and on libc++, linking fails
because `std::__libcpp_verbose_abort` is defined in both libraries.

Avoid that duplicate definition by defining `_LIBCPP_VERBOSE_ABORT`
before including any C++ headers and by renaming that symbol in the
Flang runtime to `flang_rt_verbose_abort`.

The function that is modified was originally introduced in D158957 to
solve an undefined symbol error when linking pure-Fortran projects with
the Flang runtime.
Providing a definition for that symbol in the Flang runtime might work
correctly for ELF or Mach-O if that symbol has weak linkage in libc++.
But at least for COFF, this now causes multiple-definition errors for
projects that are linking to the Flang runtime and to libc++.

The linker errors before this change for Windows/MinGW using
Clang+Flang+lld look like this:
```
ld.lld: error: duplicate symbol: std::__1::__libcpp_verbose_abort(char const*, ...)
>>> defined at libflang_rt.runtime.a(io-api-minimal.cpp.obj)
>>> defined at libc++.dll.a(libc++.dll)
```
This commit is contained in:
Markus Mützel 2026-03-26 14:46:34 +01:00 committed by GitHub
parent 9e6bd128f2
commit e41e7b90f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 78 additions and 35 deletions

View File

@ -238,34 +238,31 @@ function (add_flangrt_library name)
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions -fno-rtti -funwind-tables -fno-asynchronous-unwind-tables>
)
# We define our own _GLIBCXX_THROW_OR_ABORT here because, as of
# GCC 15.1, the libstdc++ header file <bits/c++config> uses
# (void)_EXC in its definition of _GLIBCXX_THROW_OR_ABORT to
# silence a warning.
#
# This is a problem for us because some compilers, specifically
# clang, do not always optimize away that (void)_EXC even though
# it is unreachable since it occurs after a call to
# _builtin_abort(). Because _EXC is typically an object derived
# from std::exception, (void)_EXC, when not optimized away,
# calls std::exception methods defined in the libstdc++ shared
# library. We shouldn't link against that library since our
# build version may conflict with the version used by a hybrid
# Fortran/C++ application.
#
# Redefining _GLIBCXX_THROW_OR_ABORT in this manner is not
# supported by the maintainers of libstdc++, so future changes
# to libstdc++ may require future changes to this build script
# and/or future changes to the Fortran runtime source code.
target_compile_options(${tgtname} PUBLIC "-D_GLIBCXX_THROW_OR_ABORT(_EXC)=(__builtin_abort())")
# Include header at the top of all compilation units to avoid dependency
# on the C++ STL (libstdc++ or libc++).
target_compile_options(${tgtname} PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:-include${FLANG_RT_SOURCE_DIR}/lib/runtime/stl-overrides.h>"
)
elseif (MSVC)
target_compile_options(${tgtname} PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:/EHs-c- /GR->
)
# Include header at the top of all compilation units to avoid dependency
# on the C++ STL (libstdc++ or libc++).
target_compile_options(${tgtname} PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:/FI${FLANG_RT_SOURCE_DIR}/lib/runtime/stl-overrides.h>"
)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "XL")
target_compile_options(${tgtname} PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-qnoeh -qnortti>
)
# Include header at the top of all compilation units to avoid dependency
# on the C++ STL (libstdc++ or libc++).
target_compile_options(${tgtname} PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:-qinclude=${FLANG_RT_SOURCE_DIR}/lib/runtime/stl-overrides.h>"
)
endif ()
# Add target specific options if necessary.

View File

@ -59,6 +59,7 @@ set(supported_sources
ragged.cpp
reduction.cpp
stat.cpp
stl-overrides.cpp
stop.cpp
sum.cpp
support.cpp

View File

@ -146,19 +146,4 @@ bool IODEF(OutputLogical)(Cookie cookie, bool truth) {
} // namespace Fortran::runtime::io
#if defined(_LIBCPP_VERBOSE_ABORT)
// Provide own definition for `std::__libcpp_verbose_abort` to avoid dependency
// on the version provided by libc++.
void std::__libcpp_verbose_abort(char const *format, ...) noexcept(
noexcept(std::__libcpp_verbose_abort(""))) {
va_list list;
va_start(list, format);
std::vfprintf(stderr, format, list);
va_end(list);
std::abort();
}
#endif
RT_EXT_API_GROUP_END

View File

@ -0,0 +1,22 @@
//===-- lib/runtime/stl-overrides.cpp ---------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
// Provide function that is used in place of `std::__libcpp_verbose_abort` to
// avoid dependency on the symbol provided by libc++.
void flang_rt_verbose_abort(char const *format, ...) {
va_list list;
va_start(list, format);
std::vfprintf(stderr, format, list);
va_end(list);
std::abort();
}

View File

@ -0,0 +1,38 @@
//===-- lib/runtime/stl-overrides.h -----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file is inserted implicitly to all translation units using -include on
// the command line. The reason is that it configures the C++ standard
// template library (libc++ or libstdc++) using preprocessor macro definitions
// that must appear before any C++ library include.
// We define our own _GLIBCXX_THROW_OR_ABORT here because, as of GCC 15.1, the
// libstdc++ header file <bits/c++config> uses (void)_EXC in its definition of
// _GLIBCXX_THROW_OR_ABORT to silence a warning.
//
// This is a problem for us because some compilers, specifically clang, do not
// always optimize away that (void)_EXC even though it is unreachable since it
// occurs after a call to _builtin_abort(). Because _EXC is typically an
// object derived from std::exception, (void)_EXC, when not optimized away,
// calls std::exception methods defined in the libstdc++ shared library. We
// shouldn't link against that library since our build version may conflict
// with the version used by a hybrid Fortran/C++ application.
//
// Redefining _GLIBCXX_THROW_OR_ABORT in this manner is not supported by the
// maintainers of libstdc++, so future changes to libstdc++ may require future
// changes to this build script and/or future changes to the Fortran runtime
// source code.
#define _GLIBCXX_THROW_OR_ABORT(_EXC) (__builtin_abort())
// Declare function that is used in place of `std::__libcpp_verbose_abort` to
// avoid dependency on the symbol provided by libc++.
#ifndef _LIBCPP_VERBOSE_ABORT
#define _LIBCPP_VERBOSE_ABORT(...) flang_rt_verbose_abort(__VA_ARGS__)
void flang_rt_verbose_abort(char const *format, ...)
__attribute__((format(printf, 1, 2)));
#endif