3d50dede07
The std::__convert_from_v helper that formats double and long double values into a char buffer was not being duplicated for the two long double ABIs. This resulted in an ODR violation inside the library, where some callers needed it to use snprintf to format __ibm128 values and other callers needed it to use __snprintfieee128 to format __ieee128 values. The linker discarded one of the definitions, leaving one set of callers using the wrong code. This puts __convert_from_v in the __gnu_cxx_ieee128 inline namespace when long double is __ieee128, so that there are two different definitions of the function. The std::money_put::__do_put overload for __ibm128 values needs a different fix, because that is defined when long double is __ieee128 and so would call the one in the inline namespace. That can be fixed by just inlining the code directly into the function and using an asm alias to call the right version of snprintf for the __ibm128 format. The code to do that can be simpler than __convert_from_v because if we're defining the ALT128_COMPAT symbols we know that we have a recent glibc and so we can assume that uselocale and snprintf are supported. libstdc++-v3/ChangeLog: PR libstdc++/100912 * config/locale/gnu/c_locale.h (__convert_from_v): Use inline namespace for IEEE128 long double mode. * config/os/gnu-linux/ldbl-ieee128-extra.ver: Add new symbol version and export __gnu_cxx_ieee128::__convert_from_v. * include/bits/locale_facets_nonio.tcc (money_put::__do_put): Make __ibm128 overload use snprintf directly * testsuite/util/testsuite_abi.cc: Add new symbol version. Remove stable IEEE128/LDBL versions.
125 lines
3.5 KiB
C++
125 lines
3.5 KiB
C++
// Wrapper for underlying C-language localization -*- C++ -*-
|
|
|
|
// Copyright (C) 2001-2022 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file bits/c++locale.h
|
|
* This is an internal header file, included by other library headers.
|
|
* Do not attempt to use it directly. @headername{locale}
|
|
*/
|
|
|
|
//
|
|
// ISO C++ 14882: 22.8 Standard locale categories.
|
|
//
|
|
|
|
// Written by Benjamin Kosnik <bkoz@redhat.com>
|
|
|
|
#ifndef _GLIBCXX_CXX_LOCALE_H
|
|
#define _GLIBCXX_CXX_LOCALE_H 1
|
|
|
|
#pragma GCC system_header
|
|
|
|
#include <clocale>
|
|
|
|
#define _GLIBCXX_C_LOCALE_GNU 1
|
|
|
|
#define _GLIBCXX_NUM_CATEGORIES 6
|
|
|
|
#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
|
|
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
extern "C" __typeof(uselocale) __uselocale;
|
|
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace
|
|
#endif
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
typedef __locale_t __c_locale;
|
|
|
|
#if defined _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT \
|
|
&& defined __LONG_DOUBLE_IEEE128__
|
|
namespace __gnu_cxx_ieee128 {
|
|
#endif
|
|
|
|
// Convert numeric value of type double and long double to string and
|
|
// return length of string. If vsnprintf is available use it, otherwise
|
|
// fall back to the unsafe vsprintf which, in general, can be dangerous
|
|
// and should be avoided.
|
|
inline int
|
|
__convert_from_v(const __c_locale& __cloc __attribute__ ((__unused__)),
|
|
char* __out,
|
|
const int __size __attribute__ ((__unused__)),
|
|
const char* __fmt, ...)
|
|
{
|
|
#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
|
|
__c_locale __old = __gnu_cxx::__uselocale(__cloc);
|
|
#else
|
|
char* __old = std::setlocale(LC_NUMERIC, 0);
|
|
char* __sav = 0;
|
|
if (__builtin_strcmp(__old, "C"))
|
|
{
|
|
const size_t __len = __builtin_strlen(__old) + 1;
|
|
__sav = new char[__len];
|
|
__builtin_memcpy(__sav, __old, __len);
|
|
std::setlocale(LC_NUMERIC, "C");
|
|
}
|
|
#endif
|
|
|
|
__builtin_va_list __args;
|
|
__builtin_va_start(__args, __fmt);
|
|
|
|
#if _GLIBCXX_USE_C99_STDIO
|
|
const int __ret = __builtin_vsnprintf(__out, __size, __fmt, __args);
|
|
#else
|
|
const int __ret = __builtin_vsprintf(__out, __fmt, __args);
|
|
#endif
|
|
|
|
__builtin_va_end(__args);
|
|
|
|
#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
|
|
__gnu_cxx::__uselocale(__old);
|
|
#else
|
|
if (__sav)
|
|
{
|
|
std::setlocale(LC_NUMERIC, __sav);
|
|
delete [] __sav;
|
|
}
|
|
#endif
|
|
return __ret;
|
|
}
|
|
|
|
#if defined _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT \
|
|
&& defined __LONG_DOUBLE_IEEE128__
|
|
} // namespace __gnu_cxx_ieee128
|
|
#endif
|
|
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace
|
|
|
|
#endif
|