Add Win64 SEH unwind codes for the ABI test trampoline.
This is all manual right now. Once we've added SEH tests, we can add support
for emitting these in x86_64-xlate.pl, probably based on MASM and Yasm's unwind
directives, and unify with CFI. (Sadly, NASM does not support these
directives.) Then we can push that upstream to replace the error-prone and
non-standard custom handlers.
Change-Id: I5a734fd494b7eaafab24a00e6df624bd03b37d43
Reviewed-on: https://boringssl-review.googlesource.com/c/33785
Reviewed-by: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <alangley@gmail.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/abi_self_test.cc b/crypto/abi_self_test.cc
index cbc771f..773c9bd 100644
--- a/crypto/abi_self_test.cc
+++ b/crypto/abi_self_test.cc
@@ -19,6 +19,10 @@
#include "test/abi_test.h"
+#if defined(OPENSSL_WINDOWS)
+#include <windows.h>
+#endif
+
static bool test_function_was_called = false;
static void TestFunction(int a1, int a2, int a3, int a4, int a5, int a6, int a7,
@@ -151,4 +155,31 @@
CHECK_ABI(abi_test_clobber_xmm15);
#endif
}
+
+#if defined(OPENSSL_WINDOWS)
+static void ThrowWindowsException() {
+ DebugBreak();
+}
+
+static void ExceptionTest() {
+ bool handled = false;
+ __try {
+ CHECK_ABI(ThrowWindowsException);
+ } __except (GetExceptionCode() == EXCEPTION_BREAKPOINT
+ ? EXCEPTION_EXECUTE_HANDLER
+ : EXCEPTION_CONTINUE_SEARCH) {
+ handled = true;
+ }
+
+ EXPECT_TRUE(handled);
+}
+
+// Test that the trampoline's SEH metadata works.
+TEST(ABITest, TrampolineSEH) {
+ // Wrap the test in |CHECK_ABI|, to confirm the register-restoring annotations
+ // were correct.
+ CHECK_ABI(ExceptionTest);
+}
+#endif // OPENSSL_WINDOWS
+
#endif // OPENSSL_X86_64 && SUPPORTS_ABI_TEST
diff --git a/crypto/test/asm/trampoline-x86_64.pl b/crypto/test/asm/trampoline-x86_64.pl
index b1f9b93..0f357b0 100755
--- a/crypto/test/asm/trampoline-x86_64.pl
+++ b/crypto/test/asm/trampoline-x86_64.pl
@@ -137,6 +137,7 @@
.globl abi_test_trampoline
.align 16
abi_test_trampoline:
+.Labi_test_trampoline_begin:
.cfi_startproc
# Stack layout:
# 8 bytes - align
@@ -162,16 +163,25 @@
$code .= <<____;
subq \$$stack_alloc_size, %rsp
.cfi_adjust_cfa_offset $stack_alloc_size
+.Labi_test_trampoline_prolog_alloc:
____
# Store our caller's state. This is needed because we modify it ourselves, and
# also to isolate the test infrastruction from the function under test failing
# to save some register.
+my %reg_offsets;
$code .= store_caller_state($caller_state_offset, "%rsp", sub {
my ($off, $reg) = @_;
$reg = substr($reg, 1);
+ $reg_offsets{$reg} = $off;
$off -= $stack_alloc_size + 8;
- return ".cfi_offset\t$reg, $off\n";
+ return <<____;
+.cfi_offset $reg, $off
+.Labi_test_trampoline_prolog_$reg:
+____
});
+$code .= <<____;
+.Labi_test_trampoline_prolog_end:
+____
$code .= load_caller_state(0, $state);
$code .= <<____;
@@ -234,6 +244,7 @@
# %rax already contains \$func's return value, unmodified.
ret
.cfi_endproc
+.Labi_test_trampoline_end:
.size abi_test_trampoline,.-abi_test_trampoline
____
@@ -263,5 +274,92 @@
____
}
+if ($win64) {
+ # Add unwind metadata for SEH.
+ #
+ # TODO(davidben): This is all manual right now. Once we've added SEH tests,
+ # add support for emitting these in x86_64-xlate.pl, probably based on MASM
+ # and Yasm's unwind directives, and unify with CFI. (Sadly, NASM does not
+ # support these directives.) Then push that upstream to replace the
+ # error-prone and non-standard custom handlers.
+
+ # See https://docs.microsoft.com/en-us/cpp/build/struct-unwind-code?view=vs-2017
+ my $UWOP_ALLOC_LARGE = 1;
+ my $UWOP_ALLOC_SMALL = 2;
+ my $UWOP_SAVE_NONVOL = 4;
+ my $UWOP_SAVE_XMM128 = 8;
+
+ my %UWOP_REG_NUMBER = (rax => 0, rcx => 1, rdx => 2, rbx => 3, rsp => 4,
+ rbp => 5, rsi => 6, rdi => 7,
+ map(("r$_" => $_), (8..15)));
+
+ my $unwind_codes = "";
+ my $num_slots = 0;
+ if ($stack_alloc_size <= 128) {
+ my $info = $UWOP_ALLOC_SMALL | ((($stack_alloc_size - 8) / 8) << 4);
+ $unwind_codes .= <<____;
+ .byte .Labi_test_trampoline_prolog_alloc-.Labi_test_trampoline_begin
+ .byte $info
+____
+ $num_slots++;
+ } else {
+ die "stack allocation needs three unwind slots" if ($stack_alloc_size > 512 * 1024 + 8);
+ my $info = $UWOP_ALLOC_LARGE;
+ my $value = $stack_alloc_size / 8;
+ $unwind_codes .= <<____;
+ .byte .Labi_test_trampoline_prolog_alloc-.Labi_test_trampoline_begin
+ .byte $info
+ .value $value
+____
+ $num_slots += 2;
+ }
+
+ foreach my $reg (@caller_state) {
+ $reg = substr($reg, 1);
+ die "unknown register $reg" unless exists($reg_offsets{$reg});
+ if ($reg =~ /^r/) {
+ die "unknown register $reg" unless exists($UWOP_REG_NUMBER{$reg});
+ my $info = $UWOP_SAVE_NONVOL | ($UWOP_REG_NUMBER{$reg} << 4);
+ my $value = $reg_offsets{$reg} / 8;
+ $unwind_codes .= <<____;
+ .byte .Labi_test_trampoline_prolog_$reg-.Labi_test_trampoline_begin
+ .byte $info
+ .value $value
+____
+ $num_slots += 2;
+ } elsif ($reg =~ /^xmm/) {
+ my $info = $UWOP_SAVE_XMM128 | (substr($reg, 3) << 4);
+ my $value = $reg_offsets{$reg} / 16;
+ $unwind_codes .= <<____;
+ .byte .Labi_test_trampoline_prolog_$reg-.Labi_test_trampoline_begin
+ .byte $info
+ .value $value
+____
+ $num_slots += 2;
+ } else {
+ die "unknown register $reg";
+ }
+ }
+
+ $code .= <<____;
+.section .pdata
+.align 4
+ # https://docs.microsoft.com/en-us/cpp/build/struct-runtime-function?view=vs-2017
+ .rva .Labi_test_trampoline_begin
+ .rva .Labi_test_trampoline_end
+ .rva .Labi_test_trampoline_info
+
+.section .xdata
+.align 8
+.Labi_test_trampoline_info:
+ # https://docs.microsoft.com/en-us/cpp/build/struct-unwind-info?view=vs-2017
+ .byte 1 # version 1, no flags
+ .byte .Labi_test_trampoline_prolog_end-.Labi_test_trampoline_begin
+ .byte $num_slots
+ .byte 0 # no frame register
+$unwind_codes
+____
+}
+
print $code;
close STDOUT;