1 /* 2 * PROJECT: ReactOS SDK 3 * LICENSE: BSD Zero Clause License (https://spdx.org/licenses/0BSD) 4 * PURPOSE: Helper pragma implementation for pseh library (amd64) 5 * COPYRIGHT: Copyright 2021 Jérôme Gardou 6 * Copyright 2024 Timo Kreuzer <timo.kreuzer@reactos.org> 7 */ 8 9 #include <gcc-plugin.h> 10 #include <plugin-version.h> 11 #include <function.h> 12 #include <tree.h> 13 #include <c-family/c-pragma.h> 14 #include <c-family/c-common.h> 15 16 #include <iostream> 17 #include <sstream> 18 #include <unordered_map> 19 #include <vector> 20 #include <cstdio> 21 22 #if 0 // To enable tracing 23 #define trace(...) fprintf(stderr, __VA_ARGS__) 24 #else 25 #define trace(...) 26 #endif 27 28 #define is_alpha(c) (((c)>64 && (c)<91) || ((c)>96 && (c)<123)) 29 30 #if defined(_WIN32) || defined(WIN32) 31 #define VISIBLE __declspec(dllexport) 32 #else 33 #define VISIBLE __attribute__((__visibility__("default"))) 34 #endif 35 36 #define UNUSED __attribute__((__unused__)) 37 38 int 39 VISIBLE 40 plugin_is_GPL_compatible = 1; 41 42 constexpr size_t k_header_statement_max_size = 20000; 43 44 struct seh_handler 45 { 46 bool is_except; 47 unsigned int line; 48 }; 49 50 struct seh_function 51 { 52 bool unwind; 53 bool except; 54 tree asm_header_text; 55 tree asm_header; 56 size_t count; 57 std::vector<seh_handler> handlers; 58 59 seh_function(struct function* fun) 60 : unwind(false) 61 , except(false) 62 , count(0) 63 { 64 /* Reserve space for our header statement */ 65 #if 0 // FIXME: crashes on older GCC 66 asm_header_text = build_string(k_header_statement_max_size, ""); 67 #else 68 char buf[k_header_statement_max_size]; 69 memset(buf, 0, sizeof(buf)); 70 asm_header_text = build_string(sizeof(buf), buf); 71 #endif 72 asm_header = build_stmt(fun->function_start_locus, ASM_EXPR, asm_header_text, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); 73 ASM_VOLATILE_P(asm_header) = 1; 74 add_stmt(asm_header); 75 } 76 }; 77 78 static std::unordered_map<struct function*, struct seh_function*> func_seh_map = {}; 79 80 static 81 struct seh_function* 82 get_seh_function() 83 { 84 auto search = func_seh_map.find(cfun); 85 if (search != func_seh_map.end()) 86 return search->second; 87 88 auto seh_fun = new seh_function(cfun); 89 func_seh_map.insert({cfun, seh_fun}); 90 91 return seh_fun; 92 } 93 94 static 95 void 96 handle_seh_pragma(cpp_reader* UNUSED parser) 97 { 98 tree x, arg, line; 99 std::stringstream label_decl; 100 bool is_except; 101 102 if (!cfun) 103 { 104 error("%<#pragma REACTOS seh%> is not allowed outside functions"); 105 return; 106 } 107 108 if ((pragma_lex(&x) != CPP_OPEN_PAREN) || 109 (pragma_lex(&arg) != CPP_NAME) || // except or finally 110 (pragma_lex(&x) != CPP_COMMA) || 111 (pragma_lex(&line) != CPP_NUMBER) || // Line number 112 (pragma_lex(&x) != CPP_CLOSE_PAREN) || 113 (pragma_lex(&x) != CPP_EOF) 114 ) 115 { 116 error("%<#pragma REACTOS seh%> needs two parameters%>"); 117 return; 118 } 119 120 trace(stderr, "Pragma: %s, %u\n", IDENTIFIER_POINTER(arg), TREE_INT_CST_LOW(line)); 121 122 const char* op = IDENTIFIER_POINTER(arg); 123 124 seh_function* seh_fun = get_seh_function(); 125 if (strcmp(op, "__seh$$except") == 0) 126 { 127 is_except = true; 128 seh_fun->except = true; 129 } 130 else if (strcmp(op, "__seh$$finally") == 0) 131 { 132 is_except = false; 133 seh_fun->unwind = true; 134 } 135 else 136 { 137 error("Wrong argument for %<#pragma REACTOS seh%>. Expected \"except\" or \"finally\""); 138 return; 139 } 140 seh_fun->count++; 141 142 seh_fun->handlers.push_back({is_except, (unsigned int)TREE_INT_CST_LOW(line)}); 143 144 /* Make sure we use a frame pointer. REACTOS' PSEH depends on this */ 145 cfun->machine->accesses_prev_frame = 1; 146 } 147 148 static 149 void 150 finish_seh_function(void* event_data, void* UNUSED user_data) 151 { 152 tree fndef = (tree)event_data; 153 struct function* fun = DECL_STRUCT_FUNCTION(fndef); 154 155 auto search = func_seh_map.find(fun); 156 if (search == func_seh_map.end()) 157 return; 158 159 /* Get our SEH details and remove us from the map */ 160 seh_function* seh_fun = search->second; 161 func_seh_map.erase(search); 162 163 if (DECL_FUNCTION_PERSONALITY(fndef) != nullptr) 164 { 165 error("Function %s has a personality. Are you mixing SEH with C++ exceptions ?", 166 IDENTIFIER_POINTER(fndef)); 167 return; 168 } 169 170 /* Update asm statement */ 171 std::stringstream asm_str; 172 asm_str << ".seh_handler __C_specific_handler"; 173 if (seh_fun->unwind) 174 asm_str << ", @unwind"; 175 if (seh_fun->except) 176 asm_str << ", @except"; 177 asm_str << "\n"; 178 asm_str << "\t.seh_handlerdata\n"; 179 asm_str << "\t.long " << seh_fun->count << "\n"; 180 181 for (auto& handler : seh_fun->handlers) 182 { 183 asm_str << "\n\t.rva " << "__seh2$$begin_try__" << handler.line; /* Begin of tried code */ 184 asm_str << "\n\t.rva " << "__seh2$$end_try__" << handler.line; /* End of tried code */ 185 asm_str << "\n\t.rva " << "__seh2$$filter__" << handler.line; /* Filter function */ 186 if (handler.is_except) 187 asm_str << "\n\t.rva " << "__seh2$$begin_except__" << handler.line; /* Called on except */ 188 else 189 asm_str << "\n\t.long 0"; /* No unwind handler */ 190 } 191 asm_str << "\n\t.seh_code\n"; 192 193 strncpy(const_cast<char*>(TREE_STRING_POINTER(seh_fun->asm_header_text)), 194 asm_str.str().c_str(), 195 TREE_STRING_LENGTH(seh_fun->asm_header_text)); 196 197 trace(stderr, "ASM: %s\n", asm_str.str().c_str()); 198 199 delete seh_fun; 200 } 201 202 static 203 void 204 register_seh_pragmas(void* UNUSED event_data, void* UNUSED user_data) 205 { 206 c_register_pragma("REACTOS", "seh", handle_seh_pragma); 207 } 208 209 /* Return 0 on success or error code on failure */ 210 extern "C" 211 VISIBLE 212 int plugin_init(struct plugin_name_args *info, /* Argument infor */ 213 struct plugin_gcc_version *version) /* Version of GCC */ 214 { 215 if (!plugin_default_version_check (version, &gcc_version)) 216 { 217 std::cerr << "This GCC plugin is for version " << GCCPLUGIN_VERSION_MAJOR << "." << GCCPLUGIN_VERSION_MINOR << "\n"; 218 return 1; 219 } 220 221 register_callback(info->base_name, PLUGIN_PRAGMAS, register_seh_pragmas, NULL); 222 register_callback(info->base_name, PLUGIN_FINISH_PARSE_FUNCTION, finish_seh_function, NULL); 223 224 return 0; 225 } 226