blob: 0cec7917e5eb6997efe0ffe61081a9b441fb8bef [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "asm.h"
#include "setjmp_impl.h"
#include <zircon/tls.h>
#define DW_CFA_expression 0x10
#define DW_CFA_val_expression 0x16
#define DW_OP_breg0 0x70
#define DW_OP_breg2 (DW_OP_breg0 + 2)
#define DW_OP_breg5 (DW_OP_breg0 + 5)
#define DW_OP_breg7 (DW_OP_breg0 + 7)
#define DW_OP_deref 0x06
// CFI to find regno at 8*index(%rdi).
.macro jb_cfi regno, index
.if 8 * \index < 0x7f
.cfi_escape DW_CFA_expression, \regno, 2, DW_OP_breg5, 8 * \index
.else
.error "offset too large for one-byte SLEB128"
.endif
.endm
.macro restore reg, index
mov 8*\index(%rdi), \reg
.cfi_same_value \reg
.endm
ENTRY(longjmp)
// The sanitizer runtime wants to be informed of non-local exits.
// Call __asan_handle_no_return() before doing the actual longjmp.
#if __has_feature(address_sanitizer)
// Save our incoming argument registers on the stack around calling
// __asan_handle_no_return. The incoming stack is misaligned by one
// word, so we need to move the stack by an odd number of words,
// hence the unnecessary save/restore of %rax.
push_reg %rax
push_reg %rsi
push_reg %rdi
call __asan_handle_no_return@PLT
pop_reg %rdi
pop_reg %rsi
pop_reg %rax
#endif
// Calculate return value: %r11d = %esi ?: 1
mov $1, %r11d
test %esi, %esi
cmovnz %esi, %r11d
// Load the mangled values into temporaries.
mov 8*JB_PC(%rdi), %rax
.cfi_undefined %rax
mov 8*JB_SP(%rdi), %rcx
mov 8*JB_FP(%rdi), %rdx
mov 8*JB_USP(%rdi), %rsi
.cfi_undefined %rsi
// Demangle each temporary.
xor __setjmp_manglers+8*JB_PC(%rip), %rax
xor __setjmp_manglers+8*JB_SP(%rip), %rcx
xor __setjmp_manglers+8*JB_FP(%rip), %rdx
xor __setjmp_manglers+8*JB_USP(%rip), %rsi
// The next instruction clobbers the state of longjmp's caller.
// So from here on, we'll use CFI that unwinds to setjmp's caller instead.
// Both callers have the same %rdi value, which we're still using.
.cfi_register %rip, %rax
.cfi_register %rsp, %rcx
.cfi_register %rbp, %rdx
//.cfi_register %unsafe_sp, %rsi -- No DWARF register number for it!
jb_cfi 3, JB_RBX
jb_cfi 12, JB_R12
jb_cfi 13, JB_R13
jb_cfi 14, JB_R14
jb_cfi 15, JB_R15
// Restore all the vanilla callee-saves registers.
restore %rbx, JB_RBX
restore %r12, JB_R12
restore %r13, JB_R13
restore %r14, JB_R14
restore %r15, JB_R15
// Restore the demangled values.
mov %rdx, %rbp
.cfi_same_value %rbp
mov %rsi, %fs:ZX_TLS_UNSAFE_SP_OFFSET
//.cfi_same_value %unsafe_sp -- No DWARF register number for it!
mov %rax, (%rcx)
.cfi_escape DW_CFA_expression, 16, 3, DW_OP_breg2, 0, DW_OP_deref
// Restore SP last.
// After this, our CFA is setjmp's CFA rather than longjmp's CFA.
mov %rcx, %rsp
.cfi_same_value %rsp
.cfi_escape DW_CFA_expression, 16, 3, DW_OP_breg7, 0, DW_OP_deref
// Don't leak the demangled values.
xor %ecx, %ecx
xor %edx, %edx
xor %esi, %esi
mov %r11d, %eax
ret
END(longjmp)
ALIAS(longjmp, _longjmp)
WEAK_ALIAS(longjmp, siglongjmp)