2021-04-22 08:37:50 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS SDK
|
2022-11-03 15:36:27 +00:00
|
|
|
* LICENSE: BSD Zero Clause License (https://spdx.org/licenses/0BSD)
|
2021-04-22 08:37:50 +00:00
|
|
|
* PURPOSE: Helper pragma implementation for pseh library (amd64)
|
|
|
|
* COPYRIGHT: Copyright 2021 Jérôme Gardou
|
2024-03-15 19:17:23 +00:00
|
|
|
* Copyright 2024 Timo Kreuzer <timo.kreuzer@reactos.org>
|
2021-04-22 08:37:50 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <gcc-plugin.h>
|
|
|
|
#include <plugin-version.h>
|
|
|
|
#include <function.h>
|
|
|
|
#include <tree.h>
|
|
|
|
#include <c-family/c-pragma.h>
|
|
|
|
#include <c-family/c-common.h>
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <vector>
|
2024-03-15 19:17:23 +00:00
|
|
|
#include <cstdio>
|
|
|
|
|
|
|
|
#if 0 // To enable tracing
|
|
|
|
#define trace(...) fprintf(stderr, __VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define trace(...)
|
|
|
|
#endif
|
2021-04-22 08:37:50 +00:00
|
|
|
|
|
|
|
#define is_alpha(c) (((c)>64 && (c)<91) || ((c)>96 && (c)<123))
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(WIN32)
|
2021-05-19 12:13:51 +00:00
|
|
|
#define VISIBLE __declspec(dllexport)
|
2021-04-22 08:37:50 +00:00
|
|
|
#else
|
|
|
|
#define VISIBLE __attribute__((__visibility__("default")))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define UNUSED __attribute__((__unused__))
|
|
|
|
|
|
|
|
int
|
|
|
|
VISIBLE
|
|
|
|
plugin_is_GPL_compatible = 1;
|
|
|
|
|
2024-03-15 19:17:23 +00:00
|
|
|
constexpr size_t k_header_statement_max_size = 20000;
|
|
|
|
|
|
|
|
struct seh_handler
|
|
|
|
{
|
|
|
|
bool is_except;
|
|
|
|
unsigned int line;
|
|
|
|
};
|
|
|
|
|
2021-04-22 08:37:50 +00:00
|
|
|
struct seh_function
|
|
|
|
{
|
|
|
|
bool unwind;
|
|
|
|
bool except;
|
|
|
|
tree asm_header_text;
|
|
|
|
tree asm_header;
|
|
|
|
size_t count;
|
2024-03-15 19:17:23 +00:00
|
|
|
std::vector<seh_handler> handlers;
|
2021-04-22 08:37:50 +00:00
|
|
|
|
|
|
|
seh_function(struct function* fun)
|
|
|
|
: unwind(false)
|
|
|
|
, except(false)
|
|
|
|
, count(0)
|
|
|
|
{
|
|
|
|
/* Reserve space for our header statement */
|
2024-03-15 19:17:23 +00:00
|
|
|
#if 0 // FIXME: crashes on older GCC
|
|
|
|
asm_header_text = build_string(k_header_statement_max_size, "");
|
|
|
|
#else
|
|
|
|
char buf[k_header_statement_max_size];
|
2021-04-22 08:37:50 +00:00
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
asm_header_text = build_string(sizeof(buf), buf);
|
2024-03-15 19:17:23 +00:00
|
|
|
#endif
|
2021-04-22 08:37:50 +00:00
|
|
|
asm_header = build_stmt(fun->function_start_locus, ASM_EXPR, asm_header_text, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
|
|
|
|
ASM_VOLATILE_P(asm_header) = 1;
|
|
|
|
add_stmt(asm_header);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static std::unordered_map<struct function*, struct seh_function*> func_seh_map = {};
|
|
|
|
|
|
|
|
static
|
|
|
|
struct seh_function*
|
|
|
|
get_seh_function()
|
|
|
|
{
|
|
|
|
auto search = func_seh_map.find(cfun);
|
|
|
|
if (search != func_seh_map.end())
|
|
|
|
return search->second;
|
|
|
|
|
|
|
|
auto seh_fun = new seh_function(cfun);
|
|
|
|
func_seh_map.insert({cfun, seh_fun});
|
|
|
|
|
|
|
|
return seh_fun;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void
|
|
|
|
handle_seh_pragma(cpp_reader* UNUSED parser)
|
|
|
|
{
|
2024-03-15 19:17:23 +00:00
|
|
|
tree x, arg, line;
|
2021-04-22 08:37:50 +00:00
|
|
|
std::stringstream label_decl;
|
2024-03-15 19:17:23 +00:00
|
|
|
bool is_except;
|
2021-04-22 08:37:50 +00:00
|
|
|
|
|
|
|
if (!cfun)
|
|
|
|
{
|
|
|
|
error("%<#pragma REACTOS seh%> is not allowed outside functions");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((pragma_lex(&x) != CPP_OPEN_PAREN) ||
|
2024-03-15 19:17:23 +00:00
|
|
|
(pragma_lex(&arg) != CPP_NAME) || // except or finally
|
|
|
|
(pragma_lex(&x) != CPP_COMMA) ||
|
|
|
|
(pragma_lex(&line) != CPP_NUMBER) || // Line number
|
2021-04-22 08:37:50 +00:00
|
|
|
(pragma_lex(&x) != CPP_CLOSE_PAREN) ||
|
2024-03-15 19:17:23 +00:00
|
|
|
(pragma_lex(&x) != CPP_EOF)
|
|
|
|
)
|
2021-04-22 08:37:50 +00:00
|
|
|
{
|
2024-03-15 19:17:23 +00:00
|
|
|
error("%<#pragma REACTOS seh%> needs two parameters%>");
|
2021-04-22 08:37:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-03-15 19:17:23 +00:00
|
|
|
trace(stderr, "Pragma: %s, %u\n", IDENTIFIER_POINTER(arg), TREE_INT_CST_LOW(line));
|
|
|
|
|
2021-04-22 08:37:50 +00:00
|
|
|
const char* op = IDENTIFIER_POINTER(arg);
|
|
|
|
|
|
|
|
seh_function* seh_fun = get_seh_function();
|
2024-03-15 19:17:23 +00:00
|
|
|
if (strcmp(op, "__seh$$except") == 0)
|
|
|
|
{
|
|
|
|
is_except = true;
|
2021-04-22 08:37:50 +00:00
|
|
|
seh_fun->except = true;
|
2024-03-15 19:17:23 +00:00
|
|
|
}
|
|
|
|
else if (strcmp(op, "__seh$$finally") == 0)
|
|
|
|
{
|
|
|
|
is_except = false;
|
2021-04-22 08:37:50 +00:00
|
|
|
seh_fun->unwind = true;
|
2024-03-15 19:17:23 +00:00
|
|
|
}
|
2021-04-22 08:37:50 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
error("Wrong argument for %<#pragma REACTOS seh%>. Expected \"except\" or \"finally\"");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
seh_fun->count++;
|
|
|
|
|
2024-03-15 19:17:23 +00:00
|
|
|
seh_fun->handlers.push_back({is_except, (unsigned int)TREE_INT_CST_LOW(line)});
|
|
|
|
|
2021-04-22 08:37:50 +00:00
|
|
|
/* Make sure we use a frame pointer. REACTOS' PSEH depends on this */
|
|
|
|
cfun->machine->accesses_prev_frame = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void
|
|
|
|
finish_seh_function(void* event_data, void* UNUSED user_data)
|
|
|
|
{
|
|
|
|
tree fndef = (tree)event_data;
|
|
|
|
struct function* fun = DECL_STRUCT_FUNCTION(fndef);
|
|
|
|
|
|
|
|
auto search = func_seh_map.find(fun);
|
|
|
|
if (search == func_seh_map.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Get our SEH details and remove us from the map */
|
|
|
|
seh_function* seh_fun = search->second;
|
|
|
|
func_seh_map.erase(search);
|
|
|
|
|
|
|
|
if (DECL_FUNCTION_PERSONALITY(fndef) != nullptr)
|
|
|
|
{
|
|
|
|
error("Function %s has a personality. Are you mixing SEH with C++ exceptions ?",
|
|
|
|
IDENTIFIER_POINTER(fndef));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update asm statement */
|
|
|
|
std::stringstream asm_str;
|
|
|
|
asm_str << ".seh_handler __C_specific_handler";
|
|
|
|
if (seh_fun->unwind)
|
|
|
|
asm_str << ", @unwind";
|
|
|
|
if (seh_fun->except)
|
|
|
|
asm_str << ", @except";
|
|
|
|
asm_str << "\n";
|
|
|
|
asm_str << "\t.seh_handlerdata\n";
|
|
|
|
asm_str << "\t.long " << seh_fun->count << "\n";
|
2024-03-15 19:17:23 +00:00
|
|
|
|
|
|
|
for (auto& handler : seh_fun->handlers)
|
|
|
|
{
|
|
|
|
asm_str << "\n\t.rva " << "__seh2$$begin_try__" << handler.line; /* Begin of tried code */
|
|
|
|
asm_str << "\n\t.rva " << "__seh2$$end_try__" << handler.line; /* End of tried code */
|
|
|
|
asm_str << "\n\t.rva " << "__seh2$$filter__" << handler.line; /* Filter function */
|
|
|
|
if (handler.is_except)
|
|
|
|
asm_str << "\n\t.rva " << "__seh2$$begin_except__" << handler.line; /* Called on except */
|
|
|
|
else
|
|
|
|
asm_str << "\n\t.long 0"; /* No unwind handler */
|
|
|
|
}
|
|
|
|
asm_str << "\n\t.seh_code\n";
|
2021-04-22 08:37:50 +00:00
|
|
|
|
|
|
|
strncpy(const_cast<char*>(TREE_STRING_POINTER(seh_fun->asm_header_text)),
|
|
|
|
asm_str.str().c_str(),
|
|
|
|
TREE_STRING_LENGTH(seh_fun->asm_header_text));
|
|
|
|
|
2024-03-15 19:17:23 +00:00
|
|
|
trace(stderr, "ASM: %s\n", asm_str.str().c_str());
|
|
|
|
|
2021-04-22 08:37:50 +00:00
|
|
|
delete seh_fun;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void
|
|
|
|
register_seh_pragmas(void* UNUSED event_data, void* UNUSED user_data)
|
|
|
|
{
|
|
|
|
c_register_pragma("REACTOS", "seh", handle_seh_pragma);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return 0 on success or error code on failure */
|
|
|
|
extern "C"
|
|
|
|
VISIBLE
|
|
|
|
int plugin_init(struct plugin_name_args *info, /* Argument infor */
|
|
|
|
struct plugin_gcc_version *version) /* Version of GCC */
|
|
|
|
{
|
|
|
|
if (!plugin_default_version_check (version, &gcc_version))
|
|
|
|
{
|
|
|
|
std::cerr << "This GCC plugin is for version " << GCCPLUGIN_VERSION_MAJOR << "." << GCCPLUGIN_VERSION_MINOR << "\n";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
register_callback(info->base_name, PLUGIN_PRAGMAS, register_seh_pragmas, NULL);
|
|
|
|
register_callback(info->base_name, PLUGIN_FINISH_PARSE_FUNCTION, finish_seh_function, NULL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|