PR libstdc++/85494 use rdseed and rand_s in std::random_device
Add support for additional sources of randomness to std::random_device, to allow using RDSEED for Intel CPUs and rand_s for Windows. When supported these can be selected using the tokens "rdseed" and "rand_s". For *-w64-mingw32 targets the "default" token will now use rand_s, and for other i?86-*-* and x86_64-*-* targets it will try to use "rdseed" first, then "rdrand", and finally "/dev/urandom". To simplify the declaration of std::random_device in <bits/random.h> the constructors now unconditionally call _M_init instead of _M_init_pretr1, and the function call operator now unconditionally calls _M_getval. The library code now decides whether _M_init and _M_getval should use a real source of randomness or the mt19937 engine. Existing code compiled against old libstdc++ headers will still call _M_init_pretr1 and _M_getval_pretr1, but those functions now forward to _M_init and _M_getval if a real source of randomness is available. This means existing code compiled for mingw-w64 will start to use rand_s just by linking to a new libstdc++.dll. * acinclude.m4 (GLIBCXX_CHECK_X86_RDSEED): Define macro to check if the assembler supports rdseed. * config.h.in: Regenerate. * configure: Regenerate. * configure.ac: Use GLIBCXX_CHECK_X86_RDSEED. * config/os/mingw32-w64/os_defines.h (_GLIBCXX_USE_CRT_RAND_S): Define. * doc/html/*: Regenerate. * doc/xml/manual/status_cxx2011.xml: Document new tokens. * include/bits/random.h (random_device::random_device()): Always call _M_init rather than _M_init_pretr1. (random_device::random_device(const string&)): Likewise. (random_device::operator()()): Always call _M_getval(). (random_device::_M_file): Replace first member of union with an anonymous struct, with _M_file as its first member. * src/c++11/random.cc [_GLIBCXX_X86_RDRAND] (USE_RDRAND): Define. [_GLIBCXX_X86_RDSEED] (USE_RDSEED): Define. (USE_MT19937): Define if none of the above are defined. (USE_POSIX_FILE_IO): Define. (_M_strtoul): Remove. [USE_RDSEED] (__x86_rdseed): Define new function. [_GLIBCXX_USE_CRT_RAND_S] (__winxp_rand_s): Define new function. (random_device::_M_init(const string&)): Initialize new union members. Add support for "rdseed" and "rand_s" tokens. Decide what the "default" token does according to which USE_* macros are defined. [USE_POSIX_FILE_IO]: Store a file descriptor. [USE_MT19937]: Forward to _M_init_pretr1 instead. (random_device::_M_init_pretr1(const string&)) [USE_MT19937]: Inline code from _M_strtoul. [!USE_MT19937]: Call _M_init, transforming the old default token or numeric tokens to "default". (random_device::_M_fini()) [USE_POSIX_FILE_IO]: Use close not fclose. (random_device::_M_getval()): Use new union members to obtain a random number from the stored function pointer or file descriptor. [USE_MT19937]: Obtain a value from the mt19937 engine. (random_device::_M_getval_pretr1()): Call _M_getval(). (random_device::_M_getentropy()) [USE_POSIX_FILE_IO]: Use _M_fd instead of fileno. [!USE_MT19937] (mersenne_twister): Do not instantiate when not needed. * testsuite/26_numerics/random/random_device/85494.cc: New test. From-SVN: r271740
This commit is contained in:
parent
0617e23c95
commit
b0c0d878a8
12 changed files with 492 additions and 97 deletions
|
@ -1,3 +1,46 @@
|
|||
2019-05-29 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
PR libstdc++/85494 use rdseed and rand_s in std::random_device
|
||||
* acinclude.m4 (GLIBCXX_CHECK_X86_RDSEED): Define macro to check if
|
||||
the assembler supports rdseed.
|
||||
* config.h.in: Regenerate.
|
||||
* configure: Regenerate.
|
||||
* configure.ac: Use GLIBCXX_CHECK_X86_RDSEED.
|
||||
* config/os/mingw32-w64/os_defines.h (_GLIBCXX_USE_CRT_RAND_S): Define.
|
||||
* doc/html/*: Regenerate.
|
||||
* doc/xml/manual/status_cxx2011.xml: Document new tokens.
|
||||
* include/bits/random.h (random_device::random_device()): Always call
|
||||
_M_init rather than _M_init_pretr1.
|
||||
(random_device::random_device(const string&)): Likewise.
|
||||
(random_device::operator()()): Always call _M_getval().
|
||||
(random_device::_M_file): Replace first member of union with an
|
||||
anonymous struct, with _M_file as its first member.
|
||||
* src/c++11/random.cc [_GLIBCXX_X86_RDRAND] (USE_RDRAND): Define.
|
||||
[_GLIBCXX_X86_RDSEED] (USE_RDSEED): Define.
|
||||
(USE_MT19937): Define if none of the above are defined.
|
||||
(USE_POSIX_FILE_IO): Define.
|
||||
(_M_strtoul): Remove.
|
||||
[USE_RDSEED] (__x86_rdseed): Define new function.
|
||||
[_GLIBCXX_USE_CRT_RAND_S] (__winxp_rand_s): Define new function.
|
||||
(random_device::_M_init(const string&)): Initialize new union members.
|
||||
Add support for "rdseed" and "rand_s" tokens. Decide what the
|
||||
"default" token does according to which USE_* macros are defined.
|
||||
[USE_POSIX_FILE_IO]: Store a file descriptor.
|
||||
[USE_MT19937]: Forward to _M_init_pretr1 instead.
|
||||
(random_device::_M_init_pretr1(const string&)) [USE_MT19937]: Inline
|
||||
code from _M_strtoul.
|
||||
[!USE_MT19937]: Call _M_init, transforming the old default token or
|
||||
numeric tokens to "default".
|
||||
(random_device::_M_fini()) [USE_POSIX_FILE_IO]: Use close not fclose.
|
||||
(random_device::_M_getval()): Use new union members to obtain a
|
||||
random number from the stored function pointer or file descriptor.
|
||||
[USE_MT19937]: Obtain a value from the mt19937 engine.
|
||||
(random_device::_M_getval_pretr1()): Call _M_getval().
|
||||
(random_device::_M_getentropy()) [USE_POSIX_FILE_IO]: Use _M_fd
|
||||
instead of fileno.
|
||||
[!USE_MT19937] (mersenne_twister): Do not instantiate when not needed.
|
||||
* testsuite/26_numerics/random/random_device/85494.cc: New test.
|
||||
|
||||
2019-05-28 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
PR libstdc++/90634
|
||||
|
|
|
@ -4053,6 +4053,26 @@ AC_DEFUN([GLIBCXX_CHECK_X86_RDRAND], [
|
|||
AC_MSG_RESULT($ac_cv_x86_rdrand)
|
||||
])
|
||||
|
||||
dnl
|
||||
dnl Check whether rdseed is supported in the assembler.
|
||||
AC_DEFUN([GLIBCXX_CHECK_X86_RDSEED], [
|
||||
AC_MSG_CHECKING([for rdseed support in assembler])
|
||||
AC_CACHE_VAL(ac_cv_x86_rdseed, [
|
||||
ac_cv_x86_rdseed=no
|
||||
case "$target" in
|
||||
i?86-*-* | \
|
||||
x86_64-*-*)
|
||||
AC_TRY_COMPILE(, [asm("rdseed %eax");],
|
||||
[ac_cv_x86_rdseed=yes], [ac_cv_x86_rdseed=no])
|
||||
esac
|
||||
])
|
||||
if test $ac_cv_x86_rdseed = yes; then
|
||||
AC_DEFINE(_GLIBCXX_X86_RDSEED, 1,
|
||||
[ Defined if as can handle rdseed. ])
|
||||
fi
|
||||
AC_MSG_RESULT($ac_cv_x86_rdseed)
|
||||
])
|
||||
|
||||
dnl
|
||||
dnl Check whether get_nprocs is available in <sys/sysinfo.h>, and define _GLIBCXX_USE_GET_NPROCS.
|
||||
dnl
|
||||
|
|
|
@ -1038,6 +1038,9 @@
|
|||
/* Defined if as can handle rdrand. */
|
||||
#undef _GLIBCXX_X86_RDRAND
|
||||
|
||||
/* Defined if as can handle rdseed. */
|
||||
#undef _GLIBCXX_X86_RDSEED
|
||||
|
||||
/* Define to 1 if mutex_timedlock is available. */
|
||||
#undef _GTHREAD_USE_MUTEX_TIMEDLOCK
|
||||
|
||||
|
|
|
@ -88,4 +88,6 @@
|
|||
// See libstdc++/59807
|
||||
#define _GTHREAD_USE_MUTEX_INIT_FUNC 1
|
||||
|
||||
#define _GLIBCXX_USE_CRT_RAND_S 1
|
||||
|
||||
#endif
|
||||
|
|
41
libstdc++-v3/configure
vendored
41
libstdc++-v3/configure
vendored
|
@ -79870,6 +79870,47 @@ $as_echo "#define _GLIBCXX_X86_RDRAND 1" >>confdefs.h
|
|||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_x86_rdrand" >&5
|
||||
$as_echo "$ac_cv_x86_rdrand" >&6; }
|
||||
|
||||
# Check if assembler supports rdseed opcode.
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rdseed support in assembler" >&5
|
||||
$as_echo_n "checking for rdseed support in assembler... " >&6; }
|
||||
if test "${ac_cv_x86_rdseed+set}" = set; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
ac_cv_x86_rdseed=no
|
||||
case "$target" in
|
||||
i?86-*-* | \
|
||||
x86_64-*-*)
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
asm("rdseed %eax");
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_compile "$LINENO"; then :
|
||||
ac_cv_x86_rdseed=yes
|
||||
else
|
||||
ac_cv_x86_rdseed=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
esac
|
||||
|
||||
fi
|
||||
|
||||
if test $ac_cv_x86_rdseed = yes; then
|
||||
|
||||
$as_echo "#define _GLIBCXX_X86_RDSEED 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_x86_rdseed" >&5
|
||||
$as_echo "$ac_cv_x86_rdseed" >&6; }
|
||||
|
||||
|
||||
# This depends on GLIBCXX_ENABLE_SYMVERS and GLIBCXX_IS_NATIVE.
|
||||
|
||||
|
|
|
@ -417,6 +417,8 @@ GCC_CHECK_ASSEMBLER_HWCAP
|
|||
|
||||
# Check if assembler supports rdrand opcode.
|
||||
GLIBCXX_CHECK_X86_RDRAND
|
||||
# Check if assembler supports rdseed opcode.
|
||||
GLIBCXX_CHECK_X86_RDSEED
|
||||
|
||||
# This depends on GLIBCXX_ENABLE_SYMVERS and GLIBCXX_IS_NATIVE.
|
||||
GLIBCXX_CONFIGURE_TESTSUITE
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
organization. In the USA, this national standards
|
||||
organization is
|
||||
<a class="link" href="https://www.ansi.org" target="_top">ANSI</a>.
|
||||
(And if you've already registered with them you can <a class="link" href="https://webstore.ansi.org/RecordDetail.aspx?sku=ISO%2fIEC+14882%3a2014" target="_top">buy
|
||||
(And if you've already registered with them you can <a class="link" href="https://webstore.ansi.org/Standards/ISO/ISOIEC148822014" target="_top">buy
|
||||
the standard on-line</a>.)
|
||||
</p></li><li class="listitem"><p>
|
||||
The library working group bugs, and known defects, can
|
||||
|
|
|
@ -303,18 +303,38 @@ particular release.
|
|||
<code class="classname">minstd_rand0</code>.
|
||||
</p><p>
|
||||
<span class="emphasis"><em> 26.5.6 [rand.device] </em></span>
|
||||
The default <code class="code">token</code> argument to the
|
||||
<code class="classname">random_device</code> constructor is
|
||||
<code class="literal">"default"</code>. Other valid arguments are
|
||||
<code class="literal">"/dev/random"</code> and <code class="literal">"/dev/urandom"</code>,
|
||||
which determine the character special file to read random bytes from.
|
||||
The <code class="literal">"default"</code> token will read bytes from a hardware
|
||||
RNG if available (currently this only supports the IA-32 RDRAND
|
||||
instruction) otherwise it is equivalent to
|
||||
<code class="literal">"/dev/urandom"</code>.
|
||||
The <code class="code">token</code> parameter of the
|
||||
<code class="classname">random_device</code> constructor can be used to select
|
||||
a specific source of random bytes. The valid token values are shown
|
||||
in the list below.
|
||||
The default constructor uses the token <code class="literal">"default"</code>.
|
||||
</p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="literal">"default"</code></span></dt><dd>
|
||||
Select the first available source from the other entries below.
|
||||
This is the only token that is always valid.
|
||||
</dd><dt><span class="term"><code class="literal">"rand_s"</code></span></dt><dd>
|
||||
Use the MSVCRT <code class="function">rand_s</code> function.
|
||||
This token is only valid for mingw-w64 targets.
|
||||
</dd><dt><span class="term"><code class="literal">"rdseed"</code>, </span><span class="term"><code class="literal">"rdrand"</code> or <code class="literal">"rdrnd"</code></span></dt><dd>
|
||||
Use the IA-32 <code class="literal">RDSEED</code> or <code class="literal">RDRAND</code>
|
||||
instruction to read from an on-chip hardware random number generator.
|
||||
These tokens are only valid for x86 and x86_64 targets when both
|
||||
the assembler and CPU support the corresponding instruction.
|
||||
</dd><dt><span class="term"><code class="literal">"/dev/urandom"</code>, </span><span class="term"><code class="literal">"/dev/random"</code></span></dt><dd>
|
||||
Use the named character special file to read random bytes from.
|
||||
These tokens are only valid when the device files are present
|
||||
and readable by the current user.
|
||||
</dd><dt><span class="term"><code class="literal">"mt19937"</code>, </span><span class="term">seed value</span></dt><dd>
|
||||
When no source of nondeterministic random numbers is available a
|
||||
<code class="classname">mersenne_twister_engine</code> will be used.
|
||||
An integer seed value can be used as the token and will be converted
|
||||
to an <code class="code">unsigned long</code> using <code class="function">strtoul</code>.
|
||||
These tokens are only valid when no other source of random bytes
|
||||
is available.
|
||||
</dd></dl></div><p>
|
||||
An exception of type <code class="classname">runtime_error</code> will be
|
||||
thrown if a <code class="classname">random_device</code> object cannot open
|
||||
or read from the source of random bytes.
|
||||
thrown if a <code class="classname">random_device</code> object is constructed
|
||||
with an invalid token, or if it cannot open or read from the source
|
||||
of random bytes.
|
||||
</p><p>
|
||||
<span class="emphasis"><em>26.5.8.1 [rand.dist.general]</em></span>
|
||||
The algorithms used by the distributions should be documented here.
|
||||
|
|
|
@ -2682,18 +2682,62 @@ particular release.
|
|||
|
||||
<para>
|
||||
<emphasis> 26.5.6 [rand.device] </emphasis>
|
||||
The default <code>token</code> argument to the
|
||||
<classname>random_device</classname> constructor is
|
||||
<literal>"default"</literal>. Other valid arguments are
|
||||
<literal>"/dev/random"</literal> and <literal>"/dev/urandom"</literal>,
|
||||
which determine the character special file to read random bytes from.
|
||||
The <literal>"default"</literal> token will read bytes from a hardware
|
||||
RNG if available (currently this only supports the IA-32 RDRAND
|
||||
instruction) otherwise it is equivalent to
|
||||
<literal>"/dev/urandom"</literal>.
|
||||
The <code>token</code> parameter of the
|
||||
<classname>random_device</classname> constructor can be used to select
|
||||
a specific source of random bytes. The valid token values are shown
|
||||
in the list below.
|
||||
The default constructor uses the token <literal>"default"</literal>.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>"default"</literal></term>
|
||||
<listitem>
|
||||
Select the first available source from the other entries below.
|
||||
This is the only token that is always valid.
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><literal>"rand_s"</literal></term>
|
||||
<listitem>
|
||||
Use the MSVCRT <function>rand_s</function> function.
|
||||
This token is only valid for mingw-w64 targets.
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><literal>"rdseed"</literal></term>
|
||||
<term><literal>"rdrand"</literal> or <literal>"rdrnd"</literal></term>
|
||||
<listitem>
|
||||
Use the IA-32 <literal>RDSEED</literal> or <literal>RDRAND</literal>
|
||||
instruction to read from an on-chip hardware random number generator.
|
||||
These tokens are only valid for x86 and x86_64 targets when both
|
||||
the assembler and CPU support the corresponding instruction.
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><literal>"/dev/urandom"</literal></term>
|
||||
<term><literal>"/dev/random"</literal></term>
|
||||
<listitem>
|
||||
Use the named character special file to read random bytes from.
|
||||
These tokens are only valid when the device files are present
|
||||
and readable by the current user.
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><literal>"mt19937"</literal></term>
|
||||
<term>seed value</term>
|
||||
<listitem>
|
||||
When no source of nondeterministic random numbers is available a
|
||||
<classname>mersenne_twister_engine</classname> will be used.
|
||||
An integer seed value can be used as the token and will be converted
|
||||
to an <code>unsigned long</code> using <function>strtoul</function>.
|
||||
These tokens are only valid when no other source of random bytes
|
||||
is available.
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
An exception of type <classname>runtime_error</classname> will be
|
||||
thrown if a <classname>random_device</classname> object cannot open
|
||||
or read from the source of random bytes.
|
||||
thrown if a <classname>random_device</classname> object is constructed
|
||||
with an invalid token, or if it cannot open or read from the source
|
||||
of random bytes.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
|
|
@ -1602,20 +1602,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
|
||||
// constructors, destructors and member functions
|
||||
|
||||
#ifdef _GLIBCXX_USE_DEV_RANDOM
|
||||
random_device() { _M_init("default"); }
|
||||
|
||||
explicit
|
||||
random_device(const std::string& __token) { _M_init(__token); }
|
||||
|
||||
#if defined _GLIBCXX_USE_DEV_RANDOM
|
||||
~random_device()
|
||||
{ _M_fini(); }
|
||||
#else
|
||||
random_device() { _M_init_pretr1("mt19937"); }
|
||||
|
||||
explicit
|
||||
random_device(const std::string& __token)
|
||||
{ _M_init_pretr1(__token); }
|
||||
#endif
|
||||
|
||||
static constexpr result_type
|
||||
|
@ -1638,13 +1632,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
|
||||
result_type
|
||||
operator()()
|
||||
{
|
||||
#ifdef _GLIBCXX_USE_DEV_RANDOM
|
||||
return this->_M_getval();
|
||||
#else
|
||||
return this->_M_getval_pretr1();
|
||||
#endif
|
||||
}
|
||||
{ return this->_M_getval(); }
|
||||
|
||||
// No copy functions.
|
||||
random_device(const random_device&) = delete;
|
||||
|
@ -1662,7 +1650,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
|
||||
union
|
||||
{
|
||||
void* _M_file;
|
||||
struct
|
||||
{
|
||||
void* _M_file;
|
||||
result_type (*_M_func)(void*);
|
||||
int _M_fd;
|
||||
};
|
||||
mt19937 _M_mt;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -23,19 +23,30 @@
|
|||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#define _GLIBCXX_USE_CXX11_ABI 1
|
||||
#define _CRT_RAND_S // define this before including <stdlib.h> to get rand_s
|
||||
|
||||
#include <random>
|
||||
|
||||
#ifdef _GLIBCXX_USE_C99_STDINT_TR1
|
||||
|
||||
#if defined __i386__ || defined __x86_64__
|
||||
# include <cpuid.h>
|
||||
# ifdef _GLIBCXX_X86_RDRAND
|
||||
# define USE_RDRAND 1
|
||||
# endif
|
||||
# ifdef _GLIBCXX_X86_RDSEED
|
||||
# define USE_RDSEED 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef _GLIBCXX_HAVE_UNISTD_H
|
||||
#if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H
|
||||
# include <unistd.h>
|
||||
# include <fcntl.h>
|
||||
// Use POSIX open, close, read etc. instead of ISO fopen, fclose, fread
|
||||
# define USE_POSIX_FILE_IO
|
||||
#endif
|
||||
|
||||
#ifdef _GLIBCXX_HAVE_SYS_IOCTL_H
|
||||
|
@ -50,105 +61,273 @@
|
|||
# include <linux/random.h>
|
||||
#endif
|
||||
|
||||
#ifdef _GLIBCXX_USE_CRT_RAND_S
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if defined USE_RDRAND || defined USE_RDSEED \
|
||||
|| defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM
|
||||
# pragma GCC poison _M_mt
|
||||
#else
|
||||
// Use the mt19937 member of the union, as in previous GCC releases.
|
||||
# define USE_MT19937 1
|
||||
#endif
|
||||
|
||||
namespace std _GLIBCXX_VISIBILITY(default)
|
||||
{
|
||||
namespace
|
||||
{
|
||||
static unsigned long
|
||||
_M_strtoul(const std::string& __str)
|
||||
{
|
||||
unsigned long __ret = 5489UL;
|
||||
if (__str != "mt19937")
|
||||
{
|
||||
const char* __nptr = __str.c_str();
|
||||
char* __endptr;
|
||||
__ret = std::strtoul(__nptr, &__endptr, 0);
|
||||
if (*__nptr == '\0' || *__endptr != '\0')
|
||||
std::__throw_runtime_error(__N("random_device::_M_strtoul"
|
||||
"(const std::string&)"));
|
||||
}
|
||||
return __ret;
|
||||
}
|
||||
|
||||
#if (defined __i386__ || defined __x86_64__) && defined _GLIBCXX_X86_RDRAND
|
||||
#if USE_RDRAND
|
||||
unsigned int
|
||||
__attribute__ ((target("rdrnd")))
|
||||
__x86_rdrand(void)
|
||||
__x86_rdrand(void*)
|
||||
{
|
||||
unsigned int retries = 100;
|
||||
unsigned int val;
|
||||
|
||||
while (__builtin_ia32_rdrand32_step(&val) == 0)
|
||||
if (--retries == 0)
|
||||
std::__throw_runtime_error(__N("random_device::__x86_rdrand(void)"));
|
||||
std::__throw_runtime_error(__N("random_device: rdrand failed"));
|
||||
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_RDSEED
|
||||
unsigned int
|
||||
__attribute__ ((target("rdseed")))
|
||||
__x86_rdseed(void*)
|
||||
{
|
||||
unsigned int retries = 100;
|
||||
unsigned int val;
|
||||
|
||||
while (__builtin_ia32_rdseed_si_step(&val) == 0)
|
||||
{
|
||||
if (--retries == 0)
|
||||
std::__throw_runtime_error(__N("random_device: rdseed failed"));
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _GLIBCXX_USE_CRT_RAND_S
|
||||
unsigned int
|
||||
__winxp_rand_s(void*)
|
||||
{
|
||||
unsigned int val;
|
||||
if (::rand_s(&val) != 0)
|
||||
std::__throw_runtime_error(__N("random_device: rand_s failed"));
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
random_device::_M_init(const std::string& token)
|
||||
{
|
||||
const char *fname = token.c_str();
|
||||
#ifdef USE_MT19937
|
||||
// If no real random device is supported then use the mt19937 engine.
|
||||
_M_init_pretr1(token);
|
||||
return;
|
||||
#else
|
||||
|
||||
_M_file = nullptr;
|
||||
_M_func = nullptr;
|
||||
_M_fd = -1;
|
||||
|
||||
const char* fname [[gnu::unused]] = nullptr;
|
||||
bool default_token [[gnu::unused]] = false;
|
||||
|
||||
enum { rand_s, rdseed, rdrand, device_file } which;
|
||||
|
||||
if (token == "default")
|
||||
{
|
||||
#if (defined __i386__ || defined __x86_64__) && defined _GLIBCXX_X86_RDRAND
|
||||
default_token = true;
|
||||
fname = "/dev/urandom";
|
||||
#if defined _GLIBCXX_USE_CRT_RAND_S
|
||||
which = rand_s;
|
||||
#elif defined USE_RDSEED
|
||||
which = rdseed;
|
||||
#elif defined USE_RDRAND
|
||||
which = rdrand;
|
||||
#elif defined _GLIBCXX_USE_DEV_RANDOM
|
||||
which = device_file;
|
||||
#else
|
||||
# error "either define USE_MT19937 above or set the default device here"
|
||||
#endif
|
||||
}
|
||||
#ifdef USE_RDSEED
|
||||
else if (token == "rdseed")
|
||||
which = rdseed;
|
||||
#endif // USE_RDSEED
|
||||
#ifdef USE_RDRAND
|
||||
else if (token == "rdrand" || token == "rdrnd")
|
||||
which = rdrand;
|
||||
#endif // USE_RDRAND
|
||||
#ifdef _GLIBCXX_USE_CRT_RAND_S
|
||||
else if (token == "rand_s")
|
||||
which = rand_s;
|
||||
#endif // _GLIBCXX_USE_CRT_RAND_S
|
||||
#ifdef _GLIBCXX_USE_DEV_RANDOM
|
||||
else if (token == "/dev/urandom" || token == "/dev/random")
|
||||
{
|
||||
fname = token.c_str();
|
||||
which = device_file;
|
||||
}
|
||||
#endif // _GLIBCXX_USE_DEV_RANDOM
|
||||
else
|
||||
std::__throw_runtime_error(
|
||||
__N("random_device::random_device(const std::string&):"
|
||||
" unsupported token"));
|
||||
|
||||
switch (which)
|
||||
{
|
||||
#ifdef _GLIBCXX_USE_CRT_RAND_S
|
||||
case rand_s:
|
||||
{
|
||||
_M_func = &__winxp_rand_s;
|
||||
return;
|
||||
}
|
||||
#endif // _GLIBCXX_USE_CRT_RAND_S
|
||||
#ifdef USE_RDSEED
|
||||
case rdseed:
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
// Check availability of cpuid and, for now at least, also the
|
||||
// CPU signature for Intel's
|
||||
if (__get_cpuid_max(0, &ebx) > 0 && ebx == signature_INTEL_ebx)
|
||||
// CPU signature for Intel and AMD.
|
||||
if (__get_cpuid_max(0, &ebx) > 0
|
||||
&& (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx))
|
||||
{
|
||||
__cpuid(1, eax, ebx, ecx, edx);
|
||||
if (ecx & bit_RDRND)
|
||||
// CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18]
|
||||
__cpuid_count(7, 0, eax, ebx, ecx, edx);
|
||||
if (ebx & bit_RDSEED)
|
||||
{
|
||||
_M_file = nullptr;
|
||||
_M_func = &__x86_rdseed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
fname = "/dev/urandom";
|
||||
// If rdseed was explicitly requested then we're done here.
|
||||
if (!default_token)
|
||||
break;
|
||||
// Otherwise fall through to try the next available option.
|
||||
[[gnu::fallthrough]];
|
||||
}
|
||||
else if (token != "/dev/urandom" && token != "/dev/random")
|
||||
fail:
|
||||
std::__throw_runtime_error(__N("random_device::"
|
||||
"random_device(const std::string&)"));
|
||||
|
||||
_M_file = static_cast<void*>(std::fopen(fname, "rb"));
|
||||
if (!_M_file)
|
||||
goto fail;
|
||||
#endif // USE_RDSEED
|
||||
#ifdef USE_RDRAND
|
||||
case rdrand:
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
// Check availability of cpuid and, for now at least, also the
|
||||
// CPU signature for Intel and AMD.
|
||||
if (__get_cpuid_max(0, &ebx) > 0
|
||||
&& (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx))
|
||||
{
|
||||
// CPUID.01H:ECX.RDRAND[bit 30]
|
||||
__cpuid(1, eax, ebx, ecx, edx);
|
||||
if (ecx & bit_RDRND)
|
||||
{
|
||||
_M_func = &__x86_rdrand;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If rdrand was explicitly requested then we're done here.
|
||||
if (!default_token)
|
||||
break;
|
||||
// Otherwise fall through to try the next available option.
|
||||
[[gnu::fallthrough]];
|
||||
}
|
||||
#endif // USE_RDRAND
|
||||
#ifdef _GLIBCXX_USE_DEV_RANDOM
|
||||
case device_file:
|
||||
{
|
||||
#ifdef USE_POSIX_FILE_IO
|
||||
_M_fd = ::open(fname, O_RDONLY);
|
||||
if (_M_fd != -1)
|
||||
{
|
||||
// Set _M_file to non-null so that _M_fini() will do clean up.
|
||||
_M_file = &_M_fd;
|
||||
return;
|
||||
}
|
||||
#else // USE_POSIX_FILE_IO
|
||||
_M_file = static_cast<void*>(std::fopen(fname, "rb"));
|
||||
if (_M_file)
|
||||
return;
|
||||
#endif // USE_POSIX_FILE_IO
|
||||
[[gnu::fallthrough]];
|
||||
}
|
||||
#endif // _GLIBCXX_USE_DEV_RANDOM
|
||||
default:
|
||||
{ }
|
||||
}
|
||||
std::__throw_runtime_error(
|
||||
__N("random_device::random_device(const std::string&):"
|
||||
" device not available"));
|
||||
#endif // USE_MT19937
|
||||
}
|
||||
|
||||
// This function is called by _M_init for targets that use mt19937 for
|
||||
// randomness, and by code compiled against old releases of libstdc++.
|
||||
void
|
||||
random_device::_M_init_pretr1(const std::string& token)
|
||||
{
|
||||
_M_mt.seed(_M_strtoul(token));
|
||||
#ifdef USE_MT19937
|
||||
unsigned long seed = 5489UL;
|
||||
if (token != "default" && token != "mt19937")
|
||||
{
|
||||
const char* nptr = token.c_str();
|
||||
char* endptr;
|
||||
seed = std::strtoul(nptr, &endptr, 0);
|
||||
if (*nptr == '\0' || *endptr != '\0')
|
||||
std::__throw_runtime_error(__N("random_device::_M_init_pretr1"
|
||||
"(const std::string&)"));
|
||||
}
|
||||
_M_mt.seed(seed);
|
||||
#else
|
||||
// Convert old default token "mt19937" or numeric seed tokens to "default".
|
||||
if (token == "mt19937" || isdigit((unsigned char)token[0]))
|
||||
_M_init("default");
|
||||
else
|
||||
_M_init(token);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
random_device::_M_fini()
|
||||
{
|
||||
if (_M_file)
|
||||
std::fclose(static_cast<FILE*>(_M_file));
|
||||
// _M_file == nullptr means no resources to free.
|
||||
if (!_M_file)
|
||||
return;
|
||||
|
||||
#ifdef USE_POSIX_FILE_IO
|
||||
::close(_M_fd);
|
||||
_M_fd = -1;
|
||||
#else
|
||||
std::fclose(static_cast<FILE*>(_M_file));
|
||||
#endif
|
||||
_M_file = nullptr;
|
||||
}
|
||||
|
||||
random_device::result_type
|
||||
random_device::_M_getval()
|
||||
{
|
||||
#if (defined __i386__ || defined __x86_64__) && defined _GLIBCXX_X86_RDRAND
|
||||
if (!_M_file)
|
||||
return __x86_rdrand();
|
||||
#ifdef USE_MT19937
|
||||
return _M_mt();
|
||||
#else
|
||||
|
||||
#if defined USE_RDRAND || defined USE_RDSEED || defined _GLIBCXX_USE_CRT_RAND_S
|
||||
if (_M_func)
|
||||
return _M_func(nullptr);
|
||||
#endif
|
||||
|
||||
result_type __ret;
|
||||
void* p = &__ret;
|
||||
result_type ret;
|
||||
void* p = &ret;
|
||||
size_t n = sizeof(result_type);
|
||||
#ifdef _GLIBCXX_HAVE_UNISTD_H
|
||||
#ifdef USE_POSIX_FILE_IO
|
||||
do
|
||||
{
|
||||
const int e = read(fileno(static_cast<FILE*>(_M_file)), p, n);
|
||||
const int e = ::read(_M_fd, p, n);
|
||||
if (e > 0)
|
||||
{
|
||||
n -= e;
|
||||
|
@ -158,34 +337,40 @@ namespace std _GLIBCXX_VISIBILITY(default)
|
|||
__throw_runtime_error(__N("random_device could not be read"));
|
||||
}
|
||||
while (n > 0);
|
||||
#else
|
||||
#else // USE_POSIX_FILE_IO
|
||||
const size_t e = std::fread(p, n, 1, static_cast<FILE*>(_M_file));
|
||||
if (e != 1)
|
||||
__throw_runtime_error(__N("random_device could not be read"));
|
||||
#endif
|
||||
#endif // USE_POSIX_FILE_IO
|
||||
|
||||
return __ret;
|
||||
return ret;
|
||||
#endif // USE_MT19937
|
||||
}
|
||||
|
||||
// Only called by code compiled against old releases of libstdc++.
|
||||
// Forward the call to _M_getval() and let it decide what to do.
|
||||
random_device::result_type
|
||||
random_device::_M_getval_pretr1()
|
||||
{
|
||||
return _M_mt();
|
||||
}
|
||||
{ return _M_getval(); }
|
||||
|
||||
double
|
||||
random_device::_M_getentropy() const noexcept
|
||||
{
|
||||
#if defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT
|
||||
#if defined _GLIBCXX_USE_DEV_RANDOM \
|
||||
&& defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT
|
||||
if (!_M_file)
|
||||
return 0.0;
|
||||
|
||||
const int fd = fileno(static_cast<FILE*>(_M_file));
|
||||
#ifdef USE_POSIX_FILE_IO
|
||||
const int fd = _M_fd;
|
||||
#else
|
||||
const int fd = ::fileno(static_cast<FILE*>(_M_file));
|
||||
#endif
|
||||
if (fd < 0)
|
||||
return 0.0;
|
||||
|
||||
int ent;
|
||||
if (ioctl(fd, RNDGETENTCNT, &ent) < 0)
|
||||
if (::ioctl(fd, RNDGETENTCNT, &ent) < 0)
|
||||
return 0.0;
|
||||
|
||||
if (ent < 0)
|
||||
|
@ -198,9 +383,10 @@ namespace std _GLIBCXX_VISIBILITY(default)
|
|||
return static_cast<double>(ent);
|
||||
#else
|
||||
return 0.0;
|
||||
#endif
|
||||
#endif // _GLIBCXX_USE_DEV_RANDOM && _GLIBCXX_HAVE_SYS_IOCTL_H && RNDGETENTCNT
|
||||
}
|
||||
|
||||
#ifdef USE_MT19937
|
||||
template class mersenne_twister_engine<
|
||||
uint_fast32_t,
|
||||
32, 624, 397, 31,
|
||||
|
@ -208,5 +394,6 @@ namespace std _GLIBCXX_VISIBILITY(default)
|
|||
0xffffffffUL, 7,
|
||||
0x9d2c5680UL, 15,
|
||||
0xefc60000UL, 18, 1812433253UL>;
|
||||
#endif // USE_MT19937
|
||||
}
|
||||
#endif
|
||||
#endif // _GLIBCXX_USE_C99_STDINT_TR1
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (C) 2019 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.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-do run { target c++11 } }
|
||||
// { dg-require-effective-target random_device }
|
||||
|
||||
#include <random>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
unsigned v1[3], v2[3];
|
||||
std::random_device d1, d2;
|
||||
for (auto& v : v1)
|
||||
v = d1();
|
||||
for (auto& v : v2)
|
||||
v = d2();
|
||||
VERIFY (v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] );
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test01();
|
||||
}
|
Loading…
Add table
Reference in a new issue