blob: c86cc4c57bd2b7cdd1fbc72f365b574eb01524bf [file] [log] [blame]
// Copyright 2023 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 <lib/arch/asm.h>
#include <zircon/tls.h>
#include "setjmp_impl.h"
.function longjmp, export
// 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 and return address on the stack
// around calling __asan_handle_no_return.
add sp, sp, -32
.cfi_adjust_cfa_offset 32
sd ra, 0(sp)
.cfi_offset ra, -32 + 0
sd a0, 8(sp)
.cfi_offset ra, -32 + 8
sd a1, 16(sp)
.cfi_offset ra, -32 + 16
call __asan_handle_no_return
ld ra, 0(sp)
.cfi_same_value ra
ld a0, 8(sp)
.cfi_same_value a0
ld a1, 16(sp)
.cfi_same_value a1
add sp, sp, 32
.cfi_adjust_cfa_offset -32
#endif
// Find the manglers.
lla a2, __setjmp_manglers
// Load the words that need to be demangled into temporaries.
// These just hold values that were in the jmp_buf, so we don't
// care about leaking them.
ld a3, JB_PC*8(a0)
ld a4, JB_SP*8(a0)
ld a5, JB_FP*8(a0)
ld a6, JB_USP*8(a0)
ld a7, JB_SCSP*8(a0)
// 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 a0 value (the jmp_buf).
// The a1 value is that of longjmp's caller, not setjmp's.
.cfi_same_value a0
.cfi_undefined a1
// CFI to find regno at \offset(a0).
.macro jb_cfi regno, offset
.if \offset < 0x7f
.cfi_escape DW_CFA_expression, \regno, 2, DW_OP_breg(10), \offset
.elseif \offset < 0x3fff
.cfi_escape DW_CFA_expression, \regno, 3, DW_OP_breg(10), \
(\offset & 0x7f) | 0x80, \offset >> 7
.else
.error "offset too large for two-byte SLEB128"
.endif
.endm
// CFI to find fs\n at JB_FS(\n)*8(a0).
.macro jb_cfi_fs n
.if \n < 2
jb_cfi (32 + 8 + \n), JB_FS(\n)*8
.else
jb_cfi (32 + 18 + \n - 2), JB_FS(\n)*8
.endif
.endm
// CFI to compute regno as (\offset(a0) ^ \offset(a2)).
.macro jb_cfi_mangled regno, offset
.ifgt \offset - 0x7f
.error "offset too large for one-byte SLEB128"
.endif
.cfi_escape DW_CFA_val_expression, \regno, 7, \
DW_OP_breg(10), \offset, DW_OP_deref, \
DW_OP_breg(12), \offset, DW_OP_deref, DW_OP_xor
.endm
jb_cfi_mangled 1, JB_PC*8 // ra
jb_cfi_mangled 2, JB_SP*8 // sp
jb_cfi_mangled 8, JB_FP*8 // s0
// There's no CFI for the unsafe SP!
jb_cfi_mangled 3, JB_SCSP*8 // gp
jb_cfi 9, JB_S(1)*8
jb_cfi 18, JB_S(2)*8
jb_cfi 19, JB_S(3)*8
jb_cfi 20, JB_S(4)*8
jb_cfi 21, JB_S(5)*8
jb_cfi 22, JB_S(6)*8
jb_cfi 23, JB_S(7)*8
jb_cfi 24, JB_S(8)*8
jb_cfi 25, JB_S(9)*8
jb_cfi 26, JB_S(10)*8
jb_cfi 27, JB_S(11)*8
#ifndef __riscv_float_abi_soft
jb_cfi_fs 0
jb_cfi_fs 1
jb_cfi_fs 2
jb_cfi_fs 3
jb_cfi_fs 4
jb_cfi_fs 5
jb_cfi_fs 6
jb_cfi_fs 7
jb_cfi_fs 8
jb_cfi_fs 9
jb_cfi_fs 10
jb_cfi_fs 11
#endif
// We don't want to leak the raw mangler values, so load
// them into their target registers rather than temporaries
// so we don't have more temporaries to clear.
.macro load_mangler regno, offset, tmpno
ld x\regno, \offset(a2)
// CFI to compute x\regno as (x\regno ^ x\tmpno).
.cfi_escape DW_CFA_val_expression, \regno, 5, \
DW_OP_breg(\regno), 0, DW_OP_breg(\tmpno), 0, DW_OP_xor
.endm
load_mangler 1, JB_PC*8, 13 // a3 = x13
load_mangler 2, JB_SP*8, 14 // a4 = x14
load_mangler 8, JB_FP*8, 15 // a5 = x15
load_mangler 3, JB_SCSP*8, 17 // a7 = x17
// Reuse the temporary for the unsafe SP. There's no CFI for it!
ld a2, JB_USP*8(a2)
// Now demangle the jmp_buf values in place.
.macro restore_mangled reg, tmp
xor \reg, \reg, \tmp
.cfi_same_value \reg
.endm
xor a2, a2, a6 // Unsafe SP temporary location.
restore_mangled ra, a3
restore_mangled sp, a4
restore_mangled s0, a5
restore_mangled gp, a7
// Store the unsafe SP into its real slot.
// Then clear the temporary so its value isn't leaked.
sd a2, ZX_TLS_UNSAFE_SP_OFFSET(tp)
mv a2, zero
// Restore the vanilla call-saved registers.
.macro restore_s n
ld s\n, JB_S(\n)*8(a0)
.cfi_same_value s\n
.endm
restore_s 1
restore_s 2
restore_s 3
restore_s 4
restore_s 5
restore_s 6
restore_s 7
restore_s 8
restore_s 9
restore_s 10
restore_s 11
#ifndef __riscv_float_abi_soft
// Restore the call-saved FP registers.
.macro restore_fs n
fld fs\n, JB_FS(\n)*8(a0)
.cfi_same_value fs\n
.endm
restore_fs 0
restore_fs 1
restore_fs 2
restore_fs 3
restore_fs 4
restore_fs 5
restore_fs 6
restore_fs 7
restore_fs 8
restore_fs 9
restore_fs 10
restore_fs 11
#endif
mv a0, a1
.cfi_undefined a0
bnez a0, .Lreturn
li a0, 1
.Lreturn:
ret
.end_function
.label _longjmp, export, function, longjmp
.label siglongjmp, weak, function, longjmp