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