mirror of
https://github.com/reactos/reactos.git
synced 2024-12-26 17:14:41 +00:00
[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
This commit is contained in:
parent
8abcd18742
commit
37bc01f42b
4 changed files with 211 additions and 1 deletions
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
4
sdk/tools/gcc_plugin_seh/CMakeLists.txt
Normal file
4
sdk/tools/gcc_plugin_seh/CMakeLists.txt
Normal file
|
@ -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)
|
177
sdk/tools/gcc_plugin_seh/main.cpp
Normal file
177
sdk/tools/gcc_plugin_seh/main.cpp
Normal file
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue