Index: Makefile.inc1 =================================================================== --- Makefile.inc1 +++ Makefile.inc1 @@ -2715,8 +2715,12 @@ _startup_libs+= lib/libc _startup_libs+= lib/libc_nonshared .if ${MK_LIBCPLUSPLUS} != "no" +.if ${MK_LIBCXXABI} != "no" +_startup_libs+= lib/libc++abi +.else _startup_libs+= lib/libcxxrt .endif +.endif .if ${MK_LLVM_LIBUNWIND} != "no" _prereq_libs+= lib/libgcc_eh lib/libgcc_s @@ -2725,6 +2729,7 @@ lib/libgcc_s__L: lib/libc__L lib/libgcc_s__L: lib/libc_nonshared__L .if ${MK_LIBCPLUSPLUS} != "no" +lib/libc++abi__L: lib/libgcc_s__L lib/libcxxrt__L: lib/libgcc_s__L .endif @@ -2736,6 +2741,7 @@ gnu/lib/libgcc__L: lib/libc__L gnu/lib/libgcc__L: lib/libc_nonshared__L .if ${MK_LIBCPLUSPLUS} != "no" +lib/libc++abi__L: gnu/lib/libgcc__L lib/libcxxrt__L: gnu/lib/libgcc__L .endif .endif @@ -2936,7 +2942,11 @@ ${_cddl_lib_libctf:D${_cddl_lib_libctf}__L} lib/libelf__L lib/librtld_db__L lib/libutil__L .if ${MK_CXX} != "no" .if ${MK_LIBCPLUSPLUS} != "no" +.if ${MK_LIBCXXABI} != "no" +lib/libproc__L: lib/libc++abi__L +.else lib/libproc__L: lib/libcxxrt__L +.endif .else # This implies MK_GNUCXX != "no"; see lib/libproc lib/libproc__L: gnu/lib/libsupc++__L .endif Index: contrib/libcxxabi/CREDITS.TXT =================================================================== --- /dev/null +++ contrib/libcxxabi/CREDITS.TXT @@ -0,0 +1,71 @@ +This file is a partial list of people who have contributed to the LLVM/libc++abi +project. If you have contributed a patch or made some other contribution to +LLVM/libc++abi, please submit a patch to this file to add yourself, and it will be +done! + +The list is sorted by surname and formatted to allow easy grepping and +beautification by scripts. The fields are: name (N), email (E), web-address +(W), PGP key ID and fingerprint (P), description (D), and snail-mail address +(S). + +N: Aaron Ballman +E: aaron@aaronballman.com +D: Minor patches + +N: Logan Chien +E: logan.chien@mediatek.com +D: ARM EHABI Unwind & Exception Handling + +N: Marshall Clow +E: mclow.lists@gmail.com +E: marshall@idio.com +D: Architect and primary coauthor of libc++abi + +N: Matthew Dempsky +E: matthew@dempsky.org +D: Minor patches and bug fixes. + +N: Nowar Gu +E: wenhan.gu@gmail.com +D: Minor patches and fixes + +N: Howard Hinnant +E: hhinnant@apple.com +D: Architect and primary coauthor of libc++abi + +N: Dana Jansens +E: danakj@chromium.org +D: ARM EHABI Unwind & Exception Handling + +N: Nick Kledzik +E: kledzik@apple.com + +N: Antoine Labour +E: piman@chromium.org +D: ARM EHABI Unwind & Exception Handling + +N: Bruce Mitchener, Jr. +E: bruce.mitchener@gmail.com +D: Minor typo fixes + +N: Andrew Morrow +E: andrew.c.morrow@gmail.com +D: Minor patches and fixes + +N: Erik Olofsson +E: erik.olofsson@hansoft.se +E: erik@olofsson.info +D: Minor patches and fixes + +N: Jon Roelofs +E: jroelofs@jroelofs.com +D: ARM EHABI Unwind & Exception Handling, Bare-metal + +N: Nico Weber +E: thakis@chromium.org +D: ARM EHABI Unwind & Exception Handling + +N: Albert J. Wong +E: ajwong@google.com +D: ARM EHABI Unwind & Exception Handling + Index: contrib/libcxxabi/FREEBSD-upgrade =================================================================== --- /dev/null +++ contrib/libcxxabi/FREEBSD-upgrade @@ -0,0 +1,4 @@ +$FreeBSD$ + +The FreeBSD copy of libcxxabi This contains everything from the include and +source directories upstream. Index: contrib/libcxxabi/LICENSE.TXT =================================================================== --- /dev/null +++ contrib/libcxxabi/LICENSE.TXT @@ -0,0 +1,311 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== + +The libc++abi library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. Index: contrib/libcxxabi/include/__cxxabi_config.h =================================================================== --- /dev/null +++ contrib/libcxxabi/include/__cxxabi_config.h @@ -0,0 +1,72 @@ +//===-------------------------- __cxxabi_config.h -------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef ____CXXABI_CONFIG_H +#define ____CXXABI_CONFIG_H + +#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ + !defined(__ARM_DWARF_EH__) +#define _LIBCXXABI_ARM_EHABI +#endif + +#if !defined(__has_attribute) +#define __has_attribute(_attribute_) 0 +#endif + +#if defined(_WIN32) + #if defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) + #define _LIBCXXABI_HIDDEN + #define _LIBCXXABI_DATA_VIS + #define _LIBCXXABI_FUNC_VIS + #define _LIBCXXABI_TYPE_VIS + #elif defined(_LIBCXXABI_BUILDING_LIBRARY) + #define _LIBCXXABI_HIDDEN + #define _LIBCXXABI_DATA_VIS __declspec(dllexport) + #define _LIBCXXABI_FUNC_VIS __declspec(dllexport) + #define _LIBCXXABI_TYPE_VIS __declspec(dllexport) + #else + #define _LIBCXXABI_HIDDEN + #define _LIBCXXABI_DATA_VIS __declspec(dllimport) + #define _LIBCXXABI_FUNC_VIS __declspec(dllimport) + #define _LIBCXXABI_TYPE_VIS __declspec(dllimport) + #endif +#else + #if !defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) + #define _LIBCXXABI_HIDDEN __attribute__((__visibility__("hidden"))) + #define _LIBCXXABI_DATA_VIS __attribute__((__visibility__("default"))) + #define _LIBCXXABI_FUNC_VIS __attribute__((__visibility__("default"))) + #if __has_attribute(__type_visibility__) + #define _LIBCXXABI_TYPE_VIS __attribute__((__type_visibility__("default"))) + #else + #define _LIBCXXABI_TYPE_VIS __attribute__((__visibility__("default"))) + #endif + #else + #define _LIBCXXABI_HIDDEN + #define _LIBCXXABI_DATA_VIS + #define _LIBCXXABI_FUNC_VIS + #define _LIBCXXABI_TYPE_VIS + #endif +#endif + +#if defined(_WIN32) +#define _LIBCXXABI_WEAK +#else +#define _LIBCXXABI_WEAK __attribute__((__weak__)) +#endif + +#if defined(__clang__) +#define _LIBCXXABI_COMPILER_CLANG +#endif + +#if __has_attribute(__no_sanitize__) && defined(_LIBCXXABI_COMPILER_CLANG) +#define _LIBCXXABI_NO_CFI __attribute__((__no_sanitize__("cfi"))) +#else +#define _LIBCXXABI_NO_CFI +#endif + +#endif // ____CXXABI_CONFIG_H Index: contrib/libcxxabi/include/cxxabi.h =================================================================== --- /dev/null +++ contrib/libcxxabi/include/cxxabi.h @@ -0,0 +1,176 @@ +//===--------------------------- cxxabi.h ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef __CXXABI_H +#define __CXXABI_H + +/* + * This header provides the interface to the C++ ABI as defined at: + * http://www.codesourcery.com/cxx-abi/ + */ + +#include +#include + +#include <__cxxabi_config.h> + +#define _LIBCPPABI_VERSION 1002 +#define _LIBCXXABI_NORETURN __attribute__((noreturn)) + +#ifdef __cplusplus + +namespace std { +#if defined(_WIN32) +class _LIBCXXABI_TYPE_VIS type_info; // forward declaration +#else +class type_info; // forward declaration +#endif +} + + +// runtime routines use C calling conventions, but are in __cxxabiv1 namespace +namespace __cxxabiv1 { +extern "C" { + +// 2.4.2 Allocating the Exception Object +extern _LIBCXXABI_FUNC_VIS void * +__cxa_allocate_exception(size_t thrown_size) throw(); +extern _LIBCXXABI_FUNC_VIS void +__cxa_free_exception(void *thrown_exception) throw(); + +// 2.4.3 Throwing the Exception Object +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void +__cxa_throw(void *thrown_exception, std::type_info *tinfo, + void (*dest)(void *)); + +// 2.5.3 Exception Handlers +extern _LIBCXXABI_FUNC_VIS void * +__cxa_get_exception_ptr(void *exceptionObject) throw(); +extern _LIBCXXABI_FUNC_VIS void * +__cxa_begin_catch(void *exceptionObject) throw(); +extern _LIBCXXABI_FUNC_VIS void __cxa_end_catch(); +#if defined(_LIBCXXABI_ARM_EHABI) +extern _LIBCXXABI_FUNC_VIS bool +__cxa_begin_cleanup(void *exceptionObject) throw(); +extern _LIBCXXABI_FUNC_VIS void __cxa_end_cleanup(); +#endif +extern _LIBCXXABI_FUNC_VIS std::type_info *__cxa_current_exception_type(); + +// 2.5.4 Rethrowing Exceptions +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_rethrow(); + +// 2.6 Auxiliary Runtime APIs +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void); +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void); +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void +__cxa_throw_bad_array_new_length(void); + +// 3.2.6 Pure Virtual Function API +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_pure_virtual(void); + +// 3.2.7 Deleted Virtual Function API +extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_deleted_virtual(void); + +// 3.3.2 One-time Construction API +#ifdef __arm__ +extern _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(uint32_t *); +extern _LIBCXXABI_FUNC_VIS void __cxa_guard_release(uint32_t *); +extern _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(uint32_t *); +#else +extern _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(uint64_t *); +extern _LIBCXXABI_FUNC_VIS void __cxa_guard_release(uint64_t *); +extern _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(uint64_t *); +#endif + +// 3.3.3 Array Construction and Destruction API +extern _LIBCXXABI_FUNC_VIS void * +__cxa_vec_new(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *)); + +extern _LIBCXXABI_FUNC_VIS void * +__cxa_vec_new2(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *), + void *(*alloc)(size_t), void (*dealloc)(void *)); + +extern _LIBCXXABI_FUNC_VIS void * +__cxa_vec_new3(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *), + void *(*alloc)(size_t), void (*dealloc)(void *, size_t)); + +extern _LIBCXXABI_FUNC_VIS void +__cxa_vec_ctor(void *array_address, size_t element_count, size_t element_size, + void (*constructor)(void *), void (*destructor)(void *)); + +extern _LIBCXXABI_FUNC_VIS void __cxa_vec_dtor(void *array_address, + size_t element_count, + size_t element_size, + void (*destructor)(void *)); + +extern _LIBCXXABI_FUNC_VIS void __cxa_vec_cleanup(void *array_address, + size_t element_count, + size_t element_size, + void (*destructor)(void *)); + +extern _LIBCXXABI_FUNC_VIS void __cxa_vec_delete(void *array_address, + size_t element_size, + size_t padding_size, + void (*destructor)(void *)); + +extern _LIBCXXABI_FUNC_VIS void +__cxa_vec_delete2(void *array_address, size_t element_size, size_t padding_size, + void (*destructor)(void *), void (*dealloc)(void *)); + +extern _LIBCXXABI_FUNC_VIS void +__cxa_vec_delete3(void *__array_address, size_t element_size, + size_t padding_size, void (*destructor)(void *), + void (*dealloc)(void *, size_t)); + +extern _LIBCXXABI_FUNC_VIS void +__cxa_vec_cctor(void *dest_array, void *src_array, size_t element_count, + size_t element_size, void (*constructor)(void *, void *), + void (*destructor)(void *)); + +// 3.3.5.3 Runtime API +extern _LIBCXXABI_FUNC_VIS int __cxa_atexit(void (*f)(void *), void *p, + void *d); +extern _LIBCXXABI_FUNC_VIS int __cxa_finalize(void *); + +// 3.4 Demangler API +extern _LIBCXXABI_FUNC_VIS char *__cxa_demangle(const char *mangled_name, + char *output_buffer, + size_t *length, int *status); + +// Apple additions to support C++ 0x exception_ptr class +// These are primitives to wrap a smart pointer around an exception object +extern _LIBCXXABI_FUNC_VIS void *__cxa_current_primary_exception() throw(); +extern _LIBCXXABI_FUNC_VIS void +__cxa_rethrow_primary_exception(void *primary_exception); +extern _LIBCXXABI_FUNC_VIS void +__cxa_increment_exception_refcount(void *primary_exception) throw(); +extern _LIBCXXABI_FUNC_VIS void +__cxa_decrement_exception_refcount(void *primary_exception) throw(); + +// Apple extension to support std::uncaught_exception() +extern _LIBCXXABI_FUNC_VIS bool __cxa_uncaught_exception() throw(); +extern _LIBCXXABI_FUNC_VIS unsigned int __cxa_uncaught_exceptions() throw(); + +#if defined(__linux__) || defined(__Fuchsia__) +// Linux and Fuchsia TLS support. Not yet an official part of the Itanium ABI. +// https://sourceware.org/glibc/wiki/Destructor%20support%20for%20thread_local%20variables +extern _LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(void (*)(void *), void *, + void *) throw(); +#endif + +} // extern "C" +} // namespace __cxxabiv1 + +namespace abi = __cxxabiv1; + +#endif // __cplusplus + +#endif // __CXXABI_H Index: contrib/libcxxabi/src/CMakeLists.txt =================================================================== --- /dev/null +++ contrib/libcxxabi/src/CMakeLists.txt @@ -0,0 +1,275 @@ +# Get sources +set(LIBCXXABI_SOURCES + # C++ABI files + cxa_aux_runtime.cpp + cxa_default_handlers.cpp + cxa_demangle.cpp + cxa_exception_storage.cpp + cxa_guard.cpp + cxa_handlers.cpp + cxa_unexpected.cpp + cxa_vector.cpp + cxa_virtual.cpp + # C++ STL files + stdlib_exception.cpp + stdlib_stdexcept.cpp + stdlib_typeinfo.cpp + # Internal files + abort_message.cpp + fallback_malloc.cpp + private_typeinfo.cpp +) + +if (LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS) + list(APPEND LIBCXXABI_SOURCES stdlib_new_delete.cpp) +endif() + +if (LIBCXXABI_ENABLE_EXCEPTIONS) + list(APPEND LIBCXXABI_SOURCES cxa_exception.cpp) + list(APPEND LIBCXXABI_SOURCES cxa_personality.cpp) +else() + list(APPEND LIBCXXABI_SOURCES cxa_noexception.cpp) +endif() + +if (LIBCXXABI_ENABLE_THREADS AND (UNIX OR FUCHSIA) AND NOT (APPLE OR CYGWIN)) + list(APPEND LIBCXXABI_SOURCES cxa_thread_atexit.cpp) +endif() + +set(LIBCXXABI_HEADERS ../include/cxxabi.h) + +# Add all the headers to the project for IDEs. +if (MSVC_IDE OR XCODE) + # Force them all into the headers dir on MSVC, otherwise they end up at + # project scope because they don't have extensions. + if (MSVC_IDE) + source_group("Header Files" FILES ${LIBCXXABI_HEADERS}) + endif() +endif() + +include_directories("${LIBCXXABI_LIBCXX_INCLUDES}") + +if (LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL) + add_definitions(-DHAVE___CXA_THREAD_ATEXIT_IMPL) +endif() + +if (LIBCXXABI_ENABLE_THREADS) + add_library_flags_if(LIBCXXABI_HAS_PTHREAD_LIB pthread) +endif() + +add_library_flags_if(LIBCXXABI_HAS_C_LIB c) +if (LIBCXXABI_USE_LLVM_UNWINDER) + # Prefer using the in-tree version of libunwind, either shared or static. If + # none are found fall back to using -lunwind. + # FIXME: Is it correct to prefer the static version of libunwind? + if (NOT LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY AND (TARGET unwind_shared OR HAVE_LIBUNWIND)) + list(APPEND LIBCXXABI_SHARED_LIBRARIES unwind_shared) + elseif (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY AND (TARGET unwind_static OR HAVE_LIBUNWIND)) + list(APPEND LIBCXXABI_SHARED_LIBRARIES unwind_static) + else() + list(APPEND LIBCXXABI_SHARED_LIBRARIES unwind) + endif() + if (NOT LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY AND (TARGET unwind_shared OR HAVE_LIBUNWIND)) + list(APPEND LIBCXXABI_STATIC_LIBRARIES unwind_shared) + elseif (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY AND (TARGET unwind_static OR HAVE_LIBUNWIND)) + # We handle this by directly merging libunwind objects into libc++abi. + else() + list(APPEND LIBCXXABI_STATIC_LIBRARIES unwind) + endif() +else() + add_library_flags_if(LIBCXXABI_HAS_GCC_S_LIB gcc_s) +endif() +if (MINGW) + # MINGW_LIBRARIES is defined in config-ix.cmake + list(APPEND LIBCXXABI_LIBRARIES ${MINGW_LIBRARIES}) +endif() + +# Setup flags. +add_link_flags_if_supported(-nodefaultlibs) + +set(LIBCXXABI_SHARED_LINK_FLAGS) + +if ( APPLE ) + if ( CMAKE_OSX_DEPLOYMENT_TARGET STREQUAL "10.6" ) + list(APPEND LIBCXXABI_COMPILE_FLAGS "-U__STRICT_ANSI__") + list(APPEND LIBCXXABI_SHARED_LINK_FLAGS + "-compatibility_version 1" + "-current_version 1" + "-install_name /usr/lib/libc++abi.1.dylib") + list(APPEND LIBCXXABI_LINK_FLAGS + "/usr/lib/libSystem.B.dylib") + else() + list(APPEND LIBCXXABI_SHARED_LINK_FLAGS + "-compatibility_version 1" + "-install_name /usr/lib/libc++abi.1.dylib") + endif() + + if (LLVM_USE_SANITIZER) + if (("${LLVM_USE_SANITIZER}" STREQUAL "Address") OR + ("${LLVM_USE_SANITIZER}" STREQUAL "Address;Undefined") OR + ("${LLVM_USE_SANITIZER}" STREQUAL "Undefined;Address")) + set(LIBFILE "libclang_rt.asan_osx_dynamic.dylib") + elseif("${LLVM_USE_SANITIZER}" STREQUAL "Undefined") + set(LIBFILE "libclang_rt.ubsan_osx_dynamic.dylib") + elseif("${LLVM_USE_SANITIZER}" STREQUAL "Thread") + set(LIBFILE "libclang_rt.tsan_osx_dynamic.dylib") + else() + message(WARNING "LLVM_USE_SANITIZER=${LLVM_USE_SANITIZER} is not supported on OS X") + endif() + if (LIBFILE) + find_compiler_rt_dir(LIBDIR) + if (NOT IS_DIRECTORY "${LIBDIR}") + message(FATAL_ERROR "Cannot find compiler-rt directory on OS X required for LLVM_USE_SANITIZER") + endif() + set(LIBCXXABI_SANITIZER_LIBRARY "${LIBDIR}/${LIBFILE}") + set(LIBCXXABI_SANITIZER_LIBRARY "${LIBCXXABI_SANITIZER_LIBRARY}" PARENT_SCOPE) + message(STATUS "Manually linking compiler-rt library: ${LIBCXXABI_SANITIZER_LIBRARY}") + add_library_flags("${LIBCXXABI_SANITIZER_LIBRARY}") + add_link_flags("-Wl,-rpath,${LIBDIR}") + endif() + endif() +endif() + +split_list(LIBCXXABI_COMPILE_FLAGS) +split_list(LIBCXXABI_LINK_FLAGS) +split_list(LIBCXXABI_SHARED_LINK_FLAGS) + +# FIXME: libc++abi.so will not link when modules are enabled because it depends +# on symbols defined in libc++.so which has not yet been built. +if (LLVM_ENABLE_MODULES) + string(REPLACE "-Wl,-z,defs" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") +endif() + +macro(cxxabi_object_library name) + cmake_parse_arguments(ARGS "" "" "DEFINES;FLAGS" ${ARGN}) + + # Add a object library that contains the compiled source files. + add_library(${name} OBJECT ${LIBCXXABI_SOURCES} ${LIBCXXABI_HEADERS}) + set_target_properties(${name} + PROPERTIES + CXX_EXTENSIONS + OFF + CXX_STANDARD + 11 + CXX_STANDARD_REQUIRED + ON + COMPILE_FLAGS + "${LIBCXXABI_COMPILE_FLAGS}" + POSITION_INDEPENDENT_CODE + ON) + + if(ARGS_DEFINES) + target_compile_definitions(${name} PRIVATE ${ARGS_DEFINES}) + endif() + + if(ARGS_FLAGS) + target_compile_options(${name} PRIVATE ${ARGS_FLAGS}) + endif() +endmacro() + +if(LIBCXXABI_HERMETIC_STATIC_LIBRARY) + append_flags_if_supported(CXXABI_STATIC_OBJECTS_FLAGS -fvisibility=hidden) + append_flags_if_supported(CXXABI_STATIC_OBJECTS_FLAGS -fvisibility-global-new-delete-hidden) + cxxabi_object_library(cxxabi_static_objects + DEFINES + _LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS + _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS + FLAGS ${CXXABI_STATIC_OBJECTS_FLAGS}) + cxxabi_object_library(cxxabi_shared_objects) + set(cxxabi_static_sources $) + set(cxxabi_shared_sources $) +else() + cxxabi_object_library(cxxabi_objects) + set(cxxabi_static_sources $) + set(cxxabi_shared_sources $) +endif() + +# Build the shared library. +if (LIBCXXABI_ENABLE_SHARED) + add_library(cxxabi_shared SHARED ${cxxabi_shared_sources}) + if(COMMAND llvm_setup_rpath) + llvm_setup_rpath(cxxabi_shared) + endif() + target_link_libraries(cxxabi_shared PRIVATE ${LIBCXXABI_SHARED_LIBRARIES} ${LIBCXXABI_LIBRARIES}) + set_target_properties(cxxabi_shared + PROPERTIES + CXX_EXTENSIONS + OFF + CXX_STANDARD + 11 + CXX_STANDARD_REQUIRED + ON + LINK_FLAGS + "${LIBCXXABI_LINK_FLAGS} ${LIBCXXABI_SHARED_LINK_FLAGS}" + OUTPUT_NAME + "c++abi" + POSITION_INDEPENDENT_CODE + ON + SOVERSION + "1" + VERSION + "1.0") + list(APPEND LIBCXXABI_BUILD_TARGETS "cxxabi_shared") + if (LIBCXXABI_INSTALL_SHARED_LIBRARY) + list(APPEND LIBCXXABI_INSTALL_TARGETS "cxxabi_shared") + endif() +endif() + +# Build the static library. +if (LIBCXXABI_ENABLE_STATIC) + if (LIBCXXABI_USE_LLVM_UNWINDER AND LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY) + if (TARGET unwind_static OR HAVE_LIBUNWIND) + if(LIBUNWIND_HERMETIC_STATIC_LIBRARY) + list(APPEND cxxabi_static_sources $) + else() + list(APPEND cxxabi_static_sources $) + endif() + endif() + endif() + add_library(cxxabi_static STATIC ${cxxabi_static_sources}) + target_link_libraries(cxxabi_static PRIVATE ${LIBCXXABI_STATIC_LIBRARIES} ${LIBCXXABI_LIBRARIES}) + set_target_properties(cxxabi_static + PROPERTIES + CXX_EXTENSIONS + OFF + CXX_STANDARD + 11 + CXX_STANDARD_REQUIRED + ON + LINK_FLAGS + "${LIBCXXABI_LINK_FLAGS}" + OUTPUT_NAME + "c++abi" + POSITION_INDEPENDENT_CODE + ON) + list(APPEND LIBCXXABI_BUILD_TARGETS "cxxabi_static") + if (LIBCXXABI_INSTALL_STATIC_LIBRARY) + list(APPEND LIBCXXABI_INSTALL_TARGETS "cxxabi_static") + endif() +endif() + +# Add a meta-target for both libraries. +add_custom_target(cxxabi DEPENDS ${LIBCXXABI_BUILD_TARGETS}) + +if (LIBCXXABI_INSTALL_LIBRARY) + install(TARGETS ${LIBCXXABI_INSTALL_TARGETS} + LIBRARY DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi + ARCHIVE DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi + ) +endif() + +if (NOT CMAKE_CONFIGURATION_TYPES AND LIBCXXABI_INSTALL_LIBRARY) + add_custom_target(install-cxxabi + DEPENDS cxxabi + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=cxxabi + -P "${LIBCXXABI_BINARY_DIR}/cmake_install.cmake") + add_custom_target(install-cxxabi-stripped + DEPENDS cxxabi + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=cxxabi + -DCMAKE_INSTALL_DO_STRIP=1 + -P "${LIBCXXABI_BINARY_DIR}/cmake_install.cmake") + + # TODO: This is a legacy target name and should be removed at some point. + add_custom_target(install-libcxxabi DEPENDS install-cxxabi) +endif() Index: contrib/libcxxabi/src/abort_message.h =================================================================== --- /dev/null +++ contrib/libcxxabi/src/abort_message.h @@ -0,0 +1,26 @@ +//===-------------------------- abort_message.h-----------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef __ABORT_MESSAGE_H_ +#define __ABORT_MESSAGE_H_ + +#include "cxxabi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void +abort_message(const char *format, ...) __attribute__((format(printf, 1, 2))); + +#ifdef __cplusplus +} +#endif + +#endif + Index: contrib/libcxxabi/src/abort_message.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/abort_message.cpp @@ -0,0 +1,77 @@ +//===------------------------- abort_message.cpp --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include "abort_message.h" + +#ifdef __BIONIC__ +#include +#if __ANDROID_API__ >= 21 +#include +extern "C" void android_set_abort_message(const char* msg); +#else +#include +#endif // __ANDROID_API__ >= 21 +#endif // __BIONIC__ + +#ifdef __APPLE__ +# if defined(__has_include) && __has_include() +# define HAVE_CRASHREPORTERCLIENT_H +# include +# endif +#endif + +void abort_message(const char* format, ...) +{ + // write message to stderr +#if !defined(NDEBUG) || !defined(LIBCXXABI_BAREMETAL) +#ifdef __APPLE__ + fprintf(stderr, "libc++abi.dylib: "); +#endif + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +#endif + +#if defined(__APPLE__) && defined(HAVE_CRASHREPORTERCLIENT_H) + // record message in crash report + char* buffer; + va_list list2; + va_start(list2, format); + vasprintf(&buffer, format, list2); + va_end(list2); + CRSetCrashLogMessage(buffer); +#elif defined(__BIONIC__) + char* buffer; + va_list list2; + va_start(list2, format); + vasprintf(&buffer, format, list2); + va_end(list2); + +#if __ANDROID_API__ >= 21 + // Show error in tombstone. + android_set_abort_message(buffer); + + // Show error in logcat. + openlog("libc++abi", 0, 0); + syslog(LOG_CRIT, "%s", buffer); + closelog(); +#else + // The good error reporting wasn't available in Android until L. Since we're + // about to abort anyway, just call __assert2, which will log _somewhere_ + // (tombstone and/or logcat) in older releases. + __assert2(__FILE__, __LINE__, __func__, buffer); +#endif // __ANDROID_API__ >= 21 +#endif // __BIONIC__ + + abort(); +} Index: contrib/libcxxabi/src/cxa_aux_runtime.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_aux_runtime.cpp @@ -0,0 +1,43 @@ +//===------------------------ cxa_aux_runtime.cpp -------------------------===// +// +// 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 +// +// +// This file implements the "Auxiliary Runtime APIs" +// http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-aux +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" +#include +#include + +namespace __cxxabiv1 { +extern "C" { +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_cast(); +#else + std::terminate(); +#endif +} + +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_typeid(); +#else + std::terminate(); +#endif +} + +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void +__cxa_throw_bad_array_new_length(void) { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_array_new_length(); +#else + std::terminate(); +#endif +} +} // extern "C" +} // abi Index: contrib/libcxxabi/src/cxa_default_handlers.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_default_handlers.cpp @@ -0,0 +1,122 @@ +//===------------------------- cxa_default_handlers.cpp -------------------===// +// +// 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 +// +// +// This file implements the default terminate_handler and unexpected_handler. +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include "abort_message.h" +#include "cxxabi.h" +#include "cxa_handlers.hpp" +#include "cxa_exception.hpp" +#include "private_typeinfo.h" +#include "include/atomic_support.h" + +#if !defined(LIBCXXABI_SILENT_TERMINATE) +static const char* cause = "uncaught"; + +__attribute__((noreturn)) +static void demangling_terminate_handler() +{ + // If there might be an uncaught exception + using namespace __cxxabiv1; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (globals) + { + __cxa_exception* exception_header = globals->caughtExceptions; + // If there is an uncaught exception + if (exception_header) + { + _Unwind_Exception* unwind_exception = + reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1; + if (__isOurExceptionClass(unwind_exception)) + { + void* thrown_object = + __getExceptionClass(unwind_exception) == kOurDependentExceptionClass ? + ((__cxa_dependent_exception*)exception_header)->primaryException : + exception_header + 1; + const __shim_type_info* thrown_type = + static_cast(exception_header->exceptionType); + // Try to get demangled name of thrown_type + int status; + char buf[1024]; + size_t len = sizeof(buf); + const char* name = __cxa_demangle(thrown_type->name(), buf, &len, &status); + if (status != 0) + name = thrown_type->name(); + // If the uncaught exception can be caught with std::exception& + const __shim_type_info* catch_type = + static_cast(&typeid(std::exception)); + if (catch_type->can_catch(thrown_type, thrown_object)) + { + // Include the what() message from the exception + const std::exception* e = static_cast(thrown_object); + abort_message("terminating with %s exception of type %s: %s", + cause, name, e->what()); + } + else + // Else just note that we're terminating with an exception + abort_message("terminating with %s exception of type %s", + cause, name); + } + else + // Else we're terminating with a foreign exception + abort_message("terminating with %s foreign exception", cause); + } + } + // Else just note that we're terminating + abort_message("terminating"); +} + +__attribute__((noreturn)) +static void demangling_unexpected_handler() +{ + cause = "unexpected"; + std::terminate(); +} + +static std::terminate_handler default_terminate_handler = demangling_terminate_handler; +static std::terminate_handler default_unexpected_handler = demangling_unexpected_handler; +#else +static std::terminate_handler default_terminate_handler = std::abort; +static std::terminate_handler default_unexpected_handler = std::terminate; +#endif + +// +// Global variables that hold the pointers to the current handler +// +_LIBCXXABI_DATA_VIS +std::terminate_handler __cxa_terminate_handler = default_terminate_handler; + +_LIBCXXABI_DATA_VIS +std::unexpected_handler __cxa_unexpected_handler = default_unexpected_handler; + +namespace std +{ + +unexpected_handler +set_unexpected(unexpected_handler func) _NOEXCEPT +{ + if (func == 0) + func = default_unexpected_handler; + return __libcpp_atomic_exchange(&__cxa_unexpected_handler, func, + _AO_Acq_Rel); +} + +terminate_handler +set_terminate(terminate_handler func) _NOEXCEPT +{ + if (func == 0) + func = default_terminate_handler; + return __libcpp_atomic_exchange(&__cxa_terminate_handler, func, + _AO_Acq_Rel); +} + +} Index: contrib/libcxxabi/src/cxa_demangle.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_demangle.cpp @@ -0,0 +1,364 @@ +//===-------------------------- cxa_demangle.cpp --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// FIXME: (possibly) incomplete list of features that clang mangles that this +// file does not yet support: +// - C++ modules TS + +#include "demangle/ItaniumDemangle.h" +#include "__cxxabi_config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace itanium_demangle; + +constexpr const char *itanium_demangle::FloatData::spec; +constexpr const char *itanium_demangle::FloatData::spec; +constexpr const char *itanium_demangle::FloatData::spec; + +// := _ # when number < 10 +// := __ _ # when number >= 10 +// extension := decimal-digit+ # at the end of string +const char *itanium_demangle::parse_discriminator(const char *first, + const char *last) { + // parse but ignore discriminator + if (first != last) { + if (*first == '_') { + const char *t1 = first + 1; + if (t1 != last) { + if (std::isdigit(*t1)) + first = t1 + 1; + else if (*t1 == '_') { + for (++t1; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 != last && *t1 == '_') + first = t1 + 1; + } + } + } else if (std::isdigit(*first)) { + const char *t1 = first + 1; + for (; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 == last) + first = last; + } + } + return first; +} + +#ifndef NDEBUG +namespace { +struct DumpVisitor { + unsigned Depth = 0; + bool PendingNewline = false; + + template static constexpr bool wantsNewline(const NodeT *) { + return true; + } + static bool wantsNewline(NodeArray A) { return !A.empty(); } + static constexpr bool wantsNewline(...) { return false; } + + template static bool anyWantNewline(Ts ...Vs) { + for (bool B : {wantsNewline(Vs)...}) + if (B) + return true; + return false; + } + + void printStr(const char *S) { fprintf(stderr, "%s", S); } + void print(StringView SV) { + fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.begin()); + } + void print(const Node *N) { + if (N) + N->visit(std::ref(*this)); + else + printStr(""); + } + void print(NodeOrString NS) { + if (NS.isNode()) + print(NS.asNode()); + else if (NS.isString()) + print(NS.asString()); + else + printStr("NodeOrString()"); + } + void print(NodeArray A) { + ++Depth; + printStr("{"); + bool First = true; + for (const Node *N : A) { + if (First) + print(N); + else + printWithComma(N); + First = false; + } + printStr("}"); + --Depth; + } + + // Overload used when T is exactly 'bool', not merely convertible to 'bool'. + void print(bool B) { printStr(B ? "true" : "false"); } + + template + typename std::enable_if::value>::type print(T N) { + fprintf(stderr, "%llu", (unsigned long long)N); + } + + template + typename std::enable_if::value>::type print(T N) { + fprintf(stderr, "%lld", (long long)N); + } + + void print(ReferenceKind RK) { + switch (RK) { + case ReferenceKind::LValue: + return printStr("ReferenceKind::LValue"); + case ReferenceKind::RValue: + return printStr("ReferenceKind::RValue"); + } + } + void print(FunctionRefQual RQ) { + switch (RQ) { + case FunctionRefQual::FrefQualNone: + return printStr("FunctionRefQual::FrefQualNone"); + case FunctionRefQual::FrefQualLValue: + return printStr("FunctionRefQual::FrefQualLValue"); + case FunctionRefQual::FrefQualRValue: + return printStr("FunctionRefQual::FrefQualRValue"); + } + } + void print(Qualifiers Qs) { + if (!Qs) return printStr("QualNone"); + struct QualName { Qualifiers Q; const char *Name; } Names[] = { + {QualConst, "QualConst"}, + {QualVolatile, "QualVolatile"}, + {QualRestrict, "QualRestrict"}, + }; + for (QualName Name : Names) { + if (Qs & Name.Q) { + printStr(Name.Name); + Qs = Qualifiers(Qs & ~Name.Q); + if (Qs) printStr(" | "); + } + } + } + void print(SpecialSubKind SSK) { + switch (SSK) { + case SpecialSubKind::allocator: + return printStr("SpecialSubKind::allocator"); + case SpecialSubKind::basic_string: + return printStr("SpecialSubKind::basic_string"); + case SpecialSubKind::string: + return printStr("SpecialSubKind::string"); + case SpecialSubKind::istream: + return printStr("SpecialSubKind::istream"); + case SpecialSubKind::ostream: + return printStr("SpecialSubKind::ostream"); + case SpecialSubKind::iostream: + return printStr("SpecialSubKind::iostream"); + } + } + + void newLine() { + printStr("\n"); + for (unsigned I = 0; I != Depth; ++I) + printStr(" "); + PendingNewline = false; + } + + template void printWithPendingNewline(T V) { + print(V); + if (wantsNewline(V)) + PendingNewline = true; + } + + template void printWithComma(T V) { + if (PendingNewline || wantsNewline(V)) { + printStr(","); + newLine(); + } else { + printStr(", "); + } + + printWithPendingNewline(V); + } + + struct CtorArgPrinter { + DumpVisitor &Visitor; + + template void operator()(T V, Rest ...Vs) { + if (Visitor.anyWantNewline(V, Vs...)) + Visitor.newLine(); + Visitor.printWithPendingNewline(V); + int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 }; + (void)PrintInOrder; + } + }; + + template void operator()(const NodeT *Node) { + Depth += 2; + fprintf(stderr, "%s(", itanium_demangle::NodeKind::name()); + Node->match(CtorArgPrinter{*this}); + fprintf(stderr, ")"); + Depth -= 2; + } + + void operator()(const ForwardTemplateReference *Node) { + Depth += 2; + fprintf(stderr, "ForwardTemplateReference("); + if (Node->Ref && !Node->Printing) { + Node->Printing = true; + CtorArgPrinter{*this}(Node->Ref); + Node->Printing = false; + } else { + CtorArgPrinter{*this}(Node->Index); + } + fprintf(stderr, ")"); + Depth -= 2; + } +}; +} + +void itanium_demangle::Node::dump() const { + DumpVisitor V; + visit(std::ref(V)); + V.newLine(); +} +#endif + +namespace { +class BumpPointerAllocator { + struct BlockMeta { + BlockMeta* Next; + size_t Current; + }; + + static constexpr size_t AllocSize = 4096; + static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta); + + alignas(long double) char InitialBuffer[AllocSize]; + BlockMeta* BlockList = nullptr; + + void grow() { + char* NewMeta = static_cast(std::malloc(AllocSize)); + if (NewMeta == nullptr) + std::terminate(); + BlockList = new (NewMeta) BlockMeta{BlockList, 0}; + } + + void* allocateMassive(size_t NBytes) { + NBytes += sizeof(BlockMeta); + BlockMeta* NewMeta = reinterpret_cast(std::malloc(NBytes)); + if (NewMeta == nullptr) + std::terminate(); + BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0}; + return static_cast(NewMeta + 1); + } + +public: + BumpPointerAllocator() + : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {} + + void* allocate(size_t N) { + N = (N + 15u) & ~15u; + if (N + BlockList->Current >= UsableAllocSize) { + if (N > UsableAllocSize) + return allocateMassive(N); + grow(); + } + BlockList->Current += N; + return static_cast(reinterpret_cast(BlockList + 1) + + BlockList->Current - N); + } + + void reset() { + while (BlockList) { + BlockMeta* Tmp = BlockList; + BlockList = BlockList->Next; + if (reinterpret_cast(Tmp) != InitialBuffer) + std::free(Tmp); + } + BlockList = new (InitialBuffer) BlockMeta{nullptr, 0}; + } + + ~BumpPointerAllocator() { reset(); } +}; + +class DefaultAllocator { + BumpPointerAllocator Alloc; + +public: + void reset() { Alloc.reset(); } + + template T *makeNode(Args &&...args) { + return new (Alloc.allocate(sizeof(T))) + T(std::forward(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.allocate(sizeof(Node *) * sz); + } +}; +} // unnamed namespace + +//===----------------------------------------------------------------------===// +// Code beyond this point should not be synchronized with LLVM. +//===----------------------------------------------------------------------===// + +using Demangler = itanium_demangle::ManglingParser; + +namespace { +enum : int { + demangle_invalid_args = -3, + demangle_invalid_mangled_name = -2, + demangle_memory_alloc_failure = -1, + demangle_success = 0, +}; +} + +namespace __cxxabiv1 { +extern "C" _LIBCXXABI_FUNC_VIS char * +__cxa_demangle(const char *MangledName, char *Buf, size_t *N, int *Status) { + if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) { + if (Status) + *Status = demangle_invalid_args; + return nullptr; + } + + int InternalStatus = demangle_success; + Demangler Parser(MangledName, MangledName + std::strlen(MangledName)); + OutputStream S; + + Node *AST = Parser.parse(); + + if (AST == nullptr) + InternalStatus = demangle_invalid_mangled_name; + else if (!initializeOutputStream(Buf, N, S, 1024)) + InternalStatus = demangle_memory_alloc_failure; + else { + assert(Parser.ForwardTemplateRefs.empty()); + AST->print(S); + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + Buf = S.getBuffer(); + } + + if (Status) + *Status = InternalStatus; + return InternalStatus == demangle_success ? Buf : nullptr; +} +} // __cxxabiv1 Index: contrib/libcxxabi/src/cxa_exception.hpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_exception.hpp @@ -0,0 +1,119 @@ +//===------------------------- cxa_exception.hpp --------------------------===// +// +// 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 +// +// +// This file implements the "Exception Handling APIs" +// http://mentorembedded.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#ifndef _CXA_EXCEPTION_H +#define _CXA_EXCEPTION_H + +#include // for std::unexpected_handler and std::terminate_handler +#include "cxxabi.h" +#include "unwind.h" + +namespace __cxxabiv1 { + +static const uint64_t kOurExceptionClass = 0x434C4E47432B2B00; // CLNGC++\0 +static const uint64_t kOurDependentExceptionClass = 0x434C4E47432B2B01; // CLNGC++\1 +static const uint64_t get_vendor_and_language = 0xFFFFFFFFFFFFFF00; // mask for CLNGC++ + +uint64_t __getExceptionClass (const _Unwind_Exception*); +void __setExceptionClass ( _Unwind_Exception*, uint64_t); +bool __isOurExceptionClass(const _Unwind_Exception*); + +struct _LIBCXXABI_HIDDEN __cxa_exception { +#if defined(__LP64__) || defined(_LIBCXXABI_ARM_EHABI) + // This is a new field to support C++ 0x exception_ptr. + // For binary compatibility it is at the start of this + // struct which is prepended to the object thrown in + // __cxa_allocate_exception. + size_t referenceCount; +#endif + + // Manage the exception object itself. + std::type_info *exceptionType; + void (*exceptionDestructor)(void *); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + + __cxa_exception *nextException; + + int handlerCount; + +#if defined(_LIBCXXABI_ARM_EHABI) + __cxa_exception* nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const unsigned char *actionRecord; + const unsigned char *languageSpecificData; + void *catchTemp; + void *adjustedPtr; +#endif + +#if !defined(__LP64__) && !defined(_LIBCXXABI_ARM_EHABI) + // This is a new field to support C++ 0x exception_ptr. + // For binary compatibility it is placed where the compiler + // previously adding padded to 64-bit align unwindHeader. + size_t referenceCount; +#endif + _Unwind_Exception unwindHeader; +}; + +// http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html +// The layout of this structure MUST match the layout of __cxa_exception, with +// primaryException instead of referenceCount. +struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { +#if defined(__LP64__) || defined(_LIBCXXABI_ARM_EHABI) + void* primaryException; +#endif + + std::type_info *exceptionType; + void (*exceptionDestructor)(void *); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + + __cxa_exception *nextException; + + int handlerCount; + +#if defined(_LIBCXXABI_ARM_EHABI) + __cxa_exception* nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const unsigned char *actionRecord; + const unsigned char *languageSpecificData; + void * catchTemp; + void *adjustedPtr; +#endif + +#if !defined(__LP64__) && !defined(_LIBCXXABI_ARM_EHABI) + void* primaryException; +#endif + _Unwind_Exception unwindHeader; +}; + +struct _LIBCXXABI_HIDDEN __cxa_eh_globals { + __cxa_exception * caughtExceptions; + unsigned int uncaughtExceptions; +#if defined(_LIBCXXABI_ARM_EHABI) + __cxa_exception* propagatingExceptions; +#endif +}; + +extern "C" _LIBCXXABI_FUNC_VIS __cxa_eh_globals * __cxa_get_globals (); +extern "C" _LIBCXXABI_FUNC_VIS __cxa_eh_globals * __cxa_get_globals_fast (); + +extern "C" _LIBCXXABI_FUNC_VIS void * __cxa_allocate_dependent_exception (); +extern "C" _LIBCXXABI_FUNC_VIS void __cxa_free_dependent_exception (void * dependent_exception); + +} // namespace __cxxabiv1 + +#endif // _CXA_EXCEPTION_H Index: contrib/libcxxabi/src/cxa_exception.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_exception.cpp @@ -0,0 +1,756 @@ +//===------------------------- cxa_exception.cpp --------------------------===// +// +// 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 +// +// +// This file implements the "Exception Handling APIs" +// http://mentorembedded.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" + +#include // for std::terminate +#include // for memset +#include "cxa_exception.hpp" +#include "cxa_handlers.hpp" +#include "fallback_malloc.h" +#include "include/atomic_support.h" + +#if __has_feature(address_sanitizer) +extern "C" void __asan_handle_no_return(void); +#endif + +// +---------------------------+-----------------------------+---------------+ +// | __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object | +// +---------------------------+-----------------------------+---------------+ +// ^ +// | +// +-------------------------------------------------------+ +// | +// +---------------------------+-----------------------------+ +// | __cxa_dependent_exception | _Unwind_Exception CLNGC++\1 | +// +---------------------------+-----------------------------+ + +namespace __cxxabiv1 { + +// Utility routines +static +inline +__cxa_exception* +cxa_exception_from_thrown_object(void* thrown_object) +{ + return static_cast<__cxa_exception*>(thrown_object) - 1; +} + +// Note: This is never called when exception_header is masquerading as a +// __cxa_dependent_exception. +static +inline +void* +thrown_object_from_cxa_exception(__cxa_exception* exception_header) +{ + return static_cast(exception_header + 1); +} + +// Get the exception object from the unwind pointer. +// Relies on the structure layout, where the unwind pointer is right in +// front of the user's exception object +static +inline +__cxa_exception* +cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exception) +{ + return cxa_exception_from_thrown_object(unwind_exception + 1 ); +} + +// Round s up to next multiple of a. +static inline +size_t aligned_allocation_size(size_t s, size_t a) { + return (s + a - 1) & ~(a - 1); +} + +static inline +size_t cxa_exception_size_from_exception_thrown_size(size_t size) { + return aligned_allocation_size(size + sizeof (__cxa_exception), + alignof(__cxa_exception)); +} + +void __setExceptionClass(_Unwind_Exception* unwind_exception, uint64_t newValue) { + ::memcpy(&unwind_exception->exception_class, &newValue, sizeof(newValue)); + } + + +static void setOurExceptionClass(_Unwind_Exception* unwind_exception) { + __setExceptionClass(unwind_exception, kOurExceptionClass); +} + +static void setDependentExceptionClass(_Unwind_Exception* unwind_exception) { + __setExceptionClass(unwind_exception, kOurDependentExceptionClass); +} + +// Is it one of ours? +uint64_t __getExceptionClass(const _Unwind_Exception* unwind_exception) { +// On x86 and some ARM unwinders, unwind_exception->exception_class is +// a uint64_t. On other ARM unwinders, it is a char[8] +// See: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf +// So we just copy it into a uint64_t to be sure. + uint64_t exClass; + ::memcpy(&exClass, &unwind_exception->exception_class, sizeof(exClass)); + return exClass; +} + +bool __isOurExceptionClass(const _Unwind_Exception* unwind_exception) { + return (__getExceptionClass(unwind_exception) & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); +} + +static bool isDependentException(_Unwind_Exception* unwind_exception) { + return (__getExceptionClass(unwind_exception) & 0xFF) == 0x01; +} + +// This does not need to be atomic +static inline int incrementHandlerCount(__cxa_exception *exception) { + return ++exception->handlerCount; +} + +// This does not need to be atomic +static inline int decrementHandlerCount(__cxa_exception *exception) { + return --exception->handlerCount; +} + +/* + If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler + stored in exc is called. Otherwise the exceptionDestructor stored in + exc is called, and then the memory for the exception is deallocated. + + This is never called for a __cxa_dependent_exception. +*/ +static +void +exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exception) +{ + __cxa_exception* exception_header = cxa_exception_from_exception_unwind_exception(unwind_exception); + if (_URC_FOREIGN_EXCEPTION_CAUGHT != reason) + std::__terminate(exception_header->terminateHandler); + // Just in case there exists a dependent exception that is pointing to this, + // check the reference count and only destroy this if that count goes to zero. + __cxa_decrement_exception_refcount(unwind_exception + 1); +} + +static _LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) { +// Section 2.5.3 says: +// * For purposes of this ABI, several things are considered exception handlers: +// ** A terminate() call due to a throw. +// and +// * Upon entry, Following initialization of the catch parameter, +// a handler must call: +// * void *__cxa_begin_catch(void *exceptionObject ); + (void) __cxa_begin_catch(&exception_header->unwindHeader); + std::__terminate(exception_header->terminateHandler); +} + +// Return the offset of the __cxa_exception header from the start of the +// allocated buffer. If __cxa_exception's alignment is smaller than the maximum +// useful alignment for the target machine, padding has to be inserted before +// the header to ensure the thrown object that follows the header is +// sufficiently aligned. This happens if _Unwind_exception isn't double-word +// aligned (on Darwin, for example). +static size_t get_cxa_exception_offset() { + struct S { + } __attribute__((aligned)); + + // Compute the maximum alignment for the target machine. + constexpr size_t alignment = std::alignment_of::value; + constexpr size_t excp_size = sizeof(__cxa_exception); + constexpr size_t aligned_size = + (excp_size + alignment - 1) / alignment * alignment; + constexpr size_t offset = aligned_size - excp_size; + static_assert((offset == 0 || + std::alignment_of<_Unwind_Exception>::value < alignment), + "offset is non-zero only if _Unwind_Exception isn't aligned"); + return offset; +} + +extern "C" { + +// Allocate a __cxa_exception object, and zero-fill it. +// Reserve "thrown_size" bytes on the end for the user's exception +// object. Zero-fill the object. If memory can't be allocated, call +// std::terminate. Return a pointer to the memory to be used for the +// user's exception object. +void *__cxa_allocate_exception(size_t thrown_size) throw() { + size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size); + + // Allocate extra space before the __cxa_exception header to ensure the + // start of the thrown object is sufficiently aligned. + size_t header_offset = get_cxa_exception_offset(); + char *raw_buffer = + (char *)__aligned_malloc_with_fallback(header_offset + actual_size); + if (NULL == raw_buffer) + std::terminate(); + __cxa_exception *exception_header = + static_cast<__cxa_exception *>((void *)(raw_buffer + header_offset)); + std::memset(exception_header, 0, actual_size); + return thrown_object_from_cxa_exception(exception_header); +} + + +// Free a __cxa_exception object allocated with __cxa_allocate_exception. +void __cxa_free_exception(void *thrown_object) throw() { + // Compute the size of the padding before the header. + size_t header_offset = get_cxa_exception_offset(); + char *raw_buffer = + ((char *)cxa_exception_from_thrown_object(thrown_object)) - header_offset; + __aligned_free_with_fallback((void *)raw_buffer); +} + + +// This function shall allocate a __cxa_dependent_exception and +// return a pointer to it. (Really to the object, not past its' end). +// Otherwise, it will work like __cxa_allocate_exception. +void * __cxa_allocate_dependent_exception () { + size_t actual_size = sizeof(__cxa_dependent_exception); + void *ptr = __aligned_malloc_with_fallback(actual_size); + if (NULL == ptr) + std::terminate(); + std::memset(ptr, 0, actual_size); + return ptr; +} + + +// This function shall free a dependent_exception. +// It does not affect the reference count of the primary exception. +void __cxa_free_dependent_exception (void * dependent_exception) { + __aligned_free_with_fallback(dependent_exception); +} + + +// 2.4.3 Throwing the Exception Object +/* +After constructing the exception object with the throw argument value, +the generated code calls the __cxa_throw runtime library routine. This +routine never returns. + +The __cxa_throw routine will do the following: + +* Obtain the __cxa_exception header from the thrown exception object address, +which can be computed as follows: + __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1); +* Save the current unexpected_handler and terminate_handler in the __cxa_exception header. +* Save the tinfo and dest arguments in the __cxa_exception header. +* Set the exception_class field in the unwind header. This is a 64-bit value +representing the ASCII string "XXXXC++\0", where "XXXX" is a +vendor-dependent string. That is, for implementations conforming to this +ABI, the low-order 4 bytes of this 64-bit value will be "C++\0". +* Increment the uncaught_exception flag. +* Call _Unwind_RaiseException in the system unwind library, Its argument is the +pointer to the thrown exception, which __cxa_throw itself received as an argument. +__Unwind_RaiseException begins the process of stack unwinding, described +in Section 2.5. In special cases, such as an inability to find a +handler, _Unwind_RaiseException may return. In that case, __cxa_throw +will call terminate, assuming that there was no handler for the +exception. +*/ +void +__cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(void *)) { + __cxa_eh_globals *globals = __cxa_get_globals(); + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + + exception_header->unexpectedHandler = std::get_unexpected(); + exception_header->terminateHandler = std::get_terminate(); + exception_header->exceptionType = tinfo; + exception_header->exceptionDestructor = dest; + setOurExceptionClass(&exception_header->unwindHeader); + exception_header->referenceCount = 1; // This is a newly allocated exception, no need for thread safety. + globals->uncaughtExceptions += 1; // Not atomically, since globals are thread-local + + exception_header->unwindHeader.exception_cleanup = exception_cleanup_func; + +#if __has_feature(address_sanitizer) + // Inform the ASan runtime that now might be a good time to clean stuff up. + __asan_handle_no_return(); +#endif + +#ifdef __USING_SJLJ_EXCEPTIONS__ + _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); +#else + _Unwind_RaiseException(&exception_header->unwindHeader); +#endif + // This only happens when there is no handler, or some unexpected unwinding + // error happens. + failed_throw(exception_header); +} + + +// 2.5.3 Exception Handlers +/* +The adjusted pointer is computed by the personality routine during phase 1 + and saved in the exception header (either __cxa_exception or + __cxa_dependent_exception). + + Requires: exception is native +*/ +void *__cxa_get_exception_ptr(void *unwind_exception) throw() { +#if defined(_LIBCXXABI_ARM_EHABI) + return reinterpret_cast( + static_cast<_Unwind_Control_Block*>(unwind_exception)->barrier_cache.bitpattern[0]); +#else + return cxa_exception_from_exception_unwind_exception( + static_cast<_Unwind_Exception*>(unwind_exception))->adjustedPtr; +#endif +} + +#if defined(_LIBCXXABI_ARM_EHABI) +/* +The routine to be called before the cleanup. This will save __cxa_exception in +__cxa_eh_globals, so that __cxa_end_cleanup() can recover later. +*/ +bool __cxa_begin_cleanup(void *unwind_arg) throw() { + _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg); + __cxa_eh_globals* globals = __cxa_get_globals(); + __cxa_exception* exception_header = + cxa_exception_from_exception_unwind_exception(unwind_exception); + + if (__isOurExceptionClass(unwind_exception)) + { + if (0 == exception_header->propagationCount) + { + exception_header->nextPropagatingException = globals->propagatingExceptions; + globals->propagatingExceptions = exception_header; + } + ++exception_header->propagationCount; + } + else + { + // If the propagatingExceptions stack is not empty, since we can't + // chain the foreign exception, terminate it. + if (NULL != globals->propagatingExceptions) + std::terminate(); + globals->propagatingExceptions = exception_header; + } + return true; +} + +/* +The routine to be called after the cleanup has been performed. It will get the +propagating __cxa_exception from __cxa_eh_globals, and continue the stack +unwinding with _Unwind_Resume. + +According to ARM EHABI 8.4.1, __cxa_end_cleanup() should not clobber any +register, thus we have to write this function in assembly so that we can save +{r1, r2, r3}. We don't have to save r0 because it is the return value and the +first argument to _Unwind_Resume(). In addition, we are saving r4 in order to +align the stack to 16 bytes, even though it is a callee-save register. +*/ +__attribute__((used)) static _Unwind_Exception * +__cxa_end_cleanup_impl() +{ + __cxa_eh_globals* globals = __cxa_get_globals(); + __cxa_exception* exception_header = globals->propagatingExceptions; + if (NULL == exception_header) + { + // It seems that __cxa_begin_cleanup() is not called properly. + // We have no choice but terminate the program now. + std::terminate(); + } + + if (__isOurExceptionClass(&exception_header->unwindHeader)) + { + --exception_header->propagationCount; + if (0 == exception_header->propagationCount) + { + globals->propagatingExceptions = exception_header->nextPropagatingException; + exception_header->nextPropagatingException = NULL; + } + } + else + { + globals->propagatingExceptions = NULL; + } + return &exception_header->unwindHeader; +} + +asm ( + " .pushsection .text.__cxa_end_cleanup,\"ax\",%progbits\n" + " .globl __cxa_end_cleanup\n" + " .type __cxa_end_cleanup,%function\n" + "__cxa_end_cleanup:\n" + " push {r1, r2, r3, r4}\n" + " bl __cxa_end_cleanup_impl\n" + " pop {r1, r2, r3, r4}\n" + " bl _Unwind_Resume\n" + " bl abort\n" + " .popsection" +); +#endif // defined(_LIBCXXABI_ARM_EHABI) + +/* +This routine can catch foreign or native exceptions. If native, the exception +can be a primary or dependent variety. This routine may remain blissfully +ignorant of whether the native exception is primary or dependent. + +If the exception is native: +* Increment's the exception's handler count. +* Push the exception on the stack of currently-caught exceptions if it is not + already there (from a rethrow). +* Decrements the uncaught_exception count. +* Returns the adjusted pointer to the exception object, which is stored in + the __cxa_exception by the personality routine. + +If the exception is foreign, this means it did not originate from one of throw +routines. The foreign exception does not necessarily have a __cxa_exception +header. However we can catch it here with a catch (...), or with a call +to terminate or unexpected during unwinding. +* Do not try to increment the exception's handler count, we don't know where + it is. +* Push the exception on the stack of currently-caught exceptions only if the + stack is empty. The foreign exception has no way to link to the current + top of stack. If the stack is not empty, call terminate. Even with an + empty stack, this is hacked in by pushing a pointer to an imaginary + __cxa_exception block in front of the foreign exception. It would be better + if the __cxa_eh_globals structure had a stack of _Unwind_Exception, but it + doesn't. It has a stack of __cxa_exception (which has a next* in it). +* Do not decrement the uncaught_exception count because we didn't increment it + in __cxa_throw (or one of our rethrow functions). +* If we haven't terminated, assume the exception object is just past the + _Unwind_Exception and return a pointer to that. +*/ +void* +__cxa_begin_catch(void* unwind_arg) throw() +{ + _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg); + bool native_exception = __isOurExceptionClass(unwind_exception); + __cxa_eh_globals* globals = __cxa_get_globals(); + // exception_header is a hackish offset from a foreign exception, but it + // works as long as we're careful not to try to access any __cxa_exception + // parts. + __cxa_exception* exception_header = + cxa_exception_from_exception_unwind_exception + ( + static_cast<_Unwind_Exception*>(unwind_exception) + ); + if (native_exception) + { + // Increment the handler count, removing the flag about being rethrown + exception_header->handlerCount = exception_header->handlerCount < 0 ? + -exception_header->handlerCount + 1 : exception_header->handlerCount + 1; + // place the exception on the top of the stack if it's not already + // there by a previous rethrow + if (exception_header != globals->caughtExceptions) + { + exception_header->nextException = globals->caughtExceptions; + globals->caughtExceptions = exception_header; + } + globals->uncaughtExceptions -= 1; // Not atomically, since globals are thread-local +#if defined(_LIBCXXABI_ARM_EHABI) + return reinterpret_cast(exception_header->unwindHeader.barrier_cache.bitpattern[0]); +#else + return exception_header->adjustedPtr; +#endif + } + // Else this is a foreign exception + // If the caughtExceptions stack is not empty, terminate + if (globals->caughtExceptions != 0) + std::terminate(); + // Push the foreign exception on to the stack + globals->caughtExceptions = exception_header; + return unwind_exception + 1; +} + + +/* +Upon exit for any reason, a handler must call: + void __cxa_end_catch (); + +This routine can be called for either a native or foreign exception. +For a native exception: +* Locates the most recently caught exception and decrements its handler count. +* Removes the exception from the caught exception stack, if the handler count goes to zero. +* If the handler count goes down to zero, and the exception was not re-thrown + by throw, it locates the primary exception (which may be the same as the one + it's handling) and decrements its reference count. If that reference count + goes to zero, the function destroys the exception. In any case, if the current + exception is a dependent exception, it destroys that. + +For a foreign exception: +* If it has been rethrown, there is nothing to do. +* Otherwise delete the exception and pop the catch stack to empty. +*/ +void __cxa_end_catch() { + static_assert(sizeof(__cxa_exception) == sizeof(__cxa_dependent_exception), + "sizeof(__cxa_exception) must be equal to " + "sizeof(__cxa_dependent_exception)"); + static_assert(__builtin_offsetof(__cxa_exception, referenceCount) == + __builtin_offsetof(__cxa_dependent_exception, + primaryException), + "the layout of __cxa_exception must match the layout of " + "__cxa_dependent_exception"); + static_assert(__builtin_offsetof(__cxa_exception, handlerCount) == + __builtin_offsetof(__cxa_dependent_exception, handlerCount), + "the layout of __cxa_exception must match the layout of " + "__cxa_dependent_exception"); + __cxa_eh_globals* globals = __cxa_get_globals_fast(); // __cxa_get_globals called in __cxa_begin_catch + __cxa_exception* exception_header = globals->caughtExceptions; + // If we've rethrown a foreign exception, then globals->caughtExceptions + // will have been made an empty stack by __cxa_rethrow() and there is + // nothing more to be done. Do nothing! + if (NULL != exception_header) + { + bool native_exception = __isOurExceptionClass(&exception_header->unwindHeader); + if (native_exception) + { + // This is a native exception + if (exception_header->handlerCount < 0) + { + // The exception has been rethrown by __cxa_rethrow, so don't delete it + if (0 == incrementHandlerCount(exception_header)) + { + // Remove from the chain of uncaught exceptions + globals->caughtExceptions = exception_header->nextException; + // but don't destroy + } + // Keep handlerCount negative in case there are nested catch's + // that need to be told that this exception is rethrown. Don't + // erase this rethrow flag until the exception is recaught. + } + else + { + // The native exception has not been rethrown + if (0 == decrementHandlerCount(exception_header)) + { + // Remove from the chain of uncaught exceptions + globals->caughtExceptions = exception_header->nextException; + // Destroy this exception, being careful to distinguish + // between dependent and primary exceptions + if (isDependentException(&exception_header->unwindHeader)) + { + // Reset exception_header to primaryException and deallocate the dependent exception + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(exception_header); + exception_header = + cxa_exception_from_thrown_object(dep_exception_header->primaryException); + __cxa_free_dependent_exception(dep_exception_header); + } + // Destroy the primary exception only if its referenceCount goes to 0 + // (this decrement must be atomic) + __cxa_decrement_exception_refcount(thrown_object_from_cxa_exception(exception_header)); + } + } + } + else + { + // The foreign exception has not been rethrown. Pop the stack + // and delete it. If there are nested catch's and they try + // to touch a foreign exception in any way, that is undefined + // behavior. They likely can't since the only way to catch + // a foreign exception is with catch (...)! + _Unwind_DeleteException(&globals->caughtExceptions->unwindHeader); + globals->caughtExceptions = 0; + } + } +} + +// Note: exception_header may be masquerading as a __cxa_dependent_exception +// and that's ok. exceptionType is there too. +// However watch out for foreign exceptions. Return null for them. +std::type_info *__cxa_current_exception_type() { +// get the current exception + __cxa_eh_globals *globals = __cxa_get_globals_fast(); + if (NULL == globals) + return NULL; // If there have never been any exceptions, there are none now. + __cxa_exception *exception_header = globals->caughtExceptions; + if (NULL == exception_header) + return NULL; // No current exception + if (!__isOurExceptionClass(&exception_header->unwindHeader)) + return NULL; + return exception_header->exceptionType; +} + +// 2.5.4 Rethrowing Exceptions +/* This routine can rethrow native or foreign exceptions. +If the exception is native: +* marks the exception object on top of the caughtExceptions stack + (in an implementation-defined way) as being rethrown. +* If the caughtExceptions stack is empty, it calls terminate() + (see [C++FDIS] [except.throw], 15.1.8). +* It then calls _Unwind_RaiseException which should not return + (terminate if it does). + Note: exception_header may be masquerading as a __cxa_dependent_exception + and that's ok. +*/ +void __cxa_rethrow() { + __cxa_eh_globals* globals = __cxa_get_globals(); + __cxa_exception* exception_header = globals->caughtExceptions; + if (NULL == exception_header) + std::terminate(); // throw; called outside of a exception handler + bool native_exception = __isOurExceptionClass(&exception_header->unwindHeader); + if (native_exception) + { + // Mark the exception as being rethrown (reverse the effects of __cxa_begin_catch) + exception_header->handlerCount = -exception_header->handlerCount; + globals->uncaughtExceptions += 1; + // __cxa_end_catch will remove this exception from the caughtExceptions stack if necessary + } + else // this is a foreign exception + { + // The only way to communicate to __cxa_end_catch that we've rethrown + // a foreign exception, so don't delete us, is to pop the stack here + // which must be empty afterwards. Then __cxa_end_catch will do + // nothing + globals->caughtExceptions = 0; + } +#ifdef __USING_SJLJ_EXCEPTIONS__ + _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); +#else + _Unwind_RaiseException(&exception_header->unwindHeader); +#endif + + // If we get here, some kind of unwinding error has occurred. + // There is some weird code generation bug happening with + // Apple clang version 4.0 (tags/Apple/clang-418.0.2) (based on LLVM 3.1svn) + // If we call failed_throw here. Turns up with -O2 or higher, and -Os. + __cxa_begin_catch(&exception_header->unwindHeader); + if (native_exception) + std::__terminate(exception_header->terminateHandler); + // Foreign exception: can't get exception_header->terminateHandler + std::terminate(); +} + +/* + If thrown_object is not null, atomically increment the referenceCount field + of the __cxa_exception header associated with the thrown object referred to + by thrown_object. + + Requires: If thrown_object is not NULL, it is a native exception. +*/ +void +__cxa_increment_exception_refcount(void *thrown_object) throw() { + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(1)); + } +} + +/* + If thrown_object is not null, atomically decrement the referenceCount field + of the __cxa_exception header associated with the thrown object referred to + by thrown_object. If the referenceCount drops to zero, destroy and + deallocate the exception. + + Requires: If thrown_object is not NULL, it is a native exception. +*/ +_LIBCXXABI_NO_CFI +void __cxa_decrement_exception_refcount(void *thrown_object) throw() { + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + if (std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(-1)) == 0) + { + if (NULL != exception_header->exceptionDestructor) + exception_header->exceptionDestructor(thrown_object); + __cxa_free_exception(thrown_object); + } + } +} + +/* + Returns a pointer to the thrown object (if any) at the top of the + caughtExceptions stack. Atomically increment the exception's referenceCount. + If there is no such thrown object or if the thrown object is foreign, + returns null. + + We can use __cxa_get_globals_fast here to get the globals because if there have + been no exceptions thrown, ever, on this thread, we can return NULL without + the need to allocate the exception-handling globals. +*/ +void *__cxa_current_primary_exception() throw() { +// get the current exception + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (NULL == globals) + return NULL; // If there are no globals, there is no exception + __cxa_exception* exception_header = globals->caughtExceptions; + if (NULL == exception_header) + return NULL; // No current exception + if (!__isOurExceptionClass(&exception_header->unwindHeader)) + return NULL; // Can't capture a foreign exception (no way to refcount it) + if (isDependentException(&exception_header->unwindHeader)) { + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(exception_header); + exception_header = cxa_exception_from_thrown_object(dep_exception_header->primaryException); + } + void* thrown_object = thrown_object_from_cxa_exception(exception_header); + __cxa_increment_exception_refcount(thrown_object); + return thrown_object; +} + +/* + If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler + stored in exc is called. Otherwise the referenceCount stored in the + primary exception is decremented, destroying the primary if necessary. + Finally the dependent exception is destroyed. +*/ +static +void +dependent_exception_cleanup(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exception) +{ + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(unwind_exception + 1) - 1; + if (_URC_FOREIGN_EXCEPTION_CAUGHT != reason) + std::__terminate(dep_exception_header->terminateHandler); + __cxa_decrement_exception_refcount(dep_exception_header->primaryException); + __cxa_free_dependent_exception(dep_exception_header); +} + +/* + If thrown_object is not null, allocate, initialize and throw a dependent + exception. +*/ +void +__cxa_rethrow_primary_exception(void* thrown_object) +{ + if ( thrown_object != NULL ) + { + // thrown_object guaranteed to be native because + // __cxa_current_primary_exception returns NULL for foreign exceptions + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + __cxa_dependent_exception* dep_exception_header = + static_cast<__cxa_dependent_exception*>(__cxa_allocate_dependent_exception()); + dep_exception_header->primaryException = thrown_object; + __cxa_increment_exception_refcount(thrown_object); + dep_exception_header->exceptionType = exception_header->exceptionType; + dep_exception_header->unexpectedHandler = std::get_unexpected(); + dep_exception_header->terminateHandler = std::get_terminate(); + setDependentExceptionClass(&dep_exception_header->unwindHeader); + __cxa_get_globals()->uncaughtExceptions += 1; + dep_exception_header->unwindHeader.exception_cleanup = dependent_exception_cleanup; +#ifdef __USING_SJLJ_EXCEPTIONS__ + _Unwind_SjLj_RaiseException(&dep_exception_header->unwindHeader); +#else + _Unwind_RaiseException(&dep_exception_header->unwindHeader); +#endif + // Some sort of unwinding error. Note that terminate is a handler. + __cxa_begin_catch(&dep_exception_header->unwindHeader); + } + // If we return client will call terminate() +} + +bool +__cxa_uncaught_exception() throw() { return __cxa_uncaught_exceptions() != 0; } + +unsigned int +__cxa_uncaught_exceptions() throw() +{ + // This does not report foreign exceptions in flight + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (globals == 0) + return 0; + return globals->uncaughtExceptions; +} + +} // extern "C" + +} // abi Index: contrib/libcxxabi/src/cxa_exception_storage.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_exception_storage.cpp @@ -0,0 +1,101 @@ +//===--------------------- cxa_exception_storage.cpp ----------------------===// +// +// 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 +// +// +// This file implements the storage for the "Caught Exception Stack" +// http://mentorembedded.github.io/cxx-abi/abi-eh.html (section 2.2.2) +// +//===----------------------------------------------------------------------===// + +#include "cxa_exception.hpp" + +#include <__threading_support> + +#if defined(_LIBCXXABI_HAS_NO_THREADS) + +namespace __cxxabiv1 { +extern "C" { + static __cxa_eh_globals eh_globals; + __cxa_eh_globals *__cxa_get_globals() { return &eh_globals; } + __cxa_eh_globals *__cxa_get_globals_fast() { return &eh_globals; } + } +} + +#elif defined(HAS_THREAD_LOCAL) + +namespace __cxxabiv1 { + +namespace { + __cxa_eh_globals * __globals () { + static thread_local __cxa_eh_globals eh_globals; + return &eh_globals; + } + } + +extern "C" { + __cxa_eh_globals * __cxa_get_globals () { return __globals (); } + __cxa_eh_globals * __cxa_get_globals_fast () { return __globals (); } + } +} + +#else + +#include "abort_message.h" +#include "fallback_malloc.h" + +// In general, we treat all threading errors as fatal. +// We cannot call std::terminate() because that will in turn +// call __cxa_get_globals() and cause infinite recursion. + +namespace __cxxabiv1 { +namespace { + std::__libcpp_tls_key key_; + std::__libcpp_exec_once_flag flag_ = _LIBCPP_EXEC_ONCE_INITIALIZER; + + void _LIBCPP_TLS_DESTRUCTOR_CC destruct_ (void *p) { + __free_with_fallback ( p ); + if ( 0 != std::__libcpp_tls_set ( key_, NULL ) ) + abort_message("cannot zero out thread value for __cxa_get_globals()"); + } + + void construct_ () { + if ( 0 != std::__libcpp_tls_create ( &key_, destruct_ ) ) + abort_message("cannot create thread specific key for __cxa_get_globals()"); + } +} + +extern "C" { + __cxa_eh_globals * __cxa_get_globals () { + // Try to get the globals for this thread + __cxa_eh_globals* retVal = __cxa_get_globals_fast (); + + // If this is the first time we've been asked for these globals, create them + if ( NULL == retVal ) { + retVal = static_cast<__cxa_eh_globals*> + (__calloc_with_fallback (1, sizeof (__cxa_eh_globals))); + if ( NULL == retVal ) + abort_message("cannot allocate __cxa_eh_globals"); + if ( 0 != std::__libcpp_tls_set ( key_, retVal ) ) + abort_message("std::__libcpp_tls_set failure in __cxa_get_globals()"); + } + return retVal; + } + + // Note that this implementation will reliably return NULL if not + // preceded by a call to __cxa_get_globals(). This is an extension + // to the Itanium ABI and is taken advantage of in several places in + // libc++abi. + __cxa_eh_globals * __cxa_get_globals_fast () { + // First time through, create the key. + if (0 != std::__libcpp_execute_once(&flag_, construct_)) + abort_message("execute once failure in __cxa_get_globals_fast()"); +// static int init = construct_(); + return static_cast<__cxa_eh_globals*>(std::__libcpp_tls_get(key_)); + } + +} +} +#endif Index: contrib/libcxxabi/src/cxa_guard.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_guard.cpp @@ -0,0 +1,264 @@ +//===---------------------------- cxa_guard.cpp ---------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "__cxxabi_config.h" + +#include "abort_message.h" +#include <__threading_support> + +#include + +/* + This implementation must be careful to not call code external to this file + which will turn around and try to call __cxa_guard_acquire reentrantly. + For this reason, the headers of this file are as restricted as possible. + Previous implementations of this code for __APPLE__ have used + std::__libcpp_mutex_lock and the abort_message utility without problem. This + implementation also uses std::__libcpp_condvar_wait which has tested + to not be a problem. +*/ + +namespace __cxxabiv1 +{ + +namespace +{ + +#ifdef __arm__ +// A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must +// be statically initialized to 0. +typedef uint32_t guard_type; + +inline void set_initialized(guard_type* guard_object) { + *guard_object |= 1; +} +#else +typedef uint64_t guard_type; + +void set_initialized(guard_type* guard_object) { + char* initialized = (char*)guard_object; + *initialized = 1; +} +#endif + +#if defined(_LIBCXXABI_HAS_NO_THREADS) || (defined(__APPLE__) && !defined(__arm__)) +#ifdef __arm__ + +// Test the lowest bit. +inline bool is_initialized(guard_type* guard_object) { + return (*guard_object) & 1; +} + +#else + +bool is_initialized(guard_type* guard_object) { + char* initialized = (char*)guard_object; + return *initialized; +} + +#endif +#endif + +#ifndef _LIBCXXABI_HAS_NO_THREADS +std::__libcpp_mutex_t guard_mut = _LIBCPP_MUTEX_INITIALIZER; +std::__libcpp_condvar_t guard_cv = _LIBCPP_CONDVAR_INITIALIZER; +#endif + +#if defined(__APPLE__) && !defined(__arm__) + +typedef uint32_t lock_type; + +#if __LITTLE_ENDIAN__ + +inline +lock_type +get_lock(uint64_t x) +{ + return static_cast(x >> 32); +} + +inline +void +set_lock(uint64_t& x, lock_type y) +{ + x = static_cast(y) << 32; +} + +#else // __LITTLE_ENDIAN__ + +inline +lock_type +get_lock(uint64_t x) +{ + return static_cast(x); +} + +inline +void +set_lock(uint64_t& x, lock_type y) +{ + x = y; +} + +#endif // __LITTLE_ENDIAN__ + +#else // !__APPLE__ || __arm__ + +typedef bool lock_type; + +#if !defined(__arm__) +static_assert(std::is_same::value, ""); + +inline lock_type get_lock(uint64_t x) +{ + union + { + uint64_t guard; + uint8_t lock[2]; + } f = {x}; + return f.lock[1] != 0; +} + +inline void set_lock(uint64_t& x, lock_type y) +{ + union + { + uint64_t guard; + uint8_t lock[2]; + } f = {0}; + f.lock[1] = y; + x = f.guard; +} +#else // defined(__arm__) +static_assert(std::is_same::value, ""); + +inline lock_type get_lock(uint32_t x) +{ + union + { + uint32_t guard; + uint8_t lock[2]; + } f = {x}; + return f.lock[1] != 0; +} + +inline void set_lock(uint32_t& x, lock_type y) +{ + union + { + uint32_t guard; + uint8_t lock[2]; + } f = {0}; + f.lock[1] = y; + x = f.guard; +} + +#endif // !defined(__arm__) + +#endif // __APPLE__ && !__arm__ + +} // unnamed namespace + +extern "C" +{ + +#ifndef _LIBCXXABI_HAS_NO_THREADS +_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { + char* initialized = (char*)guard_object; + if (std::__libcpp_mutex_lock(&guard_mut)) + abort_message("__cxa_guard_acquire failed to acquire mutex"); + int result = *initialized == 0; + if (result) + { +#if defined(__APPLE__) && !defined(__arm__) + // This is a special-case pthread dependency for Mac. We can't pull this + // out into libcxx's threading API (__threading_support) because not all + // supported Mac environments provide this function (in pthread.h). To + // make it possible to build/use libcxx in those environments, we have to + // keep this pthread dependency local to libcxxabi. If there is some + // convenient way to detect precisely when pthread_mach_thread_np is + // available in a given Mac environment, it might still be possible to + // bury this dependency in __threading_support. + #ifdef _LIBCPP_HAS_THREAD_API_PTHREAD + const lock_type id = pthread_mach_thread_np(std::__libcpp_thread_get_current_id()); + #else + #error "How do I pthread_mach_thread_np()?" + #endif + lock_type lock = get_lock(*guard_object); + if (lock) + { + // if this thread set lock for this same guard_object, abort + if (lock == id) + abort_message("__cxa_guard_acquire detected deadlock"); + do + { + if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) + abort_message("__cxa_guard_acquire condition variable wait failed"); + lock = get_lock(*guard_object); + } while (lock); + result = !is_initialized(guard_object); + if (result) + set_lock(*guard_object, id); + } + else + set_lock(*guard_object, id); +#else // !__APPLE__ || __arm__ + while (get_lock(*guard_object)) + if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) + abort_message("__cxa_guard_acquire condition variable wait failed"); + result = *initialized == 0; + if (result) + set_lock(*guard_object, true); +#endif // !__APPLE__ || __arm__ + } + if (std::__libcpp_mutex_unlock(&guard_mut)) + abort_message("__cxa_guard_acquire failed to release mutex"); + return result; +} + +_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { + if (std::__libcpp_mutex_lock(&guard_mut)) + abort_message("__cxa_guard_release failed to acquire mutex"); + *guard_object = 0; + set_initialized(guard_object); + if (std::__libcpp_mutex_unlock(&guard_mut)) + abort_message("__cxa_guard_release failed to release mutex"); + if (std::__libcpp_condvar_broadcast(&guard_cv)) + abort_message("__cxa_guard_release failed to broadcast condition variable"); +} + +_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { + if (std::__libcpp_mutex_lock(&guard_mut)) + abort_message("__cxa_guard_abort failed to acquire mutex"); + *guard_object = 0; + if (std::__libcpp_mutex_unlock(&guard_mut)) + abort_message("__cxa_guard_abort failed to release mutex"); + if (std::__libcpp_condvar_broadcast(&guard_cv)) + abort_message("__cxa_guard_abort failed to broadcast condition variable"); +} + +#else // _LIBCXXABI_HAS_NO_THREADS + +_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { + return !is_initialized(guard_object); +} + +_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { + *guard_object = 0; + set_initialized(guard_object); +} + +_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { + *guard_object = 0; +} + +#endif // !_LIBCXXABI_HAS_NO_THREADS + +} // extern "C" + +} // __cxxabiv1 Index: contrib/libcxxabi/src/cxa_handlers.hpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_handlers.hpp @@ -0,0 +1,55 @@ +//===------------------------- cxa_handlers.cpp ---------------------------===// +// +// 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 +// +// +// This file implements the functionality associated with the terminate_handler, +// unexpected_handler, and new_handler. +//===----------------------------------------------------------------------===// + +#ifndef _CXA_HANDLERS_H +#define _CXA_HANDLERS_H + +#include <__cxxabi_config.h> + +#include + +namespace std +{ + +_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN +void +__unexpected(unexpected_handler func); + +_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN +void +__terminate(terminate_handler func) _NOEXCEPT; + +} // std + +extern "C" +{ + +_LIBCXXABI_DATA_VIS extern void (*__cxa_terminate_handler)(); +_LIBCXXABI_DATA_VIS extern void (*__cxa_unexpected_handler)(); +_LIBCXXABI_DATA_VIS extern void (*__cxa_new_handler)(); + +/* + + At some point in the future these three symbols will become + C++11 atomic variables: + + extern std::atomic __cxa_terminate_handler; + extern std::atomic __cxa_unexpected_handler; + extern std::atomic __cxa_new_handler; + + This change will not impact their ABI. But it will allow for a + portable performance optimization. + +*/ + +} // extern "C" + +#endif // _CXA_HANDLERS_H Index: contrib/libcxxabi/src/cxa_handlers.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_handlers.cpp @@ -0,0 +1,109 @@ +//===------------------------- cxa_handlers.cpp ---------------------------===// +// +// 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 +// +// +// This file implements the functionality associated with the terminate_handler, +// unexpected_handler, and new_handler. +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include "abort_message.h" +#include "cxxabi.h" +#include "cxa_handlers.hpp" +#include "cxa_exception.hpp" +#include "private_typeinfo.h" +#include "include/atomic_support.h" + +namespace std +{ + +unexpected_handler +get_unexpected() _NOEXCEPT +{ + return __libcpp_atomic_load(&__cxa_unexpected_handler, _AO_Acquire); +} + +void +__unexpected(unexpected_handler func) +{ + func(); + // unexpected handler should not return + abort_message("unexpected_handler unexpectedly returned"); +} + +__attribute__((noreturn)) +void +unexpected() +{ + __unexpected(get_unexpected()); +} + +terminate_handler +get_terminate() _NOEXCEPT +{ + return __libcpp_atomic_load(&__cxa_terminate_handler, _AO_Acquire); +} + +void +__terminate(terminate_handler func) _NOEXCEPT +{ +#ifndef _LIBCXXABI_NO_EXCEPTIONS + try + { +#endif // _LIBCXXABI_NO_EXCEPTIONS + func(); + // handler should not return + abort_message("terminate_handler unexpectedly returned"); +#ifndef _LIBCXXABI_NO_EXCEPTIONS + } + catch (...) + { + // handler should not throw exception + abort_message("terminate_handler unexpectedly threw an exception"); + } +#endif // _LIBCXXABI_NO_EXCEPTIONS +} + +__attribute__((noreturn)) +void +terminate() _NOEXCEPT +{ + // If there might be an uncaught exception + using namespace __cxxabiv1; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (globals) + { + __cxa_exception* exception_header = globals->caughtExceptions; + if (exception_header) + { + _Unwind_Exception* unwind_exception = + reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1; + if (__isOurExceptionClass(unwind_exception)) + __terminate(exception_header->terminateHandler); + } + } + __terminate(get_terminate()); +} + +extern "C" { +new_handler __cxa_new_handler = 0; +} + +new_handler +set_new_handler(new_handler handler) _NOEXCEPT +{ + return __libcpp_atomic_exchange(&__cxa_new_handler, handler, _AO_Acq_Rel); +} + +new_handler +get_new_handler() _NOEXCEPT +{ + return __libcpp_atomic_load(&__cxa_new_handler, _AO_Acquire); +} + +} // std Index: contrib/libcxxabi/src/cxa_noexception.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_noexception.cpp @@ -0,0 +1,59 @@ +//===------------------------- cxa_exception.cpp --------------------------===// +// +// 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 +// +// +// This file implements the "Exception Handling APIs" +// http://mentorembedded.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +// Support functions for the no-exceptions libc++ library + +#include "cxxabi.h" + +#include // for std::terminate +#include "cxa_exception.hpp" +#include "cxa_handlers.hpp" + +namespace __cxxabiv1 { + +extern "C" { + +void +__cxa_increment_exception_refcount(void *thrown_object) throw() { + if (thrown_object != nullptr) + std::terminate(); +} + +void +__cxa_decrement_exception_refcount(void *thrown_object) throw() { + if (thrown_object != nullptr) + std::terminate(); +} + + +void *__cxa_current_primary_exception() throw() { return nullptr; } + +void +__cxa_rethrow_primary_exception(void* thrown_object) { + if (thrown_object != nullptr) + std::terminate(); +} + +bool +__cxa_uncaught_exception() throw() { return false; } + +unsigned int +__cxa_uncaught_exceptions() throw() { return 0; } + +} // extern "C" + +// provide dummy implementations for the 'no exceptions' case. +uint64_t __getExceptionClass (const _Unwind_Exception*) { return 0; } +void __setExceptionClass ( _Unwind_Exception*, uint64_t) {} +bool __isOurExceptionClass(const _Unwind_Exception*) { return false; } + +} // abi Index: contrib/libcxxabi/src/cxa_personality.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_personality.cpp @@ -0,0 +1,1324 @@ +//===------------------------- cxa_exception.cpp --------------------------===// +// +// 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 +// +// +// This file implements the "Exception Handling APIs" +// http://mentorembedded.github.io/cxx-abi/abi-eh.html +// http://www.intel.com/design/itanium/downloads/245358.htm +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +#include "__cxxabi_config.h" +#include "cxa_exception.hpp" +#include "cxa_handlers.hpp" +#include "private_typeinfo.h" +#include "unwind.h" + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +#include +#include + +extern "C" EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, + void *, PCONTEXT, + PDISPATCHER_CONTEXT, + _Unwind_Personality_Fn); +#endif + +/* + Exception Header Layout: + ++---------------------------+-----------------------------+---------------+ +| __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object | ++---------------------------+-----------------------------+---------------+ + ^ + | + +-------------------------------------------------------+ + | ++---------------------------+-----------------------------+ +| __cxa_dependent_exception | _Unwind_Exception CLNGC++\1 | ++---------------------------+-----------------------------+ + + Exception Handling Table Layout: + ++-----------------+--------+ +| lpStartEncoding | (char) | ++---------+-------+--------+---------------+-----------------------+ +| lpStart | (encoded with lpStartEncoding) | defaults to funcStart | ++---------+-----+--------+-----------------+---------------+-------+ +| ttypeEncoding | (char) | Encoding of the type_info table | ++---------------+-+------+----+----------------------------+----------------+ +| classInfoOffset | (ULEB128) | Offset to type_info table, defaults to null | ++-----------------++--------+-+----------------------------+----------------+ +| callSiteEncoding | (char) | Encoding for Call Site Table | ++------------------+--+-----+-----+------------------------+--------------------------+ +| callSiteTableLength | (ULEB128) | Call Site Table length, used to find Action table | ++---------------------+-----------+---------------------------------------------------+ +#ifndef __USING_SJLJ_EXCEPTIONS__ ++---------------------+-----------+------------------------------------------------+ +| Beginning of Call Site Table The current ip lies within the | +| ... (start, length) range of one of these | +| call sites. There may be action needed. | +| +-------------+---------------------------------+------------------------------+ | +| | start | (encoded with callSiteEncoding) | offset relative to funcStart | | +| | length | (encoded with callSiteEncoding) | length of code fragment | | +| | landingPad | (encoded with callSiteEncoding) | offset relative to lpStart | | +| | actionEntry | (ULEB128) | Action Table Index 1-based | | +| | | | actionEntry == 0 -> cleanup | | +| +-------------+---------------------------------+------------------------------+ | +| ... | ++----------------------------------------------------------------------------------+ +#else // __USING_SJLJ_EXCEPTIONS__ ++---------------------+-----------+------------------------------------------------+ +| Beginning of Call Site Table The current ip is a 1-based index into | +| ... this table. Or it is -1 meaning no | +| action is needed. Or it is 0 meaning | +| terminate. | +| +-------------+---------------------------------+------------------------------+ | +| | landingPad | (ULEB128) | offset relative to lpStart | | +| | actionEntry | (ULEB128) | Action Table Index 1-based | | +| | | | actionEntry == 0 -> cleanup | | +| +-------------+---------------------------------+------------------------------+ | +| ... | ++----------------------------------------------------------------------------------+ +#endif // __USING_SJLJ_EXCEPTIONS__ ++---------------------------------------------------------------------+ +| Beginning of Action Table ttypeIndex == 0 : cleanup | +| ... ttypeIndex > 0 : catch | +| ttypeIndex < 0 : exception spec | +| +--------------+-----------+--------------------------------------+ | +| | ttypeIndex | (SLEB128) | Index into type_info Table (1-based) | | +| | actionOffset | (SLEB128) | Offset into next Action Table entry | | +| +--------------+-----------+--------------------------------------+ | +| ... | ++---------------------------------------------------------------------+-----------------+ +| type_info Table, but classInfoOffset does *not* point here! | +| +----------------+------------------------------------------------+-----------------+ | +| | Nth type_info* | Encoded with ttypeEncoding, 0 means catch(...) | ttypeIndex == N | | +| +----------------+------------------------------------------------+-----------------+ | +| ... | +| +----------------+------------------------------------------------+-----------------+ | +| | 1st type_info* | Encoded with ttypeEncoding, 0 means catch(...) | ttypeIndex == 1 | | +| +----------------+------------------------------------------------+-----------------+ | +| +---------------------------------------+-----------+------------------------------+ | +| | 1st ttypeIndex for 1st exception spec | (ULEB128) | classInfoOffset points here! | | +| | ... | (ULEB128) | | | +| | Mth ttypeIndex for 1st exception spec | (ULEB128) | | | +| | 0 | (ULEB128) | | | +| +---------------------------------------+------------------------------------------+ | +| ... | +| +---------------------------------------+------------------------------------------+ | +| | 0 | (ULEB128) | throw() | | +| +---------------------------------------+------------------------------------------+ | +| ... | +| +---------------------------------------+------------------------------------------+ | +| | 1st ttypeIndex for Nth exception spec | (ULEB128) | | | +| | ... | (ULEB128) | | | +| | Mth ttypeIndex for Nth exception spec | (ULEB128) | | | +| | 0 | (ULEB128) | | | +| +---------------------------------------+------------------------------------------+ | ++---------------------------------------------------------------------------------------+ + +Notes: + +* ttypeIndex in the Action Table, and in the exception spec table, is an index, + not a byte count, if positive. It is a negative index offset of + classInfoOffset and the sizeof entry depends on ttypeEncoding. + But if ttypeIndex is negative, it is a positive 1-based byte offset into the + type_info Table. + And if ttypeIndex is zero, it refers to a catch (...). + +* landingPad can be 0, this implies there is nothing to be done. + +* landingPad != 0 and actionEntry == 0 implies a cleanup needs to be done + @landingPad. + +* A cleanup can also be found under landingPad != 0 and actionEntry != 0 in + the Action Table with ttypeIndex == 0. +*/ + +namespace __cxxabiv1 +{ + +namespace +{ + +template +uintptr_t readPointerHelper(const uint8_t*& p) { + AsType value; + memcpy(&value, p, sizeof(AsType)); + p += sizeof(AsType); + return static_cast(value); +} + +} // end namespace + +extern "C" +{ + +// private API + +// Heavily borrowed from llvm/examples/ExceptionDemo/ExceptionDemo.cpp + +// DWARF Constants +enum +{ + DW_EH_PE_absptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + +/// Read a uleb128 encoded value and advance pointer +/// See Variable Length Data Appendix C in: +/// @link http://dwarfstd.org/Dwarf4.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @returns decoded value +static +uintptr_t +readULEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t *p = *data; + do + { + byte = *p++; + result |= static_cast(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + return result; +} + +/// Read a sleb128 encoded value and advance pointer +/// See Variable Length Data Appendix C in: +/// @link http://dwarfstd.org/Dwarf4.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @returns decoded value +static +intptr_t +readSLEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t *p = *data; + do + { + byte = *p++; + result |= static_cast(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + if ((byte & 0x40) && (shift < (sizeof(result) << 3))) + result |= static_cast(~0) << shift; + return static_cast(result); +} + +/// Read a pointer encoded value and advance pointer +/// See Variable Length Data in: +/// @link http://dwarfstd.org/Dwarf3.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @param encoding dwarf encoding type +/// @returns decoded value +static +uintptr_t +readEncodedPointer(const uint8_t** data, uint8_t encoding) +{ + uintptr_t result = 0; + if (encoding == DW_EH_PE_omit) + return result; + const uint8_t* p = *data; + // first get value + switch (encoding & 0x0F) + { + case DW_EH_PE_absptr: + result = readPointerHelper(p); + break; + case DW_EH_PE_uleb128: + result = readULEB128(&p); + break; + case DW_EH_PE_sleb128: + result = static_cast(readSLEB128(&p)); + break; + case DW_EH_PE_udata2: + result = readPointerHelper(p); + break; + case DW_EH_PE_udata4: + result = readPointerHelper(p); + break; + case DW_EH_PE_udata8: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata2: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata4: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata8: + result = readPointerHelper(p); + break; + default: + // not supported + abort(); + break; + } + // then add relative offset + switch (encoding & 0x70) + { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + if (result) + result += (uintptr_t)(*data); + break; + case DW_EH_PE_textrel: + case DW_EH_PE_datarel: + case DW_EH_PE_funcrel: + case DW_EH_PE_aligned: + default: + // not supported + abort(); + break; + } + // then apply indirection + if (result && (encoding & DW_EH_PE_indirect)) + result = *((uintptr_t*)result); + *data = p; + return result; +} + +static +void +call_terminate(bool native_exception, _Unwind_Exception* unwind_exception) +{ + __cxa_begin_catch(unwind_exception); + if (native_exception) + { + // Use the stored terminate_handler if possible + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + std::__terminate(exception_header->terminateHandler); + } + std::terminate(); +} + +#if defined(_LIBCXXABI_ARM_EHABI) +static const void* read_target2_value(const void* ptr) +{ + uintptr_t offset = *reinterpret_cast(ptr); + if (!offset) + return 0; + // "ARM EABI provides a TARGET2 relocation to describe these typeinfo + // pointers. The reason being it allows their precise semantics to be + // deferred to the linker. For bare-metal they turn into absolute + // relocations. For linux they turn into GOT-REL relocations." + // https://gcc.gnu.org/ml/gcc-patches/2009-08/msg00264.html +#if defined(LIBCXXABI_BAREMETAL) + return reinterpret_cast(reinterpret_cast(ptr) + + offset); +#else + return *reinterpret_cast(reinterpret_cast(ptr) + + offset); +#endif +} + +static const __shim_type_info* +get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, bool native_exception, + _Unwind_Exception* unwind_exception) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + + assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 + (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal + (ttypeEncoding == (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux + "Unexpected TTypeEncoding"); + (void)ttypeEncoding; + + const uint8_t* ttypePtr = classInfo - ttypeIndex * sizeof(uintptr_t); + return reinterpret_cast( + read_target2_value(ttypePtr)); +} +#else // !defined(_LIBCXXABI_ARM_EHABI) +static +const __shim_type_info* +get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, bool native_exception, + _Unwind_Exception* unwind_exception) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + switch (ttypeEncoding & 0x0F) + { + case DW_EH_PE_absptr: + ttypeIndex *= sizeof(void*); + break; + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + ttypeIndex *= 2; + break; + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + ttypeIndex *= 4; + break; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + ttypeIndex *= 8; + break; + default: + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + classInfo -= ttypeIndex; + return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding); +} +#endif // !defined(_LIBCXXABI_ARM_EHABI) + +/* + This is checking a thrown exception type, excpType, against a possibly empty + list of catchType's which make up an exception spec. + + An exception spec acts like a catch handler, but in reverse. This "catch + handler" will catch an excpType if and only if none of the catchType's in + the list will catch a excpType. If any catchType in the list can catch an + excpType, then this exception spec does not catch the excpType. +*/ +#if defined(_LIBCXXABI_ARM_EHABI) +static +bool +exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, const __shim_type_info* excpType, + void* adjustedPtr, _Unwind_Exception* unwind_exception) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(false, unwind_exception); + } + + assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 + (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal + (ttypeEncoding == (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux + "Unexpected TTypeEncoding"); + (void)ttypeEncoding; + + // specIndex is negative of 1-based byte offset into classInfo; + specIndex = -specIndex; + --specIndex; + const void** temp = reinterpret_cast( + reinterpret_cast(classInfo) + + static_cast(specIndex) * sizeof(uintptr_t)); + // If any type in the spec list can catch excpType, return false, else return true + // adjustments to adjustedPtr are ignored. + while (true) + { + // ARM EHABI exception specification table (filter table) consists of + // several pointers which will directly point to the type info object + // (instead of ttypeIndex). The table will be terminated with 0. + const void** ttypePtr = temp++; + if (*ttypePtr == 0) + break; + // We can get the __shim_type_info simply by performing a + // R_ARM_TARGET2 relocation, and cast the result to __shim_type_info. + const __shim_type_info* catchType = + static_cast(read_target2_value(ttypePtr)); + void* tempPtr = adjustedPtr; + if (catchType->can_catch(excpType, tempPtr)) + return false; + } + return true; +} +#else +static +bool +exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, const __shim_type_info* excpType, + void* adjustedPtr, _Unwind_Exception* unwind_exception) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(false, unwind_exception); + } + // specIndex is negative of 1-based byte offset into classInfo; + specIndex = -specIndex; + --specIndex; + const uint8_t* temp = classInfo + specIndex; + // If any type in the spec list can catch excpType, return false, else return true + // adjustments to adjustedPtr are ignored. + while (true) + { + uint64_t ttypeIndex = readULEB128(&temp); + if (ttypeIndex == 0) + break; + const __shim_type_info* catchType = get_shim_type_info(ttypeIndex, + classInfo, + ttypeEncoding, + true, + unwind_exception); + void* tempPtr = adjustedPtr; + if (catchType->can_catch(excpType, tempPtr)) + return false; + } + return true; +} +#endif + +static +void* +get_thrown_object_ptr(_Unwind_Exception* unwind_exception) +{ + // Even for foreign exceptions, the exception object is *probably* at unwind_exception + 1 + // Regardless, this library is prohibited from touching a foreign exception + void* adjustedPtr = unwind_exception + 1; + if (__getExceptionClass(unwind_exception) == kOurDependentExceptionClass) + adjustedPtr = ((__cxa_dependent_exception*)adjustedPtr - 1)->primaryException; + return adjustedPtr; +} + +namespace +{ + +struct scan_results +{ + int64_t ttypeIndex; // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup + const uint8_t* actionRecord; // Currently unused. Retained to ease future maintenance. + const uint8_t* languageSpecificData; // Needed only for __cxa_call_unexpected + uintptr_t landingPad; // null -> nothing found, else something found + void* adjustedPtr; // Used in cxa_exception.cpp + _Unwind_Reason_Code reason; // One of _URC_FATAL_PHASE1_ERROR, + // _URC_FATAL_PHASE2_ERROR, + // _URC_CONTINUE_UNWIND, + // _URC_HANDLER_FOUND +}; + +} // unnamed namespace + +static +void +set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context, + const scan_results& results) +{ +#if defined(__USING_SJLJ_EXCEPTIONS__) +#define __builtin_eh_return_data_regno(regno) regno +#endif + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), + reinterpret_cast(unwind_exception)); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), + static_cast(results.ttypeIndex)); + _Unwind_SetIP(context, results.landingPad); +} + +/* + There are 3 types of scans needed: + + 1. Scan for handler with native or foreign exception. If handler found, + save state and return _URC_HANDLER_FOUND, else return _URC_CONTINUE_UNWIND. + May also report an error on invalid input. + May terminate for invalid exception table. + _UA_SEARCH_PHASE + + 2. Scan for handler with foreign exception. Must return _URC_HANDLER_FOUND, + or call terminate. + _UA_CLEANUP_PHASE && _UA_HANDLER_FRAME && !native_exception + + 3. Scan for cleanups. If a handler is found and this isn't forced unwind, + then terminate, otherwise ignore the handler and keep looking for cleanup. + If a cleanup is found, return _URC_HANDLER_FOUND, else return _URC_CONTINUE_UNWIND. + May also report an error on invalid input. + May terminate for invalid exception table. + _UA_CLEANUP_PHASE && !_UA_HANDLER_FRAME +*/ + +static void scan_eh_tab(scan_results &results, _Unwind_Action actions, + bool native_exception, + _Unwind_Exception *unwind_exception, + _Unwind_Context *context) { + // Initialize results to found nothing but an error + results.ttypeIndex = 0; + results.actionRecord = 0; + results.languageSpecificData = 0; + results.landingPad = 0; + results.adjustedPtr = 0; + results.reason = _URC_FATAL_PHASE1_ERROR; + // Check for consistent actions + if (actions & _UA_SEARCH_PHASE) + { + // Do Phase 1 + if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) + { + // None of these flags should be set during Phase 1 + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + } + else if (actions & _UA_CLEANUP_PHASE) + { + if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) + { + // _UA_HANDLER_FRAME should only be set if phase 1 found a handler. + // If _UA_FORCE_UNWIND is set, phase 1 shouldn't have happened. + // Client error + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } + } + else // Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE is set + { + // One of these should be set. + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + // Start scan by getting exception table address + const uint8_t *lsda = (const uint8_t *)_Unwind_GetLanguageSpecificData(context); + if (lsda == 0) + { + // There is no exception table + results.reason = _URC_CONTINUE_UNWIND; + return; + } + results.languageSpecificData = lsda; + // Get the current instruction pointer and offset it before next + // instruction in the current frame which threw the exception. + uintptr_t ip = _Unwind_GetIP(context) - 1; + // Get beginning current frame's code (as defined by the + // emitted dwarf code) + uintptr_t funcStart = _Unwind_GetRegionStart(context); +#ifdef __USING_SJLJ_EXCEPTIONS__ + if (ip == uintptr_t(-1)) + { + // no action + results.reason = _URC_CONTINUE_UNWIND; + return; + } + else if (ip == 0) + call_terminate(native_exception, unwind_exception); + // ip is 1-based index into call site table +#else // !__USING_SJLJ_EXCEPTIONS__ + uintptr_t ipOffset = ip - funcStart; +#endif // !defined(_USING_SLJL_EXCEPTIONS__) + const uint8_t* classInfo = NULL; + // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding + // dwarf emission + // Parse LSDA header. + uint8_t lpStartEncoding = *lsda++; + const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); + if (lpStart == 0) + lpStart = (const uint8_t*)funcStart; + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding != DW_EH_PE_omit) + { + // Calculate type info locations in emitted dwarf code which + // were flagged by type info arguments to llvm.eh.selector + // intrinsic + uintptr_t classInfoOffset = readULEB128(&lsda); + classInfo = lsda + classInfoOffset; + } + // Walk call-site table looking for range that + // includes current PC. + uint8_t callSiteEncoding = *lsda++; +#ifdef __USING_SJLJ_EXCEPTIONS__ + (void)callSiteEncoding; // When using SjLj exceptions, callSiteEncoding is never used +#endif + uint32_t callSiteTableLength = static_cast(readULEB128(&lsda)); + const uint8_t* callSiteTableStart = lsda; + const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; + const uint8_t* actionTableStart = callSiteTableEnd; + const uint8_t* callSitePtr = callSiteTableStart; + while (callSitePtr < callSiteTableEnd) + { + // There is one entry per call site. +#ifndef __USING_SJLJ_EXCEPTIONS__ + // The call sites are non-overlapping in [start, start+length) + // The call sites are ordered in increasing value of start + uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t actionEntry = readULEB128(&callSitePtr); + if ((start <= ipOffset) && (ipOffset < (start + length))) +#else // __USING_SJLJ_EXCEPTIONS__ + // ip is 1-based index into this table + uintptr_t landingPad = readULEB128(&callSitePtr); + uintptr_t actionEntry = readULEB128(&callSitePtr); + if (--ip == 0) +#endif // __USING_SJLJ_EXCEPTIONS__ + { + // Found the call site containing ip. +#ifndef __USING_SJLJ_EXCEPTIONS__ + if (landingPad == 0) + { + // No handler here + results.reason = _URC_CONTINUE_UNWIND; + return; + } + landingPad = (uintptr_t)lpStart + landingPad; +#else // __USING_SJLJ_EXCEPTIONS__ + ++landingPad; +#endif // __USING_SJLJ_EXCEPTIONS__ + if (actionEntry == 0) + { + // Found a cleanup + // If this is a type 1 or type 2 search, there are no handlers + // If this is a type 3 search, you want to install the cleanup. + if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) + { + results.ttypeIndex = 0; // Redundant but clarifying + results.landingPad = landingPad; + results.reason = _URC_HANDLER_FOUND; + return; + } + // No handler here + results.reason = _URC_CONTINUE_UNWIND; + return; + } + // Convert 1-based byte offset into + const uint8_t* action = actionTableStart + (actionEntry - 1); + // Scan action entries until you find a matching handler, cleanup, or the end of action list + while (true) + { + const uint8_t* actionRecord = action; + int64_t ttypeIndex = readSLEB128(&action); + if (ttypeIndex > 0) + { + // Found a catch, does it actually catch? + // First check for catch (...) + const __shim_type_info* catchType = + get_shim_type_info(static_cast(ttypeIndex), + classInfo, ttypeEncoding, + native_exception, unwind_exception); + if (catchType == 0) + { + // Found catch (...) catches everything, including foreign exceptions + // If this is a type 1 search save state and return _URC_HANDLER_FOUND + // If this is a type 2 search save state and return _URC_HANDLER_FOUND + // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! + // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan + if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME)) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = get_thrown_object_ptr(unwind_exception); + results.reason = _URC_HANDLER_FOUND; + return; + } + else if (!(actions & _UA_FORCE_UNWIND)) + { + // It looks like the exception table has changed + // on us. Likely stack corruption! + call_terminate(native_exception, unwind_exception); + } + } + // Else this is a catch (T) clause and will never + // catch a foreign exception + else if (native_exception) + { + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + void* adjustedPtr = get_thrown_object_ptr(unwind_exception); + const __shim_type_info* excpType = + static_cast(exception_header->exceptionType); + if (adjustedPtr == 0 || excpType == 0) + { + // Something very bad happened + call_terminate(native_exception, unwind_exception); + } + if (catchType->can_catch(excpType, adjustedPtr)) + { + // Found a matching handler + // If this is a type 1 search save state and return _URC_HANDLER_FOUND + // If this is a type 3 search and !_UA_FORCE_UNWIND, we should have found this in phase 1! + // If this is a type 3 search and _UA_FORCE_UNWIND, ignore handler and continue scan + if (actions & _UA_SEARCH_PHASE) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = adjustedPtr; + results.reason = _URC_HANDLER_FOUND; + return; + } + else if (!(actions & _UA_FORCE_UNWIND)) + { + // It looks like the exception table has changed + // on us. Likely stack corruption! + call_terminate(native_exception, unwind_exception); + } + } + } + // Scan next action ... + } + else if (ttypeIndex < 0) + { + // Found an exception spec. If this is a foreign exception, + // it is always caught. + if (native_exception) + { + // Does the exception spec catch this native exception? + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + void* adjustedPtr = get_thrown_object_ptr(unwind_exception); + const __shim_type_info* excpType = + static_cast(exception_header->exceptionType); + if (adjustedPtr == 0 || excpType == 0) + { + // Something very bad happened + call_terminate(native_exception, unwind_exception); + } + if (exception_spec_can_catch(ttypeIndex, classInfo, + ttypeEncoding, excpType, + adjustedPtr, unwind_exception)) + { + // native exception caught by exception spec + // If this is a type 1 search, save state and return _URC_HANDLER_FOUND + // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! + // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan + if (actions & _UA_SEARCH_PHASE) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = adjustedPtr; + results.reason = _URC_HANDLER_FOUND; + return; + } + else if (!(actions & _UA_FORCE_UNWIND)) + { + // It looks like the exception table has changed + // on us. Likely stack corruption! + call_terminate(native_exception, unwind_exception); + } + } + } + else + { + // foreign exception caught by exception spec + // If this is a type 1 search, save state and return _URC_HANDLER_FOUND + // If this is a type 2 search, save state and return _URC_HANDLER_FOUND + // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! + // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan + if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME)) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = get_thrown_object_ptr(unwind_exception); + results.reason = _URC_HANDLER_FOUND; + return; + } + else if (!(actions & _UA_FORCE_UNWIND)) + { + // It looks like the exception table has changed + // on us. Likely stack corruption! + call_terminate(native_exception, unwind_exception); + } + } + // Scan next action ... + } + else // ttypeIndex == 0 + { + // Found a cleanup + // If this is a type 1 search, ignore it and continue scan + // If this is a type 2 search, ignore it and continue scan + // If this is a type 3 search, save state and return _URC_HANDLER_FOUND + if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = get_thrown_object_ptr(unwind_exception); + results.reason = _URC_HANDLER_FOUND; + return; + } + } + const uint8_t* temp = action; + int64_t actionOffset = readSLEB128(&temp); + if (actionOffset == 0) + { + // End of action list, no matching handler or cleanup found + results.reason = _URC_CONTINUE_UNWIND; + return; + } + // Go to next action + action += actionOffset; + } // there is no break out of this loop, only return + } +#ifndef __USING_SJLJ_EXCEPTIONS__ + else if (ipOffset < start) + { + // There is no call site for this ip + // Something bad has happened. We should never get here. + // Possible stack corruption. + call_terminate(native_exception, unwind_exception); + } +#endif // !__USING_SJLJ_EXCEPTIONS__ + } // there might be some tricky cases which break out of this loop + + // It is possible that no eh table entry specify how to handle + // this exception. By spec, terminate it immediately. + call_terminate(native_exception, unwind_exception); +} + +// public API + +/* +The personality function branches on actions like so: + +_UA_SEARCH_PHASE + + If _UA_CLEANUP_PHASE or _UA_HANDLER_FRAME or _UA_FORCE_UNWIND there's + an error from above, return _URC_FATAL_PHASE1_ERROR. + + Scan for anything that could stop unwinding: + + 1. A catch clause that will catch this exception + (will never catch foreign). + 2. A catch (...) (will always catch foreign). + 3. An exception spec that will catch this exception + (will always catch foreign). + If a handler is found + If not foreign + Save state in header + return _URC_HANDLER_FOUND + Else a handler not found + return _URC_CONTINUE_UNWIND + +_UA_CLEANUP_PHASE + + If _UA_HANDLER_FRAME + If _UA_FORCE_UNWIND + How did this happen? return _URC_FATAL_PHASE2_ERROR + If foreign + Do _UA_SEARCH_PHASE to recover state + else + Recover state from header + Transfer control to landing pad. return _URC_INSTALL_CONTEXT + + Else + + This branch handles both normal C++ non-catching handlers (cleanups) + and forced unwinding. + Scan for anything that can not stop unwinding: + + 1. A cleanup. + + If a cleanup is found + transfer control to it. return _URC_INSTALL_CONTEXT + Else a cleanup is not found: return _URC_CONTINUE_UNWIND +*/ + +#if !defined(_LIBCXXABI_ARM_EHABI) +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +static _Unwind_Reason_Code __gxx_personality_imp +#else +_LIBCXXABI_FUNC_VIS _Unwind_Reason_Code +#ifdef __USING_SJLJ_EXCEPTIONS__ +__gxx_personality_sj0 +#else +__gxx_personality_v0 +#endif +#endif + (int version, _Unwind_Action actions, uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) +{ + if (version != 1 || unwind_exception == 0 || context == 0) + return _URC_FATAL_PHASE1_ERROR; + + bool native_exception = (exceptionClass & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + scan_results results; + if (actions & _UA_SEARCH_PHASE) + { + // Phase 1 search: All we're looking for in phase 1 is a handler that + // halts unwinding + scan_eh_tab(results, actions, native_exception, unwind_exception, context); + if (results.reason == _URC_HANDLER_FOUND) + { + // Found one. Can we cache the results somewhere to optimize phase 2? + if (native_exception) + { + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + exception_header->handlerSwitchValue = static_cast(results.ttypeIndex); + exception_header->actionRecord = results.actionRecord; + exception_header->languageSpecificData = results.languageSpecificData; + exception_header->catchTemp = reinterpret_cast(results.landingPad); + exception_header->adjustedPtr = results.adjustedPtr; + } + return _URC_HANDLER_FOUND; + } + // Did not find a catching-handler. Return the results of the scan + // (normally _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE1_ERROR + // if we were called improperly). + return results.reason; + } + if (actions & _UA_CLEANUP_PHASE) + { + // Phase 2 search: + // Did we find a catching handler in phase 1? + if (actions & _UA_HANDLER_FRAME) + { + // Yes, phase 1 said we have a catching handler here. + // Did we cache the results of the scan? + if (native_exception) + { + // Yes, reload the results from the cache. + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + results.ttypeIndex = exception_header->handlerSwitchValue; + results.actionRecord = exception_header->actionRecord; + results.languageSpecificData = exception_header->languageSpecificData; + results.landingPad = reinterpret_cast(exception_header->catchTemp); + results.adjustedPtr = exception_header->adjustedPtr; + } + else + { + // No, do the scan again to reload the results. + scan_eh_tab(results, actions, native_exception, unwind_exception, context); + // Phase 1 told us we would find a handler. Now in Phase 2 we + // didn't find a handler. The eh table should not be changing! + if (results.reason != _URC_HANDLER_FOUND) + call_terminate(native_exception, unwind_exception); + } + // Jump to the handler + set_registers(unwind_exception, context, results); + return _URC_INSTALL_CONTEXT; + } + // Either we didn't do a phase 1 search (due to forced unwinding), or + // phase 1 reported no catching-handlers. + // Search for a (non-catching) cleanup + scan_eh_tab(results, actions, native_exception, unwind_exception, context); + if (results.reason == _URC_HANDLER_FOUND) + { + // Found a non-catching handler. Jump to it: + set_registers(unwind_exception, context, results); + return _URC_INSTALL_CONTEXT; + } + // Did not find a cleanup. Return the results of the scan + // (normally _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE2_ERROR + // if we were called improperly). + return results.reason; + } + // We were called improperly: neither a phase 1 or phase 2 search + return _URC_FATAL_PHASE1_ERROR; +} + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +extern "C" _LIBCXXABI_FUNC_VIS EXCEPTION_DISPOSITION +__gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) +{ + return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp, + __gxx_personality_imp); +} +#endif + +#else + +extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*, + _Unwind_Context*); + +// Helper function to unwind one frame. +// ARM EHABI 7.3 and 7.4: If the personality function returns _URC_CONTINUE_UNWIND, the +// personality routine should update the virtual register set (VRS) according to the +// corresponding frame unwinding instructions (ARM EHABI 9.3.) +static _Unwind_Reason_Code continue_unwind(_Unwind_Exception* unwind_exception, + _Unwind_Context* context) +{ + if (__gnu_unwind_frame(unwind_exception, context) != _URC_OK) + return _URC_FAILURE; + return _URC_CONTINUE_UNWIND; +} + +// ARM register names +#if !defined(LIBCXXABI_USE_LLVM_UNWINDER) +static const uint32_t REG_UCB = 12; // Register to save _Unwind_Control_Block +#endif +static const uint32_t REG_SP = 13; + +static void save_results_to_barrier_cache(_Unwind_Exception* unwind_exception, + const scan_results& results) +{ + unwind_exception->barrier_cache.bitpattern[0] = (uint32_t)results.adjustedPtr; + unwind_exception->barrier_cache.bitpattern[1] = (uint32_t)results.actionRecord; + unwind_exception->barrier_cache.bitpattern[2] = (uint32_t)results.languageSpecificData; + unwind_exception->barrier_cache.bitpattern[3] = (uint32_t)results.landingPad; + unwind_exception->barrier_cache.bitpattern[4] = (uint32_t)results.ttypeIndex; +} + +static void load_results_from_barrier_cache(scan_results& results, + const _Unwind_Exception* unwind_exception) +{ + results.adjustedPtr = (void*)unwind_exception->barrier_cache.bitpattern[0]; + results.actionRecord = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[1]; + results.languageSpecificData = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[2]; + results.landingPad = (uintptr_t)unwind_exception->barrier_cache.bitpattern[3]; + results.ttypeIndex = (int64_t)(int32_t)unwind_exception->barrier_cache.bitpattern[4]; +} + +extern "C" _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code +__gxx_personality_v0(_Unwind_State state, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context) +{ + if (unwind_exception == 0 || context == 0) + return _URC_FATAL_PHASE1_ERROR; + + bool native_exception = __isOurExceptionClass(unwind_exception); + +#if !defined(LIBCXXABI_USE_LLVM_UNWINDER) + // Copy the address of _Unwind_Control_Block to r12 so that + // _Unwind_GetLanguageSpecificData() and _Unwind_GetRegionStart() can + // return correct address. + _Unwind_SetGR(context, REG_UCB, reinterpret_cast(unwind_exception)); +#endif + + // Check the undocumented force unwinding behavior + bool is_force_unwinding = state & _US_FORCE_UNWIND; + state &= ~_US_FORCE_UNWIND; + + scan_results results; + switch (state) { + case _US_VIRTUAL_UNWIND_FRAME: + if (is_force_unwinding) + return continue_unwind(unwind_exception, context); + + // Phase 1 search: All we're looking for in phase 1 is a handler that halts unwinding + scan_eh_tab(results, _UA_SEARCH_PHASE, native_exception, unwind_exception, context); + if (results.reason == _URC_HANDLER_FOUND) + { + unwind_exception->barrier_cache.sp = _Unwind_GetGR(context, REG_SP); + if (native_exception) + save_results_to_barrier_cache(unwind_exception, results); + return _URC_HANDLER_FOUND; + } + // Did not find the catch handler + if (results.reason == _URC_CONTINUE_UNWIND) + return continue_unwind(unwind_exception, context); + return results.reason; + + case _US_UNWIND_FRAME_STARTING: + // TODO: Support force unwinding in the phase 2 search. + // NOTE: In order to call the cleanup functions, _Unwind_ForcedUnwind() + // will call this personality function with (_US_FORCE_UNWIND | + // _US_UNWIND_FRAME_STARTING). + + // Phase 2 search + if (unwind_exception->barrier_cache.sp == _Unwind_GetGR(context, REG_SP)) + { + // Found a catching handler in phase 1 + if (native_exception) + { + // Load the result from the native exception barrier cache. + load_results_from_barrier_cache(results, unwind_exception); + results.reason = _URC_HANDLER_FOUND; + } + else + { + // Search for the catching handler again for the foreign exception. + scan_eh_tab(results, static_cast<_Unwind_Action>(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME), + native_exception, unwind_exception, context); + if (results.reason != _URC_HANDLER_FOUND) // phase1 search should guarantee to find one + call_terminate(native_exception, unwind_exception); + } + + // Install the context for the catching handler + set_registers(unwind_exception, context, results); + return _URC_INSTALL_CONTEXT; + } + + // Either we didn't do a phase 1 search (due to forced unwinding), or + // phase 1 reported no catching-handlers. + // Search for a (non-catching) cleanup + scan_eh_tab(results, _UA_CLEANUP_PHASE, native_exception, unwind_exception, context); + if (results.reason == _URC_HANDLER_FOUND) + { + // Found a non-catching handler + + // ARM EHABI 8.4.2: Before we can jump to the cleanup handler, we have to setup some + // internal data structures, so that __cxa_end_cleanup() can get unwind_exception from + // __cxa_get_globals(). + __cxa_begin_cleanup(unwind_exception); + + // Install the context for the cleanup handler + set_registers(unwind_exception, context, results); + return _URC_INSTALL_CONTEXT; + } + + // Did not find any handler + if (results.reason == _URC_CONTINUE_UNWIND) + return continue_unwind(unwind_exception, context); + return results.reason; + + case _US_UNWIND_FRAME_RESUME: + return continue_unwind(unwind_exception, context); + } + + // We were called improperly: neither a phase 1 or phase 2 search + return _URC_FATAL_PHASE1_ERROR; +} +#endif + + +__attribute__((noreturn)) +_LIBCXXABI_FUNC_VIS void +__cxa_call_unexpected(void* arg) +{ + _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg); + if (unwind_exception == 0) + call_terminate(false, unwind_exception); + __cxa_begin_catch(unwind_exception); + bool native_old_exception = __isOurExceptionClass(unwind_exception); + std::unexpected_handler u_handler; + std::terminate_handler t_handler; + __cxa_exception* old_exception_header = 0; + int64_t ttypeIndex; + const uint8_t* lsda; + if (native_old_exception) + { + old_exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + t_handler = old_exception_header->terminateHandler; + u_handler = old_exception_header->unexpectedHandler; + // If std::__unexpected(u_handler) rethrows the same exception, + // these values get overwritten by the rethrow. So save them now: +#if defined(_LIBCXXABI_ARM_EHABI) + ttypeIndex = (int64_t)(int32_t)unwind_exception->barrier_cache.bitpattern[4]; + lsda = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[2]; +#else + ttypeIndex = old_exception_header->handlerSwitchValue; + lsda = old_exception_header->languageSpecificData; +#endif + } + else + { + t_handler = std::get_terminate(); + u_handler = std::get_unexpected(); + } + try + { + std::__unexpected(u_handler); + } + catch (...) + { + // If the old exception is foreign, then all we can do is terminate. + // We have no way to recover the needed old exception spec. There's + // no way to pass that information here. And the personality routine + // can't call us directly and do anything but terminate() if we throw + // from here. + if (native_old_exception) + { + // Have: + // old_exception_header->languageSpecificData + // old_exception_header->actionRecord + // Need + // const uint8_t* classInfo + // uint8_t ttypeEncoding + uint8_t lpStartEncoding = *lsda++; + const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); + (void)lpStart; // purposefully unused. Just needed to increment lsda. + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding == DW_EH_PE_omit) + std::__terminate(t_handler); + uintptr_t classInfoOffset = readULEB128(&lsda); + const uint8_t* classInfo = lsda + classInfoOffset; + // Is this new exception catchable by the exception spec at ttypeIndex? + // The answer is obviously yes if the new and old exceptions are the same exception + // If no + // throw; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + __cxa_exception* new_exception_header = globals->caughtExceptions; + if (new_exception_header == 0) + // This shouldn't be able to happen! + std::__terminate(t_handler); + bool native_new_exception = __isOurExceptionClass(&new_exception_header->unwindHeader); + void* adjustedPtr; + if (native_new_exception && (new_exception_header != old_exception_header)) + { + const __shim_type_info* excpType = + static_cast(new_exception_header->exceptionType); + adjustedPtr = + __getExceptionClass(&new_exception_header->unwindHeader) == kOurDependentExceptionClass ? + ((__cxa_dependent_exception*)new_exception_header)->primaryException : + new_exception_header + 1; + if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, + excpType, adjustedPtr, unwind_exception)) + { + // We need to __cxa_end_catch, but for the old exception, + // not the new one. This is a little tricky ... + // Disguise new_exception_header as a rethrown exception, but + // don't actually rethrow it. This means you can temporarily + // end the catch clause enclosing new_exception_header without + // __cxa_end_catch destroying new_exception_header. + new_exception_header->handlerCount = -new_exception_header->handlerCount; + globals->uncaughtExceptions += 1; + // Call __cxa_end_catch for new_exception_header + __cxa_end_catch(); + // Call __cxa_end_catch for old_exception_header + __cxa_end_catch(); + // Renter this catch clause with new_exception_header + __cxa_begin_catch(&new_exception_header->unwindHeader); + // Rethrow new_exception_header + throw; + } + } + // Will a std::bad_exception be catchable by the exception spec at + // ttypeIndex? + // If no + // throw std::bad_exception(); + const __shim_type_info* excpType = + static_cast(&typeid(std::bad_exception)); + std::bad_exception be; + adjustedPtr = &be; + if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, + excpType, adjustedPtr, unwind_exception)) + { + // We need to __cxa_end_catch for both the old exception and the + // new exception. Technically we should do it in that order. + // But it is expedient to do it in the opposite order: + // Call __cxa_end_catch for new_exception_header + __cxa_end_catch(); + // Throw std::bad_exception will __cxa_end_catch for + // old_exception_header + throw be; + } + } + } + std::__terminate(t_handler); +} + +} // extern "C" + +} // __cxxabiv1 Index: contrib/libcxxabi/src/cxa_thread_atexit.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_thread_atexit.cpp @@ -0,0 +1,139 @@ +//===----------------------- cxa_thread_atexit.cpp ------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "abort_message.h" +#include "cxxabi.h" +#include <__threading_support> +#include + +namespace __cxxabiv1 { + + using Dtor = void(*)(void*); + + extern "C" +#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL + // A weak symbol is used to detect this function's presence in the C library + // at runtime, even if libc++ is built against an older libc + _LIBCXXABI_WEAK +#endif + int __cxa_thread_atexit_impl(Dtor, void*, void*); + +#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL + +namespace { + // This implementation is used if the C library does not provide + // __cxa_thread_atexit_impl() for us. It has a number of limitations that are + // difficult to impossible to address without ..._impl(): + // + // - dso_symbol is ignored. This means that a shared library may be unloaded + // (via dlclose()) before its thread_local destructors have run. + // + // - thread_local destructors for the main thread are run by the destructor of + // a static object. This is later than expected; they should run before the + // destructors of any objects with static storage duration. + // + // - thread_local destructors on non-main threads run on the first iteration + // through the __libccpp_tls_key destructors. + // std::notify_all_at_thread_exit() and similar functions must be careful to + // wait until the second iteration to provide their intended ordering + // guarantees. + // + // Another limitation, though one shared with ..._impl(), is that any + // thread_locals that are first initialized after non-thread_local global + // destructors begin to run will not be destroyed. [basic.start.term] states + // that all thread_local destructors are sequenced before the destruction of + // objects with static storage duration, resulting in a contradiction if a + // thread_local is constructed after that point. Thus we consider such + // programs ill-formed, and don't bother to run those destructors. (If the + // program terminates abnormally after such a thread_local is constructed, + // the destructor is not expected to run and thus there is no contradiction. + // So construction still has to work.) + + struct DtorList { + Dtor dtor; + void* obj; + DtorList* next; + }; + + // The linked list of thread-local destructors to run + __thread DtorList* dtors = nullptr; + // True if the destructors are currently scheduled to run on this thread + __thread bool dtors_alive = false; + // Used to trigger destructors on thread exit; value is ignored + std::__libcpp_tls_key dtors_key; + + void run_dtors(void*) { + while (auto head = dtors) { + dtors = head->next; + head->dtor(head->obj); + std::free(head); + } + + dtors_alive = false; + } + + struct DtorsManager { + DtorsManager() { + // There is intentionally no matching std::__libcpp_tls_delete call, as + // __cxa_thread_atexit() may be called arbitrarily late (for example, from + // global destructors or atexit() handlers). + if (std::__libcpp_tls_create(&dtors_key, run_dtors) != 0) { + abort_message("std::__libcpp_tls_create() failed in __cxa_thread_atexit()"); + } + } + + ~DtorsManager() { + // std::__libcpp_tls_key destructors do not run on threads that call exit() + // (including when the main thread returns from main()), so we explicitly + // call the destructor here. This runs at exit time (potentially earlier + // if libc++abi is dlclose()'d). Any thread_locals initialized after this + // point will not be destroyed. + run_dtors(nullptr); + } + }; +} // namespace + +#endif // HAVE___CXA_THREAD_ATEXIT_IMPL + +extern "C" { + + _LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() { +#ifdef HAVE___CXA_THREAD_ATEXIT_IMPL + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); +#else + if (__cxa_thread_atexit_impl) { + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); + } else { + // Initialize the dtors std::__libcpp_tls_key (uses __cxa_guard_*() for + // one-time initialization and __cxa_atexit() for destruction) + static DtorsManager manager; + + if (!dtors_alive) { + if (std::__libcpp_tls_set(dtors_key, &dtors_key) != 0) { + return -1; + } + dtors_alive = true; + } + + auto head = static_cast(std::malloc(sizeof(DtorList))); + if (!head) { + return -1; + } + + head->dtor = dtor; + head->obj = obj; + head->next = dtors; + dtors = head; + + return 0; + } +#endif // HAVE___CXA_THREAD_ATEXIT_IMPL + } + +} // extern "C" +} // namespace __cxxabiv1 Index: contrib/libcxxabi/src/cxa_unexpected.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_unexpected.cpp @@ -0,0 +1,22 @@ +//===------------------------- cxa_unexpected.cpp -------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include +#include "cxxabi.h" +#include "cxa_exception.hpp" + +namespace __cxxabiv1 +{ + +extern "C" +{ + +} + +} // namespace __cxxabiv1 + Index: contrib/libcxxabi/src/cxa_vector.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_vector.cpp @@ -0,0 +1,366 @@ +//===-------------------------- cxa_vector.cpp ---------------------------===// +// +// 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 +// +// +// This file implements the "Array Construction and Destruction APIs" +// http://mentorembedded.github.io/cxx-abi/abi.html#array-ctor +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" + +#include // for std::terminate + +namespace __cxxabiv1 { + +#if 0 +#pragma mark --Helper routines and classes -- +#endif + +namespace { + inline static size_t __get_element_count ( void *p ) { + return static_cast (p)[-1]; + } + + inline static void __set_element_count ( void *p, size_t element_count ) { + static_cast (p)[-1] = element_count; + } + + +// A pair of classes to simplify exception handling and control flow. +// They get passed a block of memory in the constructor, and unless the +// 'release' method is called, they deallocate the memory in the destructor. +// Preferred usage is to allocate some memory, attach it to one of these objects, +// and then, when all the operations to set up the memory block have succeeded, +// call 'release'. If any of the setup operations fail, or an exception is +// thrown, then the block is automatically deallocated. +// +// The only difference between these two classes is the signature for the +// deallocation function (to match new2/new3 and delete2/delete3. + class st_heap_block2 { + public: + typedef void (*dealloc_f)(void *); + + st_heap_block2 ( dealloc_f dealloc, void *ptr ) + : dealloc_ ( dealloc ), ptr_ ( ptr ), enabled_ ( true ) {} + ~st_heap_block2 () { if ( enabled_ ) dealloc_ ( ptr_ ) ; } + void release () { enabled_ = false; } + + private: + dealloc_f dealloc_; + void *ptr_; + bool enabled_; + }; + + class st_heap_block3 { + public: + typedef void (*dealloc_f)(void *, size_t); + + st_heap_block3 ( dealloc_f dealloc, void *ptr, size_t size ) + : dealloc_ ( dealloc ), ptr_ ( ptr ), size_ ( size ), enabled_ ( true ) {} + ~st_heap_block3 () { if ( enabled_ ) dealloc_ ( ptr_, size_ ) ; } + void release () { enabled_ = false; } + + private: + dealloc_f dealloc_; + void *ptr_; + size_t size_; + bool enabled_; + }; + + class st_cxa_cleanup { + public: + typedef void (*destruct_f)(void *); + + st_cxa_cleanup ( void *ptr, size_t &idx, size_t element_size, destruct_f destructor ) + : ptr_ ( ptr ), idx_ ( idx ), element_size_ ( element_size ), + destructor_ ( destructor ), enabled_ ( true ) {} + ~st_cxa_cleanup () { + if ( enabled_ ) + __cxa_vec_cleanup ( ptr_, idx_, element_size_, destructor_ ); + } + + void release () { enabled_ = false; } + + private: + void *ptr_; + size_t &idx_; + size_t element_size_; + destruct_f destructor_; + bool enabled_; + }; + + class st_terminate { + public: + st_terminate ( bool enabled = true ) : enabled_ ( enabled ) {} + ~st_terminate () { if ( enabled_ ) std::terminate (); } + void release () { enabled_ = false; } + private: + bool enabled_ ; + }; +} + +#if 0 +#pragma mark --Externally visible routines-- +#endif + +extern "C" { + +// Equivalent to +// +// __cxa_vec_new2(element_count, element_size, padding_size, constructor, +// destructor, &::operator new[], &::operator delete[]) +_LIBCXXABI_FUNC_VIS void * +__cxa_vec_new(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *)) { + return __cxa_vec_new2 ( element_count, element_size, padding_size, + constructor, destructor, &::operator new [], &::operator delete [] ); +} + + + +// Given the number and size of elements for an array and the non-negative +// size of prefix padding for a cookie, allocate space (using alloc) for +// the array preceded by the specified padding, initialize the cookie if +// the padding is non-zero, and call the given constructor on each element. +// Return the address of the array proper, after the padding. +// +// If alloc throws an exception, rethrow the exception. If alloc returns +// NULL, return NULL. If the constructor throws an exception, call +// destructor for any already constructed elements, and rethrow the +// exception. If the destructor throws an exception, call std::terminate. +// +// The constructor may be NULL, in which case it must not be called. If the +// padding_size is zero, the destructor may be NULL; in that case it must +// not be called. +// +// Neither alloc nor dealloc may be NULL. +_LIBCXXABI_FUNC_VIS void * +__cxa_vec_new2(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *), + void *(*alloc)(size_t), void (*dealloc)(void *)) { + const size_t heap_size = element_count * element_size + padding_size; + char * const heap_block = static_cast ( alloc ( heap_size )); + char *vec_base = heap_block; + + if ( NULL != vec_base ) { + st_heap_block2 heap ( dealloc, heap_block ); + + // put the padding before the array elements + if ( 0 != padding_size ) { + vec_base += padding_size; + __set_element_count ( vec_base, element_count ); + } + + // Construct the elements + __cxa_vec_ctor ( vec_base, element_count, element_size, constructor, destructor ); + heap.release (); // We're good! + } + + return vec_base; +} + + +// Same as __cxa_vec_new2 except that the deallocation function takes both +// the object address and its size. +_LIBCXXABI_FUNC_VIS void * +__cxa_vec_new3(size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void *), void (*destructor)(void *), + void *(*alloc)(size_t), void (*dealloc)(void *, size_t)) { + const size_t heap_size = element_count * element_size + padding_size; + char * const heap_block = static_cast ( alloc ( heap_size )); + char *vec_base = heap_block; + + if ( NULL != vec_base ) { + st_heap_block3 heap ( dealloc, heap_block, heap_size ); + + // put the padding before the array elements + if ( 0 != padding_size ) { + vec_base += padding_size; + __set_element_count ( vec_base, element_count ); + } + + // Construct the elements + __cxa_vec_ctor ( vec_base, element_count, element_size, constructor, destructor ); + heap.release (); // We're good! + } + + return vec_base; +} + + +// Given the (data) addresses of a destination and a source array, an +// element count and an element size, call the given copy constructor to +// copy each element from the source array to the destination array. The +// copy constructor's arguments are the destination address and source +// address, respectively. If an exception occurs, call the given destructor +// (if non-NULL) on each copied element and rethrow. If the destructor +// throws an exception, call terminate(). The constructor and or destructor +// pointers may be NULL. If either is NULL, no action is taken when it +// would have been called. + +_LIBCXXABI_FUNC_VIS void __cxa_vec_cctor(void *dest_array, void *src_array, + size_t element_count, + size_t element_size, + void (*constructor)(void *, void *), + void (*destructor)(void *)) { + if ( NULL != constructor ) { + size_t idx = 0; + char *src_ptr = static_cast(src_array); + char *dest_ptr = static_cast(dest_array); + st_cxa_cleanup cleanup ( dest_array, idx, element_size, destructor ); + + for ( idx = 0; idx < element_count; + ++idx, src_ptr += element_size, dest_ptr += element_size ) + constructor ( dest_ptr, src_ptr ); + cleanup.release (); // We're good! + } +} + + +// Given the (data) address of an array, not including any cookie padding, +// and the number and size of its elements, call the given constructor on +// each element. If the constructor throws an exception, call the given +// destructor for any already-constructed elements, and rethrow the +// exception. If the destructor throws an exception, call terminate(). The +// constructor and/or destructor pointers may be NULL. If either is NULL, +// no action is taken when it would have been called. +_LIBCXXABI_FUNC_VIS void +__cxa_vec_ctor(void *array_address, size_t element_count, size_t element_size, + void (*constructor)(void *), void (*destructor)(void *)) { + if ( NULL != constructor ) { + size_t idx; + char *ptr = static_cast ( array_address ); + st_cxa_cleanup cleanup ( array_address, idx, element_size, destructor ); + + // Construct the elements + for ( idx = 0; idx < element_count; ++idx, ptr += element_size ) + constructor ( ptr ); + cleanup.release (); // We're good! + } +} + +// Given the (data) address of an array, the number of elements, and the +// size of its elements, call the given destructor on each element. If the +// destructor throws an exception, rethrow after destroying the remaining +// elements if possible. If the destructor throws a second exception, call +// terminate(). The destructor pointer may be NULL, in which case this +// routine does nothing. +_LIBCXXABI_FUNC_VIS void __cxa_vec_dtor(void *array_address, + size_t element_count, + size_t element_size, + void (*destructor)(void *)) { + if ( NULL != destructor ) { + char *ptr = static_cast (array_address); + size_t idx = element_count; + st_cxa_cleanup cleanup ( array_address, idx, element_size, destructor ); + { + st_terminate exception_guard (__cxa_uncaught_exception ()); + ptr += element_count * element_size; // one past the last element + + while ( idx-- > 0 ) { + ptr -= element_size; + destructor ( ptr ); + } + exception_guard.release (); // We're good ! + } + cleanup.release (); // We're still good! + } +} + +// Given the (data) address of an array, the number of elements, and the +// size of its elements, call the given destructor on each element. If the +// destructor throws an exception, call terminate(). The destructor pointer +// may be NULL, in which case this routine does nothing. +_LIBCXXABI_FUNC_VIS void __cxa_vec_cleanup(void *array_address, + size_t element_count, + size_t element_size, + void (*destructor)(void *)) { + if ( NULL != destructor ) { + char *ptr = static_cast (array_address); + size_t idx = element_count; + st_terminate exception_guard; + + ptr += element_count * element_size; // one past the last element + while ( idx-- > 0 ) { + ptr -= element_size; + destructor ( ptr ); + } + exception_guard.release (); // We're done! + } +} + + +// If the array_address is NULL, return immediately. Otherwise, given the +// (data) address of an array, the non-negative size of prefix padding for +// the cookie, and the size of its elements, call the given destructor on +// each element, using the cookie to determine the number of elements, and +// then delete the space by calling ::operator delete[](void *). If the +// destructor throws an exception, rethrow after (a) destroying the +// remaining elements, and (b) deallocating the storage. If the destructor +// throws a second exception, call terminate(). If padding_size is 0, the +// destructor pointer must be NULL. If the destructor pointer is NULL, no +// destructor call is to be made. +// +// The intent of this function is to permit an implementation to call this +// function when confronted with an expression of the form delete[] p in +// the source code, provided that the default deallocation function can be +// used. Therefore, the semantics of this function are consistent with +// those required by the standard. The requirement that the deallocation +// function be called even if the destructor throws an exception derives +// from the resolution to DR 353 to the C++ standard, which was adopted in +// April, 2003. +_LIBCXXABI_FUNC_VIS void __cxa_vec_delete(void *array_address, + size_t element_size, + size_t padding_size, + void (*destructor)(void *)) { + __cxa_vec_delete2 ( array_address, element_size, padding_size, + destructor, &::operator delete [] ); +} + +// Same as __cxa_vec_delete, except that the given function is used for +// deallocation instead of the default delete function. If dealloc throws +// an exception, the result is undefined. The dealloc pointer may not be +// NULL. +_LIBCXXABI_FUNC_VIS void +__cxa_vec_delete2(void *array_address, size_t element_size, size_t padding_size, + void (*destructor)(void *), void (*dealloc)(void *)) { + if ( NULL != array_address ) { + char *vec_base = static_cast (array_address); + char *heap_block = vec_base - padding_size; + st_heap_block2 heap ( dealloc, heap_block ); + + if ( 0 != padding_size && NULL != destructor ) // call the destructors + __cxa_vec_dtor ( array_address, __get_element_count ( vec_base ), + element_size, destructor ); + } +} + + +// Same as __cxa_vec_delete, except that the given function is used for +// deallocation instead of the default delete function. The deallocation +// function takes both the object address and its size. If dealloc throws +// an exception, the result is undefined. The dealloc pointer may not be +// NULL. +_LIBCXXABI_FUNC_VIS void +__cxa_vec_delete3(void *array_address, size_t element_size, size_t padding_size, + void (*destructor)(void *), void (*dealloc)(void *, size_t)) { + if ( NULL != array_address ) { + char *vec_base = static_cast (array_address); + char *heap_block = vec_base - padding_size; + const size_t element_count = padding_size ? __get_element_count ( vec_base ) : 0; + const size_t heap_block_size = element_size * element_count + padding_size; + st_heap_block3 heap ( dealloc, heap_block, heap_block_size ); + + if ( 0 != padding_size && NULL != destructor ) // call the destructors + __cxa_vec_dtor ( array_address, element_count, element_size, destructor ); + } +} + + +} // extern "C" + +} // abi Index: contrib/libcxxabi/src/cxa_virtual.cpp =================================================================== --- /dev/null +++ contrib/libcxxabi/src/cxa_virtual.cpp @@ -0,0 +1,24 @@ +//===-------------------------- cxa_virtual.cpp ---------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" +#include "abort_message.h" + +namespace __cxxabiv1 { +extern "C" { +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN +void __cxa_pure_virtual(void) { + abort_message("Pure virtual function called!"); +} + +_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN +void __cxa_deleted_virtual(void) { + abort_message("Deleted virtual function called!"); +} +} // extern "C" +} // abi Index: contrib/libcxxabi/src/demangle/.clang-format =================================================================== --- /dev/null +++ contrib/libcxxabi/src/demangle/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM + Index: contrib/libcxxabi/src/demangle/DemangleConfig.h =================================================================== --- /dev/null +++ contrib/libcxxabi/src/demangle/DemangleConfig.h @@ -0,0 +1,41 @@ +//===--- Compiler.h ---------------------------------------------*- C++ -*-===// +// +// 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 +// +// This file is contains a subset of macros copied from +// llvm/lib/Demangle/Compiler.h. +//===----------------------------------------------------------------------===// + +#ifndef LIBCXX_DEMANGLE_COMPILER_H +#define LIBCXX_DEMANGLE_COMPILER_H + +#include "__config" + +#ifdef _MSC_VER +// snprintf is implemented in VS 2015 +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef NDEBUG +#if __has_attribute(noinline) && __has_attribute(used) +#define DEMANGLE_DUMP_METHOD __attribute__((noinline, used)) +#else +#define DEMANGLE_DUMP_METHOD +#endif +#endif + +#define DEMANGLE_FALLTHROUGH _LIBCPP_FALLTHROUGH() +#define DEMANGLE_UNREACHABLE _LIBCPP_UNREACHABLE() + +#define DEMANGLE_NAMESPACE_BEGIN namespace { namespace itanium_demangle { +#define DEMANGLE_NAMESPACE_END } } + +#endif Index: contrib/libcxxabi/src/demangle/ItaniumDemangle.h =================================================================== --- /dev/null +++ contrib/libcxxabi/src/demangle/ItaniumDemangle.h @@ -0,0 +1,5192 @@ +//===------------------------- ItaniumDemangle.h ----------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Generic itanium demangler library. This file has two byte-per-byte identical +// copies in the source tree, one in libcxxabi, and the other in llvm. +// +//===----------------------------------------------------------------------===// + +#ifndef DEMANGLE_ITANIUMDEMANGLE_H +#define DEMANGLE_ITANIUMDEMANGLE_H + +// FIXME: (possibly) incomplete list of features that clang mangles that this +// file does not yet support: +// - C++ modules TS + +#include "DemangleConfig.h" +#include "StringView.h" +#include "Utility.h" +#include +#include +#include +#include +#include +#include +#include + +#define FOR_EACH_NODE_KIND(X) \ + X(NodeArrayNode) \ + X(DotSuffix) \ + X(VendorExtQualType) \ + X(QualType) \ + X(ConversionOperatorType) \ + X(PostfixQualifiedType) \ + X(ElaboratedTypeSpefType) \ + X(NameType) \ + X(AbiTagAttr) \ + X(EnableIfAttr) \ + X(ObjCProtoName) \ + X(PointerType) \ + X(ReferenceType) \ + X(PointerToMemberType) \ + X(ArrayType) \ + X(FunctionType) \ + X(NoexceptSpec) \ + X(DynamicExceptionSpec) \ + X(FunctionEncoding) \ + X(LiteralOperator) \ + X(SpecialName) \ + X(CtorVtableSpecialName) \ + X(QualifiedName) \ + X(NestedName) \ + X(LocalName) \ + X(VectorType) \ + X(PixelVectorType) \ + X(ParameterPack) \ + X(TemplateArgumentPack) \ + X(ParameterPackExpansion) \ + X(TemplateArgs) \ + X(ForwardTemplateReference) \ + X(NameWithTemplateArgs) \ + X(GlobalQualifiedName) \ + X(StdQualifiedName) \ + X(ExpandedSpecialSubstitution) \ + X(SpecialSubstitution) \ + X(CtorDtorName) \ + X(DtorName) \ + X(UnnamedTypeName) \ + X(ClosureTypeName) \ + X(StructuredBindingName) \ + X(BinaryExpr) \ + X(ArraySubscriptExpr) \ + X(PostfixExpr) \ + X(ConditionalExpr) \ + X(MemberExpr) \ + X(EnclosingExpr) \ + X(CastExpr) \ + X(SizeofParamPackExpr) \ + X(CallExpr) \ + X(NewExpr) \ + X(DeleteExpr) \ + X(PrefixExpr) \ + X(FunctionParam) \ + X(ConversionExpr) \ + X(InitListExpr) \ + X(FoldExpr) \ + X(ThrowExpr) \ + X(BoolExpr) \ + X(IntegerCastExpr) \ + X(IntegerLiteral) \ + X(FloatLiteral) \ + X(DoubleLiteral) \ + X(LongDoubleLiteral) \ + X(BracedExpr) \ + X(BracedRangeExpr) + +DEMANGLE_NAMESPACE_BEGIN + +// Base class of all AST nodes. The AST is built by the parser, then is +// traversed by the printLeft/Right functions to produce a demangled string. +class Node { +public: + enum Kind : unsigned char { +#define ENUMERATOR(NodeKind) K ## NodeKind, + FOR_EACH_NODE_KIND(ENUMERATOR) +#undef ENUMERATOR + }; + + /// Three-way bool to track a cached value. Unknown is possible if this node + /// has an unexpanded parameter pack below it that may affect this cache. + enum class Cache : unsigned char { Yes, No, Unknown, }; + +private: + Kind K; + + // FIXME: Make these protected. +public: + /// Tracks if this node has a component on its right side, in which case we + /// need to call printRight. + Cache RHSComponentCache; + + /// Track if this node is a (possibly qualified) array type. This can affect + /// how we format the output string. + Cache ArrayCache; + + /// Track if this node is a (possibly qualified) function type. This can + /// affect how we format the output string. + Cache FunctionCache; + +public: + Node(Kind K_, Cache RHSComponentCache_ = Cache::No, + Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No) + : K(K_), RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_), + FunctionCache(FunctionCache_) {} + + /// Visit the most-derived object corresponding to this object. + template void visit(Fn F) const; + + // The following function is provided by all derived classes: + // + // Call F with arguments that, when passed to the constructor of this node, + // would construct an equivalent node. + //template void match(Fn F) const; + + bool hasRHSComponent(OutputStream &S) const { + if (RHSComponentCache != Cache::Unknown) + return RHSComponentCache == Cache::Yes; + return hasRHSComponentSlow(S); + } + + bool hasArray(OutputStream &S) const { + if (ArrayCache != Cache::Unknown) + return ArrayCache == Cache::Yes; + return hasArraySlow(S); + } + + bool hasFunction(OutputStream &S) const { + if (FunctionCache != Cache::Unknown) + return FunctionCache == Cache::Yes; + return hasFunctionSlow(S); + } + + Kind getKind() const { return K; } + + virtual bool hasRHSComponentSlow(OutputStream &) const { return false; } + virtual bool hasArraySlow(OutputStream &) const { return false; } + virtual bool hasFunctionSlow(OutputStream &) const { return false; } + + // Dig through "glue" nodes like ParameterPack and ForwardTemplateReference to + // get at a node that actually represents some concrete syntax. + virtual const Node *getSyntaxNode(OutputStream &) const { + return this; + } + + void print(OutputStream &S) const { + printLeft(S); + if (RHSComponentCache != Cache::No) + printRight(S); + } + + // Print the "left" side of this Node into OutputStream. + virtual void printLeft(OutputStream &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implementation. + virtual void printRight(OutputStream &) const {} + + virtual StringView getBaseName() const { return StringView(); } + + // Silence compiler warnings, this dtor will never be called. + virtual ~Node() = default; + +#ifndef NDEBUG + DEMANGLE_DUMP_METHOD void dump() const; +#endif +}; + +class NodeArray { + Node **Elements; + size_t NumElements; + +public: + NodeArray() : Elements(nullptr), NumElements(0) {} + NodeArray(Node **Elements_, size_t NumElements_) + : Elements(Elements_), NumElements(NumElements_) {} + + bool empty() const { return NumElements == 0; } + size_t size() const { return NumElements; } + + Node **begin() const { return Elements; } + Node **end() const { return Elements + NumElements; } + + Node *operator[](size_t Idx) const { return Elements[Idx]; } + + void printWithComma(OutputStream &S) const { + bool FirstElement = true; + for (size_t Idx = 0; Idx != NumElements; ++Idx) { + size_t BeforeComma = S.getCurrentPosition(); + if (!FirstElement) + S += ", "; + size_t AfterComma = S.getCurrentPosition(); + Elements[Idx]->print(S); + + // Elements[Idx] is an empty parameter pack expansion, we should erase the + // comma we just printed. + if (AfterComma == S.getCurrentPosition()) { + S.setCurrentPosition(BeforeComma); + continue; + } + + FirstElement = false; + } + } +}; + +struct NodeArrayNode : Node { + NodeArray Array; + NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {} + + template void match(Fn F) const { F(Array); } + + void printLeft(OutputStream &S) const override { + Array.printWithComma(S); + } +}; + +class DotSuffix final : public Node { + const Node *Prefix; + const StringView Suffix; + +public: + DotSuffix(const Node *Prefix_, StringView Suffix_) + : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {} + + template void match(Fn F) const { F(Prefix, Suffix); } + + void printLeft(OutputStream &s) const override { + Prefix->print(s); + s += " ("; + s += Suffix; + s += ")"; + } +}; + +class VendorExtQualType final : public Node { + const Node *Ty; + StringView Ext; + +public: + VendorExtQualType(const Node *Ty_, StringView Ext_) + : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_) {} + + template void match(Fn F) const { F(Ty, Ext); } + + void printLeft(OutputStream &S) const override { + Ty->print(S); + S += " "; + S += Ext; + } +}; + +enum FunctionRefQual : unsigned char { + FrefQualNone, + FrefQualLValue, + FrefQualRValue, +}; + +enum Qualifiers { + QualNone = 0, + QualConst = 0x1, + QualVolatile = 0x2, + QualRestrict = 0x4, +}; + +inline Qualifiers operator|=(Qualifiers &Q1, Qualifiers Q2) { + return Q1 = static_cast(Q1 | Q2); +} + +class QualType : public Node { +protected: + const Qualifiers Quals; + const Node *Child; + + void printQuals(OutputStream &S) const { + if (Quals & QualConst) + S += " const"; + if (Quals & QualVolatile) + S += " volatile"; + if (Quals & QualRestrict) + S += " restrict"; + } + +public: + QualType(const Node *Child_, Qualifiers Quals_) + : Node(KQualType, Child_->RHSComponentCache, + Child_->ArrayCache, Child_->FunctionCache), + Quals(Quals_), Child(Child_) {} + + template void match(Fn F) const { F(Child, Quals); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Child->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + return Child->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + return Child->hasFunction(S); + } + + void printLeft(OutputStream &S) const override { + Child->printLeft(S); + printQuals(S); + } + + void printRight(OutputStream &S) const override { Child->printRight(S); } +}; + +class ConversionOperatorType final : public Node { + const Node *Ty; + +public: + ConversionOperatorType(const Node *Ty_) + : Node(KConversionOperatorType), Ty(Ty_) {} + + template void match(Fn F) const { F(Ty); } + + void printLeft(OutputStream &S) const override { + S += "operator "; + Ty->print(S); + } +}; + +class PostfixQualifiedType final : public Node { + const Node *Ty; + const StringView Postfix; + +public: + PostfixQualifiedType(Node *Ty_, StringView Postfix_) + : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {} + + template void match(Fn F) const { F(Ty, Postfix); } + + void printLeft(OutputStream &s) const override { + Ty->printLeft(s); + s += Postfix; + } +}; + +class NameType final : public Node { + const StringView Name; + +public: + NameType(StringView Name_) : Node(KNameType), Name(Name_) {} + + template void match(Fn F) const { F(Name); } + + StringView getName() const { return Name; } + StringView getBaseName() const override { return Name; } + + void printLeft(OutputStream &s) const override { s += Name; } +}; + +class ElaboratedTypeSpefType : public Node { + StringView Kind; + Node *Child; +public: + ElaboratedTypeSpefType(StringView Kind_, Node *Child_) + : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {} + + template void match(Fn F) const { F(Kind, Child); } + + void printLeft(OutputStream &S) const override { + S += Kind; + S += ' '; + Child->print(S); + } +}; + +struct AbiTagAttr : Node { + Node *Base; + StringView Tag; + + AbiTagAttr(Node* Base_, StringView Tag_) + : Node(KAbiTagAttr, Base_->RHSComponentCache, + Base_->ArrayCache, Base_->FunctionCache), + Base(Base_), Tag(Tag_) {} + + template void match(Fn F) const { F(Base, Tag); } + + void printLeft(OutputStream &S) const override { + Base->printLeft(S); + S += "[abi:"; + S += Tag; + S += "]"; + } +}; + +class EnableIfAttr : public Node { + NodeArray Conditions; +public: + EnableIfAttr(NodeArray Conditions_) + : Node(KEnableIfAttr), Conditions(Conditions_) {} + + template void match(Fn F) const { F(Conditions); } + + void printLeft(OutputStream &S) const override { + S += " [enable_if:"; + Conditions.printWithComma(S); + S += ']'; + } +}; + +class ObjCProtoName : public Node { + const Node *Ty; + StringView Protocol; + + friend class PointerType; + +public: + ObjCProtoName(const Node *Ty_, StringView Protocol_) + : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} + + template void match(Fn F) const { F(Ty, Protocol); } + + bool isObjCObject() const { + return Ty->getKind() == KNameType && + static_cast(Ty)->getName() == "objc_object"; + } + + void printLeft(OutputStream &S) const override { + Ty->print(S); + S += "<"; + S += Protocol; + S += ">"; + } +}; + +class PointerType final : public Node { + const Node *Pointee; + +public: + PointerType(const Node *Pointee_) + : Node(KPointerType, Pointee_->RHSComponentCache), + Pointee(Pointee_) {} + + template void match(Fn F) const { F(Pointee); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Pointee->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + // We rewrite objc_object* into id. + if (Pointee->getKind() != KObjCProtoName || + !static_cast(Pointee)->isObjCObject()) { + Pointee->printLeft(s); + if (Pointee->hasArray(s)) + s += " "; + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += "("; + s += "*"; + } else { + const auto *objcProto = static_cast(Pointee); + s += "id<"; + s += objcProto->Protocol; + s += ">"; + } + } + + void printRight(OutputStream &s) const override { + if (Pointee->getKind() != KObjCProtoName || + !static_cast(Pointee)->isObjCObject()) { + if (Pointee->hasArray(s) || Pointee->hasFunction(s)) + s += ")"; + Pointee->printRight(s); + } + } +}; + +enum class ReferenceKind { + LValue, + RValue, +}; + +// Represents either a LValue or an RValue reference type. +class ReferenceType : public Node { + const Node *Pointee; + ReferenceKind RK; + + mutable bool Printing = false; + + // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The + // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any + // other combination collapses to a lvalue ref. + std::pair collapse(OutputStream &S) const { + auto SoFar = std::make_pair(RK, Pointee); + for (;;) { + const Node *SN = SoFar.second->getSyntaxNode(S); + if (SN->getKind() != KReferenceType) + break; + auto *RT = static_cast(SN); + SoFar.second = RT->Pointee; + SoFar.first = std::min(SoFar.first, RT->RK); + } + return SoFar; + } + +public: + ReferenceType(const Node *Pointee_, ReferenceKind RK_) + : Node(KReferenceType, Pointee_->RHSComponentCache), + Pointee(Pointee_), RK(RK_) {} + + template void match(Fn F) const { F(Pointee, RK); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Pointee->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + if (Printing) + return; + SwapAndRestore SavePrinting(Printing, true); + std::pair Collapsed = collapse(s); + Collapsed.second->printLeft(s); + if (Collapsed.second->hasArray(s)) + s += " "; + if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) + s += "("; + + s += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); + } + void printRight(OutputStream &s) const override { + if (Printing) + return; + SwapAndRestore SavePrinting(Printing, true); + std::pair Collapsed = collapse(s); + if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) + s += ")"; + Collapsed.second->printRight(s); + } +}; + +class PointerToMemberType final : public Node { + const Node *ClassType; + const Node *MemberType; + +public: + PointerToMemberType(const Node *ClassType_, const Node *MemberType_) + : Node(KPointerToMemberType, MemberType_->RHSComponentCache), + ClassType(ClassType_), MemberType(MemberType_) {} + + template void match(Fn F) const { F(ClassType, MemberType); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + return MemberType->hasRHSComponent(S); + } + + void printLeft(OutputStream &s) const override { + MemberType->printLeft(s); + if (MemberType->hasArray(s) || MemberType->hasFunction(s)) + s += "("; + else + s += " "; + ClassType->print(s); + s += "::*"; + } + + void printRight(OutputStream &s) const override { + if (MemberType->hasArray(s) || MemberType->hasFunction(s)) + s += ")"; + MemberType->printRight(s); + } +}; + +class NodeOrString { + const void *First; + const void *Second; + +public: + /* implicit */ NodeOrString(StringView Str) { + const char *FirstChar = Str.begin(); + const char *SecondChar = Str.end(); + if (SecondChar == nullptr) { + assert(FirstChar == SecondChar); + ++FirstChar, ++SecondChar; + } + First = static_cast(FirstChar); + Second = static_cast(SecondChar); + } + + /* implicit */ NodeOrString(Node *N) + : First(static_cast(N)), Second(nullptr) {} + NodeOrString() : First(nullptr), Second(nullptr) {} + + bool isString() const { return Second && First; } + bool isNode() const { return First && !Second; } + bool isEmpty() const { return !First && !Second; } + + StringView asString() const { + assert(isString()); + return StringView(static_cast(First), + static_cast(Second)); + } + + const Node *asNode() const { + assert(isNode()); + return static_cast(First); + } +}; + +class ArrayType final : public Node { + const Node *Base; + NodeOrString Dimension; + +public: + ArrayType(const Node *Base_, NodeOrString Dimension_) + : Node(KArrayType, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::Yes), + Base(Base_), Dimension(Dimension_) {} + + template void match(Fn F) const { F(Base, Dimension); } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasArraySlow(OutputStream &) const override { return true; } + + void printLeft(OutputStream &S) const override { Base->printLeft(S); } + + void printRight(OutputStream &S) const override { + if (S.back() != ']') + S += " "; + S += "["; + if (Dimension.isString()) + S += Dimension.asString(); + else if (Dimension.isNode()) + Dimension.asNode()->print(S); + S += "]"; + Base->printRight(S); + } +}; + +class FunctionType final : public Node { + const Node *Ret; + NodeArray Params; + Qualifiers CVQuals; + FunctionRefQual RefQual; + const Node *ExceptionSpec; + +public: + FunctionType(const Node *Ret_, NodeArray Params_, Qualifiers CVQuals_, + FunctionRefQual RefQual_, const Node *ExceptionSpec_) + : Node(KFunctionType, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Params(Params_), CVQuals(CVQuals_), RefQual(RefQual_), + ExceptionSpec(ExceptionSpec_) {} + + template void match(Fn F) const { + F(Ret, Params, CVQuals, RefQual, ExceptionSpec); + } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasFunctionSlow(OutputStream &) const override { return true; } + + // Handle C++'s ... quirky decl grammar by using the left & right + // distinction. Consider: + // int (*f(float))(char) {} + // f is a function that takes a float and returns a pointer to a function + // that takes a char and returns an int. If we're trying to print f, start + // by printing out the return types's left, then print our parameters, then + // finally print right of the return type. + void printLeft(OutputStream &S) const override { + Ret->printLeft(S); + S += " "; + } + + void printRight(OutputStream &S) const override { + S += "("; + Params.printWithComma(S); + S += ")"; + Ret->printRight(S); + + if (CVQuals & QualConst) + S += " const"; + if (CVQuals & QualVolatile) + S += " volatile"; + if (CVQuals & QualRestrict) + S += " restrict"; + + if (RefQual == FrefQualLValue) + S += " &"; + else if (RefQual == FrefQualRValue) + S += " &&"; + + if (ExceptionSpec != nullptr) { + S += ' '; + ExceptionSpec->print(S); + } + } +}; + +class NoexceptSpec : public Node { + const Node *E; +public: + NoexceptSpec(const Node *E_) : Node(KNoexceptSpec), E(E_) {} + + template void match(Fn F) const { F(E); } + + void printLeft(OutputStream &S) const override { + S += "noexcept("; + E->print(S); + S += ")"; + } +}; + +class DynamicExceptionSpec : public Node { + NodeArray Types; +public: + DynamicExceptionSpec(NodeArray Types_) + : Node(KDynamicExceptionSpec), Types(Types_) {} + + template void match(Fn F) const { F(Types); } + + void printLeft(OutputStream &S) const override { + S += "throw("; + Types.printWithComma(S); + S += ')'; + } +}; + +class FunctionEncoding final : public Node { + const Node *Ret; + const Node *Name; + NodeArray Params; + const Node *Attrs; + Qualifiers CVQuals; + FunctionRefQual RefQual; + +public: + FunctionEncoding(const Node *Ret_, const Node *Name_, NodeArray Params_, + const Node *Attrs_, Qualifiers CVQuals_, + FunctionRefQual RefQual_) + : Node(KFunctionEncoding, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_), + CVQuals(CVQuals_), RefQual(RefQual_) {} + + template void match(Fn F) const { + F(Ret, Name, Params, Attrs, CVQuals, RefQual); + } + + Qualifiers getCVQuals() const { return CVQuals; } + FunctionRefQual getRefQual() const { return RefQual; } + NodeArray getParams() const { return Params; } + const Node *getReturnType() const { return Ret; } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasFunctionSlow(OutputStream &) const override { return true; } + + const Node *getName() const { return Name; } + + void printLeft(OutputStream &S) const override { + if (Ret) { + Ret->printLeft(S); + if (!Ret->hasRHSComponent(S)) + S += " "; + } + Name->print(S); + } + + void printRight(OutputStream &S) const override { + S += "("; + Params.printWithComma(S); + S += ")"; + if (Ret) + Ret->printRight(S); + + if (CVQuals & QualConst) + S += " const"; + if (CVQuals & QualVolatile) + S += " volatile"; + if (CVQuals & QualRestrict) + S += " restrict"; + + if (RefQual == FrefQualLValue) + S += " &"; + else if (RefQual == FrefQualRValue) + S += " &&"; + + if (Attrs != nullptr) + Attrs->print(S); + } +}; + +class LiteralOperator : public Node { + const Node *OpName; + +public: + LiteralOperator(const Node *OpName_) + : Node(KLiteralOperator), OpName(OpName_) {} + + template void match(Fn F) const { F(OpName); } + + void printLeft(OutputStream &S) const override { + S += "operator\"\" "; + OpName->print(S); + } +}; + +class SpecialName final : public Node { + const StringView Special; + const Node *Child; + +public: + SpecialName(StringView Special_, const Node *Child_) + : Node(KSpecialName), Special(Special_), Child(Child_) {} + + template void match(Fn F) const { F(Special, Child); } + + void printLeft(OutputStream &S) const override { + S += Special; + Child->print(S); + } +}; + +class CtorVtableSpecialName final : public Node { + const Node *FirstType; + const Node *SecondType; + +public: + CtorVtableSpecialName(const Node *FirstType_, const Node *SecondType_) + : Node(KCtorVtableSpecialName), + FirstType(FirstType_), SecondType(SecondType_) {} + + template void match(Fn F) const { F(FirstType, SecondType); } + + void printLeft(OutputStream &S) const override { + S += "construction vtable for "; + FirstType->print(S); + S += "-in-"; + SecondType->print(S); + } +}; + +struct NestedName : Node { + Node *Qual; + Node *Name; + + NestedName(Node *Qual_, Node *Name_) + : Node(KNestedName), Qual(Qual_), Name(Name_) {} + + template void match(Fn F) const { F(Qual, Name); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Qual->print(S); + S += "::"; + Name->print(S); + } +}; + +struct LocalName : Node { + Node *Encoding; + Node *Entity; + + LocalName(Node *Encoding_, Node *Entity_) + : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {} + + template void match(Fn F) const { F(Encoding, Entity); } + + void printLeft(OutputStream &S) const override { + Encoding->print(S); + S += "::"; + Entity->print(S); + } +}; + +class QualifiedName final : public Node { + // qualifier::name + const Node *Qualifier; + const Node *Name; + +public: + QualifiedName(const Node *Qualifier_, const Node *Name_) + : Node(KQualifiedName), Qualifier(Qualifier_), Name(Name_) {} + + template void match(Fn F) const { F(Qualifier, Name); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Qualifier->print(S); + S += "::"; + Name->print(S); + } +}; + +class VectorType final : public Node { + const Node *BaseType; + const NodeOrString Dimension; + +public: + VectorType(const Node *BaseType_, NodeOrString Dimension_) + : Node(KVectorType), BaseType(BaseType_), + Dimension(Dimension_) {} + + template void match(Fn F) const { F(BaseType, Dimension); } + + void printLeft(OutputStream &S) const override { + BaseType->print(S); + S += " vector["; + if (Dimension.isNode()) + Dimension.asNode()->print(S); + else if (Dimension.isString()) + S += Dimension.asString(); + S += "]"; + } +}; + +class PixelVectorType final : public Node { + const NodeOrString Dimension; + +public: + PixelVectorType(NodeOrString Dimension_) + : Node(KPixelVectorType), Dimension(Dimension_) {} + + template void match(Fn F) const { F(Dimension); } + + void printLeft(OutputStream &S) const override { + // FIXME: This should demangle as "vector pixel". + S += "pixel vector["; + S += Dimension.asString(); + S += "]"; + } +}; + +/// An unexpanded parameter pack (either in the expression or type context). If +/// this AST is correct, this node will have a ParameterPackExpansion node above +/// it. +/// +/// This node is created when some are found that apply to an +/// , and is stored in the TemplateParams table. In order for this to +/// appear in the final AST, it has to referenced via a (ie, +/// T_). +class ParameterPack final : public Node { + NodeArray Data; + + // Setup OutputStream for a pack expansion unless we're already expanding one. + void initializePackExpansion(OutputStream &S) const { + if (S.CurrentPackMax == std::numeric_limits::max()) { + S.CurrentPackMax = static_cast(Data.size()); + S.CurrentPackIndex = 0; + } + } + +public: + ParameterPack(NodeArray Data_) : Node(KParameterPack), Data(Data_) { + ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->ArrayCache == Cache::No; + })) + ArrayCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->FunctionCache == Cache::No; + })) + FunctionCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { + return P->RHSComponentCache == Cache::No; + })) + RHSComponentCache = Cache::No; + } + + template void match(Fn F) const { F(Data); } + + bool hasRHSComponentSlow(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(S); + } + const Node *getSyntaxNode(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() ? Data[Idx]->getSyntaxNode(S) : this; + } + + void printLeft(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printLeft(S); + } + void printRight(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printRight(S); + } +}; + +/// A variadic template argument. This node represents an occurrence of +/// JE in some . It isn't itself unexpanded, unless +/// one of it's Elements is. The parser inserts a ParameterPack into the +/// TemplateParams table if the this pack belongs to apply to an +/// . +class TemplateArgumentPack final : public Node { + NodeArray Elements; +public: + TemplateArgumentPack(NodeArray Elements_) + : Node(KTemplateArgumentPack), Elements(Elements_) {} + + template void match(Fn F) const { F(Elements); } + + NodeArray getElements() const { return Elements; } + + void printLeft(OutputStream &S) const override { + Elements.printWithComma(S); + } +}; + +/// A pack expansion. Below this node, there are some unexpanded ParameterPacks +/// which each have Child->ParameterPackSize elements. +class ParameterPackExpansion final : public Node { + const Node *Child; + +public: + ParameterPackExpansion(const Node *Child_) + : Node(KParameterPackExpansion), Child(Child_) {} + + template void match(Fn F) const { F(Child); } + + const Node *getChild() const { return Child; } + + void printLeft(OutputStream &S) const override { + constexpr unsigned Max = std::numeric_limits::max(); + SwapAndRestore SavePackIdx(S.CurrentPackIndex, Max); + SwapAndRestore SavePackMax(S.CurrentPackMax, Max); + size_t StreamPos = S.getCurrentPosition(); + + // Print the first element in the pack. If Child contains a ParameterPack, + // it will set up S.CurrentPackMax and print the first element. + Child->print(S); + + // No ParameterPack was found in Child. This can occur if we've found a pack + // expansion on a . + if (S.CurrentPackMax == Max) { + S += "..."; + return; + } + + // We found a ParameterPack, but it has no elements. Erase whatever we may + // of printed. + if (S.CurrentPackMax == 0) { + S.setCurrentPosition(StreamPos); + return; + } + + // Else, iterate through the rest of the elements in the pack. + for (unsigned I = 1, E = S.CurrentPackMax; I < E; ++I) { + S += ", "; + S.CurrentPackIndex = I; + Child->print(S); + } + } +}; + +class TemplateArgs final : public Node { + NodeArray Params; + +public: + TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {} + + template void match(Fn F) const { F(Params); } + + NodeArray getParams() { return Params; } + + void printLeft(OutputStream &S) const override { + S += "<"; + Params.printWithComma(S); + if (S.back() == '>') + S += " "; + S += ">"; + } +}; + +/// A forward-reference to a template argument that was not known at the point +/// where the template parameter name was parsed in a mangling. +/// +/// This is created when demangling the name of a specialization of a +/// conversion function template: +/// +/// \code +/// struct A { +/// template operator T*(); +/// }; +/// \endcode +/// +/// When demangling a specialization of the conversion function template, we +/// encounter the name of the template (including the \c T) before we reach +/// the template argument list, so we cannot substitute the parameter name +/// for the corresponding argument while parsing. Instead, we create a +/// \c ForwardTemplateReference node that is resolved after we parse the +/// template arguments. +struct ForwardTemplateReference : Node { + size_t Index; + Node *Ref = nullptr; + + // If we're currently printing this node. It is possible (though invalid) for + // a forward template reference to refer to itself via a substitution. This + // creates a cyclic AST, which will stack overflow printing. To fix this, bail + // out if more than one print* function is active. + mutable bool Printing = false; + + ForwardTemplateReference(size_t Index_) + : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown, + Cache::Unknown), + Index(Index_) {} + + // We don't provide a matcher for these, because the value of the node is + // not determined by its construction parameters, and it generally needs + // special handling. + template void match(Fn F) const = delete; + + bool hasRHSComponentSlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore SavePrinting(Printing, true); + return Ref->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore SavePrinting(Printing, true); + return Ref->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore SavePrinting(Printing, true); + return Ref->hasFunction(S); + } + const Node *getSyntaxNode(OutputStream &S) const override { + if (Printing) + return this; + SwapAndRestore SavePrinting(Printing, true); + return Ref->getSyntaxNode(S); + } + + void printLeft(OutputStream &S) const override { + if (Printing) + return; + SwapAndRestore SavePrinting(Printing, true); + Ref->printLeft(S); + } + void printRight(OutputStream &S) const override { + if (Printing) + return; + SwapAndRestore SavePrinting(Printing, true); + Ref->printRight(S); + } +}; + +struct NameWithTemplateArgs : Node { + // name + Node *Name; + Node *TemplateArgs; + + NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) + : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {} + + template void match(Fn F) const { F(Name, TemplateArgs); } + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Name->print(S); + TemplateArgs->print(S); + } +}; + +class GlobalQualifiedName final : public Node { + Node *Child; + +public: + GlobalQualifiedName(Node* Child_) + : Node(KGlobalQualifiedName), Child(Child_) {} + + template void match(Fn F) const { F(Child); } + + StringView getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputStream &S) const override { + S += "::"; + Child->print(S); + } +}; + +struct StdQualifiedName : Node { + Node *Child; + + StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {} + + template void match(Fn F) const { F(Child); } + + StringView getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputStream &S) const override { + S += "std::"; + Child->print(S); + } +}; + +enum class SpecialSubKind { + allocator, + basic_string, + string, + istream, + ostream, + iostream, +}; + +class ExpandedSpecialSubstitution final : public Node { + SpecialSubKind SSK; + +public: + ExpandedSpecialSubstitution(SpecialSubKind SSK_) + : Node(KExpandedSpecialSubstitution), SSK(SSK_) {} + + template void match(Fn F) const { F(SSK); } + + StringView getBaseName() const override { + switch (SSK) { + case SpecialSubKind::allocator: + return StringView("allocator"); + case SpecialSubKind::basic_string: + return StringView("basic_string"); + case SpecialSubKind::string: + return StringView("basic_string"); + case SpecialSubKind::istream: + return StringView("basic_istream"); + case SpecialSubKind::ostream: + return StringView("basic_ostream"); + case SpecialSubKind::iostream: + return StringView("basic_iostream"); + } + DEMANGLE_UNREACHABLE; + } + + void printLeft(OutputStream &S) const override { + switch (SSK) { + case SpecialSubKind::allocator: + S += "std::allocator"; + break; + case SpecialSubKind::basic_string: + S += "std::basic_string"; + break; + case SpecialSubKind::string: + S += "std::basic_string, " + "std::allocator >"; + break; + case SpecialSubKind::istream: + S += "std::basic_istream >"; + break; + case SpecialSubKind::ostream: + S += "std::basic_ostream >"; + break; + case SpecialSubKind::iostream: + S += "std::basic_iostream >"; + break; + } + } +}; + +class SpecialSubstitution final : public Node { +public: + SpecialSubKind SSK; + + SpecialSubstitution(SpecialSubKind SSK_) + : Node(KSpecialSubstitution), SSK(SSK_) {} + + template void match(Fn F) const { F(SSK); } + + StringView getBaseName() const override { + switch (SSK) { + case SpecialSubKind::allocator: + return StringView("allocator"); + case SpecialSubKind::basic_string: + return StringView("basic_string"); + case SpecialSubKind::string: + return StringView("string"); + case SpecialSubKind::istream: + return StringView("istream"); + case SpecialSubKind::ostream: + return StringView("ostream"); + case SpecialSubKind::iostream: + return StringView("iostream"); + } + DEMANGLE_UNREACHABLE; + } + + void printLeft(OutputStream &S) const override { + switch (SSK) { + case SpecialSubKind::allocator: + S += "std::allocator"; + break; + case SpecialSubKind::basic_string: + S += "std::basic_string"; + break; + case SpecialSubKind::string: + S += "std::string"; + break; + case SpecialSubKind::istream: + S += "std::istream"; + break; + case SpecialSubKind::ostream: + S += "std::ostream"; + break; + case SpecialSubKind::iostream: + S += "std::iostream"; + break; + } + } +}; + +class CtorDtorName final : public Node { + const Node *Basename; + const bool IsDtor; + const int Variant; + +public: + CtorDtorName(const Node *Basename_, bool IsDtor_, int Variant_) + : Node(KCtorDtorName), Basename(Basename_), IsDtor(IsDtor_), + Variant(Variant_) {} + + template void match(Fn F) const { F(Basename, IsDtor, Variant); } + + void printLeft(OutputStream &S) const override { + if (IsDtor) + S += "~"; + S += Basename->getBaseName(); + } +}; + +class DtorName : public Node { + const Node *Base; + +public: + DtorName(const Node *Base_) : Node(KDtorName), Base(Base_) {} + + template void match(Fn F) const { F(Base); } + + void printLeft(OutputStream &S) const override { + S += "~"; + Base->printLeft(S); + } +}; + +class UnnamedTypeName : public Node { + const StringView Count; + +public: + UnnamedTypeName(StringView Count_) : Node(KUnnamedTypeName), Count(Count_) {} + + template void match(Fn F) const { F(Count); } + + void printLeft(OutputStream &S) const override { + S += "'unnamed"; + S += Count; + S += "\'"; + } +}; + +class ClosureTypeName : public Node { + NodeArray Params; + StringView Count; + +public: + ClosureTypeName(NodeArray Params_, StringView Count_) + : Node(KClosureTypeName), Params(Params_), Count(Count_) {} + + template void match(Fn F) const { F(Params, Count); } + + void printLeft(OutputStream &S) const override { + S += "\'lambda"; + S += Count; + S += "\'("; + Params.printWithComma(S); + S += ")"; + } +}; + +class StructuredBindingName : public Node { + NodeArray Bindings; +public: + StructuredBindingName(NodeArray Bindings_) + : Node(KStructuredBindingName), Bindings(Bindings_) {} + + template void match(Fn F) const { F(Bindings); } + + void printLeft(OutputStream &S) const override { + S += '['; + Bindings.printWithComma(S); + S += ']'; + } +}; + +// -- Expression Nodes -- + +class BinaryExpr : public Node { + const Node *LHS; + const StringView InfixOperator; + const Node *RHS; + +public: + BinaryExpr(const Node *LHS_, StringView InfixOperator_, const Node *RHS_) + : Node(KBinaryExpr), LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) { + } + + template void match(Fn F) const { F(LHS, InfixOperator, RHS); } + + void printLeft(OutputStream &S) const override { + // might be a template argument expression, then we need to disambiguate + // with parens. + if (InfixOperator == ">") + S += "("; + + S += "("; + LHS->print(S); + S += ") "; + S += InfixOperator; + S += " ("; + RHS->print(S); + S += ")"; + + if (InfixOperator == ">") + S += ")"; + } +}; + +class ArraySubscriptExpr : public Node { + const Node *Op1; + const Node *Op2; + +public: + ArraySubscriptExpr(const Node *Op1_, const Node *Op2_) + : Node(KArraySubscriptExpr), Op1(Op1_), Op2(Op2_) {} + + template void match(Fn F) const { F(Op1, Op2); } + + void printLeft(OutputStream &S) const override { + S += "("; + Op1->print(S); + S += ")["; + Op2->print(S); + S += "]"; + } +}; + +class PostfixExpr : public Node { + const Node *Child; + const StringView Operator; + +public: + PostfixExpr(const Node *Child_, StringView Operator_) + : Node(KPostfixExpr), Child(Child_), Operator(Operator_) {} + + template void match(Fn F) const { F(Child, Operator); } + + void printLeft(OutputStream &S) const override { + S += "("; + Child->print(S); + S += ")"; + S += Operator; + } +}; + +class ConditionalExpr : public Node { + const Node *Cond; + const Node *Then; + const Node *Else; + +public: + ConditionalExpr(const Node *Cond_, const Node *Then_, const Node *Else_) + : Node(KConditionalExpr), Cond(Cond_), Then(Then_), Else(Else_) {} + + template void match(Fn F) const { F(Cond, Then, Else); } + + void printLeft(OutputStream &S) const override { + S += "("; + Cond->print(S); + S += ") ? ("; + Then->print(S); + S += ") : ("; + Else->print(S); + S += ")"; + } +}; + +class MemberExpr : public Node { + const Node *LHS; + const StringView Kind; + const Node *RHS; + +public: + MemberExpr(const Node *LHS_, StringView Kind_, const Node *RHS_) + : Node(KMemberExpr), LHS(LHS_), Kind(Kind_), RHS(RHS_) {} + + template void match(Fn F) const { F(LHS, Kind, RHS); } + + void printLeft(OutputStream &S) const override { + LHS->print(S); + S += Kind; + RHS->print(S); + } +}; + +class EnclosingExpr : public Node { + const StringView Prefix; + const Node *Infix; + const StringView Postfix; + +public: + EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_) + : Node(KEnclosingExpr), Prefix(Prefix_), Infix(Infix_), + Postfix(Postfix_) {} + + template void match(Fn F) const { F(Prefix, Infix, Postfix); } + + void printLeft(OutputStream &S) const override { + S += Prefix; + Infix->print(S); + S += Postfix; + } +}; + +class CastExpr : public Node { + // cast_kind(from) + const StringView CastKind; + const Node *To; + const Node *From; + +public: + CastExpr(StringView CastKind_, const Node *To_, const Node *From_) + : Node(KCastExpr), CastKind(CastKind_), To(To_), From(From_) {} + + template void match(Fn F) const { F(CastKind, To, From); } + + void printLeft(OutputStream &S) const override { + S += CastKind; + S += "<"; + To->printLeft(S); + S += ">("; + From->printLeft(S); + S += ")"; + } +}; + +class SizeofParamPackExpr : public Node { + const Node *Pack; + +public: + SizeofParamPackExpr(const Node *Pack_) + : Node(KSizeofParamPackExpr), Pack(Pack_) {} + + template void match(Fn F) const { F(Pack); } + + void printLeft(OutputStream &S) const override { + S += "sizeof...("; + ParameterPackExpansion PPE(Pack); + PPE.printLeft(S); + S += ")"; + } +}; + +class CallExpr : public Node { + const Node *Callee; + NodeArray Args; + +public: + CallExpr(const Node *Callee_, NodeArray Args_) + : Node(KCallExpr), Callee(Callee_), Args(Args_) {} + + template void match(Fn F) const { F(Callee, Args); } + + void printLeft(OutputStream &S) const override { + Callee->print(S); + S += "("; + Args.printWithComma(S); + S += ")"; + } +}; + +class NewExpr : public Node { + // new (expr_list) type(init_list) + NodeArray ExprList; + Node *Type; + NodeArray InitList; + bool IsGlobal; // ::operator new ? + bool IsArray; // new[] ? +public: + NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_, + bool IsArray_) + : Node(KNewExpr), ExprList(ExprList_), Type(Type_), InitList(InitList_), + IsGlobal(IsGlobal_), IsArray(IsArray_) {} + + template void match(Fn F) const { + F(ExprList, Type, InitList, IsGlobal, IsArray); + } + + void printLeft(OutputStream &S) const override { + if (IsGlobal) + S += "::operator "; + S += "new"; + if (IsArray) + S += "[]"; + S += ' '; + if (!ExprList.empty()) { + S += "("; + ExprList.printWithComma(S); + S += ")"; + } + Type->print(S); + if (!InitList.empty()) { + S += "("; + InitList.printWithComma(S); + S += ")"; + } + + } +}; + +class DeleteExpr : public Node { + Node *Op; + bool IsGlobal; + bool IsArray; + +public: + DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_) + : Node(KDeleteExpr), Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} + + template void match(Fn F) const { F(Op, IsGlobal, IsArray); } + + void printLeft(OutputStream &S) const override { + if (IsGlobal) + S += "::"; + S += "delete"; + if (IsArray) + S += "[] "; + Op->print(S); + } +}; + +class PrefixExpr : public Node { + StringView Prefix; + Node *Child; + +public: + PrefixExpr(StringView Prefix_, Node *Child_) + : Node(KPrefixExpr), Prefix(Prefix_), Child(Child_) {} + + template void match(Fn F) const { F(Prefix, Child); } + + void printLeft(OutputStream &S) const override { + S += Prefix; + S += "("; + Child->print(S); + S += ")"; + } +}; + +class FunctionParam : public Node { + StringView Number; + +public: + FunctionParam(StringView Number_) : Node(KFunctionParam), Number(Number_) {} + + template void match(Fn F) const { F(Number); } + + void printLeft(OutputStream &S) const override { + S += "fp"; + S += Number; + } +}; + +class ConversionExpr : public Node { + const Node *Type; + NodeArray Expressions; + +public: + ConversionExpr(const Node *Type_, NodeArray Expressions_) + : Node(KConversionExpr), Type(Type_), Expressions(Expressions_) {} + + template void match(Fn F) const { F(Type, Expressions); } + + void printLeft(OutputStream &S) const override { + S += "("; + Type->print(S); + S += ")("; + Expressions.printWithComma(S); + S += ")"; + } +}; + +class InitListExpr : public Node { + const Node *Ty; + NodeArray Inits; +public: + InitListExpr(const Node *Ty_, NodeArray Inits_) + : Node(KInitListExpr), Ty(Ty_), Inits(Inits_) {} + + template void match(Fn F) const { F(Ty, Inits); } + + void printLeft(OutputStream &S) const override { + if (Ty) + Ty->print(S); + S += '{'; + Inits.printWithComma(S); + S += '}'; + } +}; + +class BracedExpr : public Node { + const Node *Elem; + const Node *Init; + bool IsArray; +public: + BracedExpr(const Node *Elem_, const Node *Init_, bool IsArray_) + : Node(KBracedExpr), Elem(Elem_), Init(Init_), IsArray(IsArray_) {} + + template void match(Fn F) const { F(Elem, Init, IsArray); } + + void printLeft(OutputStream &S) const override { + if (IsArray) { + S += '['; + Elem->print(S); + S += ']'; + } else { + S += '.'; + Elem->print(S); + } + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) + S += " = "; + Init->print(S); + } +}; + +class BracedRangeExpr : public Node { + const Node *First; + const Node *Last; + const Node *Init; +public: + BracedRangeExpr(const Node *First_, const Node *Last_, const Node *Init_) + : Node(KBracedRangeExpr), First(First_), Last(Last_), Init(Init_) {} + + template void match(Fn F) const { F(First, Last, Init); } + + void printLeft(OutputStream &S) const override { + S += '['; + First->print(S); + S += " ... "; + Last->print(S); + S += ']'; + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) + S += " = "; + Init->print(S); + } +}; + +class FoldExpr : public Node { + const Node *Pack, *Init; + StringView OperatorName; + bool IsLeftFold; + +public: + FoldExpr(bool IsLeftFold_, StringView OperatorName_, const Node *Pack_, + const Node *Init_) + : Node(KFoldExpr), Pack(Pack_), Init(Init_), OperatorName(OperatorName_), + IsLeftFold(IsLeftFold_) {} + + template void match(Fn F) const { + F(IsLeftFold, OperatorName, Pack, Init); + } + + void printLeft(OutputStream &S) const override { + auto PrintPack = [&] { + S += '('; + ParameterPackExpansion(Pack).print(S); + S += ')'; + }; + + S += '('; + + if (IsLeftFold) { + // init op ... op pack + if (Init != nullptr) { + Init->print(S); + S += ' '; + S += OperatorName; + S += ' '; + } + // ... op pack + S += "... "; + S += OperatorName; + S += ' '; + PrintPack(); + } else { // !IsLeftFold + // pack op ... + PrintPack(); + S += ' '; + S += OperatorName; + S += " ..."; + // pack op ... op init + if (Init != nullptr) { + S += ' '; + S += OperatorName; + S += ' '; + Init->print(S); + } + } + S += ')'; + } +}; + +class ThrowExpr : public Node { + const Node *Op; + +public: + ThrowExpr(const Node *Op_) : Node(KThrowExpr), Op(Op_) {} + + template void match(Fn F) const { F(Op); } + + void printLeft(OutputStream &S) const override { + S += "throw "; + Op->print(S); + } +}; + +class BoolExpr : public Node { + bool Value; + +public: + BoolExpr(bool Value_) : Node(KBoolExpr), Value(Value_) {} + + template void match(Fn F) const { F(Value); } + + void printLeft(OutputStream &S) const override { + S += Value ? StringView("true") : StringView("false"); + } +}; + +class IntegerCastExpr : public Node { + // ty(integer) + const Node *Ty; + StringView Integer; + +public: + IntegerCastExpr(const Node *Ty_, StringView Integer_) + : Node(KIntegerCastExpr), Ty(Ty_), Integer(Integer_) {} + + template void match(Fn F) const { F(Ty, Integer); } + + void printLeft(OutputStream &S) const override { + S += "("; + Ty->print(S); + S += ")"; + S += Integer; + } +}; + +class IntegerLiteral : public Node { + StringView Type; + StringView Value; + +public: + IntegerLiteral(StringView Type_, StringView Value_) + : Node(KIntegerLiteral), Type(Type_), Value(Value_) {} + + template void match(Fn F) const { F(Type, Value); } + + void printLeft(OutputStream &S) const override { + if (Type.size() > 3) { + S += "("; + S += Type; + S += ")"; + } + + if (Value[0] == 'n') { + S += "-"; + S += Value.dropFront(1); + } else + S += Value; + + if (Type.size() <= 3) + S += Type; + } +}; + +template struct FloatData; + +namespace float_literal_impl { +constexpr Node::Kind getFloatLiteralKind(float *) { + return Node::KFloatLiteral; +} +constexpr Node::Kind getFloatLiteralKind(double *) { + return Node::KDoubleLiteral; +} +constexpr Node::Kind getFloatLiteralKind(long double *) { + return Node::KLongDoubleLiteral; +} +} + +template class FloatLiteralImpl : public Node { + const StringView Contents; + + static constexpr Kind KindForClass = + float_literal_impl::getFloatLiteralKind((Float *)nullptr); + +public: + FloatLiteralImpl(StringView Contents_) + : Node(KindForClass), Contents(Contents_) {} + + template void match(Fn F) const { F(Contents); } + + void printLeft(OutputStream &s) const override { + const char *first = Contents.begin(); + const char *last = Contents.end() + 1; + + const size_t N = FloatData::mangled_size; + if (static_cast(last - first) > N) { + last = first + N; + union { + Float value; + char buf[sizeof(Float)]; + }; + const char *t = first; + char *e = buf; + for (; t != last; ++t, ++e) { + unsigned d1 = isdigit(*t) ? static_cast(*t - '0') + : static_cast(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast(*t - '0') + : static_cast(*t - 'a' + 10); + *e = static_cast((d1 << 4) + d0); + } +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[FloatData::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), FloatData::spec, value); + s += StringView(num, num + n); + } + } +}; + +using FloatLiteral = FloatLiteralImpl; +using DoubleLiteral = FloatLiteralImpl; +using LongDoubleLiteral = FloatLiteralImpl; + +/// Visit the node. Calls \c F(P), where \c P is the node cast to the +/// appropriate derived class. +template +void Node::visit(Fn F) const { + switch (K) { +#define CASE(X) case K ## X: return F(static_cast(this)); + FOR_EACH_NODE_KIND(CASE) +#undef CASE + } + assert(0 && "unknown mangling node kind"); +} + +/// Determine the kind of a node from its type. +template struct NodeKind; +#define SPECIALIZATION(X) \ + template<> struct NodeKind { \ + static constexpr Node::Kind Kind = Node::K##X; \ + static constexpr const char *name() { return #X; } \ + }; +FOR_EACH_NODE_KIND(SPECIALIZATION) +#undef SPECIALIZATION + +#undef FOR_EACH_NODE_KIND + +template +class PODSmallVector { + static_assert(std::is_pod::value, + "T is required to be a plain old data type"); + + T* First; + T* Last; + T* Cap; + T Inline[N]; + + bool isInline() const { return First == Inline; } + + void clearInline() { + First = Inline; + Last = Inline; + Cap = Inline + N; + } + + void reserve(size_t NewCap) { + size_t S = size(); + if (isInline()) { + auto* Tmp = static_cast(std::malloc(NewCap * sizeof(T))); + if (Tmp == nullptr) + std::terminate(); + std::copy(First, Last, Tmp); + First = Tmp; + } else { + First = static_cast(std::realloc(First, NewCap * sizeof(T))); + if (First == nullptr) + std::terminate(); + } + Last = First + S; + Cap = First + NewCap; + } + +public: + PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} + + PODSmallVector(const PODSmallVector&) = delete; + PODSmallVector& operator=(const PODSmallVector&) = delete; + + PODSmallVector(PODSmallVector&& Other) : PODSmallVector() { + if (Other.isInline()) { + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return; + } + + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + } + + PODSmallVector& operator=(PODSmallVector&& Other) { + if (Other.isInline()) { + if (!isInline()) { + std::free(First); + clearInline(); + } + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return *this; + } + + if (isInline()) { + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + return *this; + } + + std::swap(First, Other.First); + std::swap(Last, Other.Last); + std::swap(Cap, Other.Cap); + Other.clear(); + return *this; + } + + void push_back(const T& Elem) { + if (Last == Cap) + reserve(size() * 2); + *Last++ = Elem; + } + + void pop_back() { + assert(Last != First && "Popping empty vector!"); + --Last; + } + + void dropBack(size_t Index) { + assert(Index <= size() && "dropBack() can't expand!"); + Last = First + Index; + } + + T* begin() { return First; } + T* end() { return Last; } + + bool empty() const { return First == Last; } + size_t size() const { return static_cast(Last - First); } + T& back() { + assert(Last != First && "Calling back() on empty vector!"); + return *(Last - 1); + } + T& operator[](size_t Index) { + assert(Index < size() && "Invalid access!"); + return *(begin() + Index); + } + void clear() { Last = First; } + + ~PODSmallVector() { + if (!isInline()) + std::free(First); + } +}; + +template struct AbstractManglingParser { + const char *First; + const char *Last; + + // Name stack, this is used by the parser to hold temporary names that were + // parsed. The parser collapses multiple names into new nodes to construct + // the AST. Once the parser is finished, names.size() == 1. + PODSmallVector Names; + + // Substitution table. Itanium supports name substitutions as a means of + // compression. The string "S42_" refers to the 44nd entry (base-36) in this + // table. + PODSmallVector Subs; + + // Template parameter table. Like the above, but referenced like "T42_". + // This has a smaller size compared to Subs and Names because it can be + // stored on the stack. + PODSmallVector TemplateParams; + + // Set of unresolved forward references. These can occur in a + // conversion operator's type, and are resolved in the enclosing . + PODSmallVector ForwardTemplateRefs; + + bool TryToParseTemplateArgs = true; + bool PermitForwardTemplateReferences = false; + bool ParsingLambdaParams = false; + + Alloc ASTAllocator; + + AbstractManglingParser(const char *First_, const char *Last_) + : First(First_), Last(Last_) {} + + Derived &getDerived() { return static_cast(*this); } + + void reset(const char *First_, const char *Last_) { + First = First_; + Last = Last_; + Names.clear(); + Subs.clear(); + TemplateParams.clear(); + ParsingLambdaParams = false; + TryToParseTemplateArgs = true; + PermitForwardTemplateReferences = false; + ASTAllocator.reset(); + } + + template Node *make(Args &&... args) { + return ASTAllocator.template makeNode(std::forward(args)...); + } + + template NodeArray makeNodeArray(It begin, It end) { + size_t sz = static_cast(end - begin); + void *mem = ASTAllocator.allocateNodeArray(sz); + Node **data = new (mem) Node *[sz]; + std::copy(begin, end, data); + return NodeArray(data, sz); + } + + NodeArray popTrailingNodeArray(size_t FromPosition) { + assert(FromPosition <= Names.size()); + NodeArray res = + makeNodeArray(Names.begin() + (long)FromPosition, Names.end()); + Names.dropBack(FromPosition); + return res; + } + + bool consumeIf(StringView S) { + if (StringView(First, Last).startsWith(S)) { + First += S.size(); + return true; + } + return false; + } + + bool consumeIf(char C) { + if (First != Last && *First == C) { + ++First; + return true; + } + return false; + } + + char consume() { return First != Last ? *First++ : '\0'; } + + char look(unsigned Lookahead = 0) { + if (static_cast(Last - First) <= Lookahead) + return '\0'; + return First[Lookahead]; + } + + size_t numLeft() const { return static_cast(Last - First); } + + StringView parseNumber(bool AllowNegative = false); + Qualifiers parseCVQualifiers(); + bool parsePositiveInteger(size_t *Out); + StringView parseBareSourceName(); + + bool parseSeqId(size_t *Out); + Node *parseSubstitution(); + Node *parseTemplateParam(); + Node *parseTemplateArgs(bool TagTemplates = false); + Node *parseTemplateArg(); + + /// Parse the production. + Node *parseExpr(); + Node *parsePrefixExpr(StringView Kind); + Node *parseBinaryExpr(StringView Kind); + Node *parseIntegerLiteral(StringView Lit); + Node *parseExprPrimary(); + template Node *parseFloatingLiteral(); + Node *parseFunctionParam(); + Node *parseNewExpr(); + Node *parseConversionExpr(); + Node *parseBracedExpr(); + Node *parseFoldExpr(); + + /// Parse the production. + Node *parseType(); + Node *parseFunctionType(); + Node *parseVectorType(); + Node *parseDecltype(); + Node *parseArrayType(); + Node *parsePointerToMemberType(); + Node *parseClassEnumType(); + Node *parseQualifiedType(); + + Node *parseEncoding(); + bool parseCallOffset(); + Node *parseSpecialName(); + + /// Holds some extra information about a that is being parsed. This + /// information is only pertinent if the refers to an . + struct NameState { + bool CtorDtorConversion = false; + bool EndsWithTemplateArgs = false; + Qualifiers CVQualifiers = QualNone; + FunctionRefQual ReferenceQualifier = FrefQualNone; + size_t ForwardTemplateRefsBegin; + + NameState(AbstractManglingParser *Enclosing) + : ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {} + }; + + bool resolveForwardTemplateRefs(NameState &State) { + size_t I = State.ForwardTemplateRefsBegin; + size_t E = ForwardTemplateRefs.size(); + for (; I < E; ++I) { + size_t Idx = ForwardTemplateRefs[I]->Index; + if (Idx >= TemplateParams.size()) + return true; + ForwardTemplateRefs[I]->Ref = TemplateParams[Idx]; + } + ForwardTemplateRefs.dropBack(State.ForwardTemplateRefsBegin); + return false; + } + + /// Parse the production> + Node *parseName(NameState *State = nullptr); + Node *parseLocalName(NameState *State); + Node *parseOperatorName(NameState *State); + Node *parseUnqualifiedName(NameState *State); + Node *parseUnnamedTypeName(NameState *State); + Node *parseSourceName(NameState *State); + Node *parseUnscopedName(NameState *State); + Node *parseNestedName(NameState *State); + Node *parseCtorDtorName(Node *&SoFar, NameState *State); + + Node *parseAbiTags(Node *N); + + /// Parse the production. + Node *parseUnresolvedName(); + Node *parseSimpleId(); + Node *parseBaseUnresolvedName(); + Node *parseUnresolvedType(); + Node *parseDestructorName(); + + /// Top-level entry point into the parser. + Node *parse(); +}; + +const char* parse_discriminator(const char* first, const char* last); + +// ::= // N +// ::= # See Scope Encoding below // Z +// ::= +// ::= +// +// ::= +// ::= +template +Node *AbstractManglingParser::parseName(NameState *State) { + consumeIf('L'); // extension + + if (look() == 'N') + return getDerived().parseNestedName(State); + if (look() == 'Z') + return getDerived().parseLocalName(State); + + // ::= + if (look() == 'S' && look(1) != 't') { + Node *S = getDerived().parseSubstitution(); + if (S == nullptr) + return nullptr; + if (look() != 'I') + return nullptr; + Node *TA = getDerived().parseTemplateArgs(State != nullptr); + if (TA == nullptr) + return nullptr; + if (State) State->EndsWithTemplateArgs = true; + return make(S, TA); + } + + Node *N = getDerived().parseUnscopedName(State); + if (N == nullptr) + return nullptr; + // ::= + if (look() == 'I') { + Subs.push_back(N); + Node *TA = getDerived().parseTemplateArgs(State != nullptr); + if (TA == nullptr) + return nullptr; + if (State) State->EndsWithTemplateArgs = true; + return make(N, TA); + } + // ::= + return N; +} + +// := Z E [] +// := Z E s [] +// := Z Ed [ ] _ +template +Node *AbstractManglingParser::parseLocalName(NameState *State) { + if (!consumeIf('Z')) + return nullptr; + Node *Encoding = getDerived().parseEncoding(); + if (Encoding == nullptr || !consumeIf('E')) + return nullptr; + + if (consumeIf('s')) { + First = parse_discriminator(First, Last); + auto *StringLitName = make("string literal"); + if (!StringLitName) + return nullptr; + return make(Encoding, StringLitName); + } + + if (consumeIf('d')) { + parseNumber(true); + if (!consumeIf('_')) + return nullptr; + Node *N = getDerived().parseName(State); + if (N == nullptr) + return nullptr; + return make(Encoding, N); + } + + Node *Entity = getDerived().parseName(State); + if (Entity == nullptr) + return nullptr; + First = parse_discriminator(First, Last); + return make(Encoding, Entity); +} + +// ::= +// ::= St # ::std:: +// extension ::= StL +template +Node * +AbstractManglingParser::parseUnscopedName(NameState *State) { + if (consumeIf("StL") || consumeIf("St")) { + Node *R = getDerived().parseUnqualifiedName(State); + if (R == nullptr) + return nullptr; + return make(R); + } + return getDerived().parseUnqualifiedName(State); +} + +// ::= [abi-tags] +// ::= +// ::= +// ::= +// ::= DC + E # structured binding declaration +template +Node * +AbstractManglingParser::parseUnqualifiedName(NameState *State) { + // s are special-cased in parseNestedName(). + Node *Result; + if (look() == 'U') + Result = getDerived().parseUnnamedTypeName(State); + else if (look() >= '1' && look() <= '9') + Result = getDerived().parseSourceName(State); + else if (consumeIf("DC")) { + size_t BindingsBegin = Names.size(); + do { + Node *Binding = getDerived().parseSourceName(State); + if (Binding == nullptr) + return nullptr; + Names.push_back(Binding); + } while (!consumeIf('E')); + Result = make(popTrailingNodeArray(BindingsBegin)); + } else + Result = getDerived().parseOperatorName(State); + if (Result != nullptr) + Result = getDerived().parseAbiTags(Result); + return Result; +} + +// ::= Ut [] _ +// ::= +// +// ::= Ul E [ ] _ +// +// ::= + # Parameter types or "v" if the lambda has no parameters +template +Node * +AbstractManglingParser::parseUnnamedTypeName(NameState *) { + if (consumeIf("Ut")) { + StringView Count = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make(Count); + } + if (consumeIf("Ul")) { + NodeArray Params; + SwapAndRestore SwapParams(ParsingLambdaParams, true); + if (!consumeIf("vE")) { + size_t ParamsBegin = Names.size(); + do { + Node *P = getDerived().parseType(); + if (P == nullptr) + return nullptr; + Names.push_back(P); + } while (!consumeIf('E')); + Params = popTrailingNodeArray(ParamsBegin); + } + StringView Count = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make(Params, Count); + } + if (consumeIf("Ub")) { + (void)parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make("'block-literal'"); + } + return nullptr; +} + +// ::= +template +Node *AbstractManglingParser::parseSourceName(NameState *) { + size_t Length = 0; + if (parsePositiveInteger(&Length)) + return nullptr; + if (numLeft() < Length || Length == 0) + return nullptr; + StringView Name(First, First + Length); + First += Length; + if (Name.startsWith("_GLOBAL__N")) + return make("(anonymous namespace)"); + return make(Name); +} + +// ::= aa # && +// ::= ad # & (unary) +// ::= an # & +// ::= aN # &= +// ::= aS # = +// ::= cl # () +// ::= cm # , +// ::= co # ~ +// ::= cv # (cast) +// ::= da # delete[] +// ::= de # * (unary) +// ::= dl # delete +// ::= dv # / +// ::= dV # /= +// ::= eo # ^ +// ::= eO # ^= +// ::= eq # == +// ::= ge # >= +// ::= gt # > +// ::= ix # [] +// ::= le # <= +// ::= li # operator "" +// ::= ls # << +// ::= lS # <<= +// ::= lt # < +// ::= mi # - +// ::= mI # -= +// ::= ml # * +// ::= mL # *= +// ::= mm # -- (postfix in context) +// ::= na # new[] +// ::= ne # != +// ::= ng # - (unary) +// ::= nt # ! +// ::= nw # new +// ::= oo # || +// ::= or # | +// ::= oR # |= +// ::= pm # ->* +// ::= pl # + +// ::= pL # += +// ::= pp # ++ (postfix in context) +// ::= ps # + (unary) +// ::= pt # -> +// ::= qu # ? +// ::= rm # % +// ::= rM # %= +// ::= rs # >> +// ::= rS # >>= +// ::= ss # <=> C++2a +// ::= v # vendor extended operator +template +Node * +AbstractManglingParser::parseOperatorName(NameState *State) { + switch (look()) { + case 'a': + switch (look(1)) { + case 'a': + First += 2; + return make("operator&&"); + case 'd': + case 'n': + First += 2; + return make("operator&"); + case 'N': + First += 2; + return make("operator&="); + case 'S': + First += 2; + return make("operator="); + } + return nullptr; + case 'c': + switch (look(1)) { + case 'l': + First += 2; + return make("operator()"); + case 'm': + First += 2; + return make("operator,"); + case 'o': + First += 2; + return make("operator~"); + // ::= cv # (cast) + case 'v': { + First += 2; + SwapAndRestore SaveTemplate(TryToParseTemplateArgs, false); + // If we're parsing an encoding, State != nullptr and the conversion + // operators' could have a that refers to some + // s further ahead in the mangled name. + SwapAndRestore SavePermit(PermitForwardTemplateReferences, + PermitForwardTemplateReferences || + State != nullptr); + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) + return nullptr; + if (State) State->CtorDtorConversion = true; + return make(Ty); + } + } + return nullptr; + case 'd': + switch (look(1)) { + case 'a': + First += 2; + return make("operator delete[]"); + case 'e': + First += 2; + return make("operator*"); + case 'l': + First += 2; + return make("operator delete"); + case 'v': + First += 2; + return make("operator/"); + case 'V': + First += 2; + return make("operator/="); + } + return nullptr; + case 'e': + switch (look(1)) { + case 'o': + First += 2; + return make("operator^"); + case 'O': + First += 2; + return make("operator^="); + case 'q': + First += 2; + return make("operator=="); + } + return nullptr; + case 'g': + switch (look(1)) { + case 'e': + First += 2; + return make("operator>="); + case 't': + First += 2; + return make("operator>"); + } + return nullptr; + case 'i': + if (look(1) == 'x') { + First += 2; + return make("operator[]"); + } + return nullptr; + case 'l': + switch (look(1)) { + case 'e': + First += 2; + return make("operator<="); + // ::= li # operator "" + case 'i': { + First += 2; + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make(SN); + } + case 's': + First += 2; + return make("operator<<"); + case 'S': + First += 2; + return make("operator<<="); + case 't': + First += 2; + return make("operator<"); + } + return nullptr; + case 'm': + switch (look(1)) { + case 'i': + First += 2; + return make("operator-"); + case 'I': + First += 2; + return make("operator-="); + case 'l': + First += 2; + return make("operator*"); + case 'L': + First += 2; + return make("operator*="); + case 'm': + First += 2; + return make("operator--"); + } + return nullptr; + case 'n': + switch (look(1)) { + case 'a': + First += 2; + return make("operator new[]"); + case 'e': + First += 2; + return make("operator!="); + case 'g': + First += 2; + return make("operator-"); + case 't': + First += 2; + return make("operator!"); + case 'w': + First += 2; + return make("operator new"); + } + return nullptr; + case 'o': + switch (look(1)) { + case 'o': + First += 2; + return make("operator||"); + case 'r': + First += 2; + return make("operator|"); + case 'R': + First += 2; + return make("operator|="); + } + return nullptr; + case 'p': + switch (look(1)) { + case 'm': + First += 2; + return make("operator->*"); + case 'l': + First += 2; + return make("operator+"); + case 'L': + First += 2; + return make("operator+="); + case 'p': + First += 2; + return make("operator++"); + case 's': + First += 2; + return make("operator+"); + case 't': + First += 2; + return make("operator->"); + } + return nullptr; + case 'q': + if (look(1) == 'u') { + First += 2; + return make("operator?"); + } + return nullptr; + case 'r': + switch (look(1)) { + case 'm': + First += 2; + return make("operator%"); + case 'M': + First += 2; + return make("operator%="); + case 's': + First += 2; + return make("operator>>"); + case 'S': + First += 2; + return make("operator>>="); + } + return nullptr; + case 's': + if (look(1) == 's') { + First += 2; + return make("operator<=>"); + } + return nullptr; + // ::= v # vendor extended operator + case 'v': + if (std::isdigit(look(1))) { + First += 2; + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make(SN); + } + return nullptr; + } + return nullptr; +} + +// ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// extension ::= C5 # ? +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor +// extension ::= D5 # ? +template +Node * +AbstractManglingParser::parseCtorDtorName(Node *&SoFar, + NameState *State) { + if (SoFar->getKind() == Node::KSpecialSubstitution) { + auto SSK = static_cast(SoFar)->SSK; + switch (SSK) { + case SpecialSubKind::string: + case SpecialSubKind::istream: + case SpecialSubKind::ostream: + case SpecialSubKind::iostream: + SoFar = make(SSK); + if (!SoFar) + return nullptr; + break; + default: + break; + } + } + + if (consumeIf('C')) { + bool IsInherited = consumeIf('I'); + if (look() != '1' && look() != '2' && look() != '3' && look() != '5') + return nullptr; + int Variant = look() - '0'; + ++First; + if (State) State->CtorDtorConversion = true; + if (IsInherited) { + if (getDerived().parseName(State) == nullptr) + return nullptr; + } + return make(SoFar, false, Variant); + } + + if (look() == 'D' && + (look(1) == '0' || look(1) == '1' || look(1) == '2' || look(1) == '5')) { + int Variant = look(1) - '0'; + First += 2; + if (State) State->CtorDtorConversion = true; + return make(SoFar, true, Variant); + } + + return nullptr; +} + +// ::= N [] [] E +// ::= N [] [] E +// +// ::= +// ::= +// ::= +// ::= +// ::= # empty +// ::= +// ::= +// extension ::= L +// +// := [] M +// +// ::=