| /* Copyright (C) 2004-2021 Free Software Foundation, Inc. |
| Contributed by Douglas B Rupp <rupp@gnat.com> |
| |
| 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/>. */ |
| |
| /* Locate the FDE entry for a given address, using VMS Starlet routines |
| to avoid register/deregister calls at DSO load/unload. */ |
| |
| #include "tconfig.h" |
| #include "tsystem.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "libgcc_tm.h" |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include "unwind-ia64.h" |
| |
| #include <ossddef.h> |
| #ifndef SS$_NORMAL |
| #define SS$_NORMAL 1 |
| #endif |
| |
| #define UNW_IVMS_MODE(HEADER) (((HEADER) >> 44) & 0x3L) |
| |
| typedef struct |
| { |
| unw_word start_offset; |
| unw_word end_offset; |
| unw_word info_offset; |
| unw_word gp_value; |
| } vms_unw_table_entry; |
| |
| typedef unsigned long long uqword; |
| |
| /* ENTRY is the unwind table entry found for a PC part of call chain we're |
| unwinding through. Return whether we should force the generic unwinder |
| to resort to "fallback" processing. */ |
| |
| static int |
| force_fallback_processing_for (void * pc, vms_unw_table_entry * entry) |
| { |
| static int eh_debug = -1; |
| |
| uqword * unw_info_block = (uqword *)entry->info_offset; |
| uqword header = *unw_info_block; |
| |
| /* We need to force fallback processing in two cases: |
| |
| 1/ The exception dispatch frame, since only our fallback |
| processing knows how to properly unwind through it, and |
| |
| 2/ A bottom of stack frame, since only our fallback processing |
| will ensure we don't try to unwind further past it, which |
| would get us into unknown territory and likely cause a severe |
| crash along the way. |
| |
| The two cases are indicated by non-default values for specific |
| bits in the OS Specific Data (OSSD) General Information block |
| associated with such frames. */ |
| |
| ossddef * ossd; |
| |
| if (eh_debug == -1) |
| { |
| char * EH_DEBUG = getenv ("EH_DEBUG"); |
| eh_debug = EH_DEBUG ? atoi (EH_DEBUG) : 0; |
| } |
| |
| if (eh_debug) |
| { |
| printf ("pc @ 0x%p, block @ 0x%p, header = 0x%016llx\n", |
| pc, unw_info_block, header); |
| printf ("mode = %d, length = %ld, handler = %d\n", |
| (int)UNW_IVMS_MODE (header), UNW_LENGTH (header), |
| UNW_FLAG_EHANDLER (header) || UNW_FLAG_EHANDLER (header)); |
| } |
| |
| /* An OSSD block is there for IVMS_MODE == 3 only. */ |
| if (UNW_IVMS_MODE (header) != 3) |
| return 0; |
| |
| /* The OSSD block is found past the header, unwind descriptor area |
| and condition handler pointer, if any. */ |
| ossd = (ossddef *) |
| /* Beware: uqword pointer arithmetic below. */ |
| (unw_info_block |
| + 1 |
| + UNW_LENGTH (header) |
| + (UNW_FLAG_EHANDLER (header) || UNW_FLAG_EHANDLER (header))); |
| |
| /* "A General Information segment may be omitted if all of its fields |
| would have their default values. If a General Information segment |
| is present, it must be the first in the OSSD area." So ... */ |
| |
| if (eh_debug) |
| printf ("ossd @ 0x%p\n", ossd); |
| |
| if (eh_debug && ossd->ossd$v_type == OSSD$K_GENERAL_INFO) |
| printf ("exc_frame = %d - bot_frame = %d - base_frame = %d\n", |
| ossd->ossd$v_exception_frame, |
| ossd->ossd$v_bottom_of_stack, |
| ossd->ossd$v_base_frame); |
| |
| return |
| ossd->ossd$v_type == OSSD$K_GENERAL_INFO |
| && (ossd->ossd$v_exception_frame |
| || ossd->ossd$v_bottom_of_stack || ossd->ossd$v_base_frame); |
| } |
| |
| /* Return a pointer to the unwind table entry for the function |
| containing PC, 0 if we cannot find an entry or if the one we find |
| calls for fallback processing. */ |
| |
| struct unw_table_entry * |
| _Unwind_FindTableEntry (void *pc, unw_word *segment_base, |
| unw_word *gp, struct unw_table_entry *ent) |
| { |
| vms_unw_table_entry vueblock; |
| |
| if (SYS$GET_UNWIND_ENTRY_INFO (pc, &vueblock, 0) != SS$_NORMAL) |
| return 0; |
| |
| /* If there is no unwind information, use fallback. */ |
| if (vueblock.info_offset == 0) |
| return 0; |
| |
| /* If we need to force fallback processing, just pretend there is |
| no entry. */ |
| if (force_fallback_processing_for (pc, &vueblock)) |
| return 0; |
| |
| *segment_base = 0; /* ??? Fixme. ??? */ |
| *gp = vueblock.gp_value; |
| ent->start_offset = vueblock.start_offset; |
| ent->end_offset = vueblock.end_offset; |
| ent->info_offset = vueblock.info_offset; |
| |
| return ent; |
| } |