[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
libcxx

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
debug mode when compiling a program against libc++, it will fail to link
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)
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.")
@ -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_NO_VCRUNTIME _LIBCPP_NO_VCRUNTIME)
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)
set(abi_defines)

View File

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

View File

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

View File

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

View File

@ -27,7 +27,6 @@ set(LIBCXX_SOURCES
mutex_destructor.cpp
new.cpp
optional.cpp
random.cpp
random_shuffle.cpp
regex.cpp
shared_mutex.cpp
@ -61,6 +60,12 @@ if (LIBCXX_ENABLE_DEBUG_MODE_SUPPORT)
)
endif()
if (LIBCXX_ENABLE_RANDOM_DEVICE)
list(APPEND LIBCXX_SOURCES
random.cpp
)
endif()
if(WIN32)
list(APPEND LIBCXX_SOURCES
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 <cassert>
#include <chrono>
#include <fstream>
#include <string>
@ -31,14 +30,6 @@ using namespace fs;
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
// to ensure that libc++ correctly copies files larger than that limit.
// However it requires allocating ~5GB of filesystem space. This might not
@ -58,12 +49,10 @@ TEST_CASE(large_file) {
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);
// Create some random data that looks different than the data before the
// size limit.
const std::string additional_data = random_hex_chars(additional_size);
// Append this known data to the end of the source file.
const std::string additional_data(additional_size, 'x');
// Append known data to the end of the source file.
{
std::ofstream outf(file.native(), std::ios_base::app);
TEST_REQUIRE(outf.good());

View File

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

View File

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

View File

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

View File

@ -3,13 +3,12 @@
#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 <cassert>
#include <cstdio> // for printf
#include <string>
#include <random>
#include <chrono>
#include <vector>
#include <regex>
@ -24,23 +23,25 @@
# include <sys/un.h>
#endif
namespace random_utils {
inline char to_hex(int ch) {
return ch < 10 ? static_cast<char>('0' + ch)
: static_cast<char>('a' + (ch - 10));
}
namespace utils {
inline std::string getcwd() {
// Assume that path lengths are not greater than this.
// 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() {
static std::mt19937 rd{std::random_device{}()};
static std::uniform_int_distribution<int> mrand{0, 15};
return to_hex(mrand(rd));
}
} // namespace random_utils
inline bool exists(std::string const& path) {
struct ::stat tmp;
return ::stat(path.c_str(), &tmp) == 0;
}
} // end namespace utils
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();
int ret = std::system(cmd.c_str());
assert(ret == 0);
@ -174,20 +175,20 @@ struct scoped_test_env
fs::path test_root;
private:
static std::string unique_path_suffix() {
std::string model = "test.%%%%%%";
for (auto & ch : model) {
if (ch == '%')
ch = random_utils::random_hex_char();
// This could potentially introduce a filesystem race if multiple
// scoped_test_envs were created concurrently in the same test (hence
// sharing the same cwd). However, it is fairly unlikely to happen as
// we generally don't use scoped_test_env from multiple threads, so
// 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;
}
};
@ -301,15 +302,10 @@ public:
};
struct CWDGuard {
// Assume that path lengths are not greater than this.
// This should be fine for testing purposes.
char OldCWD[4096];
CWDGuard() {
char* ret = ::getcwd(OldCWD, sizeof(OldCWD));
assert(ret && "getcwd failed");
}
std::string oldCwd_;
CWDGuard() : oldCwd_(utils::getcwd()) { }
~CWDGuard() {
int ret = ::chdir(OldCWD);
int ret = ::chdir(oldCwd_.c_str());
assert(ret == 0 && "chdir failed");
}

View File

@ -80,6 +80,11 @@ steps:
agents:
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"
command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh generic-cxx2a | libcxx/utils/ci/phabricator-report"
agents:

View File

@ -109,6 +109,12 @@ generic-nodebug)
args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported")
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)
export CC=clang
export CXX=clang++

View File

@ -73,7 +73,8 @@ macros = {
'_LIBCPP_HAS_THREAD_API_PTHREAD': 'libcpp-has-thread-api-pthread',
'_LIBCPP_NO_VCRUNTIME': 'libcpp-no-vcruntime',
'_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():
DEFAULT_FEATURES += [