Jakub Jelinek | 99dee82 | 2021-01-04 10:26:59 +0100 | [diff] [blame] | 1 | /* Copyright (C) 2000-2021 Free Software Foundation, Inc. |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 2 | Contributed by Richard Henderson <rth@cygnus.com>. |
| 3 | |
Nathanael Nerode | 3bed293 | 2003-03-13 18:26:30 +0000 | [diff] [blame] | 4 | This file is part of GCC. |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 5 | |
Nathanael Nerode | 3bed293 | 2003-03-13 18:26:30 +0000 | [diff] [blame] | 6 | GCC is free software; you can redistribute it and/or modify |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 7 | it under the terms of the GNU General Public License as published by |
Jakub Jelinek | 748086b | 2009-04-09 17:00:19 +0200 | [diff] [blame] | 8 | the Free Software Foundation; either version 3, or (at your option) |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 9 | any later version. |
| 10 | |
Nathanael Nerode | 3bed293 | 2003-03-13 18:26:30 +0000 | [diff] [blame] | 11 | GCC is distributed in the hope that it will be useful, |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | GNU General Public License for more details. |
| 15 | |
Jakub Jelinek | 748086b | 2009-04-09 17:00:19 +0200 | [diff] [blame] | 16 | Under Section 7 of GPL version 3, you are granted additional |
| 17 | permissions described in the GCC Runtime Library Exception, version |
| 18 | 3.1, as published by the Free Software Foundation. |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 19 | |
Jakub Jelinek | 748086b | 2009-04-09 17:00:19 +0200 | [diff] [blame] | 20 | You should have received a copy of the GNU General Public License and |
| 21 | a copy of the GCC Runtime Library Exception along with this program; |
| 22 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| 23 | <http://www.gnu.org/licenses/>. */ |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 24 | |
| 25 | /* Locate the FDE entry for a given address, using glibc ld.so routines |
| 26 | to avoid register/deregister calls at DSO load/unload. */ |
| 27 | |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 28 | #ifndef _GNU_SOURCE |
Andreas Schwab | 634879c | 2003-11-02 17:35:20 +0000 | [diff] [blame] | 29 | #define _GNU_SOURCE 1 |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 30 | #endif |
| 31 | #include "config.h" |
| 32 | #include <stddef.h> |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 33 | #include <stdlib.h> |
| 34 | #include <link.h> |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 35 | #include "unwind-ia64.h" |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 36 | |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 37 | #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) \ |
| 38 | || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && !defined(DT_CONFIG)) |
| 39 | # error You need GLIBC 2.2.4 or later on IA-64 Linux |
| 40 | #endif |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 41 | |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 42 | struct unw_ia64_callback_data |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 43 | { |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 44 | Elf64_Addr pc; |
| 45 | unsigned long *segment_base; |
| 46 | unsigned long *gp; |
| 47 | struct unw_table_entry *ret; |
| 48 | }; |
| 49 | |
| 50 | static int |
| 51 | _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) |
| 52 | { |
| 53 | struct unw_ia64_callback_data *data = (struct unw_ia64_callback_data *) ptr; |
| 54 | const Elf64_Phdr *phdr, *p_unwind, *p_dynamic; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 55 | long n, match; |
| 56 | Elf64_Addr load_base, seg_base; |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 57 | struct unw_table_entry *f_base, *f; |
Richard Henderson | cd5c404 | 2000-11-07 17:59:35 -0800 | [diff] [blame] | 58 | size_t lo, hi; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 59 | |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 60 | /* Make sure struct dl_phdr_info is at least as big as we need. */ |
| 61 | if (size < offsetof (struct dl_phdr_info, dlpi_phnum) |
| 62 | + sizeof (info->dlpi_phnum)) |
| 63 | return -1; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 64 | |
| 65 | match = 0; |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 66 | phdr = info->dlpi_phdr; |
| 67 | load_base = info->dlpi_addr; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 68 | p_unwind = NULL; |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 69 | p_dynamic = NULL; |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 70 | seg_base = ~(Elf64_Addr) 0; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 71 | |
| 72 | /* See if PC falls into one of the loaded segments. Find the unwind |
| 73 | segment at the same time. */ |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 74 | for (n = info->dlpi_phnum; --n >= 0; phdr++) |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 75 | { |
| 76 | if (phdr->p_type == PT_LOAD) |
| 77 | { |
| 78 | Elf64_Addr vaddr = phdr->p_vaddr + load_base; |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 79 | if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 80 | match = 1; |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 81 | if (vaddr < seg_base) |
| 82 | seg_base = vaddr; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 83 | } |
| 84 | else if (phdr->p_type == PT_IA_64_UNWIND) |
| 85 | p_unwind = phdr; |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 86 | else if (phdr->p_type == PT_DYNAMIC) |
| 87 | p_dynamic = phdr; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 88 | } |
| 89 | if (!match || !p_unwind) |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 90 | return 0; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 91 | |
| 92 | /* Search for the FDE within the unwind segment. */ |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 93 | |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 94 | f_base = (struct unw_table_entry *) (p_unwind->p_vaddr + load_base); |
Richard Henderson | cd5c404 | 2000-11-07 17:59:35 -0800 | [diff] [blame] | 95 | lo = 0; |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 96 | hi = p_unwind->p_memsz / sizeof (struct unw_table_entry); |
Richard Henderson | cd5c404 | 2000-11-07 17:59:35 -0800 | [diff] [blame] | 97 | |
| 98 | while (lo < hi) |
| 99 | { |
| 100 | size_t mid = (lo + hi) / 2; |
Richard Henderson | cd5c404 | 2000-11-07 17:59:35 -0800 | [diff] [blame] | 101 | |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 102 | f = f_base + mid; |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 103 | if (data->pc < f->start_offset + seg_base) |
Richard Henderson | cd5c404 | 2000-11-07 17:59:35 -0800 | [diff] [blame] | 104 | hi = mid; |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 105 | else if (data->pc >= f->end_offset + seg_base) |
Richard Henderson | cd5c404 | 2000-11-07 17:59:35 -0800 | [diff] [blame] | 106 | lo = mid + 1; |
| 107 | else |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 108 | goto found; |
| 109 | } |
Jakub Jelinek | 275b60d | 2001-12-15 12:49:18 +0100 | [diff] [blame] | 110 | /* No need to search for further libraries when we know pc is contained |
| 111 | in this library. */ |
| 112 | return 1; |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 113 | |
| 114 | found: |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 115 | *data->segment_base = seg_base; |
| 116 | *data->gp = 0; |
| 117 | data->ret = f; |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 118 | |
| 119 | if (p_dynamic) |
| 120 | { |
Kazu Hirata | 9e4f94de | 2003-01-26 20:15:36 +0000 | [diff] [blame] | 121 | /* For dynamically linked executables and shared libraries, |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 122 | DT_PLTGOT is the gp value for that object. */ |
| 123 | Elf64_Dyn *dyn = (Elf64_Dyn *)(p_dynamic->p_vaddr + load_base); |
| 124 | for (; dyn->d_tag != DT_NULL ; dyn++) |
| 125 | if (dyn->d_tag == DT_PLTGOT) |
| 126 | { |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 127 | /* On IA-64, _DYNAMIC is writable and GLIBC has relocated it. */ |
| 128 | *data->gp = dyn->d_un.d_ptr; |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 129 | break; |
| 130 | } |
| 131 | } |
| 132 | else |
| 133 | { |
| 134 | /* Otherwise this is a static executable with no _DYNAMIC. |
| 135 | The gp is constant program-wide. */ |
| 136 | register unsigned long gp __asm__("gp"); |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 137 | *data->gp = gp; |
Richard Henderson | cd5c404 | 2000-11-07 17:59:35 -0800 | [diff] [blame] | 138 | } |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 139 | |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 140 | return 1; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 141 | } |
| 142 | |
Richard Henderson | 2a1ee41 | 2001-05-11 23:03:20 -0700 | [diff] [blame] | 143 | /* Return a pointer to the unwind table entry for the function |
| 144 | containing PC. */ |
| 145 | |
| 146 | struct unw_table_entry * |
Tristan Gingold | 9e916de | 2012-03-27 09:50:10 +0000 | [diff] [blame] | 147 | _Unwind_FindTableEntry (void *pc, unw_word *segment_base, unw_word *gp, |
Douglas B Rupp | b874a90 | 2009-08-14 20:56:31 +0000 | [diff] [blame] | 148 | struct unw_table_entry *ent ATTRIBUTE_UNUSED) |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 149 | { |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 150 | struct unw_ia64_callback_data data; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 151 | |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 152 | data.pc = (Elf64_Addr) pc; |
| 153 | data.segment_base = segment_base; |
| 154 | data.gp = gp; |
| 155 | data.ret = NULL; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 156 | |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 157 | if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) |
| 158 | return NULL; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 159 | |
Jakub Jelinek | a734f2a | 2001-08-09 18:22:23 +0200 | [diff] [blame] | 160 | return data.ret; |
Richard Henderson | fee0225 | 2000-10-03 20:08:50 -0700 | [diff] [blame] | 161 | } |