From 5f313889446b18a9527a3bbe95039a7bd2d38026 Mon Sep 17 00:00:00 2001 From: Toni Uhlig Date: Wed, 15 Nov 2023 09:56:37 +0100 Subject: [PATCH] Partial SEH support. * Removed linker script; .edata *and* .pdata required by MingW for SEH. * Removed buggy `-fdata-sections` and `-ffunction-sections`. Signed-off-by: Toni Uhlig --- CRT/except.h | 35 ++++ CRT/kcrt.c | 2 + Makefile.deps | 4 +- Makefile.inc | 7 +- Makefile.native.inc | 3 +- README.md | 31 +++- examples/dpp-example.c | 44 ++++- ld-script.txt | 395 ----------------------------------------- 8 files changed, 116 insertions(+), 405 deletions(-) create mode 100644 CRT/except.h delete mode 100644 ld-script.txt diff --git a/CRT/except.h b/CRT/except.h new file mode 100644 index 0000000..7f39eac --- /dev/null +++ b/CRT/except.h @@ -0,0 +1,35 @@ +#ifndef EXCEPT_H +#define EXCEPT_H 1 + +#ifndef __SEH__ +#error "SEH not supported by your toolchain!" +#endif + +#ifdef __try1 +#undef __try1 +#endif + +#ifdef __except1 +#undef __except1 +#endif + +#define __dpptry(handler, counter) \ + __asm__ goto( \ + ".seh_handler __C_specific_handler, @except\n\t" \ + ".seh_handlerdata\n\t" \ + ".long 1\n\t" \ + ".rva .l_startw" #counter ", .l_endw" #counter ", " #handler ", .l_exceptw" #counter \ + "\n\t" \ + ".section .text\n" \ + ".l_startw" #counter ":" :: ::except); + +#define __dppexcept(counter) \ + goto end; \ + except: \ + __asm__(".l_exceptw" #counter ":"); + +#define __dpptryend(counter) \ + end: \ + __asm__(".l_endw" #counter ":"); + +#endif diff --git a/CRT/kcrt.c b/CRT/kcrt.c index c534ae3..9809197 100644 --- a/CRT/kcrt.c +++ b/CRT/kcrt.c @@ -6,6 +6,8 @@ #include +#include "except.h" + #define KCRT_POOL_DEFAULT_TAG 0xDEADBEEF extern void (*__CTOR_LIST__)(); diff --git a/Makefile.deps b/Makefile.deps index bfdfedc..0bf6256 100644 --- a/Makefile.deps +++ b/Makefile.deps @@ -49,12 +49,12 @@ endif $(LIBCRT_BUILD_DIR)/kcrt$(NAME_SUFFIX).o: $(CC) $(DPP_ROOT)/CRT/kcrt.c $(Q)test -d '$(LIBCRT_BUILD_DIR)' || mkdir -p '$(LIBCRT_BUILD_DIR)' - $(Q)$(CC) -std=c99 $(CFLAGS) -c CRT/kcrt.c -o $@ + $(Q)$(CC) -std=gnu99 $(CFLAGS) -c CRT/kcrt.c -o $@ @echo 'CC $@' $(LIBCRT_BUILD_DIR)/ntdll_zw_functions$(NAME_SUFFIX).o: $(CC) $(DPP_ROOT)/CRT/ntdll_zw_functions.c $(Q)test -d '$(LIBCRT_BUILD_DIR)' || mkdir -p '$(LIBCRT_BUILD_DIR)' - $(Q)$(CC) -std=c99 $(CFLAGS) -c CRT/ntdll_zw_functions.c -o $@ + $(Q)$(CC) -std=gnu99 $(CFLAGS) -c CRT/ntdll_zw_functions.c -o $@ @echo 'CC $@' $(LIBCRT_BUILD_DIR)/eastl_compat$(NAME_SUFFIX).opp: $(CXX) $(DPP_ROOT)/CRT/eastl_compat.cpp diff --git a/Makefile.inc b/Makefile.inc index c4ac08d..f0959d8 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -49,7 +49,7 @@ RC = $(LOCAL_MINGW64_RC) DDK_INCLUDE_DIR = $(dir $(CC))../x86_64-w64-mingw32/include/ddk CFLAGS := -Wall -Wextra -Wno-sign-compare -Wno-strict-aliasing \ -m64 -fPIC -fvisibility=hidden \ - -ffunction-sections -fdata-sections -fno-builtin -ffreestanding \ + -fno-builtin -ffreestanding \ -fno-stack-protector -mno-stack-arg-probe \ -I$(DPP_ROOT)/CRT -I$(DDK_INCLUDE_DIR) \ -D__INTRINSIC_DEFINED_InterlockedBitTestAndSet \ @@ -84,8 +84,7 @@ DRIVER_LDFLAGS := -shared \ -Wl,--exclude-all-symbols \ -Wl,--entry,_CRT_DriverEntry \ $(PRINT_LDSCRIPT) \ - -nostartfiles -nodefaultlibs -nostdlib \ - -T$(DPP_ROOT)/ld-script.txt + -nostartfiles -nodefaultlibs -nostdlib DRIVER_LIBS := -lntoskrnl -lhal USER_LDFLAGS := -Wl,--dynamicbase -Wl,--nxcompat -Wl,--gc-sections USER_LIBS := @@ -139,7 +138,7 @@ define BUILD_C_OBJECT $(call CHECK_REQUIRED_PATHS) $(call is_set,$(1),First argument: Source file missing) $(call is_set,$(2),Second argument: Output object file missing) - $(Q)$(CC) -std=c99 $(CFLAGS) $(CUSTOM_CFLAGS) $(CFLAGS_$(2)) -c $(1) -o $(2) + $(Q)$(CC) -std=gnu99 $(CFLAGS) $(CUSTOM_CFLAGS) $(CFLAGS_$(2)) -c $(1) -o $(2) @echo 'CC $(2)' endef diff --git a/Makefile.native.inc b/Makefile.native.inc index b24425a..d2293af 100644 --- a/Makefile.native.inc +++ b/Makefile.native.inc @@ -21,7 +21,6 @@ CXX = /usr/bin/c++ AR = /usr/bin/ar CFLAGS := -Wall -Wextra -Wno-sign-compare -Wno-strict-aliasing -Wno-c++20-compat \ -m64 -fPIC -fvisibility=hidden \ - -ffunction-sections -fdata-sections \ -I$(DPP_ROOT)/CRT -DNATIVE=1 ifneq ($(WERROR),) CFLAGS += -Werror @@ -68,7 +67,7 @@ define BUILD_C_OBJECT $(call CHECK_REQUIRED_PATHS) $(call is_set,$(1),First argument: Source file missing) $(call is_set,$(2),Second argument: Output object file missing) - $(Q)$(CC) -std=c99 $(CFLAGS) -c $(1) -o $(2) + $(Q)$(CC) -std=gnu99 $(CFLAGS) -c $(1) -o $(2) @echo 'CC $(2)' endef diff --git a/README.md b/README.md index d68fcb6..6eea3fa 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Instead either use Zeranoe's build script with `make -C [path-to-this-repo] -f M 1. `examples/dpp-template`: plain and stupid ddk C example 2. `examples/dpp-template-cplusplus`: same, but written in C++, including a very complex class and some MT -3. `examples/dpp-template-cplusplus-EASTL`: C++ example w/ (EA)STL integration, basicially everything usable except for SEH, VEH and assertions. +3. `examples/dpp-template-cplusplus-EASTL`: C++ example w/ (EA)STL integration, basicially everything usable except for VEH and assertions. `examples/dpp-template-cplusplus-EASTL` supports `BUILD_NATIVE`! You can build and run it on your native Linux either with the other examples e.g. `make examples`, build only native executables `make -C examples DPP_ROOT="$(realpath .)" BUILD_NATIVE=1` in the top-level directory or directly build it from the examples directory with `make DPP_ROOT="$(realpath ..)" BUILD_NATIVE=1`. @@ -195,6 +195,35 @@ You may reach a point where system functions, intrinsics or built-ins are requir For system functions that can be retrieved via `MmGetSystemRoutineAddress`, you may use `CRT/gen_wrapper.sh` to create wrapper modules. These modules retrieve the desired functions during run-time, but will be available during link-time. An example is `ZwTraceControl`, which gets exported by `ntdll.dll`. Eventually missing intrinsics/built-ins should be placed in `CRT/kcrt.c`. +## Notes on SEH + +There are some limitations regarding the use of MingW's SEH implementation in kernel mode. +You can not use it as you might have been known it from MSVC or MingW. + +Predefined statements `__try`, `__catch`, `__finally`, `__try1`, `__catch1`, etc. do **not** work! + +You need to use something like: + +```C +#include + +int handler(_In_ EXCEPTION_POINTERS * ep) +{ + (void)ep; + return EXCEPTION_EXECUTE_HANDLER; +} + +// ... + +__dpptry(handler, unique_identifier) { + *(int *)0 = 0; +} __dppexcept(unique_identifier) { + DbgPrint("%s\n", "Exception caught!"); +} __dpptryend(unique_identifier); +``` + +SEH needs to be improved. Patches are welcome! + ## Known Issues Unfortunately, `eastl::to_string` does not work. See `CRT/eastl_compat.cpp` for more details. diff --git a/examples/dpp-example.c b/examples/dpp-example.c index f5a079c..625cbde 100644 --- a/examples/dpp-example.c +++ b/examples/dpp-example.c @@ -1,8 +1,37 @@ #include +#include + DRIVER_INITIALIZE DriverEntry; DRIVER_UNLOAD DriverUnload; +extern NTSTATUS NTAPI ZwProtectVirtualMemory(_In_ HANDLE ProcessHandle, + _In_ _Out_ PVOID * BaseAddress, + _In_ _Out_ PULONG NumberOfBytesToProtect, + _In_ ULONG NewAccessProtection, + _Out_ PULONG OldAccessProtection); + +int example_exception_handler(_In_ EXCEPTION_POINTERS * lpEP) +{ + (void)lpEP; + DbgPrint("Exception handler called!\n"); + return EXCEPTION_EXECUTE_HANDLER; +} + +static void another_seh_test() +{ + DbgPrint("Another SEH test..\n"); + __dpptry(example_exception_handler, anotherseh) + { + *(int *)0 = 0; + } + __dppexcept(anotherseh) + { + DbgPrint("Success!\n"); + } + __dpptryend(anotherseh); +} + NTSTATUS DriverEntry(struct _DRIVER_OBJECT * DriverObject, PUNICODE_STRING RegistryPath) { (void)DriverObject; @@ -10,7 +39,20 @@ NTSTATUS DriverEntry(struct _DRIVER_OBJECT * DriverObject, PUNICODE_STRING Regis DbgPrint("%s\n", "Hello ring0!"); - // This is bad. Please do not call _disable/_enable in the DriverEntry. + DbgPrint("Testing SEH..\n"); + __dpptry(example_exception_handler, testseh) + { + *(int *)0 = 0; + DbgPrint("You should never see this text!\n"); + } + __dppexcept(testseh) + { + DbgPrint("Success! SEH seems to work.\n"); + } + __dpptryend(testseh); + + another_seh_test(); + DbgPrint("%s\n", "Disable/Enable Interrupts!"); _disable(); _enable(); diff --git a/ld-script.txt b/ld-script.txt deleted file mode 100644 index 92633d1..0000000 --- a/ld-script.txt +++ /dev/null @@ -1,395 +0,0 @@ -OUTPUT_FORMAT(pei-x86-64) -SEARCH_DIR("=/home/toni/git/mingw-w64-dpp/mingw-w64-sysroot/x86_64/x86_64-w64-mingw32/lib"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); -SECTIONS -{ - /* Make the virtual address and file offset synced if the alignment is - lower than the target page size. */ - . = SIZEOF_HEADERS; - . = ALIGN(__section_alignment__); - .text __image_base__ + ( __section_alignment__ < 0x1000 ? . : __section_alignment__ ) : - { - KEEP (*(SORT_NONE(.init))) - *(.text) - *(SORT(.text$*)) - *(.text.*) - *(.gnu.linkonce.t.*) - *(.glue_7t) - *(.glue_7) - . = ALIGN(8); - /* Note: we always define __CTOR_LIST__ and ___CTOR_LIST__ here, - we do not PROVIDE them. This is because the ctors.o startup - code in libgcc defines them as common symbols, with the - expectation that they will be overridden by the definitions - here. If we PROVIDE the symbols then they will not be - overridden and global constructors will not be run. - See PR 22762 for more details. - - This does mean that it is not possible for a user to define - their own __CTOR_LIST__ and __DTOR_LIST__ symbols; if they do, - the content from those variables are included but the symbols - defined here silently take precedence. If they truly need to - be redefined, a custom linker script will have to be used. - (The custom script can just be a copy of this script with the - PROVIDE() qualifiers added). - In particular this means that ld -Ur does not work, because - the proper __CTOR_LIST__ set by ld -Ur is overridden by a - bogus __CTOR_LIST__ set by the final link. See PR 46. */ - ___CTOR_LIST__ = .; - __CTOR_LIST__ = .; - LONG (-1); LONG (-1); - KEEP (*(.ctors)); - KEEP (*(.ctor)); - KEEP (*(SORT_BY_NAME(.ctors.*))); - LONG (0); LONG (0); - /* See comment about __CTOR_LIST__ above. The same reasoning - applies here too. */ - ___DTOR_LIST__ = .; - __DTOR_LIST__ = .; - LONG (-1); LONG (-1); - KEEP (*(.dtors)); - KEEP (*(.dtor)); - KEEP (*(SORT_BY_NAME(.dtors.*))); - LONG (0); LONG (0); - KEEP (*(SORT_NONE(.fini))) - /* ??? Why is .gcc_exc here? */ - *(.gcc_exc) - PROVIDE (etext = .); - KEEP (*(.gcc_except_table)) - } - /* The Cygwin32 library uses a section to avoid copying certain data - on fork. This used to be named ".data". The linker used - to include this between __data_start__ and __data_end__, but that - breaks building the cygwin32 dll. Instead, we name the section - ".data_cygwin_nocopy" and explicitly include it after __data_end__. */ - .data BLOCK(__section_alignment__) : - { - __data_start__ = . ; - *(.data) - *(.data2) - *(SORT(.data$*)) - KEEP(*(.jcr)) - __data_end__ = . ; - *(.data_cygwin_nocopy) - } - .rdata BLOCK(__section_alignment__) : - { - *(.rdata) - *(SORT(.rdata$*)) - . = ALIGN(4); - __rt_psrelocs_start = .; - KEEP(*(.rdata_runtime_pseudo_reloc)) - __rt_psrelocs_end = .; - } - __rt_psrelocs_size = __rt_psrelocs_end - __rt_psrelocs_start; - ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .; - __RUNTIME_PSEUDO_RELOC_LIST_END__ = .; - ___RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size; - __RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size; - .eh_frame BLOCK(__section_alignment__) : - { - KEEP (*(.eh_frame*)) - } - .pdata BLOCK(__section_alignment__) : - { - KEEP(*(.pdata*)) - } - .bss BLOCK(__section_alignment__) : - { - __bss_start__ = . ; - *(.bss) - *(COMMON) - __bss_end__ = . ; - } - .edata BLOCK(__section_alignment__) : - { - *(.edata) - } - /DISCARD/ : - { - *(.xdata*) - *(.debug$S) - *(.debug$T) - *(.debug$F) - *(.drectve) - *(.note.GNU-stack) - *(.gnu.lto_*) - } - .idata BLOCK(__section_alignment__) : - { - /* This cannot currently be handled with grouped sections. - See pep.em:sort_sections. */ - KEEP (SORT(*)(.idata$2)) - KEEP (SORT(*)(.idata$3)) - /* These zeroes mark the end of the import list. */ - LONG (0); LONG (0); LONG (0); LONG (0); LONG (0); - KEEP (SORT(*)(.idata$4)) - __IAT_start__ = .; - SORT(*)(.idata$5) - __IAT_end__ = .; - KEEP (SORT(*)(.idata$6)) - KEEP (SORT(*)(.idata$7)) - } - .CRT BLOCK(__section_alignment__) : - { - ___crt_xc_start__ = . ; - KEEP (*(SORT(.CRT$XC*))) /* C initialization */ - ___crt_xc_end__ = . ; - ___crt_xi_start__ = . ; - KEEP (*(SORT(.CRT$XI*))) /* C++ initialization */ - ___crt_xi_end__ = . ; - ___crt_xl_start__ = . ; - KEEP (*(SORT(.CRT$XL*))) /* TLS callbacks */ - /* ___crt_xl_end__ is defined in the TLS Directory support code */ - ___crt_xp_start__ = . ; - KEEP (*(SORT(.CRT$XP*))) /* Pre-termination */ - ___crt_xp_end__ = . ; - ___crt_xt_start__ = . ; - KEEP (*(SORT(.CRT$XT*))) /* Termination */ - ___crt_xt_end__ = . ; - } - /* Windows TLS expects .tls$AAA to be at the start and .tls$ZZZ to be - at the end of the .tls section. This is important because _tls_start MUST - be at the beginning of the section to enable SECREL32 relocations with TLS - data. */ - .tls BLOCK(__section_alignment__) : - { - ___tls_start__ = . ; - KEEP (*(.tls$AAA)) - KEEP (*(.tls)) - KEEP (*(.tls$)) - KEEP (*(SORT(.tls$*))) - KEEP (*(.tls$ZZZ)) - ___tls_end__ = . ; - } - .endjunk BLOCK(__section_alignment__) : - { - /* end is deprecated, don't use it */ - PROVIDE (end = .); - PROVIDE ( _end = .); - __end__ = .; - } - .rsrc BLOCK(__section_alignment__) : SUBALIGN(4) - { - KEEP (*(.rsrc)) - KEEP (*(.rsrc$*)) - } - .reloc BLOCK(__section_alignment__) : - { - *(.reloc) - } - .stab BLOCK(__section_alignment__) (NOLOAD) : - { - *(.stab) - } - .stabstr BLOCK(__section_alignment__) (NOLOAD) : - { - *(.stabstr) - } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section. Unlike other targets that fake this by putting the - section VMA at 0, the PE format will not allow it. */ - /* DWARF 1.1 and DWARF 2. */ - .debug_aranges BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_aranges) - } - .zdebug_aranges BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_aranges) - } - .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_pubnames) - } - .zdebug_pubnames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_pubnames) - } - /* DWARF 2. */ - .debug_info BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_info .gnu.linkonce.wi.*) - } - .zdebug_info BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_info .zdebug.gnu.linkonce.wi.*) - } - .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_abbrev) - } - .zdebug_abbrev BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_abbrev) - } - .debug_line BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_line) - } - .zdebug_line BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_line) - } - .debug_frame BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_frame*) - } - .zdebug_frame BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_frame*) - } - .debug_str BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_str) - } - .zdebug_str BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_str) - } - .debug_loc BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_loc) - } - .zdebug_loc BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_loc) - } - .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_macinfo) - } - .zdebug_macinfo BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_macinfo) - } - /* SGI/MIPS DWARF 2 extensions. */ - .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_weaknames) - } - .zdebug_weaknames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_weaknames) - } - .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_funcnames) - } - .zdebug_funcnames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_funcnames) - } - .debug_typenames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_typenames) - } - .zdebug_typenames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_typenames) - } - .debug_varnames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_varnames) - } - .zdebug_varnames BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_varnames) - } - /* DWARF 3. */ - .debug_pubtypes BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_pubtypes) - } - .zdebug_pubtypes BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_pubtypes) - } - .debug_ranges BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_ranges) - } - .zdebug_ranges BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_ranges) - } - /* DWARF 4. */ - .debug_types BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_types .gnu.linkonce.wt.*) - } - .zdebug_types BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_types .gnu.linkonce.wt.*) - } - /* DWARF 5. */ - .debug_addr BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_addr) - } - .zdebug_addr BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_addr) - } - .debug_line_str BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_line_str) - } - .zdebug_line_str BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_line_str) - } - .debug_loclists BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_loclists) - } - .zdebug_loclists BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_loclists) - } - .debug_macro BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_macro) - } - .zdebug_macro BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_macro) - } - .debug_names BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_names) - } - .zdebug_names BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_names) - } - .debug_rnglists BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_rnglists) - } - .zdebug_rnglists BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_rnglists) - } - .debug_str_offsets BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_str_offsets) - } - .zdebug_str_offsets BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_str_offsets) - } - .debug_sup BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_sup) - } - /* For Go and Rust. */ - .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : - { - *(.debug_gdb_scripts) - } - .zdebug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : - { - *(.zdebug_gdb_scripts) - } -}