|  | /* DWARF2 EH unwinding support for AIX. | 
|  | Copyright (C) 2011-2021 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GCC. | 
|  |  | 
|  | GCC is free software; you can redistribute it and/or modify it | 
|  | under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | GCC is distributed in the hope that it will be useful, but WITHOUT | 
|  | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | 
|  | or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public | 
|  | License for more details. | 
|  |  | 
|  | Under Section 7 of GPL version 3, you are granted additional | 
|  | permissions described in the GCC Runtime Library Exception, version | 
|  | 3.1, as published by the Free Software Foundation. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License and | 
|  | a copy of the GCC Runtime Library Exception along with this program; | 
|  | see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | /* Useful register numbers.  */ | 
|  |  | 
|  | #define R_LR             65 | 
|  | #define R_CR2            70 | 
|  | #define R_XER            76 | 
|  | #define R_FIRST_ALTIVEC  77 | 
|  | #define R_VRSAVE        109 | 
|  | #define R_VSCR          110 | 
|  |  | 
|  | /* If the current unwind info (FS) does not contain explicit info | 
|  | saving R2, then we have to do a minor amount of code reading to | 
|  | figure out if it was saved.  The big problem here is that the | 
|  | code that does the save/restore is generated by the linker, so | 
|  | we have no good way to determine at compile time what to do.  */ | 
|  |  | 
|  | #ifdef __64BIT__ | 
|  | #define MD_FROB_UPDATE_CONTEXT(CTX, FS)					\ | 
|  | do {									\ | 
|  | if ((FS)->regs.reg[2].how == REG_UNSAVED)				\ | 
|  | {									\ | 
|  | unsigned int *insn						\ | 
|  | = (unsigned int *)						\ | 
|  | _Unwind_GetGR ((CTX), R_LR);				\ | 
|  | if (*insn == 0xE8410028)					\ | 
|  | _Unwind_SetGRPtr ((CTX), 2, (CTX)->cfa + 40);			\ | 
|  | }									\ | 
|  | } while (0) | 
|  | #else | 
|  | #define MD_FROB_UPDATE_CONTEXT(CTX, FS)					\ | 
|  | do {									\ | 
|  | if ((FS)->regs.reg[2].how == REG_UNSAVED)				\ | 
|  | {									\ | 
|  | unsigned int *insn						\ | 
|  | = (unsigned int *)						\ | 
|  | _Unwind_GetGR ((CTX), R_LR);				\ | 
|  | if (*insn == 0x80410014)					\ | 
|  | _Unwind_SetGRPtr ((CTX), 2, (CTX)->cfa + 20);			\ | 
|  | }									\ | 
|  | } while (0) | 
|  | #endif | 
|  |  | 
|  | /* Now on to MD_FALLBACK_FRAME_STATE_FOR. | 
|  | 32bit AIX 5.2, 5.3, 6.1, 7.X and | 
|  | 64bit AIX 6.1, 7.X only at this stage.  */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stddef.h> | 
|  | #include <signal.h> | 
|  | #include <sys/machine.h> | 
|  |  | 
|  | #ifdef __64BIT__ | 
|  |  | 
|  | typedef struct __context64 mstate_t; | 
|  |  | 
|  | #else | 
|  |  | 
|  | typedef struct mstsave mstate_t; | 
|  |  | 
|  | #endif | 
|  |  | 
|  | #define MD_FALLBACK_FRAME_STATE_FOR ppc_aix_fallback_frame_state | 
|  |  | 
|  | /* If we are compiling on AIX < 5.3, the VMX related datastructs are not | 
|  | defined and we take measures to obtain proper runtime behavior if the | 
|  | compiled code happens to run on a later version with VMX enabled.  */ | 
|  |  | 
|  | #ifndef MSR_VMX | 
|  | #define MSR_VMX 0x2000000 | 
|  | #endif | 
|  |  | 
|  | typedef unsigned int uint; | 
|  | typedef struct { uint v[4]; } vreg_t; | 
|  | typedef struct { | 
|  | vreg_t regs[32]; | 
|  | uint   pad1 [3]; | 
|  | uint   vscr; | 
|  | uint   vrsave; | 
|  | uint   pad2 [3]; | 
|  | } vstate_t; | 
|  |  | 
|  | #define EXT_CONTEXT_MARK 0x45435458 | 
|  | #define EXT_CONTEXT_SIZE 4096 | 
|  | #define BUMPER_SIZE (EXT_CONTEXT_SIZE - sizeof(vstate_t) - (5 * sizeof(int))) | 
|  |  | 
|  | typedef struct { | 
|  | uint     pad1 [4]; | 
|  | vstate_t vstate; | 
|  | char     bumper [BUMPER_SIZE]; | 
|  | int      mark; | 
|  | } extended_context_t; | 
|  |  | 
|  | typedef struct { | 
|  | char bumper [offsetof (ucontext_t, uc_stack) + sizeof (stack_t)]; | 
|  | extended_context_t * ectx; | 
|  | int mark; | 
|  | } vmx_ucontext_t; | 
|  |  | 
|  | /* Determine whether CONTEXT designates a signal handler, and return the | 
|  | associated ucontext_t address if so.  Return NULL otherwise.  */ | 
|  |  | 
|  | static ucontext_t * | 
|  | ucontext_for (struct _Unwind_Context *context) | 
|  | { | 
|  | const unsigned int * ra = context->ra; | 
|  |  | 
|  | /* AIX 5.2, 5.3, 6.1 and 7.X, threaded or not, share common patterns | 
|  | and feature variants depending on the configured kernel (unix_mp | 
|  | or unix_64).  */ | 
|  |  | 
|  | #ifdef __64BIT__ | 
|  | if (*(ra - 5) == 0x4c00012c     /* isync             */ | 
|  | && *(ra - 4) == 0xe8ec0000  /* ld      r7,0(r12) */ | 
|  | && *(ra - 3) == 0xe84c0008  /* ld      r2,8(r12) */ | 
|  | && *(ra - 2) == 0x7ce903a6  /* mtctr   r7        */ | 
|  | && *(ra - 1) == 0x4e800421  /* bctrl             */ | 
|  | && *(ra - 0) == 0x7de27b78) /* mr      r2,r15   <-- context->ra */ | 
|  | { | 
|  | /* unix_64 */ | 
|  | if (*(ra - 6) == 0x7d000164)  /* mtmsrd  r8 */ | 
|  | { | 
|  | /* AIX 6.1, 7.1 and 7.2 */ | 
|  | return (ucontext_t *)(context->cfa + 0x70); | 
|  | } | 
|  | } | 
|  | #else | 
|  | if (*(ra - 5) == 0x4c00012c     /* isync             */ | 
|  | && *(ra - 4) == 0x80ec0000  /* lwz     r7,0(r12) */ | 
|  | && *(ra - 3) == 0x804c0004  /* lwz     r2,4(r12) */ | 
|  | && *(ra - 2) == 0x7ce903a6  /* mtctr   r7        */ | 
|  | && *(ra - 1) == 0x4e800421  /* bctrl             */ | 
|  | && *(ra - 0) == 0x7dc37378) /* mr      r3,r14   <-- context->ra */ | 
|  | { | 
|  | /* unix_64 */ | 
|  | if (*(ra - 6) == 0x7d000164)  /* mtmsrd  r8 */ | 
|  | { | 
|  | switch (*(ra + 18)) | 
|  | { | 
|  | /* AIX 5.2 */ | 
|  | case 0x835a0520: /* lwz r26,1312(r26) */ | 
|  | return (ucontext_t *)(context->cfa + 0x70); | 
|  |  | 
|  | /* AIX 5.3 */ | 
|  | case 0x835a0570:  /* lwz r26,1392(r26) */ | 
|  | return (ucontext_t *)(context->cfa + 0x40); | 
|  |  | 
|  | /* AIX 6.1 and 7.1 */ | 
|  | case 0x2c1a0000:  /* cmpwi   r26,0 */ | 
|  | return (ucontext_t *)(context->cfa + 0x40); | 
|  |  | 
|  | /* AIX 7.2 */ | 
|  | case 0x3800000a:  /* li   r0,A */ | 
|  | return (ucontext_t *)(context->cfa + 0x40); | 
|  |  | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* unix_mp */ | 
|  | if (*(ra - 6) == 0x7d000124)  /* mtmsr  r8 */ | 
|  | { | 
|  | typedef struct { | 
|  | char pad[56]; | 
|  | ucontext_t ucontext; | 
|  | siginfo_t siginfo; | 
|  | } aix52_stack_t; | 
|  |  | 
|  | aix52_stack_t * frame = (aix52_stack_t *) context->cfa; | 
|  | return &frame->ucontext; | 
|  | } | 
|  | } | 
|  | #endif | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* The fallback proper.  */ | 
|  |  | 
|  | #ifdef __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__ | 
|  | #define RETURN_COLUMN __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__ | 
|  | #else | 
|  | #define RETURN_COLUMN ARG_POINTER_REGNUM | 
|  | #endif | 
|  |  | 
|  | #define REGISTER_CFA_OFFSET_FOR(FS,REGNO,ADDR,CFA)\ | 
|  | do { \ | 
|  | (FS)->regs.reg[REGNO].how = REG_SAVED_OFFSET; \ | 
|  | (FS)->regs.reg[REGNO].loc.offset = (long) (ADDR) - (CFA); \ | 
|  | } while (0) | 
|  |  | 
|  | static _Unwind_Reason_Code | 
|  | ppc_aix_fallback_frame_state (struct _Unwind_Context *context, | 
|  | _Unwind_FrameState *fs) | 
|  | { | 
|  | ucontext_t * uctx = ucontext_for (context); | 
|  | mstate_t * mctx; | 
|  |  | 
|  | long new_cfa; | 
|  | int i; | 
|  |  | 
|  | if (uctx == NULL) | 
|  | return _URC_END_OF_STACK; | 
|  |  | 
|  | mctx = &uctx->uc_mcontext.jmp_context; | 
|  |  | 
|  | /* The "kernel" frame cfa is the stack pointer at the signal occurrence | 
|  | point.  */ | 
|  | new_cfa = mctx->gpr[__LIBGCC_STACK_POINTER_REGNUM__]; | 
|  |  | 
|  | fs->regs.cfa_how = CFA_REG_OFFSET; | 
|  | fs->regs.cfa_reg = __LIBGCC_STACK_POINTER_REGNUM__; | 
|  | fs->regs.cfa_offset = new_cfa - (long) context->cfa; | 
|  |  | 
|  | /* And we state how to find the various registers it has saved with | 
|  | relative offset rules from there.  */ | 
|  |  | 
|  | for (i = 0; i < 32; i++) | 
|  | if (i != __LIBGCC_STACK_POINTER_REGNUM__) | 
|  | REGISTER_CFA_OFFSET_FOR (fs, i, &mctx->gpr[i], new_cfa); | 
|  |  | 
|  | REGISTER_CFA_OFFSET_FOR (fs, R_CR2, &mctx->cr, new_cfa); | 
|  | REGISTER_CFA_OFFSET_FOR (fs, R_XER, &mctx->xer, new_cfa); | 
|  | REGISTER_CFA_OFFSET_FOR (fs, R_LR, &mctx->lr, new_cfa); | 
|  |  | 
|  | fs->retaddr_column = RETURN_COLUMN; | 
|  | REGISTER_CFA_OFFSET_FOR (fs, RETURN_COLUMN, &mctx->iar, new_cfa); | 
|  | fs->signal_frame = 1; | 
|  |  | 
|  | /* Honor FP Ever Used ...   */ | 
|  | if (mctx->fpeu) | 
|  | { | 
|  | for (i = 0; i < 32; i++) | 
|  | REGISTER_CFA_OFFSET_FOR (fs, i+32, &mctx->fpr[i], new_cfa); | 
|  | } | 
|  |  | 
|  | /* Honor VMX context, if any.  We expect the msr bit never to be set in | 
|  | environments where there is no VMX support, e.g. on AIX < 5.3.  */ | 
|  | if (mctx->msr & MSR_VMX) | 
|  | { | 
|  | vmx_ucontext_t * uc = (vmx_ucontext_t *) uctx; | 
|  |  | 
|  | if (uc->mark == EXT_CONTEXT_MARK && uc->ectx->mark == EXT_CONTEXT_MARK) | 
|  | { | 
|  | vstate_t * vstate = &uc->ectx->vstate; | 
|  |  | 
|  | for (i = 0; i < 32; i++) | 
|  | REGISTER_CFA_OFFSET_FOR | 
|  | (fs, i+R_FIRST_ALTIVEC, &vstate->regs[i], new_cfa); | 
|  |  | 
|  | REGISTER_CFA_OFFSET_FOR (fs, R_VSCR, &vstate->vscr, new_cfa); | 
|  | REGISTER_CFA_OFFSET_FOR (fs, R_VRSAVE, &vstate->vrsave, new_cfa); | 
|  | } | 
|  | } | 
|  |  | 
|  | return _URC_NO_REASON; | 
|  | } |