xref: /reactos/sdk/tools/gcc_plugin_seh/main.cpp (revision 98e8827a)
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