https://git.reactos.org/?p=reactos.git;a=commitdiff;h=37bc01f42b6d940c763b0…
commit 37bc01f42b6d940c763b0d346f44971c7abe08d9
Author: Jérôme Gardou <jerome.gardou(a)reactos.org>
AuthorDate: Thu Apr 22 10:37:50 2021 +0200
Commit: Jérôme Gardou <zefklop(a)users.noreply.github.com>
CommitDate: Wed Apr 28 13:10:23 2021 +0200
[CMAKE] Introduce a GCC plugin for helping with amd64 SEH implementation
\#pragma REACTOS SEH(except)
\#pragma REACTOS SEH(finally)
What it does is counting the number of SEH __try blocks and emit the proper assembly
statements at function prologue
It also checks for mixing C++ & SEH exception handling, which wouldn't work
---
sdk/cmake/host-tools.cmake | 23 ++++-
sdk/tools/CMakeLists.txt | 8 ++
sdk/tools/gcc_plugin_seh/CMakeLists.txt | 4 +
sdk/tools/gcc_plugin_seh/main.cpp | 177 ++++++++++++++++++++++++++++++++
4 files changed, 211 insertions(+), 1 deletion(-)
diff --git a/sdk/cmake/host-tools.cmake b/sdk/cmake/host-tools.cmake
index a738ff64c09..4633b1097d1 100644
--- a/sdk/cmake/host-tools.cmake
+++ b/sdk/cmake/host-tools.cmake
@@ -6,6 +6,13 @@ function(setup_host_tools)
if(NOT MSVC)
list(APPEND HOST_TOOLS rsym pefixup)
endif()
+ if ((ARCH STREQUAL "amd64") AND (CMAKE_C_COMPILER_ID STREQUAL
"GNU"))
+ execute_process(
+ COMMAND ${CMAKE_C_COMPILER} --print-file-name=plugin
+ OUTPUT_VARIABLE GCC_PLUGIN_DIR)
+ string(STRIP ${GCC_PLUGIN_DIR} GCC_PLUGIN_DIR)
+ list(APPEND HOST_MODULES gcc_plugin_seh)
+ endif()
list(TRANSFORM HOST_TOOLS PREPEND "${REACTOS_BINARY_DIR}/host-tools/bin/"
OUTPUT_VARIABLE HOST_TOOLS_OUTPUT)
if (CMAKE_HOST_WIN32)
list(TRANSFORM HOST_TOOLS_OUTPUT APPEND ".exe")
@@ -13,13 +20,21 @@ function(setup_host_tools)
set(HOST_EXTRA_DIR "$(ConfigurationName)/")
endif()
set(HOST_EXE_SUFFIX ".exe")
+ set(HOST_MODULE_SUFFIX ".dll")
+ else()
+ set(HOST_MODULE_SUFFIX ".so")
endif()
ExternalProject_Add(host-tools
SOURCE_DIR ${REACTOS_SOURCE_DIR}
PREFIX ${REACTOS_BINARY_DIR}/host-tools
BINARY_DIR ${REACTOS_BINARY_DIR}/host-tools/bin
- CMAKE_ARGS -UCMAKE_TOOLCHAIN_FILE -DARCH:STRING=${ARCH}
-DCMAKE_INSTALL_PREFIX=${REACTOS_BINARY_DIR}/host-tools
-DTOOLS_FOLDER=${REACTOS_BINARY_DIR}/host-tools/bin
+ CMAKE_ARGS
+ -UCMAKE_TOOLCHAIN_FILE
+ -DARCH:STRING=${ARCH}
+ -DCMAKE_INSTALL_PREFIX=${REACTOS_BINARY_DIR}/host-tools
+ -DTOOLS_FOLDER=${REACTOS_BINARY_DIR}/host-tools/bin
+ -DGCC_PLUGIN_DIR=${GCC_PLUGIN_DIR}
BUILD_ALWAYS TRUE
INSTALL_COMMAND ${CMAKE_COMMAND} -E true
BUILD_BYPRODUCTS ${HOST_TOOLS_OUTPUT}
@@ -32,4 +47,10 @@ function(setup_host_tools)
set_target_properties(native-${_tool} PROPERTIES IMPORTED_LOCATION
${INSTALL_DIR}/bin/${HOST_EXTRA_DIR}${_tool}${HOST_EXE_SUFFIX})
add_dependencies(native-${_tool} host-tools
${INSTALL_DIR}/bin/${HOST_EXTRA_DIR}${_tool}${HOST_EXE_SUFFIX})
endforeach()
+
+ foreach(_module ${HOST_MODULES})
+ add_library(native-${_module} MODULE IMPORTED)
+ set_target_properties(native-${_module} PROPERTIES IMPORTED_LOCATION
${INSTALL_DIR}/bin/${HOST_EXTRA_DIR}${_module}${HOST_MODULE_SUFFIX})
+ add_dependencies(native-${_module} host-tools
${INSTALL_DIR}/bin/${HOST_EXTRA_DIR}${_module}${HOST_MODULE_SUFFIX})
+ endforeach()
endfunction()
diff --git a/sdk/tools/CMakeLists.txt b/sdk/tools/CMakeLists.txt
index 3f836f8e9bf..956a6dc7060 100644
--- a/sdk/tools/CMakeLists.txt
+++ b/sdk/tools/CMakeLists.txt
@@ -4,6 +4,11 @@ function(add_host_tool _tool)
set_target_properties(${_tool} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TOOLS_FOLDER})
endfunction()
+function(add_host_module _module)
+ add_library(${_module} MODULE ${ARGN})
+ set_target_properties(${_module} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
${TOOLS_FOLDER})
+endfunction()
+
if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DHAVE_IO_H=1)
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:/EHsc>")
@@ -38,6 +43,9 @@ add_subdirectory(xml2sdb)
if(NOT MSVC)
add_subdirectory(log2lines)
add_subdirectory(rsym)
+if (ARCH STREQUAL "amd64")
+ add_subdirectory(gcc_plugin_seh)
+endif()
add_host_tool(pefixup pefixup.c)
if (ARCH STREQUAL "amd64")
diff --git a/sdk/tools/gcc_plugin_seh/CMakeLists.txt
b/sdk/tools/gcc_plugin_seh/CMakeLists.txt
new file mode 100644
index 00000000000..b96571f7fbe
--- /dev/null
+++ b/sdk/tools/gcc_plugin_seh/CMakeLists.txt
@@ -0,0 +1,4 @@
+
+add_host_module(gcc_plugin_seh main.cpp)
+target_include_directories(gcc_plugin_seh PRIVATE ${GCC_PLUGIN_DIR}/include)
+set_target_properties(gcc_plugin_seh PROPERTIES POSITION_INDEPENDENT_CODE ON)
diff --git a/sdk/tools/gcc_plugin_seh/main.cpp b/sdk/tools/gcc_plugin_seh/main.cpp
new file mode 100644
index 00000000000..22df978e0c6
--- /dev/null
+++ b/sdk/tools/gcc_plugin_seh/main.cpp
@@ -0,0 +1,177 @@
+/*
+ * PROJECT: ReactOS SDK
+ * LICENSE: BSD Zero Clause License (
https://spdx.org/licenses/0BSD.html)
+ * PURPOSE: Helper pragma implementation for pseh library (amd64)
+ * COPYRIGHT: Copyright 2021 Jérôme Gardou
+ */
+
+#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>
+
+#define is_alpha(c) (((c)>64 && (c)<91) || ((c)>96 &&
(c)<123))
+
+#if defined(_WIN32) || defined(WIN32)
+#define VISIBLE __decspec(dllexport)
+#else
+#define VISIBLE __attribute__((__visibility__("default")))
+#endif
+
+#define UNUSED __attribute__((__unused__))
+
+int
+VISIBLE
+plugin_is_GPL_compatible = 1;
+
+struct seh_function
+{
+ bool unwind;
+ bool except;
+ tree asm_header_text;
+ tree asm_header;
+ size_t count;
+
+ seh_function(struct function* fun)
+ : unwind(false)
+ , except(false)
+ , count(0)
+ {
+ /* Reserve space for our header statement */
+ char buf[256];
+ memset(buf, 0, sizeof(buf));
+ asm_header_text = build_string(sizeof(buf), buf);
+ 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)
+{
+ tree x, arg;
+ std::stringstream label_decl;
+
+ if (!cfun)
+ {
+ error("%<#pragma REACTOS seh%> is not allowed outside
functions");
+ return;
+ }
+
+ if ((pragma_lex(&x) != CPP_OPEN_PAREN) ||
+ (pragma_lex(&arg) != CPP_NAME) ||
+ (pragma_lex(&x) != CPP_CLOSE_PAREN) ||
+ (pragma_lex(&x) != CPP_EOF))
+ {
+ error("%<#pragma REACTOS seh%> needs one parameter%>");
+ return;
+ }
+
+ const char* op = IDENTIFIER_POINTER(arg);
+
+ seh_function* seh_fun = get_seh_function();
+ if (strcmp(op, "except") == 0)
+ seh_fun->except = true;
+ else if (strcmp(op, "finally") == 0)
+ seh_fun->unwind = true;
+ else
+ {
+ error("Wrong argument for %<#pragma REACTOS seh%>. Expected
\"except\" or \"finally\"");
+ return;
+ }
+ seh_fun->count++;
+
+ /* 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";
+ asm_str << "\t.seh_code";
+
+ 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));
+
+ 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;
+}