Skip to content

Commit 39faf42

Browse files
committed
[libc++] Ensure streams are initialized early
When statically linking libc++ on some systems, the streams are not initialized early enough, which causes all kinds of issues. This was reported e.g. in http://llvm.org/PR28954, but also in various open source projects that use libc++. Fixes http://llvm.org/PR28954. Differential Revision: https://reviews.llvm.org/D31413
1 parent 738c73a commit 39faf42

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

libcxx/src/iostream.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ __asm__("?wclog@" _LIBCPP_ABI_NAMESPACE_STR "@std@@3V?$basic_ostream@_WU?$char_t
7777
#endif
7878
;
7979

80-
_LIBCPP_HIDDEN ios_base::Init __start_std_streams;
80+
_LIBCPP_HIDDEN ios_base::Init __start_std_streams __attribute__((init_priority(101)));
8181

8282
// On Windows the TLS storage for locales needs to be initialized before we create
8383
// the standard streams, otherwise it may not be alive during program termination
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: libcpp-has-no-stdin, libcpp-has-no-stdout
10+
11+
// Make sure that the iostreams are initialized before everything else.
12+
// This has been an issue when statically linking libc++ in some contexts.
13+
// See https://llvm.org/PR28954 for details.
14+
//
15+
// This test works by checking that std::{cin,cout,cerr} is the same in a
16+
// static object constructor and in the main function. It dumps the memory of
17+
// each stream in the static object constructor and compares it with the memory
18+
// in the main function.
19+
//
20+
// The assumption is that if there are no uses of the stream object (such as
21+
// construction), then its memory must be the same. In the case where the test
22+
// "fails" and we are actually accessing an uninitialized object when we perform
23+
// the memcpy, the behavior is technically undefined (so the test could still
24+
// pass).
25+
26+
#include <cassert>
27+
#include <cstring>
28+
#include <iostream>
29+
30+
struct Checker {
31+
char *cerr_mem_dump;
32+
char *cin_mem_dump;
33+
char *cout_mem_dump;
34+
char *clog_mem_dump;
35+
36+
char *wcerr_mem_dump;
37+
char *wcin_mem_dump;
38+
char *wcout_mem_dump;
39+
char *wclog_mem_dump;
40+
41+
Checker()
42+
: cerr_mem_dump(new char[sizeof(std::cerr)])
43+
, cin_mem_dump(new char[sizeof(std::cin)])
44+
, cout_mem_dump(new char[sizeof(std::cout)])
45+
, clog_mem_dump(new char[sizeof(std::clog)])
46+
47+
, wcerr_mem_dump(new char[sizeof(std::wcerr)])
48+
, wcin_mem_dump(new char[sizeof(std::wcin)])
49+
, wcout_mem_dump(new char[sizeof(std::wcout)])
50+
, wclog_mem_dump(new char[sizeof(std::wclog)])
51+
{
52+
std::memcpy(cerr_mem_dump, (char*)&std::cerr, sizeof(std::cerr));
53+
std::memcpy(cin_mem_dump, (char*)&std::cin, sizeof(std::cin));
54+
std::memcpy(cout_mem_dump, (char*)&std::cout, sizeof(std::cout));
55+
std::memcpy(clog_mem_dump, (char*)&std::clog, sizeof(std::clog));
56+
57+
std::memcpy(wcerr_mem_dump, (char*)&std::wcerr, sizeof(std::wcerr));
58+
std::memcpy(wcin_mem_dump, (char*)&std::wcin, sizeof(std::wcin));
59+
std::memcpy(wcout_mem_dump, (char*)&std::wcout, sizeof(std::wcout));
60+
std::memcpy(wclog_mem_dump, (char*)&std::wclog, sizeof(std::wclog));
61+
}
62+
63+
~Checker() {
64+
delete[] cerr_mem_dump;
65+
delete[] cin_mem_dump;
66+
delete[] cout_mem_dump;
67+
delete[] clog_mem_dump;
68+
69+
delete[] wcerr_mem_dump;
70+
delete[] wcin_mem_dump;
71+
delete[] wcout_mem_dump;
72+
delete[] wclog_mem_dump;
73+
}
74+
};
75+
76+
static Checker check;
77+
78+
int main() {
79+
assert(std::memcmp(check.cerr_mem_dump, (char const*)&std::cerr, sizeof(std::cerr)) == 0);
80+
assert(std::memcmp(check.cin_mem_dump, (char const*)&std::cin, sizeof(std::cin)) == 0);
81+
assert(std::memcmp(check.cout_mem_dump, (char const*)&std::cout, sizeof(std::cout)) == 0);
82+
assert(std::memcmp(check.clog_mem_dump, (char const*)&std::clog, sizeof(std::clog)) == 0);
83+
84+
assert(std::memcmp(check.wcerr_mem_dump, (char const*)&std::wcerr, sizeof(std::wcerr)) == 0);
85+
assert(std::memcmp(check.wcin_mem_dump, (char const*)&std::wcin, sizeof(std::wcin)) == 0);
86+
assert(std::memcmp(check.wcout_mem_dump, (char const*)&std::wcout, sizeof(std::wcout)) == 0);
87+
assert(std::memcmp(check.wclog_mem_dump, (char const*)&std::wclog, sizeof(std::wclog)) == 0);
88+
}

0 commit comments

Comments
 (0)