[libc++] Allow building libc++ on platforms without a random device

Some platforms, like several embedded platforms, do not provide a source
of randomness through a random device. This commit makes it possible to
build and test libc++ for such platforms, i.e. without std::random_device.

Surprisingly, the only functionality that doesn't work on such platforms
is std::random_device itself -- everything else in <random> still works,
one just has to find alternative ways to seed the PRNGs.
This commit is contained in:
Louis Dionne 2020-10-15 10:32:09 -04:00
parent e9df9028a7
commit e0d01294bc
14 changed files with 92 additions and 52 deletions

View File

@ -109,6 +109,12 @@ option(LIBCXX_ENABLE_DEBUG_MODE_SUPPORT
By default, this is turned on. If you turn it off and try to enable the By default, this is turned on. If you turn it off and try to enable the
debug mode when compiling a program against libc++, it will fail to link debug mode when compiling a program against libc++, it will fail to link
since the required support isn't provided in the library." ON) since the required support isn't provided in the library." ON)
option(LIBCXX_ENABLE_RANDOM_DEVICE
"Whether to include support for std::random_device in the library. Disabling
this can be useful when building the library for platforms that don't have
a source of randomness, such as some embedded platforms. When this is not
supported, most of <random> will still be available, but std::random_device
will not." ON)
option(LIBCXX_TEST_GDB_PRETTY_PRINTERS "Test gdb pretty printers." OFF) option(LIBCXX_TEST_GDB_PRETTY_PRINTERS "Test gdb pretty printers." OFF)
set(LIBCXX_TEST_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/test/configs/legacy.cfg.in" CACHE STRING set(LIBCXX_TEST_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/test/configs/legacy.cfg.in" CACHE STRING
"The Lit testing configuration to use when running the tests.") "The Lit testing configuration to use when running the tests.")
@ -836,6 +842,7 @@ config_define_if(LIBCXX_BUILD_EXTERNAL_THREAD_LIBRARY _LIBCPP_HAS_THREAD_LIBRARY
config_define_if(LIBCXX_HAS_MUSL_LIBC _LIBCPP_HAS_MUSL_LIBC) config_define_if(LIBCXX_HAS_MUSL_LIBC _LIBCPP_HAS_MUSL_LIBC)
config_define_if(LIBCXX_NO_VCRUNTIME _LIBCPP_NO_VCRUNTIME) config_define_if(LIBCXX_NO_VCRUNTIME _LIBCPP_NO_VCRUNTIME)
config_define_if(LIBCXX_ENABLE_PARALLEL_ALGORITHMS _LIBCPP_HAS_PARALLEL_ALGORITHMS) config_define_if(LIBCXX_ENABLE_PARALLEL_ALGORITHMS _LIBCPP_HAS_PARALLEL_ALGORITHMS)
config_define_if_not(LIBCXX_ENABLE_RANDOM_DEVICE _LIBCPP_HAS_NO_RANDOM_DEVICE)
if (LIBCXX_ABI_DEFINES) if (LIBCXX_ABI_DEFINES)
set(abi_defines) set(abi_defines)

View File

@ -0,0 +1 @@
set(LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")

View File

@ -32,6 +32,7 @@
#endif #endif
#cmakedefine _LIBCPP_ABI_NAMESPACE @_LIBCPP_ABI_NAMESPACE@ #cmakedefine _LIBCPP_ABI_NAMESPACE @_LIBCPP_ABI_NAMESPACE@
#cmakedefine _LIBCPP_HAS_PARALLEL_ALGORITHMS #cmakedefine _LIBCPP_HAS_PARALLEL_ALGORITHMS
#cmakedefine _LIBCPP_HAS_NO_RANDOM_DEVICE
@_LIBCPP_ABI_DEFINES@ @_LIBCPP_ABI_DEFINES@

View File

@ -3477,6 +3477,8 @@ typedef shuffle_order_engine<minstd_rand0, 256> knuth_b;
// random_device // random_device
#if !defined(_LIBCPP_HAS_NO_RANDOM_DEVICE)
class _LIBCPP_TYPE_VIS random_device class _LIBCPP_TYPE_VIS random_device
{ {
#ifdef _LIBCPP_USING_DEV_RANDOM #ifdef _LIBCPP_USING_DEV_RANDOM
@ -3511,6 +3513,8 @@ private:
random_device& operator=(const random_device&); // = delete; random_device& operator=(const random_device&); // = delete;
}; };
#endif // !_LIBCPP_HAS_NO_RANDOM_DEVICE
// seed_seq // seed_seq
class _LIBCPP_TEMPLATE_VIS seed_seq class _LIBCPP_TEMPLATE_VIS seed_seq

View File

@ -27,7 +27,6 @@ set(LIBCXX_SOURCES
mutex_destructor.cpp mutex_destructor.cpp
new.cpp new.cpp
optional.cpp optional.cpp
random.cpp
random_shuffle.cpp random_shuffle.cpp
regex.cpp regex.cpp
shared_mutex.cpp shared_mutex.cpp
@ -61,6 +60,12 @@ if (LIBCXX_ENABLE_DEBUG_MODE_SUPPORT)
) )
endif() endif()
if (LIBCXX_ENABLE_RANDOM_DEVICE)
list(APPEND LIBCXX_SOURCES
random.cpp
)
endif()
if(WIN32) if(WIN32)
list(APPEND LIBCXX_SOURCES list(APPEND LIBCXX_SOURCES
support/win32/locale_win32.cpp support/win32/locale_win32.cpp

View File

@ -0,0 +1,19 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// Make sure that std::random_device is not available in namespace std:: when
// libc++ is built without support for random device.
// REQUIRES: libcpp-has-no-random-device
#include <random>
int main(int, char**) {
std::random_device d; // expected-error {{no type named 'random_device' in namespace 'std'}}
return 0;
}

View File

@ -19,7 +19,6 @@
#include "filesystem_include.h" #include "filesystem_include.h"
#include <cassert> #include <cassert>
#include <chrono>
#include <fstream> #include <fstream>
#include <string> #include <string>
@ -31,14 +30,6 @@ using namespace fs;
TEST_SUITE(filesystem_copy_file_test_suite) TEST_SUITE(filesystem_copy_file_test_suite)
static std::string random_hex_chars(uintmax_t size) {
std::string data;
data.reserve(size);
for (uintmax_t I = 0; I < size; ++I)
data.push_back(random_utils::random_hex_char());
return data;
}
// This test is intended to test 'sendfile's 2gb limit for a single call, and // This test is intended to test 'sendfile's 2gb limit for a single call, and
// to ensure that libc++ correctly copies files larger than that limit. // to ensure that libc++ correctly copies files larger than that limit.
// However it requires allocating ~5GB of filesystem space. This might not // However it requires allocating ~5GB of filesystem space. This might not
@ -58,12 +49,10 @@ TEST_CASE(large_file) {
TEST_UNSUPPORTED(); TEST_UNSUPPORTED();
} }
// Use python to create a file right at the size limit. // Create a file right at the size limit. The file is full of '\0's.
const path file = env.create_file("source", sendfile_size_limit); const path file = env.create_file("source", sendfile_size_limit);
// Create some random data that looks different than the data before the const std::string additional_data(additional_size, 'x');
// size limit. // Append known data to the end of the source file.
const std::string additional_data = random_hex_chars(additional_size);
// Append this known data to the end of the source file.
{ {
std::ofstream outf(file.native(), std::ios_base::app); std::ofstream outf(file.native(), std::ios_base::app);
TEST_REQUIRE(outf.good()); TEST_REQUIRE(outf.good());

View File

@ -12,6 +12,8 @@
// XFAIL: with_system_cxx_lib=macosx10.10 // XFAIL: with_system_cxx_lib=macosx10.10
// XFAIL: with_system_cxx_lib=macosx10.9 // XFAIL: with_system_cxx_lib=macosx10.9
// UNSUPPORTED: libcpp-has-no-random-device
// <random> // <random>
// class random_device; // class random_device;

View File

@ -6,6 +6,8 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// UNSUPPORTED: libcpp-has-no-random-device
// <random> // <random>
// class random_device; // class random_device;

View File

@ -12,6 +12,8 @@
// XFAIL: with_system_cxx_lib=macosx10.10 // XFAIL: with_system_cxx_lib=macosx10.10
// XFAIL: with_system_cxx_lib=macosx10.9 // XFAIL: with_system_cxx_lib=macosx10.9
// UNSUPPORTED: libcpp-has-no-random-device
// <random> // <random>
// class random_device; // class random_device;

View File

@ -3,13 +3,12 @@
#include "filesystem_include.h" #include "filesystem_include.h"
#include <sys/stat.h> // for mkdir, mkfifo #include <sys/stat.h> // for stat, mkdir, mkfifo
#include <unistd.h> // for ftruncate, link, symlink, getcwd, chdir #include <unistd.h> // for ftruncate, link, symlink, getcwd, chdir
#include <cassert> #include <cassert>
#include <cstdio> // for printf #include <cstdio> // for printf
#include <string> #include <string>
#include <random>
#include <chrono> #include <chrono>
#include <vector> #include <vector>
#include <regex> #include <regex>
@ -24,23 +23,25 @@
# include <sys/un.h> # include <sys/un.h>
#endif #endif
namespace random_utils { namespace utils {
inline char to_hex(int ch) { inline std::string getcwd() {
return ch < 10 ? static_cast<char>('0' + ch) // Assume that path lengths are not greater than this.
: static_cast<char>('a' + (ch - 10)); // This should be fine for testing purposes.
} char buf[4096];
char* ret = ::getcwd(buf, sizeof(buf));
assert(ret && "getcwd failed");
return std::string(ret);
}
inline char random_hex_char() { inline bool exists(std::string const& path) {
static std::mt19937 rd{std::random_device{}()}; struct ::stat tmp;
static std::uniform_int_distribution<int> mrand{0, 15}; return ::stat(path.c_str(), &tmp) == 0;
return to_hex(mrand(rd)); }
} } // end namespace utils
} // namespace random_utils
struct scoped_test_env struct scoped_test_env
{ {
scoped_test_env() : test_root(random_path()) { scoped_test_env() : test_root(available_cwd_path()) {
std::string cmd = "mkdir -p " + test_root.native(); std::string cmd = "mkdir -p " + test_root.native();
int ret = std::system(cmd.c_str()); int ret = std::system(cmd.c_str());
assert(ret == 0); assert(ret == 0);
@ -174,20 +175,20 @@ struct scoped_test_env
fs::path test_root; fs::path test_root;
private: private:
static std::string unique_path_suffix() { // This could potentially introduce a filesystem race if multiple
std::string model = "test.%%%%%%"; // scoped_test_envs were created concurrently in the same test (hence
for (auto & ch : model) { // sharing the same cwd). However, it is fairly unlikely to happen as
if (ch == '%') // we generally don't use scoped_test_env from multiple threads, so
ch = random_utils::random_hex_char(); // this is deemed acceptable.
static inline fs::path available_cwd_path() {
fs::path const cwd = utils::getcwd();
fs::path const tmp = fs::temp_directory_path();
fs::path const base = tmp / cwd.filename();
int i = 0;
fs::path p = base / ("static_env." + std::to_string(i));
while (utils::exists(p)) {
p = fs::path(base) / ("static_env." + std::to_string(++i));
} }
return model;
}
// This could potentially introduce a filesystem race with other tests
// running at the same time, but oh well, it's just test code.
static inline fs::path random_path() {
fs::path tmp = fs::temp_directory_path();
fs::path p = fs::path(tmp) / unique_path_suffix();
return p; return p;
} }
}; };
@ -301,15 +302,10 @@ public:
}; };
struct CWDGuard { struct CWDGuard {
// Assume that path lengths are not greater than this. std::string oldCwd_;
// This should be fine for testing purposes. CWDGuard() : oldCwd_(utils::getcwd()) { }
char OldCWD[4096];
CWDGuard() {
char* ret = ::getcwd(OldCWD, sizeof(OldCWD));
assert(ret && "getcwd failed");
}
~CWDGuard() { ~CWDGuard() {
int ret = ::chdir(OldCWD); int ret = ::chdir(oldCwd_.c_str());
assert(ret == 0 && "chdir failed"); assert(ret == 0 && "chdir failed");
} }

View File

@ -80,6 +80,11 @@ steps:
agents: agents:
queue: "libcxx-builders" queue: "libcxx-builders"
- label: "No random device"
command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh generic-no-random_device | libcxx/utils/ci/phabricator-report"
agents:
queue: "libcxx-builders"
- label: "MacOS C++20" - label: "MacOS C++20"
command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh generic-cxx2a | libcxx/utils/ci/phabricator-report" command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh generic-cxx2a | libcxx/utils/ci/phabricator-report"
agents: agents:

View File

@ -109,6 +109,12 @@ generic-nodebug)
args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported")
args+=("-C${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-nodebug.cmake") args+=("-C${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-nodebug.cmake")
;; ;;
generic-no-random_device)
export CC=clang
export CXX=clang++
args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported")
args+=("-C${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-random_device.cmake")
;;
x86_64-apple-system) x86_64-apple-system)
export CC=clang export CC=clang
export CXX=clang++ export CXX=clang++

View File

@ -73,7 +73,8 @@ macros = {
'_LIBCPP_HAS_THREAD_API_PTHREAD': 'libcpp-has-thread-api-pthread', '_LIBCPP_HAS_THREAD_API_PTHREAD': 'libcpp-has-thread-api-pthread',
'_LIBCPP_NO_VCRUNTIME': 'libcpp-no-vcruntime', '_LIBCPP_NO_VCRUNTIME': 'libcpp-no-vcruntime',
'_LIBCPP_ABI_VERSION': 'libcpp-abi-version', '_LIBCPP_ABI_VERSION': 'libcpp-abi-version',
'_LIBCPP_ABI_UNSTABLE': 'libcpp-abi-unstable' '_LIBCPP_ABI_UNSTABLE': 'libcpp-abi-unstable',
'_LIBCPP_HAS_NO_RANDOM_DEVICE': 'libcpp-has-no-random-device',
} }
for macro, feature in macros.items(): for macro, feature in macros.items():
DEFAULT_FEATURES += [ DEFAULT_FEATURES += [