/* DWARF2 exception handling and frame unwind runtime interface routines.
- Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 1997-2021 Free Software Foundation, Inc.
This file is part of GCC.
#ifndef __USING_SJLJ_EXCEPTIONS__
-#ifndef STACK_GROWS_DOWNWARD
-#define STACK_GROWS_DOWNWARD 0
+#ifndef __LIBGCC_STACK_GROWS_DOWNWARD__
+#define __LIBGCC_STACK_GROWS_DOWNWARD__ 0
#else
-#undef STACK_GROWS_DOWNWARD
-#define STACK_GROWS_DOWNWARD 1
+#undef __LIBGCC_STACK_GROWS_DOWNWARD__
+#define __LIBGCC_STACK_GROWS_DOWNWARD__ 1
#endif
/* Dwarf frame registers used for pre gcc 3.0 compiled glibc. */
#ifndef PRE_GCC3_DWARF_FRAME_REGISTERS
-#define PRE_GCC3_DWARF_FRAME_REGISTERS DWARF_FRAME_REGISTERS
+#define PRE_GCC3_DWARF_FRAME_REGISTERS __LIBGCC_DWARF_FRAME_REGISTERS__
#endif
-#ifndef DWARF_REG_TO_UNWIND_COLUMN
-#define DWARF_REG_TO_UNWIND_COLUMN(REGNO) (REGNO)
-#endif
+/* ??? For the public function interfaces, we tend to gcc_assert that the
+ column numbers are in range. For the dwarf2 unwind info this does happen,
+ although so far in a case that doesn't actually matter.
+
+ See PR49146, in which a call from x86_64 ms abi to x86_64 unix abi stores
+ the call-saved xmm registers and annotates them. We havn't bothered
+ providing support for the xmm registers for the x86_64 port primarily
+ because the 64-bit windows targets don't use dwarf2 unwind, using sjlj or
+ SEH instead. Adding the support for unix targets would generally be a
+ waste. However, some runtime libraries supplied with ICC do contain such
+ an unorthodox transition, as well as the unwind info to match. This loss
+ of register restoration doesn't matter in practice, because the exception
+ is caught in the native unix abi, where all of the xmm registers are
+ call clobbered.
+
+ Ideally, we'd record some bit to notice when we're failing to restore some
+ register recorded in the unwind info, but to do that we need annotation on
+ the unix->ms abi edge, so that we know when the register data may be
+ discarded. And since this edge is also within the ICC library, we're
+ unlikely to be able to get the new annotation.
+
+ Barring a magic solution to restore the ms abi defined 128-bit xmm registers
+ (as distictly opposed to the full runtime width) without causing extra
+ overhead for normal unix abis, the best solution seems to be to simply
+ ignore unwind data for unknown columns. */
+
+#define UNWIND_COLUMN_IN_RANGE(x) \
+ __builtin_expect((x) <= __LIBGCC_DWARF_FRAME_REGISTERS__, 1)
#ifdef REG_VALUE_IN_UNWIND_CONTEXT
typedef _Unwind_Word _Unwind_Context_Reg_Val;
to its caller. */
struct _Unwind_Context
{
- _Unwind_Context_Reg_Val reg[DWARF_FRAME_REGISTERS+1];
+ _Unwind_Context_Reg_Val reg[__LIBGCC_DWARF_FRAME_REGISTERS__+1];
void *cfa;
void *ra;
void *lsda;
#define SIGNAL_FRAME_BIT ((~(_Unwind_Word) 0 >> 1) + 1)
/* Context which has version/args_size/by_value fields. */
#define EXTENDED_CONTEXT_BIT ((~(_Unwind_Word) 0 >> 2) + 1)
+ /* Bit reserved on AArch64, return address has been signed with A or B
+ key. */
+#define RA_SIGNED_BIT ((~(_Unwind_Word) 0 >> 3) + 1)
_Unwind_Word flags;
/* 0 for now, can be increased when further fields are added to
struct _Unwind_Context. */
_Unwind_Word version;
_Unwind_Word args_size;
- char by_value[DWARF_FRAME_REGISTERS+1];
+ char by_value[__LIBGCC_DWARF_FRAME_REGISTERS__+1];
};
/* Byte size of every register managed by these routines. */
-static unsigned char dwarf_reg_size_table[DWARF_FRAME_REGISTERS+1];
+static unsigned char dwarf_reg_size_table[__LIBGCC_DWARF_FRAME_REGISTERS__+1];
\f
/* Read unaligned data from the instruction buffer. */
|| (context->flags & EXTENDED_CONTEXT_BIT));
}
\f
-/* Get the value of register INDEX as saved in CONTEXT. */
+/* Get the value of register REGNO as saved in CONTEXT. */
inline _Unwind_Word
-_Unwind_GetGR (struct _Unwind_Context *context, int index)
+_Unwind_GetGR (struct _Unwind_Context *context, int regno)
{
- int size;
+ int size, index;
_Unwind_Context_Reg_Val val;
#ifdef DWARF_ZERO_REG
- if (index == DWARF_ZERO_REG)
+ if (regno == DWARF_ZERO_REG)
return 0;
#endif
- index = DWARF_REG_TO_UNWIND_COLUMN (index);
+ index = DWARF_REG_TO_UNWIND_COLUMN (regno);
gcc_assert (index < (int) sizeof(dwarf_reg_size_table));
size = dwarf_reg_size_table[index];
val = context->reg[index];
if (_Unwind_IsExtendedContext (context) && context->by_value[index])
return _Unwind_Get_Unwind_Word (val);
+#ifdef DWARF_LAZY_REGISTER_VALUE
+ {
+ _Unwind_Word value;
+ if (DWARF_LAZY_REGISTER_VALUE (regno, &value))
+ return value;
+ }
+#endif
+
/* This will segfault if the register hasn't been saved. */
if (size == sizeof(_Unwind_Ptr))
return * (_Unwind_Ptr *) (_Unwind_Internal_Ptr) val;
fs->signal_frame = 1;
aug += 1;
}
+ /* aarch64 B-key pointer authentication. */
+ else if (aug[0] == 'B')
+ {
+ aug += 1;
+ }
/* Otherwise we have an unknown augmentation string.
Bail unless we saw a 'z' prefix. */
reg = insn & 0x3f;
insn_ptr = read_uleb128 (insn_ptr, &utmp);
offset = (_Unwind_Sword) utmp * fs->data_align;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how
- = REG_SAVED_OFFSET;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = offset;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
}
else if ((insn & 0xc0) == DW_CFA_restore)
{
reg = insn & 0x3f;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how = REG_UNSAVED;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ fs->regs.reg[reg].how = REG_UNSAVED;
}
else switch (insn)
{
insn_ptr = read_uleb128 (insn_ptr, ®);
insn_ptr = read_uleb128 (insn_ptr, &utmp);
offset = (_Unwind_Sword) utmp * fs->data_align;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how
- = REG_SAVED_OFFSET;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = offset;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
break;
case DW_CFA_restore_extended:
insn_ptr = read_uleb128 (insn_ptr, ®);
/* FIXME, this is wrong; the CIE might have said that the
register was saved somewhere. */
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN(reg)].how = REG_UNSAVED;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ fs->regs.reg[reg].how = REG_UNSAVED;
break;
case DW_CFA_same_value:
insn_ptr = read_uleb128 (insn_ptr, ®);
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN(reg)].how = REG_UNSAVED;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ fs->regs.reg[reg].how = REG_UNSAVED;
break;
case DW_CFA_undefined:
insn_ptr = read_uleb128 (insn_ptr, ®);
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN(reg)].how = REG_UNDEFINED;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ fs->regs.reg[reg].how = REG_UNDEFINED;
break;
case DW_CFA_nop:
_uleb128_t reg2;
insn_ptr = read_uleb128 (insn_ptr, ®);
insn_ptr = read_uleb128 (insn_ptr, ®2);
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how = REG_SAVED_REG;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.reg =
- (_Unwind_Word)reg2;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_REG;
+ fs->regs.reg[reg].loc.reg = (_Unwind_Word)reg2;
+ }
}
break;
case DW_CFA_expression:
insn_ptr = read_uleb128 (insn_ptr, ®);
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how = REG_SAVED_EXP;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.exp = insn_ptr;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_EXP;
+ fs->regs.reg[reg].loc.exp = insn_ptr;
+ }
insn_ptr = read_uleb128 (insn_ptr, &utmp);
insn_ptr += utmp;
break;
insn_ptr = read_uleb128 (insn_ptr, ®);
insn_ptr = read_sleb128 (insn_ptr, &stmp);
offset = stmp * fs->data_align;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how
- = REG_SAVED_OFFSET;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = offset;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
break;
case DW_CFA_def_cfa_sf:
insn_ptr = read_uleb128 (insn_ptr, ®);
insn_ptr = read_uleb128 (insn_ptr, &utmp);
offset = (_Unwind_Sword) utmp * fs->data_align;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how
- = REG_SAVED_VAL_OFFSET;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = offset;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_VAL_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
break;
case DW_CFA_val_offset_sf:
insn_ptr = read_uleb128 (insn_ptr, ®);
insn_ptr = read_sleb128 (insn_ptr, &stmp);
offset = stmp * fs->data_align;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how
- = REG_SAVED_VAL_OFFSET;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = offset;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_VAL_OFFSET;
+ fs->regs.reg[reg].loc.offset = offset;
+ }
break;
case DW_CFA_val_expression:
insn_ptr = read_uleb128 (insn_ptr, ®);
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how
- = REG_SAVED_VAL_EXP;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.exp = insn_ptr;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_VAL_EXP;
+ fs->regs.reg[reg].loc.exp = insn_ptr;
+ }
insn_ptr = read_uleb128 (insn_ptr, &utmp);
insn_ptr += utmp;
break;
case DW_CFA_GNU_window_save:
+#if defined (__aarch64__) && !defined (__ILP32__)
+ /* This CFA is multiplexed with Sparc. On AArch64 it's used to toggle
+ return address signing status. */
+ fs->regs.reg[DWARF_REGNUM_AARCH64_RA_STATE].loc.offset ^= 1;
+#else
/* ??? Hardcoded for SPARC register window configuration. */
- for (reg = 16; reg < 32; ++reg)
- {
- fs->regs.reg[reg].how = REG_SAVED_OFFSET;
- fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
- }
+ if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)
+ for (reg = 16; reg < 32; ++reg)
+ {
+ fs->regs.reg[reg].how = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
+ }
+#endif
break;
case DW_CFA_GNU_args_size:
insn_ptr = read_uleb128 (insn_ptr, ®);
insn_ptr = read_uleb128 (insn_ptr, &utmp);
offset = (_Unwind_Word) utmp * fs->data_align;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].how
- = REG_SAVED_OFFSET;
- fs->regs.reg[DWARF_REG_TO_UNWIND_COLUMN (reg)].loc.offset = -offset;
+ reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+ if (UNWIND_COLUMN_IN_RANGE (reg))
+ {
+ fs->regs.reg[reg].how = REG_SAVED_OFFSET;
+ fs->regs.reg[reg].loc.offset = -offset;
+ }
break;
default:
void *cfa;
long i;
-#ifdef EH_RETURN_STACKADJ_RTX
+#ifdef __LIBGCC_EH_RETURN_STACKADJ_RTX__
/* Special handling here: Many machines do not use a frame pointer,
and track the CFA only through offsets from the stack pointer from
one frame to the next. In this case, the stack pointer is never
context->cfa = cfa;
/* Compute the addresses of all registers saved in this frame. */
- for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)
+ for (i = 0; i < __LIBGCC_DWARF_FRAME_REGISTERS__ + 1; ++i)
switch (fs->regs.reg[i].how)
{
case REG_UNSAVED:
stack frame. */
context->ra = 0;
else
- /* Compute the return address now, since the return address column
- can change from frame to frame. */
- context->ra = __builtin_extract_return_addr
- (_Unwind_GetPtr (context, fs->retaddr_column));
+ {
+ /* Compute the return address now, since the return address column
+ can change from frame to frame. */
+ void *ret_addr;
+#ifdef MD_DEMANGLE_RETURN_ADDR
+ _Unwind_Word ra = _Unwind_GetGR (context, fs->retaddr_column);
+ ret_addr = MD_DEMANGLE_RETURN_ADDR (context, fs, ra);
+#else
+ ret_addr = _Unwind_GetPtr (context, fs->retaddr_column);
+#endif
+ context->ra = __builtin_extract_return_addr (ret_addr);
+ }
}
static void
/* Install TARGET into CURRENT so that we can return to it. This is a
macro because __builtin_eh_return must be invoked in the context of
- our caller. */
+ our caller. FRAMES is a number of frames to be unwind.
+ _Unwind_Frames_Extra is a macro to do additional work during unwinding
+ if needed, for example shadow stack pointer adjustment for Intel CET
+ technology. */
-#define uw_install_context(CURRENT, TARGET) \
+#define uw_install_context(CURRENT, TARGET, FRAMES) \
do \
{ \
long offset = uw_install_context_1 ((CURRENT), (TARGET)); \
void *handler = __builtin_frob_return_addr ((TARGET)->ra); \
_Unwind_DebugHook ((TARGET)->cfa, handler); \
+ _Unwind_Frames_Extra (FRAMES); \
__builtin_eh_return (offset, handler); \
} \
while (0)
if (!_Unwind_GetGRPtr (target, __builtin_dwarf_sp_column ()))
_Unwind_SetSpColumn (target, target->cfa, &sp_slot);
- for (i = 0; i < DWARF_FRAME_REGISTERS; ++i)
+ for (i = 0; i < __LIBGCC_DWARF_FRAME_REGISTERS__; ++i)
{
void *c = (void *) (_Unwind_Internal_Ptr) current->reg[i];
void *t = (void *) (_Unwind_Internal_Ptr)target->reg[i];
target_cfa = _Unwind_GetPtr (target, __builtin_dwarf_sp_column ());
/* We adjust SP by the difference between CURRENT and TARGET's CFA. */
- if (STACK_GROWS_DOWNWARD)
+ if (__LIBGCC_STACK_GROWS_DOWNWARD__)
return target_cfa - current->cfa + target->args_size;
else
return current->cfa - target_cfa - target->args_size;
/* The CFA is not sufficient to disambiguate the context of a function
interrupted by a signal before establishing its frame and the context
of the signal itself. */
- if (STACK_GROWS_DOWNWARD)
+ if (__LIBGCC_STACK_GROWS_DOWNWARD__)
return _Unwind_GetCFA (context) - _Unwind_IsSignalFrame (context);
else
return _Unwind_GetCFA (context) + _Unwind_IsSignalFrame (context);