diff --git a/sdk/lib/pseh/include/pseh/pseh2_64.h b/sdk/lib/pseh/include/pseh/pseh2_64.h index 21be52477ca..29af396a714 100644 --- a/sdk/lib/pseh/include/pseh/pseh2_64.h +++ b/sdk/lib/pseh/include/pseh/pseh2_64.h @@ -6,15 +6,19 @@ __asm__( ".p2align 4, 0x90\n" ".seh_proc __seh2_global_filter_func\n" "__seh2_global_filter_func:\n" + /* r8 is rbp - frame-offset. Calculate the negative frame-offset */ + "\tsub %rbp, %rax\n" "\tpush %rbp\n" "\t.seh_pushreg %rbp\n" "\tsub $32, %rsp\n" "\t.seh_stackalloc 32\n" "\t.seh_endprologue\n" + /* rdx is the original stack pointer, fix it up to be the frame pointer */ + "\tsub %rax, %rdx\n" /* Restore frame pointer. */ "\tmov %rdx, %rbp\n" /* Actually execute the filter funclet */ - "\tjmp *%rax\n" + "\tjmp *%r8\n" "__seh2_global_filter_func_exit:\n" "\t.p2align 4\n" "\tadd $32, %rsp\n" @@ -22,8 +26,36 @@ __asm__( "\tret\n" "\t.seh_endproc"); +#define STRINGIFY(a) #a +#define EMIT_PRAGMA_(params) \ + _Pragma( STRINGIFY(params) ) +#define EMIT_PRAGMA(type,line) \ + EMIT_PRAGMA_(REACTOS seh(type,line)) + +#define _SEH3$_EMIT_DEFS_AND_PRAGMA__(Line, Type) \ + /* Emit assembler constants with line number to be individual */ \ + __asm__ __volatile__ goto ("\n" \ + "\t__seh2$$begin_try__" #Line "=%l0\n" /* Begin of tried code */ \ + "\t__seh2$$end_try__" #Line "=%l1 + 1\n" /* End of tried code */ \ + "\t__seh2$$filter__" #Line "=%l2\n" /* Filter function */ \ + "\t__seh2$$begin_except__" #Line "=%l3\n" /* Called on except */ \ + : /* No output */ \ + : /* No input */ \ + : /* No clobber */ \ + : __seh2$$begin_try__, \ + __seh2$$end_try__, \ + __seh2$$filter__, \ + __seh2$$begin_except__); \ + /* Call our home-made pragma */ \ + EMIT_PRAGMA(Type,Line) + +#define _SEH3$_EMIT_DEFS_AND_PRAGMA_(Line, Type) _SEH3$_EMIT_DEFS_AND_PRAGMA__(Line, Type) +#define _SEH3$_EMIT_DEFS_AND_PRAGMA(Type) _SEH3$_EMIT_DEFS_AND_PRAGMA_(__LINE__, Type) + #define _SEH2_TRY \ { \ + __label__ __seh2$$filter__; \ + __label__ __seh2$$begin_except__; \ __label__ __seh2$$begin_try__; \ __label__ __seh2$$end_try__; \ /* \ @@ -40,41 +72,25 @@ __seh2$$begin_try__: #define _SEH2_EXCEPT(...) \ __seh2$$leave_scope__: __MINGW_ATTRIB_UNUSED; \ } \ -__seh2$$end_try__: \ +__seh2$$end_try__:(void)0; \ /* Call our home-made pragma */ \ - _Pragma("REACTOS seh(except)") \ + _SEH3$_EMIT_DEFS_AND_PRAGMA(__seh$$except); \ if (0) \ { \ __label__ __seh2$$leave_scope__; \ - __label__ __seh2$$filter__; \ - __label__ __seh2$$begin_except__; \ - LONG __MINGW_ATTRIB_UNUSED __seh2$$exception_code__ = 0; \ + LONG __MINGW_ATTRIB_UNUSED __seh2$$exception_code__; \ /* Add our handlers to the list */ \ - __asm__ __volatile__ goto ("\n" \ - "\t.seh_handlerdata\n" \ - "\t.rva %l0\n" /* Begin of tried code */ \ - "\t.rva %l1 + 1\n" /* End of tried code */ \ - "\t.rva %l2\n" /* Filter function */ \ - "\t.rva %l3\n" /* Called on except */ \ - "\t.seh_code\n" \ - : /* No output */ \ - : /* No input */ \ - : /* No clobber */ \ - : __seh2$$begin_try__, \ - __seh2$$end_try__, \ - __seh2$$filter__, \ - __seh2$$begin_except__); \ if (0) \ { \ /* Jump to the global filter. Tell it where the filter funclet lies */ \ __label__ __seh2$$filter_funclet__; \ __seh2$$filter__: \ __asm__ __volatile__ goto( \ - "\tleaq %l0(%%rip), %%rax\n" \ + "\tleaq %l1(%%rip), %%r8\n" \ "\tjmp __seh2_global_filter_func\n" \ : /* No output */ \ - : /* No input */ \ - : "%rax" \ + : "a"(__builtin_frame_address(0)) \ + : "%r8" \ : __seh2$$filter_funclet__); \ /* Actually declare our filter funclet */ \ struct _EXCEPTION_POINTERS* __seh2$$exception_ptr__; \ @@ -105,38 +121,28 @@ __seh2$$end_try__: __seh2$$leave_scope__: __MINGW_ATTRIB_UNUSED; \ } \ __seh2$$end_try__: \ +__seh2$$begin_except__: __MINGW_ATTRIB_UNUSED; \ /* Call our home-made pragma */ \ - _Pragma("REACTOS seh(finally)") \ + _SEH3$_EMIT_DEFS_AND_PRAGMA(__seh$$finally); \ if (1) \ { \ __label__ __seh2$$finally__; \ __label__ __seh2$$begin_finally__; \ __label__ __seh2$$leave_scope__; \ + __asm__ __volatile__ goto("" : : : : __seh2$$finally__); \ int __seh2$$abnormal_termination__; \ - /* Add our handlers to the list */ \ - __asm__ __volatile__ goto ("\n" \ - "\t.seh_handlerdata\n" \ - "\t.rva %l0\n" /* Begin of tried code */ \ - "\t.rva %l1 + 1\n" /* End of tried code */ \ - "\t.rva %l2\n" /* Filter function */ \ - "\t.long 0\n" /* Nothing for unwind code */ \ - "\t.seh_code\n" \ - : /* No output */ \ - : /* No input */ \ - : /* No clobber */ \ - : __seh2$$begin_try__, \ - __seh2$$end_try__, \ - __seh2$$finally__); \ if (0) \ { \ /* Jump to the global trampoline. Tell it where the unwind code really lies */ \ - __seh2$$finally__: \ + __seh2$$filter__: __MINGW_ATTRIB_UNUSED; \ + __seh2$$finally__: __MINGW_ATTRIB_UNUSED; \ __asm__ __volatile__ goto( \ - "\tleaq %l0(%%rip), %%rax\n" \ + "\t\n" \ + "\tleaq %l1(%%rip), %%r8\n" \ "\tjmp __seh2_global_filter_func\n" \ : /* No output */ \ - : /* No input */ \ - : /* No clobber */ \ + : "a"(__builtin_frame_address(0)) \ + : "%r8" \ : __seh2$$begin_finally__); \ } \ \ diff --git a/sdk/tools/gcc_plugin_seh/main.cpp b/sdk/tools/gcc_plugin_seh/main.cpp index b90c736b584..00e04c0c6ab 100644 --- a/sdk/tools/gcc_plugin_seh/main.cpp +++ b/sdk/tools/gcc_plugin_seh/main.cpp @@ -3,6 +3,7 @@ * LICENSE: BSD Zero Clause License (https://spdx.org/licenses/0BSD) * PURPOSE: Helper pragma implementation for pseh library (amd64) * COPYRIGHT: Copyright 2021 Jérôme Gardou + * Copyright 2024 Timo Kreuzer */ #include @@ -16,6 +17,13 @@ #include #include #include +#include + +#if 0 // To enable tracing +#define trace(...) fprintf(stderr, __VA_ARGS__) +#else +#define trace(...) +#endif #define is_alpha(c) (((c)>64 && (c)<91) || ((c)>96 && (c)<123)) @@ -31,6 +39,14 @@ int VISIBLE plugin_is_GPL_compatible = 1; +constexpr size_t k_header_statement_max_size = 20000; + +struct seh_handler +{ + bool is_except; + unsigned int line; +}; + struct seh_function { bool unwind; @@ -38,6 +54,7 @@ struct seh_function tree asm_header_text; tree asm_header; size_t count; + std::vector handlers; seh_function(struct function* fun) : unwind(false) @@ -45,9 +62,13 @@ struct seh_function , count(0) { /* Reserve space for our header statement */ - char buf[256]; +#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]; memset(buf, 0, sizeof(buf)); asm_header_text = build_string(sizeof(buf), buf); +#endif 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); @@ -74,8 +95,9 @@ static void handle_seh_pragma(cpp_reader* UNUSED parser) { - tree x, arg; + tree x, arg, line; std::stringstream label_decl; + bool is_except; if (!cfun) { @@ -84,21 +106,32 @@ handle_seh_pragma(cpp_reader* UNUSED parser) } if ((pragma_lex(&x) != CPP_OPEN_PAREN) || - (pragma_lex(&arg) != CPP_NAME) || + (pragma_lex(&arg) != CPP_NAME) || // except or finally + (pragma_lex(&x) != CPP_COMMA) || + (pragma_lex(&line) != CPP_NUMBER) || // Line number (pragma_lex(&x) != CPP_CLOSE_PAREN) || - (pragma_lex(&x) != CPP_EOF)) + (pragma_lex(&x) != CPP_EOF) + ) { - error("%<#pragma REACTOS seh%> needs one parameter%>"); + error("%<#pragma REACTOS seh%> needs two parameters%>"); return; } + trace(stderr, "Pragma: %s, %u\n", IDENTIFIER_POINTER(arg), TREE_INT_CST_LOW(line)); + const char* op = IDENTIFIER_POINTER(arg); seh_function* seh_fun = get_seh_function(); - if (strcmp(op, "except") == 0) + if (strcmp(op, "__seh$$except") == 0) + { + is_except = true; seh_fun->except = true; - else if (strcmp(op, "finally") == 0) + } + else if (strcmp(op, "__seh$$finally") == 0) + { + is_except = false; seh_fun->unwind = true; + } else { error("Wrong argument for %<#pragma REACTOS seh%>. Expected \"except\" or \"finally\""); @@ -106,6 +139,8 @@ handle_seh_pragma(cpp_reader* UNUSED parser) } seh_fun->count++; + seh_fun->handlers.push_back({is_except, (unsigned int)TREE_INT_CST_LOW(line)}); + /* Make sure we use a frame pointer. REACTOS' PSEH depends on this */ cfun->machine->accesses_prev_frame = 1; } @@ -142,12 +177,25 @@ finish_seh_function(void* event_data, void* UNUSED user_data) asm_str << "\n"; asm_str << "\t.seh_handlerdata\n"; asm_str << "\t.long " << seh_fun->count << "\n"; - asm_str << "\t.seh_code"; + + 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"; strncpy(const_cast(TREE_STRING_POINTER(seh_fun->asm_header_text)), asm_str.str().c_str(), TREE_STRING_LENGTH(seh_fun->asm_header_text)); + trace(stderr, "ASM: %s\n", asm_str.str().c_str()); + delete seh_fun; }