1 1.1 christos /* Invalid parameter handler for MSVC runtime libraries. 2 1.1.1.2 christos Copyright (C) 2011-2022 Free Software Foundation, Inc. 3 1.1 christos 4 1.1.1.2 christos This file is free software: you can redistribute it and/or modify 5 1.1.1.2 christos it under the terms of the GNU Lesser General Public License as 6 1.1.1.2 christos published by the Free Software Foundation; either version 2.1 of the 7 1.1.1.2 christos License, or (at your option) any later version. 8 1.1 christos 9 1.1.1.2 christos This file is distributed in the hope that it will be useful, 10 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 11 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 1.1.1.2 christos GNU Lesser General Public License for more details. 13 1.1 christos 14 1.1.1.2 christos You should have received a copy of the GNU Lesser General Public License 15 1.1.1.2 christos along with this program. If not, see <https://www.gnu.org/licenses/>. */ 16 1.1 christos 17 1.1 christos #ifndef _MSVC_INVAL_H 18 1.1 christos #define _MSVC_INVAL_H 19 1.1 christos 20 1.1 christos /* With MSVC runtime libraries with the "invalid parameter handler" concept, 21 1.1 christos functions like fprintf(), dup2(), or close() crash when the caller passes 22 1.1 christos an invalid argument. But POSIX wants error codes (such as EINVAL or EBADF) 23 1.1 christos instead. 24 1.1 christos This file defines macros that turn such an invalid parameter notification 25 1.1 christos into a non-local exit. An error code can then be produced at the target 26 1.1 christos of this exit. You can thus write code like 27 1.1 christos 28 1.1 christos TRY_MSVC_INVAL 29 1.1 christos { 30 1.1 christos <Code that can trigger an invalid parameter notification 31 1.1 christos but does not do 'return', 'break', 'continue', nor 'goto'.> 32 1.1 christos } 33 1.1 christos CATCH_MSVC_INVAL 34 1.1 christos { 35 1.1 christos <Code that handles an invalid parameter notification 36 1.1 christos but does not do 'return', 'break', 'continue', nor 'goto'.> 37 1.1 christos } 38 1.1 christos DONE_MSVC_INVAL; 39 1.1 christos 40 1.1 christos This entire block expands to a single statement. 41 1.1 christos 42 1.1 christos The handling of invalid parameters can be done in three ways: 43 1.1 christos 44 1.1 christos * The default way, which is reasonable for programs (not libraries): 45 1.1 christos AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [DEFAULT_HANDLING]) 46 1.1 christos 47 1.1 christos * The way for libraries that make "hairy" calls (like close(-1), or 48 1.1 christos fclose(fp) where fileno(fp) is closed, or simply getdtablesize()): 49 1.1 christos AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [HAIRY_LIBRARY_HANDLING]) 50 1.1 christos 51 1.1 christos * The way for libraries that make no "hairy" calls: 52 1.1 christos AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [SANE_LIBRARY_HANDLING]) 53 1.1 christos */ 54 1.1 christos 55 1.1 christos #define DEFAULT_HANDLING 0 56 1.1 christos #define HAIRY_LIBRARY_HANDLING 1 57 1.1 christos #define SANE_LIBRARY_HANDLING 2 58 1.1 christos 59 1.1 christos #if HAVE_MSVC_INVALID_PARAMETER_HANDLER \ 60 1.1 christos && !(MSVC_INVALID_PARAMETER_HANDLING == SANE_LIBRARY_HANDLING) 61 1.1 christos /* A native Windows platform with the "invalid parameter handler" concept, 62 1.1 christos and either DEFAULT_HANDLING or HAIRY_LIBRARY_HANDLING. */ 63 1.1 christos 64 1.1 christos # if MSVC_INVALID_PARAMETER_HANDLING == DEFAULT_HANDLING 65 1.1 christos /* Default handling. */ 66 1.1 christos 67 1.1 christos # ifdef __cplusplus 68 1.1 christos extern "C" { 69 1.1 christos # endif 70 1.1 christos 71 1.1 christos /* Ensure that the invalid parameter handler in installed that just returns. 72 1.1 christos Because we assume no other part of the program installs a different 73 1.1 christos invalid parameter handler, this solution is multithread-safe. */ 74 1.1 christos extern void gl_msvc_inval_ensure_handler (void); 75 1.1 christos 76 1.1 christos # ifdef __cplusplus 77 1.1 christos } 78 1.1 christos # endif 79 1.1 christos 80 1.1 christos # define TRY_MSVC_INVAL \ 81 1.1 christos do \ 82 1.1 christos { \ 83 1.1 christos gl_msvc_inval_ensure_handler (); \ 84 1.1 christos if (1) 85 1.1 christos # define CATCH_MSVC_INVAL \ 86 1.1 christos else 87 1.1 christos # define DONE_MSVC_INVAL \ 88 1.1 christos } \ 89 1.1 christos while (0) 90 1.1 christos 91 1.1 christos # else 92 1.1 christos /* Handling for hairy libraries. */ 93 1.1 christos 94 1.1 christos # include <excpt.h> 95 1.1 christos 96 1.1 christos /* Gnulib can define its own status codes, as described in the page 97 1.1 christos "Raising Software Exceptions" on microsoft.com 98 1.1 christos <https://docs.microsoft.com/en-us/cpp/cpp/raising-software-exceptions>. 99 1.1 christos Our status codes are composed of 100 1.1 christos - 0xE0000000, mandatory for all user-defined status codes, 101 1.1 christos - 0x474E550, a API identifier ("GNU"), 102 1.1 christos - 0, 1, 2, ..., used to distinguish different status codes from the 103 1.1 christos same API. */ 104 1.1 christos # define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0) 105 1.1 christos 106 1.1 christos # if defined _MSC_VER 107 1.1 christos /* A compiler that supports __try/__except, as described in the page 108 1.1 christos "try-except statement" on microsoft.com 109 1.1 christos <https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement>. 110 1.1 christos With __try/__except, we can use the multithread-safe exception handling. */ 111 1.1 christos 112 1.1 christos # ifdef __cplusplus 113 1.1 christos extern "C" { 114 1.1 christos # endif 115 1.1 christos 116 1.1 christos /* Ensure that the invalid parameter handler in installed that raises a 117 1.1 christos software exception with code STATUS_GNULIB_INVALID_PARAMETER. 118 1.1 christos Because we assume no other part of the program installs a different 119 1.1 christos invalid parameter handler, this solution is multithread-safe. */ 120 1.1 christos extern void gl_msvc_inval_ensure_handler (void); 121 1.1 christos 122 1.1 christos # ifdef __cplusplus 123 1.1 christos } 124 1.1 christos # endif 125 1.1 christos 126 1.1 christos # define TRY_MSVC_INVAL \ 127 1.1 christos do \ 128 1.1 christos { \ 129 1.1 christos gl_msvc_inval_ensure_handler (); \ 130 1.1 christos __try 131 1.1 christos # define CATCH_MSVC_INVAL \ 132 1.1 christos __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER \ 133 1.1 christos ? EXCEPTION_EXECUTE_HANDLER \ 134 1.1 christos : EXCEPTION_CONTINUE_SEARCH) 135 1.1 christos # define DONE_MSVC_INVAL \ 136 1.1 christos } \ 137 1.1 christos while (0) 138 1.1 christos 139 1.1 christos # else 140 1.1 christos /* Any compiler. 141 1.1 christos We can only use setjmp/longjmp. */ 142 1.1 christos 143 1.1 christos # include <setjmp.h> 144 1.1 christos 145 1.1 christos # ifdef __cplusplus 146 1.1 christos extern "C" { 147 1.1 christos # endif 148 1.1 christos 149 1.1 christos struct gl_msvc_inval_per_thread 150 1.1 christos { 151 1.1 christos /* The restart that will resume execution at the code between 152 1.1 christos CATCH_MSVC_INVAL and DONE_MSVC_INVAL. It is enabled only between 153 1.1 christos TRY_MSVC_INVAL and CATCH_MSVC_INVAL. */ 154 1.1 christos jmp_buf restart; 155 1.1 christos 156 1.1 christos /* Tells whether the contents of restart is valid. */ 157 1.1 christos int restart_valid; 158 1.1 christos }; 159 1.1 christos 160 1.1 christos /* Ensure that the invalid parameter handler in installed that passes 161 1.1 christos control to the gl_msvc_inval_restart if it is valid, or raises a 162 1.1 christos software exception with code STATUS_GNULIB_INVALID_PARAMETER otherwise. 163 1.1 christos Because we assume no other part of the program installs a different 164 1.1 christos invalid parameter handler, this solution is multithread-safe. */ 165 1.1 christos extern void gl_msvc_inval_ensure_handler (void); 166 1.1 christos 167 1.1 christos /* Return a pointer to the per-thread data for the current thread. */ 168 1.1 christos extern struct gl_msvc_inval_per_thread *gl_msvc_inval_current (void); 169 1.1 christos 170 1.1 christos # ifdef __cplusplus 171 1.1 christos } 172 1.1 christos # endif 173 1.1 christos 174 1.1 christos # define TRY_MSVC_INVAL \ 175 1.1 christos do \ 176 1.1 christos { \ 177 1.1 christos struct gl_msvc_inval_per_thread *msvc_inval_current; \ 178 1.1 christos gl_msvc_inval_ensure_handler (); \ 179 1.1 christos msvc_inval_current = gl_msvc_inval_current (); \ 180 1.1 christos /* First, initialize gl_msvc_inval_restart. */ \ 181 1.1 christos if (setjmp (msvc_inval_current->restart) == 0) \ 182 1.1 christos { \ 183 1.1 christos /* Then, mark it as valid. */ \ 184 1.1 christos msvc_inval_current->restart_valid = 1; 185 1.1 christos # define CATCH_MSVC_INVAL \ 186 1.1 christos /* Execution completed. \ 187 1.1 christos Mark gl_msvc_inval_restart as invalid. */ \ 188 1.1 christos msvc_inval_current->restart_valid = 0; \ 189 1.1 christos } \ 190 1.1 christos else \ 191 1.1 christos { \ 192 1.1 christos /* Execution triggered an invalid parameter notification. \ 193 1.1 christos Mark gl_msvc_inval_restart as invalid. */ \ 194 1.1 christos msvc_inval_current->restart_valid = 0; 195 1.1 christos # define DONE_MSVC_INVAL \ 196 1.1 christos } \ 197 1.1 christos } \ 198 1.1 christos while (0) 199 1.1 christos 200 1.1 christos # endif 201 1.1 christos 202 1.1 christos # endif 203 1.1 christos 204 1.1 christos #else 205 1.1 christos /* A platform that does not need to the invalid parameter handler, 206 1.1 christos or when SANE_LIBRARY_HANDLING is desired. */ 207 1.1 christos 208 1.1 christos /* The braces here avoid GCC warnings like 209 1.1 christos "warning: suggest explicit braces to avoid ambiguous 'else'". */ 210 1.1 christos # define TRY_MSVC_INVAL \ 211 1.1 christos do \ 212 1.1 christos { \ 213 1.1 christos if (1) 214 1.1 christos # define CATCH_MSVC_INVAL \ 215 1.1 christos else 216 1.1 christos # define DONE_MSVC_INVAL \ 217 1.1 christos } \ 218 1.1 christos while (0) 219 1.1 christos 220 1.1 christos #endif 221 1.1 christos 222 1.1 christos #endif /* _MSVC_INVAL_H */ 223