Brian Silverman | 8649792 | 2018-02-10 19:28:39 -0500 | [diff] [blame] | 1 | /* DW_EH_PE_* support for libdw unwinder. |
| 2 | Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc. |
| 3 | This file is part of elfutils. |
| 4 | |
| 5 | This file is free software; you can redistribute it and/or modify |
| 6 | it under the terms of either |
| 7 | |
| 8 | * the GNU Lesser General Public License as published by the Free |
| 9 | Software Foundation; either version 3 of the License, or (at |
| 10 | your option) any later version |
| 11 | |
| 12 | or |
| 13 | |
| 14 | * the GNU General Public License as published by the Free |
| 15 | Software Foundation; either version 2 of the License, or (at |
| 16 | your option) any later version |
| 17 | |
| 18 | or both in parallel, as here. |
| 19 | |
| 20 | elfutils is distributed in the hope that it will be useful, but |
| 21 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 23 | General Public License for more details. |
| 24 | |
| 25 | You should have received copies of the GNU General Public License and |
| 26 | the GNU Lesser General Public License along with this program. If |
| 27 | not, see <http://www.gnu.org/licenses/>. */ |
| 28 | |
| 29 | #ifndef _ENCODED_VALUE_H |
| 30 | #define _ENCODED_VALUE_H 1 |
| 31 | |
| 32 | #include <dwarf.h> |
| 33 | #include <stdlib.h> |
| 34 | #include "libdwP.h" |
| 35 | #include "../libelf/common.h" |
| 36 | |
| 37 | |
| 38 | /* Returns zero if the value is omitted, the encoding is unknown or |
| 39 | the (leb128) size cannot be determined. */ |
| 40 | static size_t __attribute__ ((unused)) |
| 41 | encoded_value_size (const Elf_Data *data, const unsigned char e_ident[], |
| 42 | uint8_t encoding, const uint8_t *p) |
| 43 | { |
| 44 | if (encoding == DW_EH_PE_omit) |
| 45 | return 0; |
| 46 | |
| 47 | switch (encoding & 0x07) |
| 48 | { |
| 49 | case DW_EH_PE_udata2: |
| 50 | return 2; |
| 51 | case DW_EH_PE_udata4: |
| 52 | return 4; |
| 53 | case DW_EH_PE_udata8: |
| 54 | return 8; |
| 55 | |
| 56 | case DW_EH_PE_absptr: |
| 57 | return e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; |
| 58 | |
| 59 | case DW_EH_PE_uleb128: |
| 60 | if (p != NULL) |
| 61 | { |
| 62 | const uint8_t *end = p; |
| 63 | while (end < (uint8_t *) data->d_buf + data->d_size) |
| 64 | if (*end++ & 0x80u) |
| 65 | return end - p; |
| 66 | } |
| 67 | return 0; |
| 68 | |
| 69 | default: |
| 70 | return 0; |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | /* Returns zero when value was read successfully, minus one otherwise. */ |
| 75 | static inline int __attribute__ ((unused)) |
| 76 | __libdw_cfi_read_address_inc (const Dwarf_CFI *cache, |
| 77 | const unsigned char **addrp, |
| 78 | int width, Dwarf_Addr *ret) |
| 79 | { |
| 80 | width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; |
| 81 | |
| 82 | if (cache->dbg != NULL) |
| 83 | return __libdw_read_address_inc (cache->dbg, IDX_debug_frame, |
| 84 | addrp, width, ret); |
| 85 | |
| 86 | /* Only .debug_frame might have relocation to consider. |
| 87 | Read plain values from .eh_frame data. */ |
| 88 | |
| 89 | const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size; |
| 90 | Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] }; |
| 91 | |
| 92 | if (width == 4) |
| 93 | { |
| 94 | if (unlikely (*addrp + 4 > endp)) |
| 95 | { |
| 96 | invalid_data: |
| 97 | __libdw_seterrno (DWARF_E_INVALID_CFI); |
| 98 | return -1; |
| 99 | } |
| 100 | *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp); |
| 101 | } |
| 102 | else |
| 103 | { |
| 104 | if (unlikely (*addrp + 8 > endp)) |
| 105 | goto invalid_data; |
| 106 | *ret = read_8ubyte_unaligned_inc (&eh_dbg, *addrp); |
| 107 | } |
| 108 | return 0; |
| 109 | } |
| 110 | |
| 111 | /* Returns true on error, false otherwise. */ |
| 112 | static bool __attribute__ ((unused)) |
| 113 | read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding, |
| 114 | const uint8_t **p, Dwarf_Addr *result) |
| 115 | { |
| 116 | *result = 0; |
| 117 | switch (encoding & 0x70) |
| 118 | { |
| 119 | case DW_EH_PE_absptr: |
| 120 | break; |
| 121 | case DW_EH_PE_pcrel: |
| 122 | *result = (cache->frame_vaddr |
| 123 | + (*p - (const uint8_t *) cache->data->d.d_buf)); |
| 124 | break; |
| 125 | case DW_EH_PE_textrel: |
| 126 | // ia64: segrel |
| 127 | *result = cache->textrel; |
| 128 | break; |
| 129 | case DW_EH_PE_datarel: |
| 130 | // i386: GOTOFF |
| 131 | // ia64: gprel |
| 132 | *result = cache->datarel; |
| 133 | break; |
| 134 | case DW_EH_PE_funcrel: /* XXX */ |
| 135 | break; |
| 136 | case DW_EH_PE_aligned: |
| 137 | { |
| 138 | const size_t size = encoded_value_size (&cache->data->d, |
| 139 | cache->e_ident, |
| 140 | encoding, *p); |
| 141 | if (unlikely (size == 0)) |
| 142 | return true; |
| 143 | size_t align = ((cache->frame_vaddr |
| 144 | + (*p - (const uint8_t *) cache->data->d.d_buf)) |
| 145 | & (size - 1)); |
| 146 | if (align != 0) |
| 147 | *p += size - align; |
| 148 | break; |
| 149 | } |
| 150 | |
| 151 | default: |
| 152 | __libdw_seterrno (DWARF_E_INVALID_CFI); |
| 153 | return true; |
| 154 | } |
| 155 | |
| 156 | Dwarf_Addr value = 0; |
| 157 | const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size; |
| 158 | switch (encoding & 0x0f) |
| 159 | { |
| 160 | case DW_EH_PE_udata2: |
| 161 | if (unlikely (*p + 2 > endp)) |
| 162 | { |
| 163 | invalid_data: |
| 164 | __libdw_seterrno (DWARF_E_INVALID_CFI); |
| 165 | return true; |
| 166 | } |
| 167 | value = read_2ubyte_unaligned_inc (cache, *p); |
| 168 | break; |
| 169 | |
| 170 | case DW_EH_PE_sdata2: |
| 171 | if (unlikely (*p + 2 > endp)) |
| 172 | goto invalid_data; |
| 173 | value = read_2sbyte_unaligned_inc (cache, *p); |
| 174 | break; |
| 175 | |
| 176 | case DW_EH_PE_udata4: |
| 177 | if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0)) |
| 178 | return true; |
| 179 | break; |
| 180 | |
| 181 | case DW_EH_PE_sdata4: |
| 182 | if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0)) |
| 183 | return true; |
| 184 | value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend. */ |
| 185 | break; |
| 186 | |
| 187 | case DW_EH_PE_udata8: |
| 188 | case DW_EH_PE_sdata8: |
| 189 | if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0)) |
| 190 | return true; |
| 191 | break; |
| 192 | |
| 193 | case DW_EH_PE_absptr: |
| 194 | if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0)) |
| 195 | return true; |
| 196 | break; |
| 197 | |
| 198 | case DW_EH_PE_uleb128: |
| 199 | get_uleb128 (value, *p, endp); |
| 200 | break; |
| 201 | |
| 202 | case DW_EH_PE_sleb128: |
| 203 | get_sleb128 (value, *p, endp); |
| 204 | break; |
| 205 | |
| 206 | default: |
| 207 | __libdw_seterrno (DWARF_E_INVALID_CFI); |
| 208 | return true; |
| 209 | } |
| 210 | |
| 211 | *result += value; |
| 212 | |
| 213 | if (encoding & DW_EH_PE_indirect) |
| 214 | { |
| 215 | if (unlikely (*result < cache->frame_vaddr)) |
| 216 | return true; |
| 217 | *result -= cache->frame_vaddr; |
| 218 | size_t ptrsize = encoded_value_size (NULL, cache->e_ident, |
| 219 | DW_EH_PE_absptr, NULL); |
| 220 | if (unlikely (cache->data->d.d_size < ptrsize |
| 221 | || *result > (cache->data->d.d_size - ptrsize))) |
| 222 | return true; |
| 223 | const uint8_t *ptr = cache->data->d.d_buf + *result; |
| 224 | if (unlikely (__libdw_cfi_read_address_inc (cache, &ptr, 0, result) |
| 225 | != 0)) |
| 226 | return true; |
| 227 | } |
| 228 | |
| 229 | return false; |
| 230 | } |
| 231 | |
| 232 | #endif /* encoded-value.h */ |