diff --git a/core/elfpatcher/src/windows/WindowsPePatcher.cpp b/core/elfpatcher/src/windows/WindowsPePatcher.cpp index 7ef209336..6fe156623 100644 --- a/core/elfpatcher/src/windows/WindowsPePatcher.cpp +++ b/core/elfpatcher/src/windows/WindowsPePatcher.cpp @@ -32,6 +32,13 @@ std::vector WindowsPePatcher::Patch(const std::vector directories{}; auto nextRva = image.GetEndRva(); + for (const auto& header : originalHeaders) { + if (header.Type != 0x6474e550) continue; + std::vector metadata(4); + Io::WriteU32(metadata, 0, image.GetRva(header.MappedAddress, header.FileSize)); + sections.push_back({".ehmeta", nextRva, SectionRead | 0x40u, std::move(metadata)}); + nextRva = AlignRva(nextRva + sections.back().Data.size()); + } directories[9] = WindowsTlsBuilder().Build(sourceElf, originalHeaders, image, sections, relocations.BaseRelocations, nextRva); auto relocationData = relocationBuilder.BuildBaseRelocations(relocations.BaseRelocations); if (!relocationData.empty()) { diff --git a/core/libs/nid/src/PeNidPatcher.cpp b/core/libs/nid/src/PeNidPatcher.cpp index 995d987d2..1251d9059 100644 --- a/core/libs/nid/src/PeNidPatcher.cpp +++ b/core/libs/nid/src/PeNidPatcher.cpp @@ -49,6 +49,22 @@ void PeNidPatcher::PatchNids(std::vector& pe, const std::string& l const auto sizeOfOptionalHeader = Read(pe, coffHeaderOffset + 16u); if (sizeOfOptionalHeader == 0u) throw std::runtime_error("no optional header"); + const std::size_t sectionTableOffset = coffHeaderOffset + 20u + sizeOfOptionalHeader; + const auto symbolTable = Read(pe, coffHeaderOffset + 8u); + const auto symbolCount = Read(pe, coffHeaderOffset + 12u); + for (std::uint16_t i = 0; i < numberOfSections; ++i) { + const auto offset = sectionTableOffset + i * sizeof(PeSectionHeader); + auto section = Read(pe, offset); + if (pe[offset] != '/' || !symbolTable) continue; + const std::string reference(reinterpret_cast(pe.data() + offset), 8); + const auto stringOffset = std::stoul(reference.substr(1)); + const auto name = ReadCStr(pe, static_cast(symbolTable) + static_cast(symbolCount) * 18 + stringOffset); + if (name == ".eh_frame") { + const char shortName[8] = ".ehfram"; + std::memcpy(pe.data() + offset, shortName, 8); + } + } + const std::size_t optionalHeaderOffset = coffHeaderOffset + 20u; const auto magic = Read(pe, optionalHeaderOffset); diff --git a/core/libs/prx/libc/include/exceptions/Unwind.hpp b/core/libs/prx/libc/include/exceptions/Unwind.hpp index 2559dfd45..f8b0a2a9f 100644 --- a/core/libs/prx/libc/include/exceptions/Unwind.hpp +++ b/core/libs/prx/libc/include/exceptions/Unwind.hpp @@ -9,6 +9,9 @@ struct _Unwind_Context { std::uintptr_t registers[17] {}; +#ifdef _WIN32 + std::uintptr_t vectorRegisters[16][2] {}; +#endif std::uintptr_t cfa {}; std::uintptr_t region {}; std::uintptr_t lsda {}; diff --git a/core/libs/prx/libc/src/exception/Exports.cpp b/core/libs/prx/libc/src/exception/Exports.cpp index 458fb6322..a8b31e3d5 100644 --- a/core/libs/prx/libc/src/exception/Exports.cpp +++ b/core/libs/prx/libc/src/exception/Exports.cpp @@ -20,3 +20,42 @@ GCC_GLOBAL_ALIAS(_Unwind_Resume_nid_no_patch_cut, _Unwind_Resume_nid_postfix); GCC_LOCAL_ALIAS(__cxa_free_exception, __cxa_free_exception_nid_postfix); GCC_LOCAL_ALIAS(_ZSt9terminatev, _ZSt9terminatev_nid_postfix); GCC_LOCAL_ALIAS(__cxa_call_terminate, __cxa_call_terminate_nid_postfix); + +#ifdef _WIN32 +extern "C" { +void* NativeAllocateException(std::size_t size) asm("__cxa_allocate_exception"); +void* NativeAllocateException(std::size_t size) { + void* object = __cxa_allocate_exception_nid_postfix(size); + LibcException::FromObject(object)->_pad = 1; + return object; +} +void NativeFreeException(void* object) asm("__cxa_free_exception"); +void NativeFreeException(void* object) { __cxa_free_exception_nid_postfix(object); } +[[noreturn]] void NativeThrow(void* object, std::type_info* type, void (*destructor)(void*)) asm("__cxa_throw"); +[[noreturn]] void NativeThrow(void* object, std::type_info* type, void (*destructor)(void*)) { + __cxa_throw_nid_postfix(object, type, destructor); +} +void* NativeBeginCatch(void* exception) asm("__cxa_begin_catch"); +void* NativeBeginCatch(void* exception) { return __cxa_begin_catch_nid_postfix(exception); } +void NativeEndCatch() asm("__cxa_end_catch"); +void NativeEndCatch() { __cxa_end_catch_nid_postfix(); } +[[noreturn]] void NativeRethrow() asm("__cxa_rethrow"); +[[noreturn]] void NativeRethrow() { __cxa_rethrow_nid_postfix(); } +[[noreturn]] void NativeResume(_Unwind_Exception* exception) asm("_Unwind_Resume"); +[[noreturn]] void NativeResume(_Unwind_Exception* exception) { _Unwind_Resume_nid_postfix(exception); } +[[noreturn]] void NativeTerminate() asm("_ZSt9terminatev"); +[[noreturn]] void NativeTerminate() { LibcException::Terminate(); } +[[noreturn]] void NativeCallTerminate(void* exception) asm("__cxa_call_terminate"); +[[noreturn]] void NativeCallTerminate(void* exception) { __cxa_call_terminate_nid_postfix(exception); } +void* NativeExceptionPointer(void* exception) asm("__cxa_get_exception_ptr"); +void* NativeExceptionPointer(void* exception) { return __cxa_get_exception_ptr_nid_postfix(exception); } +void* NativeGlobals() asm("__cxa_get_globals"); +void* NativeGlobals() { return &LibcException::globals; } +void* NativeGlobalsFast() asm("__cxa_get_globals_fast"); +void* NativeGlobalsFast() { return &LibcException::globals; } +unsigned NativeUncaughtExceptions() asm("__cxa_uncaught_exceptions"); +unsigned NativeUncaughtExceptions() { return LibcException::globals.uncaught; } +bool NativeUncaughtException() asm("__cxa_uncaught_exception"); +bool NativeUncaughtException() { return LibcException::globals.uncaught != 0; } +} +#endif diff --git a/core/libs/prx/libc/src/exception/Runtime.cpp b/core/libs/prx/libc/src/exception/Runtime.cpp index e5e03a909..6e1c52cff 100644 --- a/core/libs/prx/libc/src/exception/Runtime.cpp +++ b/core/libs/prx/libc/src/exception/Runtime.cpp @@ -13,6 +13,12 @@ namespace LibcException { struct VtableLayout { std::ptrdiff_t offset; const void* type; void (*destroy)(void*); void (*del)(void*); const char* (*whatFn)(const void*); }; const void* vtable = *static_cast(primary->adjusted); auto* layout = reinterpret_cast(static_cast(vtable) - offsetof(VtableLayout, destroy)); +#ifdef _WIN32 + if (!primary->_pad) { + using GuestWhat = const char* (__attribute__((sysv_abi)) *)(const void*); + what = reinterpret_cast(layout->whatFn)(primary->adjusted); + } else +#endif what = layout->whatFn(primary->adjusted); } int status = 0; @@ -46,7 +52,17 @@ void Release(void* object) { auto* header = FromObject(object); auto* allocation = AllocationOf(header); if (allocation->references.fetch_sub(1, std::memory_order_acq_rel) == 1) { - try { if (header->destructor) header->destructor(object); } + try { + if (header->destructor) { +#ifdef _WIN32 + if (!header->_pad) { + using GuestDestructor = void (__attribute__((sysv_abi)) *)(void*); + reinterpret_cast(header->destructor)(object); + } else +#endif + header->destructor(object); + } + } catch (...) { Terminate(); } allocation->~Allocation(); std::free(allocation); diff --git a/core/libs/prx/libc/src/exception/Unwind.cpp b/core/libs/prx/libc/src/exception/Unwind.cpp index 6fcb29fc3..96c1ea9cf 100644 --- a/core/libs/prx/libc/src/exception/Unwind.cpp +++ b/core/libs/prx/libc/src/exception/Unwind.cpp @@ -2,11 +2,35 @@ #include "prx/libc/include/specifics/linux/ElfTypes.hpp" #include "prx/libc/src/specifics/x86_64/RegisterContext.cpp" -#if defined(__linux__) +#ifdef _WIN32 +#include +#endif + +#if defined(__linux__) || defined(_WIN32) + +#ifdef _WIN32 +extern "C" _Unwind_Reason_Code __gxx_personality_v0(int, _Unwind_Action, std::uint64_t, _Unwind_Exception*, _Unwind_Context*); +#endif namespace LibcUnwind { +bool OwnPersonality(Word personality) { +#ifdef _WIN32 + const auto* code = reinterpret_cast(personality); + if (code[0] == 0xff && code[1] == 0x25) { + std::int32_t displacement; + std::memcpy(&displacement, code + 2, 4); + std::memcpy(&personality, code + 6 + displacement, sizeof(personality)); + } +#endif + if (personality == reinterpret_cast(__gxx_personality_v0_nid_postfix)) return true; +#ifdef _WIN32 + if (personality == reinterpret_cast(__gxx_personality_v0)) return true; +#endif + return false; +} struct Lookup { Word pc; const Byte* fde {}; Word text {}; Word data {}; }; +#ifdef __linux__ int FindFrame(dl_phdr_info* info, std::size_t, void* argument) { auto& query = *static_cast(argument); const Byte* header = nullptr; @@ -40,6 +64,8 @@ int FindFrame(dl_phdr_info* info, std::size_t, void* argument) { return 1; } +#endif + struct Frame { const Byte* cieBegin {}; const Byte* cieEnd {}; @@ -52,9 +78,7 @@ struct Frame { bool signal {}; }; -bool DecodeFrame(_Unwind_Context& context, Frame& frame) { - Lookup query {context.registers[16] - !context.signalFrame}; - dl_iterate_phdr(FindFrame, &query); +bool DecodeCandidate(_Unwind_Context& context, Frame& frame, const Lookup& query) { if (!query.fde) return false; const Byte* p = query.fde; auto length = Read(p); @@ -75,7 +99,11 @@ bool DecodeFrame(_Unwind_Context& context, Frame& frame) { frame.codeAlign = Uleb(c); frame.dataAlign = Sleb(c); frame.returnRegister = version == 1 ? *c++ : Uleb(c); +#ifdef _WIN32 + if (frame.returnRegister >= 17 && frame.returnRegister != 32) return false; +#else if (frame.returnRegister >= 17) return false; +#endif Byte pointerEncoding = 0, lsdaEncoding = 255; if (*augmentation == 'z') { Word size = Uleb(c); @@ -110,8 +138,73 @@ bool DecodeFrame(_Unwind_Context& context, Frame& frame) { return true; } +bool DecodeFrame(_Unwind_Context& context, Frame& frame) { + Lookup query {context.registers[16] - !context.signalFrame}; +#ifdef __linux__ + dl_iterate_phdr(FindFrame, &query); + return DecodeCandidate(context, frame, query); +#else + MEMORY_BASIC_INFORMATION memory{}; + if (!VirtualQuery(reinterpret_cast(query.pc), &memory, sizeof(memory)) || memory.Type != MEM_IMAGE) + return false; + const auto* base = static_cast(memory.AllocationBase); + const auto* dos = reinterpret_cast(base); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return false; + const auto* nt = reinterpret_cast(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) return false; + const auto* sections = IMAGE_FIRST_SECTION(nt); + for (unsigned i = 0; i < nt->FileHeader.NumberOfSections; ++i) { + const auto& section = sections[i]; + if (std::memcmp(section.Name, ".ehmeta", 8) == 0) { + const Byte* metadata = base + section.VirtualAddress; + const Byte* header = base + Read(metadata); + if (header[0] != 1 || header[3] == 255) return false; + const Byte* p = header + 4; + Encoded(p, header[1], Word(header)); + const Word count = Encoded(p, header[2]); + const auto width = EncodingSize(header[3]); + const Byte* table = p; + Word lo = 0, hi = count; + while (lo < hi) { + const Word mid = lo + (hi - lo) / 2; + p = table + mid * width * 2; + if (Encoded(p, header[3], Word(header)) <= query.pc) lo = mid + 1; + else hi = mid; + } + if (!lo) return false; + p = table + (lo - 1) * width * 2 + width; + query.fde = reinterpret_cast(Encoded(p, header[3], Word(header))); + return DecodeCandidate(context, frame, query); + } + if (std::memcmp(section.Name, ".ehfram", 8) != 0) continue; + const Byte* p = base + section.VirtualAddress; + const Byte* end = p + section.Misc.VirtualSize; + while (end - p >= 8) { + const Byte* record = p; + const auto length = Read(p); + if (!length) break; + if (length == 0xffffffff || length < 4 || Word(end - p) < length) return false; + const Byte* next = p + length; + if (Read(p)) { + query.fde = record; + frame = {}; + if (DecodeCandidate(context, frame, query)) return true; + } + p = next; + } + } + return false; +#endif +} + struct Rule { unsigned kind {}; std::intptr_t value {}; const Byte* expression {}; }; -struct Rules { Rule registers[17] {}; unsigned cfaRegister {7}; std::intptr_t cfaOffset {}; const Byte* cfaExpression {}; }; +#ifdef _WIN32 +constexpr unsigned RuleCount = 33; +#else +constexpr unsigned RuleCount = 17; +#endif + +struct Rules { Rule registers[RuleCount] {}; unsigned cfaRegister {7}; std::intptr_t cfaOffset {}; const Byte* cfaExpression {}; }; bool Instructions(const Byte* p, const Byte* end, const Frame& frame, Word target, Rules& state, const Rules& initial) { Word location = frame.start; @@ -124,12 +217,12 @@ bool Instructions(const Byte* p, const Byte* end, const Frame& frame, Word targe if ((opcode & 192) == 128) { reg = opcode & 63; auto offset = Uleb(p) * frame.dataAlign; - if (reg < 17) state.registers[reg] = {1, static_cast(offset)}; + if (reg < RuleCount) state.registers[reg] = {1, static_cast(offset)}; continue; } if ((opcode & 192) == 192) { reg = opcode & 63; - if (reg < 17) state.registers[reg] = initial.registers[reg]; + if (reg < RuleCount) state.registers[reg] = initial.registers[reg]; continue; } switch (opcode) { @@ -141,12 +234,12 @@ bool Instructions(const Byte* p, const Byte* end, const Frame& frame, Word targe case 5: case 17: case 20: case 21: { reg = Uleb(p); auto offset = (opcode == 17 || opcode == 21 ? Sleb(p) : std::intptr_t(Uleb(p))) * frame.dataAlign; - if (reg < 17) state.registers[reg] = {unsigned(opcode >= 20 ? 4 : 1), offset}; + if (reg < RuleCount) state.registers[reg] = {unsigned(opcode >= 20 ? 4 : 1), offset}; break; } - case 6: reg = Uleb(p); if (reg < 17) state.registers[reg] = initial.registers[reg]; break; - case 7: case 8: reg = Uleb(p); if (reg < 17) state.registers[reg] = {unsigned(opcode == 7 ? 5 : 0)}; break; - case 9: { reg = Uleb(p); auto other = Uleb(p); if (reg < 17) state.registers[reg] = {2, std::intptr_t(other)}; break; } + case 6: reg = Uleb(p); if (reg < RuleCount) state.registers[reg] = initial.registers[reg]; break; + case 7: case 8: reg = Uleb(p); if (reg < RuleCount) state.registers[reg] = {unsigned(opcode == 7 ? 5 : 0)}; break; + case 9: { reg = Uleb(p); auto other = Uleb(p); if (reg < RuleCount) state.registers[reg] = {2, std::intptr_t(other)}; break; } case 10: if (depth == 16) return false; saved[depth++] = state; break; case 11: if (!depth) return false; state = saved[--depth]; break; case 12: case 18: @@ -157,7 +250,7 @@ bool Instructions(const Byte* p, const Byte* end, const Frame& frame, Word targe case 15: { state.cfaExpression = p; auto size = Uleb(p); p += size; break; } case 16: case 22: { reg = Uleb(p); const Byte* expr = p; auto size = Uleb(p); p += size; - if (reg < 17) state.registers[reg] = {unsigned(opcode == 16 ? 3 : 6), 0, expr}; + if (reg < RuleCount) state.registers[reg] = {unsigned(opcode == 16 ? 3 : 6), 0, expr}; break; } case 46: Uleb(p); break; @@ -223,6 +316,13 @@ bool GetRules(_Unwind_Context& context, Frame& frame, Rules& rules) { if (!DecodeFrame(context, frame)) return false; Rules initial; if (!Instructions(frame.cieBegin, frame.cieEnd, frame, ~Word(0), initial, {})) return false; +#ifdef _WIN32 + if (frame.returnRegister == 32) { + initial.registers[16] = initial.registers[32]; + initial.registers[32] = {}; + frame.returnRegister = 16; + } +#endif rules = initial; if (!Instructions(frame.begin, frame.end, frame, context.registers[16] - !context.signalFrame, rules, initial)) return false; if (rules.cfaExpression) return Expression(rules.cfaExpression, context, 0, context.cfa); @@ -250,6 +350,17 @@ bool Step(_Unwind_Context& context) { case 5: next.registers[i] = 0; break; } } +#ifdef _WIN32 + for (unsigned i = 17; i < RuleCount; ++i) { + const auto& rule = rules.registers[i]; + auto* destination = next.vectorRegisters[i - 17]; + if (rule.kind == 1) std::memcpy(destination, reinterpret_cast(context.cfa + rule.value), 16); + else if (rule.kind == 2 && rule.value >= 17 && rule.value < RuleCount) + std::memcpy(destination, context.vectorRegisters[rule.value - 17], 16); + else if (rule.kind == 5) std::memset(destination, 0, 16); + else if (rule.kind != 0) return false; + } +#endif next.registers[7] = context.cfa; next.registers[16] = next.registers[frame.returnRegister]; next.signalFrame = frame.signal; @@ -277,7 +388,7 @@ _Unwind_Reason_Code PhaseTwo(_Unwind_Context context, _Unwind_Exception* excepti if (result != _URC_NO_REASON) return result; } else if (context.cfa == exception->private_2) actions = _Unwind_Action(actions | _UA_HANDLER_FRAME); if (frame.personality) { - if (frame.personality != reinterpret_cast(__gxx_personality_v0_nid_postfix)) return _URC_FATAL_PHASE2_ERROR; + if (!OwnPersonality(frame.personality)) return _URC_FATAL_PHASE2_ERROR; auto result = __gxx_personality_v0_nid_postfix(1, actions, exception->exception_class, exception, &context); if (result == _URC_INSTALL_CONTEXT) LibcRestoreRegisters(context.registers); if (result != _URC_CONTINUE_UNWIND) return _URC_FATAL_PHASE2_ERROR; @@ -306,7 +417,7 @@ _Unwind_Reason_Code APS5_VABI _Unwind_RaiseException_nid_postfix(_Unwind_Excepti LibcUnwind::Frame frame; LibcUnwind::Rules rules; if (!LibcUnwind::GetRules(context, frame, rules)) return _URC_END_OF_STACK; if (frame.personality) { - if (frame.personality != reinterpret_cast(__gxx_personality_v0_nid_postfix)) return _URC_FATAL_PHASE1_ERROR; + if (!LibcUnwind::OwnPersonality(frame.personality)) return _URC_FATAL_PHASE1_ERROR; auto result = __gxx_personality_v0_nid_postfix(1, _UA_SEARCH_PHASE, exception->exception_class, exception, &context); if (result == _URC_HANDLER_FOUND) { exception->private_2 = context.cfa; diff --git a/core/libs/prx/libc/src/specifics/x86_64/RegisterContext.cpp b/core/libs/prx/libc/src/specifics/x86_64/RegisterContext.cpp index 928b6b0ed..aece550d8 100644 --- a/core/libs/prx/libc/src/specifics/x86_64/RegisterContext.cpp +++ b/core/libs/prx/libc/src/specifics/x86_64/RegisterContext.cpp @@ -26,6 +26,57 @@ asm( "movq 104(%r10),%r13\nmovq 112(%r10),%r14\nmovq 120(%r10),%r15\njmp *%r11\n" ".size LibcRestoreRegisters,.-LibcRestoreRegisters\n" ); +#elif defined(_WIN32) && defined(__x86_64__) +asm( +".text\n" +".globl LibcCaptureRegisters\n" +"LibcCaptureRegisters:\n" +"movq %rax,0(%rcx)\nmovq %rdx,8(%rcx)\nmovq %rcx,16(%rcx)\nmovq %rbx,24(%rcx)\n" +"movq %rsi,32(%rcx)\nmovq %rdi,40(%rcx)\nmovq %rbp,48(%rcx)\n" +"leaq 8(%rsp),%rax\nmovq %rax,56(%rcx)\n" +"movq %r8,64(%rcx)\nmovq %r9,72(%rcx)\nmovq %r10,80(%rcx)\nmovq %r11,88(%rcx)\n" +"movq %r12,96(%rcx)\nmovq %r13,104(%rcx)\nmovq %r14,112(%rcx)\nmovq %r15,120(%rcx)\n" +"movdqu %xmm0,136(%rcx)\n" +"movdqu %xmm1,152(%rcx)\n" +"movdqu %xmm2,168(%rcx)\n" +"movdqu %xmm3,184(%rcx)\n" +"movdqu %xmm4,200(%rcx)\n" +"movdqu %xmm5,216(%rcx)\n" +"movdqu %xmm6,232(%rcx)\n" +"movdqu %xmm7,248(%rcx)\n" +"movdqu %xmm8,264(%rcx)\n" +"movdqu %xmm9,280(%rcx)\n" +"movdqu %xmm10,296(%rcx)\n" +"movdqu %xmm11,312(%rcx)\n" +"movdqu %xmm12,328(%rcx)\n" +"movdqu %xmm13,344(%rcx)\n" +"movdqu %xmm14,360(%rcx)\n" +"movdqu %xmm15,376(%rcx)\n" +"movq (%rsp),%rax\nmovq %rax,128(%rcx)\nret\n" +".globl LibcRestoreRegisters\n" +"LibcRestoreRegisters:\n" +"movq %rcx,%r10\nmovq 128(%r10),%r11\n" +"movdqu 136(%r10),%xmm0\n" +"movdqu 152(%r10),%xmm1\n" +"movdqu 168(%r10),%xmm2\n" +"movdqu 184(%r10),%xmm3\n" +"movdqu 200(%r10),%xmm4\n" +"movdqu 216(%r10),%xmm5\n" +"movdqu 232(%r10),%xmm6\n" +"movdqu 248(%r10),%xmm7\n" +"movdqu 264(%r10),%xmm8\n" +"movdqu 280(%r10),%xmm9\n" +"movdqu 296(%r10),%xmm10\n" +"movdqu 312(%r10),%xmm11\n" +"movdqu 328(%r10),%xmm12\n" +"movdqu 344(%r10),%xmm13\n" +"movdqu 360(%r10),%xmm14\n" +"movdqu 376(%r10),%xmm15\n" +"movq 0(%r10),%rax\nmovq 8(%r10),%rdx\nmovq 16(%r10),%rcx\nmovq 24(%r10),%rbx\n" +"movq 32(%r10),%rsi\nmovq 40(%r10),%rdi\nmovq 48(%r10),%rbp\nmovq 56(%r10),%rsp\n" +"movq 64(%r10),%r8\nmovq 72(%r10),%r9\nmovq 96(%r10),%r12\n" +"movq 104(%r10),%r13\nmovq 112(%r10),%r14\nmovq 120(%r10),%r15\njmp *%r11\n" +); #else void LibcCaptureRegisters(std::uintptr_t*) { std::abort(); } [[noreturn]] void LibcRestoreRegisters(const std::uintptr_t*) { std::abort(); } diff --git a/core/libs/tests/WindowsExceptions.cpp b/core/libs/tests/WindowsExceptions.cpp new file mode 100644 index 000000000..77430f5bf --- /dev/null +++ b/core/libs/tests/WindowsExceptions.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +extern "C" void NotImplemented_nid_no_patch(const char*); + +static int destroyed; +struct Guard { ~Guard() { ++destroyed; } }; +__attribute__((noinline)) static void ThrowNested() { + Guard guard; + throw std::runtime_error("native own unwind"); +} +__attribute__((noinline)) static void Rethrow() { + Guard guard; + try { ThrowNested(); } + catch (const std::runtime_error&) { throw; } +} +int main() { + try { + Rethrow(); + return 1; + } catch (const std::runtime_error& error) { + if (std::strcmp(error.what(), "native own unwind") || destroyed != 2) return 2; + } + try { NotImplemented_nid_no_patch("cross DLL"); } + catch (const std::runtime_error& error) { + if (std::strcmp(error.what(), "cross DLL not implemented")) return 3; + std::puts("Own Windows exceptions: catch, rethrow, destructors, cross DLL passed"); + return 0; + } + return 4; +}