<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/185724>185724</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[libc++] 608012ace43b breaks ABI for cin, cout and friends
</td>
</tr>
<tr>
<th>Labels</th>
<td>
libc++,
ABI,
regression:21
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
DimitryAndric
</td>
</tr>
</table>
<pre>
The simplification of `iostream.cpp` in `llvmorg-21-init-111-g`608012ace43b (#124103, by @philnik777, cc @ldionne) changes the sizes of `cin`, `cout`, and friends, which breaks the ABI. Because of copy relocations, those sizes can be emitted into executables (typically when not compiled in PIE mode). Those executables, compiled against libc++ 20, will no longer run properly against libc++ 21, usually throwing an exception, but other strange behavior is also possible.
Case in point is `helloworld.cpp`:
```c++
#include <iostream>
int main(void)
{
std::cout << "Hello World!\n";
return 0;
}
```
When compiled against libc++ 20 (or libc++ just before `llvmorg-21-init-111-g`608012ace43b) on a recent FreeBSD system, where PIE mode is _not_ the default, `cout` and `ctype<char>::id` will get copy relocations in the executable:
```text
Relocation section '.rela.dyn' at offset 0x9b8 contains 2 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000205230 0000001400000005 R_X86_64_COPY 0000000000205230 std::__1::cout + 0
00000000002052e0 0000001500000005 R_X86_64_COPY 00000000002052e0 std::__1::ctype<char>::id + 0
```
The symbols themselves end up in the `.dynsym` section:
```text
Symbol table '.dynsym' contains 23 entries:
Num: Value Size Type Bind Vis Ndx Name
...
20: 0000000000205230 160 OBJECT GLOBAL DEFAULT 25 std::__1::cout
21: 00000000002052e0 16 OBJECT GLOBAL DEFAULT 25 std::__1::ctype<char>::id
```
Note that `cout` is 160 bytes, while `ctype<char>::id` is 16 bytes.
Compiling the same program against libc++ 21 (or libc++ just before `llvmorg-21-init-111-g`608012ace43b) will show similar copy relocations in the executable, but for the symbols themselves:
```text
Symbol table '.dynsym' contains 23 entries:
Num: Value Size Type Bind Vis Ndx Name
...
20: 0000000000205020 384 OBJECT GLOBAL DEFAULT 25 std::__1::cout
21: 00000000002051a0 16 OBJECT GLOBAL DEFAULT 25 std::__1::ctype<char>::id
```
Note how `cout` has grown to 384 bytes.
When you run the `helloworld-against-libc++20` binary against libc++ 21 (after 608012ace43b), the latter's `iostream.cpp` will scribble its 384 bytes of data over the 160 bytes allocated for `cout`, typically zeroing `ctype<char>::id`'s state.
When that happens, the next time a locale attempts to `__get` the `id` for the `ctype<char>` fact, it will get a new id, typically 30, which is over the limit of the locale's `facets_` vector. Whenever this facet is used with `use_facet`, a `bad_cast` exception will be thrown:
```text
#0 __cxa_throw (thrown_exception=0x800a08080, tinfo=0x8003e4280 <typeinfo for std::bad_cast>, dest=0x8003e24a0 <std::exception::~exception()>) at /usr/src/contrib/libcxxrt/exception.cc:827
#1 0x00000008003502b5 in std::__1::__throw_bad_cast[abi:se210108]() () at /share/dim/src/freebsd/llvm-21-update/contrib/llvm-project/libcxx/include/typeinfo:382
#2 0x000000080037893a in std::__1::locale::__imp::use_facet (this=0x8003c7a68 <std::__1::locale::__imp::classic_locale_imp_>, id=30) at /share/dim/src/freebsd/llvm-21-update/contrib/llvm-project/libcxx/src/locale.cpp:481
#3 0x0000000800379580 in std::__1::locale::use_facet (this=0x7fffffffe228, x=...) at /share/dim/src/freebsd/llvm-21-update/contrib/llvm-project/libcxx/src/locale.cpp:575
#4 0x0000000000202dbf in std::__1::use_facet[abi:se190107]<std::__1::ctype<char> >(std::__1::locale const&) (__l=...) at /usr/include/c++/v1/__locale:168
#5 0x0000000000202cfc in std::__1::basic_ios<char, std::__1::char_traits<char> >::widen[abi:se190107](char) const (this=0x205238 <std::__1::cout+8>, __c=32 ' ') at /usr/include/c++/v1/ios:693
#6 0x00000000002028f2 in std::__1::basic_ios<char, std::__1::char_traits<char> >::fill[abi:se190107]() const (this=0x205238 <std::__1::cout+8>) at /usr/include/c++/v1/ios:701
#7 0x000000000020253e in std::__1::__put_character_sequence[abi:se190107]<char, std::__1::char_traits<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, char const*, unsigned long) (__os=..., __str=0x200b69 "Hello World!\n", __len=13) at /usr/include/c++/v1/__ostream/basic_ostream.h:512
#8 0x0000000000202479 in std::__1::operator<<[abi:se190107]<std::__1::char_traits<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, char const*) (__os=..., __str=0x200b69 "Hello World!\n") at /usr/include/c++/v1/__ostream/basic_ostream.h:619
#9 0x0000000000202438 in main () at helloworld.cpp:5
```
@emaste originally noticed this `bad_cast` exception when running the `cmake` binary from a FreeBSD package which was built against libc++ 19, on a system which had libc++ 21. But any program that uses the standard streams may crash in such a manner.
The problem is also not unique to FreeBSD, as it can be reproduced on Linux. However, most modern distributions use PIE mode by default, either by patching their compiler(s), or by submitting patches upstream. When compiled in PIE mode on x86_64, the relocations become GLOB_DAT relocations instead:
```text
Relocation section '.rela.dyn' at offset 0x9c8 contains 11 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
...
0000000000003fd0 0000000b00000006 R_X86_64_GLOB_DAT 0000000000000000 std::__1::ctype<char>::id + 0
0000000000003fd8 0000001500000006 R_X86_64_GLOB_DAT 0000000000000000 std::__1::cout + 0
```
and the `cout` and `ctype<char>::id` become `UND` instead, so the object is resolved a runtime instead:
```text
Symbol table '.dynsym' contains 25 entries:
Num: Value Size Type Bind Vis Ndx Name
...
11: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND std::__1::ctype<char>::id
...
21: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND std::__1::cout
```
But if you compile the program with `-fno-PIE -no-pie`, even on Linux will you get the copy relocations, and the sizes of the objects in the symbol table. I.e. with libc++ 20:
```text
Relocation section '.rela.dyn' at offset 0x940 contains 4 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
...
00000000004040a0 0000001400000005 R_X86_64_COPY 00000000004040a0 std::__1::ctype<char>::id + 0
00000000004040b0 0000001300000005 R_X86_64_COPY 00000000004040b0 std::__1::cout + 0
...
Symbol table '.dynsym' contains 22 entries:
Num: Value Size Type Bind Vis Ndx Name
...
19: 00000000004040b0 160 OBJECT GLOBAL DEFAULT 25 std::__1::cout
20: 00000000004040a0 16 OBJECT GLOBAL DEFAULT 25 std::__1::ctype<char>::id
```
and with libc++ 21:
```text
Relocation section '.rela.dyn' at offset 0x940 contains 4 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
...
00000000004040a0 0000001400000005 R_X86_64_COPY 00000000004040a0 std::__1::ctype<char>::id + 0
00000000004040b0 0000001300000005 R_X86_64_COPY 00000000004040b0 std::__1::cout + 0
...
Symbol table '.dynsym' contains 22 entries:
Num: Value Size Type Bind Vis Ndx Name
...
19: 00000000004040b0 264 OBJECT GLOBAL DEFAULT 25 std::__1::cout
20: 00000000004040a0 16 OBJECT GLOBAL DEFAULT 25 std::__1::ctype<char>::id
```
Here, `cout` grows from 160 to 264 bytes, and it doesn't cause the program to crash immediately, but you get a warning from glib's dynamic linker instead:
```text
$ LD_LIBRARY_PATH=~/llvm/llvmorg-21-init-111-g608012ace43b-linux6-x86_64-ninja-gcc-rel-1/lib/x86_64-unknown-linux-gnu
./llvmorg-21-init-110-g07a0e2be86f3/helloworld
./llvmorg-21-init-110-g07a0e2be86f3/helloworld: Symbol `_ZNSt3__14coutE' has different size in shared object, consider re-linking
Hello World!
```
Ergo, on Linux there is also ABI breakage. I didn't inspect other platforms, but I suspect most of them might have a similar problem.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzMWluT27aS_jWYly6pSFAX6mEepJG18ZbLSSXOnj37wgLJloiYBHgAcGaUh_PbtxogdZ_YzoxzwrJrRBK37v660f2Bwlq5U4j3bLpi0_Wd6Fylzf1aNtKZ_VKVRhZ3uS73958qBCubtpZbWQgntQK9BTaLpLbOoGjGRduyWQRS0dO6fmy02Y14PJJKulEcx6Mdm0WzKI1iLgqcJDkwnjKexHwSRwnjD5DvgU2itpK1kp_n8zk9Kwp6VpdSK4WML6CohNqhBecX9Dvafh2FVGwWURe60Z3r74QqYWskqtLS7VMliwpyg-JzGGO5ej-GFRais0hDFbrdg8FaByl9J1dpO8xWCAU5AjbSOSxBKqcBn7HonMhrtCSU27eyEHW9h6cKFSjtoNBNK2vfHn56_w4aXZI0Y_jkhz4ZwAs9tBY7IZV1UMu8YHzF-Aq4l-pJ1jUoDbVWOzRgOgWt0S2aen-zU0ydOtv5VbnK6CepdiAU4HOBLQnqDdA50K5CA9YZUjPkWIlHqQ1IC6K2GlptrcxrHLNoyaLlg7BIMrVaKkeN2CyqsK71kzZ12WOCJcvQmn77f_266BFPpCrqrkRgycMAJpa8Cz1o1EZIxXj6qGXJ-IKez6kngHUlDZ0sydzUnSUPwDj_gRYA_6AVMB6z6YNinLPEdzLoOqMgCrdsvj5dVpjzH2S0PzYBWVmb02e_ddZBjltt8GvxT2jWCgQYLFA52BjE1S9rsHvrsAlgRYMHvJB6M6Vd5nFb4lZ0tTsHvEc73bp9iyx5KCphSJdeS7KkFh45O3RXQCcz0sBHLF7bzeGzY9Hy50MvsFj4v4zPxwZrMS73ivE5CAd6u7XoIHpe5CkUWjlSJHBA5YxEG0YHAPgxNDy93quthk_7FuH8-mXf5LpmfG7hf0Td4dmTj6JBIGMsyxJVyaJldLh4NOVJBOEmnvRPp_Bz9r_pLJtNsocff_rncZ6rjgesZVl8Cjq-guhqIoyGEeLpt82EN2e6bc3D5Bf49aHaa8VHuMZi_YgWUJXQtYOV2SwiU9l9Q6DorfiiwYOSwaPCm7rvyucnhk0uLPuxa1jiDdyb6mBE-TsejLuSqqQm0oaXH8tnb0gWLcfjcY8QTjHk2igA8SyCH1f__e7hE8B_ffhxtfwA63eb5a8fPlGv6QtmG0aNr0cl09Gw3zrqCw53ZZyP2iG4SrhTr5XWC5LvHQ6bVI1f8GPfJ3QZYrEPWRTX_d5IztAavTOiub0lvFkQ8yHFVvqJMgRZC_M1saXfbrbahOVeAfZtwQgDHL8HFiNOqEnSyZtiMRZ_BRbJbCdQrISFndFPCpz2Ep0izO-Me935fKOPIsfdftSjbHREFKfJIJdKmBfyEgKh2Do0cIGqkHch1MI5ND6-38g2A_QKI3MCg3T2uGZK5krhBOhHDBg7-BiI2kMTS4-_84TxmL39jkaTO_2hI_qVWSccnirJe3gl2haHDBJB4bMDJxsEATR9jUCyNa2zpGw2i7Jsh94KvW6Dpw8ucmMZ9FYUPgmQ7ri1C1D4BJQunYqTRMcEWNqjWmrK9klb_sYvbFD3VhTobEbzPGLhtBkDiYehq7TgG9BoncUSnqSrqFtnMfNvhhycHuaizAphvXyHrDOsOceQk768ATEf7rOseBaZb-rzbN8nO6awyTp6TqNIRGmUBltKtdXD4wQnPI0oTyQt0huv2oMDHRaYvKO-JdLPoSufCN_10PpkVrr990kinRJ8aZAFpUGMbzprGN9YUzC-oRBlZM74hvzg-dk4xjeHzuOiYMky5fMgcwwQPfcRgZYxjXg-pWB67fVZ0Et2kGK6ErlkydIij6M4Stl0HZYG_Z-wNlsJg4xvStkc1rg1iLktaY31Y0Pxv2tL4fB8-fSqNfo3JAD20jC-6dN5xjeDmlmyTFIeJOLnAs3TRSJuC9QjsX8omzb8PGArIEDag42KuZilZzb60lhFTQVwkYXX9DzrjS9LlqzJYb6bmsIQYWYfzZLlJI2DkpILJS2mafRlJd3WzHwbLuRk9Qd4ZsmaNrO_VLLpfBokmxwl85scL_PtbcmOMeQI5HgRxdGcgHzLyBfBEbwp05d0RrmCdYzPeo_IsvpSM8Ftj3geqla-eYwZ32TZQfvxLA3yTeFSwGJb3BYwFwQ9qe2wZP5wazOvhMmcEdLZC9H86ydZorqpIZ6GQRdB0DNM-PT5BVfxOyFfpb0jZFlBnsAp0aL_X60dL9hytkiCYmZXikm3_DsqZivr-gW9vFIn3yT_POpden4p_jTBlwJ527mMBBKFQ5NZ_FeHqsAX_ODPaOiWVwStH_iXPzfuzLNXla8AvHctPe-kPMtYerbq4G6kn-BvhDLrTG-GKJ8tXmZxfOMaaa-Pk29w1UEwvjkTdFxRdIr7zSm9AulkvrhtJd2iEU6bwDp9fYz6e9jkdSZ4E6XP4kVQ-uJa6UlKSm-EVCe5ygWtmCyn1xUNm0TYCOsQtJE7qXzeq7STBZYhYX05EaW03XRKDQU0pduN-IwnFczW6AbEgahrRfFZ7LBPqZ-EhbyTtbtV6MS-nvFsX6D3-k6VKM_roTGsOgdC7Q-1uy8lOjtQ3k6oUpgSgiotNGIPhRG28jjtigoENEIpNOMjHdQandfYHFhcpR10Sv6rQ6o8enl8pm6pkOg5boOt0WVHutMKPkjVPY_hB_1E6T81brR1npk0CkppKS3oQrnf2RPaMt-fkpUoPcGc76EVrqh6bUszMK6GPKGv_7RvZ7u8kc5RS98FLXRtjyQ4J2tP2HVa8rMn3Ib665SPyLHQDfqCOlsvP11wFdahKN-M_SxO2M84_nr68_TyDMU3kp-BsohOrmRbDhRolPd_Z0di8qCNC2IyXH-CmLyYO71iRV81-Sn_ehEHhCoPPvzVzHiPCTaLfv24DkdZAQgUcbUfT-eU7JIbGbS6fsQSBAUNX9V_CTdfxVtNvx-JGl9QTL1mvbpfpJh-_bj-JorphCd7w-kCT3ZhZAqUcuv5qD4AeBsNcXMgI0ZbpUcUFUZKj1qJPSeBj6gOYS3QEDTSDp0f5dYx4ICqw7HjERIHhtOeGHkM78c4Dgs5P8F7q9AyiY7QmbwisrzqkOUyzkyiSSSOJyDfcNbS93xNoKEh8uPkybdNnn850AzyfpU3Xx52vaU3L87daxD8lUci0fWo35uGJre68pH4L_aRv49ffA93-Ht6A1z5wxu6AZ-95jTmP-AGP6DBi9P8ndFPNlQd5NVOe6kOx4TkONJBqdES7Clvp8T7dBN0eigOmgZLKRzW--HobdjuBDwJ48seP9OulrlHdLlXopEF1FJ9RvPF9IbxCXxYZx_er35e_vzP7Kflpx9Ysv53TxT2f66OE09PfUY17cWzUUjbR0qq38RoVxQjg_UoDhQj45v-dac-K_2kQqfRTnWEi5vTRKNdNBcR8hzT2TZhfHOsJf9cp2TZu74_tfm_j7-4JMviCdntHWG-EhZKud2iQeV8vuCrs0oYqqXywJg--IJclmjAIInxWaqdB8JZ1X0FlHdmp_tqMiQvzn8mMtR2y9X78H2T2FEGAqUsAzqksi1lr-ELn7YWbqtNYwc4vAfbhQa-rgvpTQON3FUOKvGIVLv257t9PUlOeFfeJ-UiWYg7vI_n8ySeLaIkvavuxTaPi4jHiCnn-XwxF_E2n6c8ErhNoqi4k_c84rMoiaM4nc7jdBwnk3yeTsU8mcV5mfBQzct6TNYZa7O7k9Z2eE_N-eSuFjnW1n-6xvlx7wgsEeN8uXp_-G1wZ9DacFzDY3o-Xd-Ze89h593OsklUS-vscS4nXe0_jDsZero-O6YcviMjlW-1gUKqYFZfwh--O7vrTH1fOdf6wMc3jG920lVdPi50c-4fJ3y6l9UyvunFfbzn_x8AAP__Vs6VIw">