1 /* 2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2020, NTT DATA. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 * 24 */ 25 26 #include <cstring> 27 28 #include "dwarf.hpp" 29 #include "libproc_impl.h" 30 31 /* from read_leb128() in dwarf.c in binutils */ 32 uintptr_t DwarfParser::read_leb(bool sign) { 33 uintptr_t result = 0L; 34 unsigned char b; 35 unsigned int shift = 0; 36 37 while (true) { 38 b = *_buf++; 39 result |= static_cast<uintptr_t>(b & 0x7f) << shift; 40 shift += 7; 41 if ((b & 0x80) == 0) { 42 break; 43 } 44 } 45 46 if (sign && (shift < (8 * sizeof(result))) && (b & 0x40)) { 47 result |= static_cast<uintptr_t>(-1L) << shift; 48 } 49 50 return result; 51 } 52 53 uint64_t DwarfParser::get_entry_length() { 54 uint64_t length = *(reinterpret_cast<uint32_t *>(_buf)); 55 _buf += 4; 56 if (length == 0xffffffff) { 57 length = *(reinterpret_cast<uint64_t *>(_buf)); 58 _buf += 8; 59 } 60 return length; 61 } 62 63 void DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { 64 unsigned char *orig_pos = _buf; 65 _buf = start_of_entry - id; 66 67 uint64_t length = get_entry_length(); 68 unsigned char *end = _buf + length; 69 70 _buf += 4; // Skip ID (This value of CIE would be always 0) 71 _buf++; // Skip version (assume to be "1") 72 73 char *augmentation_string = reinterpret_cast<char *>(_buf); 74 bool has_ehdata = (strcmp("eh", augmentation_string) == 0); 75 bool fde_encoded = (strchr(augmentation_string, 'R') != NULL); 76 _buf += strlen(augmentation_string) + 1; // includes '\0' 77 if (has_ehdata) { 78 _buf += sizeof(void *); // Skip EH data 79 } 80 81 _code_factor = read_leb(false); 82 _data_factor = static_cast<int>(read_leb(true)); 83 _return_address_reg = static_cast<enum DWARF_Register>(*_buf++); 84 85 if (fde_encoded) { 86 uintptr_t augmentation_length = read_leb(false); 87 _encoding = *_buf++; 88 } 89 90 // Clear state 91 _current_pc = 0L; 92 _cfa_reg = RSP; 93 _return_address_reg = RA; 94 _cfa_offset = 0; 95 _ra_cfa_offset = 0; 96 _bp_cfa_offset = 0; 97 _bp_offset_available = false; 98 99 parse_dwarf_instructions(0L, static_cast<uintptr_t>(-1L), end); 100 101 _buf = orig_pos; 102 } 103 104 void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const unsigned char *end) { 105 uintptr_t operand1; 106 _current_pc = begin; 107 108 /* for remember state */ 109 enum DWARF_Register rem_cfa_reg = MAX_VALUE; 110 int rem_cfa_offset = 0; 111 int rem_ra_cfa_offset = 0; 112 int rem_bp_cfa_offset = 0; 113 114 while ((_buf < end) && (_current_pc < pc)) { 115 unsigned char op = *_buf++; 116 unsigned char opa = op & 0x3f; 117 if (op & 0xc0) { 118 op &= 0xc0; 119 } 120 121 switch (op) { 122 case 0x0: // DW_CFA_nop 123 return; 124 case 0x01: // DW_CFA_set_loc 125 operand1 = get_decoded_value(); 126 if (_current_pc != 0L) { 127 _current_pc = operand1; 128 } 129 break; 130 case 0x0c: // DW_CFA_def_cfa 131 _cfa_reg = static_cast<enum DWARF_Register>(read_leb(false)); 132 _cfa_offset = read_leb(false); 133 break; 134 case 0x80: {// DW_CFA_offset 135 operand1 = read_leb(false); 136 enum DWARF_Register reg = static_cast<enum DWARF_Register>(opa); 137 if (reg == RBP) { 138 _bp_cfa_offset = operand1 * _data_factor; 139 _bp_offset_available = true; 140 } else if (reg == RA) { 141 _ra_cfa_offset = operand1 * _data_factor; 142 } 143 break; 144 } 145 case 0xe: // DW_CFA_def_cfa_offset 146 _cfa_offset = read_leb(false); 147 break; 148 case 0x40: // DW_CFA_advance_loc 149 if (_current_pc != 0L) { 150 _current_pc += opa * _code_factor; 151 } 152 break; 153 case 0x02: { // DW_CFA_advance_loc1 154 unsigned char ofs = *_buf++; 155 if (_current_pc != 0L) { 156 _current_pc += ofs * _code_factor; 157 } 158 break; 159 } 160 case 0x03: { // DW_CFA_advance_loc2 161 unsigned short ofs = *(reinterpret_cast<unsigned short *>(_buf)); 162 _buf += 2; 163 if (_current_pc != 0L) { 164 _current_pc += ofs * _code_factor; 165 } 166 break; 167 } 168 case 0x04: { // DW_CFA_advance_loc4 169 unsigned int ofs = *(reinterpret_cast<unsigned int *>(_buf)); 170 _buf += 4; 171 if (_current_pc != 0L) { 172 _current_pc += ofs * _code_factor; 173 } 174 break; 175 } 176 case 0x0d: {// DW_CFA_def_cfa_register 177 _cfa_reg = static_cast<enum DWARF_Register>(read_leb(false)); 178 break; 179 } 180 case 0x0a: // DW_CFA_remember_state 181 rem_cfa_reg = _cfa_reg; 182 rem_cfa_offset = _cfa_offset; 183 rem_ra_cfa_offset = _ra_cfa_offset; 184 rem_bp_cfa_offset = _bp_cfa_offset; 185 break; 186 case 0x0b: // DW_CFA_restore_state 187 _cfa_reg = rem_cfa_reg; 188 _cfa_offset = rem_cfa_offset; 189 _ra_cfa_offset = rem_ra_cfa_offset; 190 _bp_cfa_offset = rem_bp_cfa_offset; 191 break; 192 default: 193 print_debug("DWARF: Unknown opcode: 0x%x\n", op); 194 return; 195 } 196 } 197 } 198 199 /* from dwarf.c in binutils */ 200 uint32_t DwarfParser::get_decoded_value() { 201 int size; 202 uintptr_t result; 203 204 switch (_encoding & 0x7) { 205 case 0: // DW_EH_PE_absptr 206 size = sizeof(void *); 207 result = *(reinterpret_cast<uintptr_t *>(_buf)); 208 break; 209 case 2: // DW_EH_PE_udata2 210 size = 2; 211 result = *(reinterpret_cast<unsigned int *>(_buf)); 212 break; 213 case 3: // DW_EH_PE_udata4 214 size = 4; 215 result = *(reinterpret_cast<uint32_t *>(_buf)); 216 break; 217 case 4: // DW_EH_PE_udata8 218 size = 8; 219 result = *(reinterpret_cast<uint64_t *>(_buf)); 220 break; 221 default: 222 return 0; 223 } 224 225 // On x86-64, we have to handle it as 32 bit value, and it is PC relative. 226 // https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html 227 #if defined(_LP64) 228 if (size == 8) { 229 result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data); 230 size = 4; 231 } else 232 #endif 233 if ((_encoding & 0x70) == 0x10) { // 0x10 = DW_EH_PE_pcrel 234 result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data); 235 } else if (size == 2) { 236 result = static_cast<int>(result) + _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data); 237 size = 4; 238 } 239 240 _buf += size; 241 return static_cast<uint32_t>(result); 242 } 243 244 unsigned int DwarfParser::get_pc_range() { 245 int size; 246 uintptr_t result; 247 248 switch (_encoding & 0x7) { 249 case 0: // DW_EH_PE_absptr 250 size = sizeof(void *); 251 result = *(reinterpret_cast<uintptr_t *>(_buf)); 252 break; 253 case 2: // DW_EH_PE_udata2 254 size = 2; 255 result = *(reinterpret_cast<unsigned int *>(_buf)); 256 break; 257 case 3: // DW_EH_PE_udata4 258 size = 4; 259 result = *(reinterpret_cast<uint32_t *>(_buf)); 260 break; 261 case 4: // DW_EH_PE_udata8 262 size = 8; 263 result = *(reinterpret_cast<uint64_t *>(_buf)); 264 break; 265 default: 266 return 0; 267 } 268 269 // On x86-64, we have to handle it as 32 bit value, and it is PC relative. 270 // https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html 271 #if defined(_LP64) 272 if ((size == 8) || (size == 2)) { 273 size = 4; 274 } 275 #endif 276 277 _buf += size; 278 return static_cast<unsigned int>(result); 279 } 280 281 uintptr_t DwarfParser::process_dwarf(const uintptr_t pc) { 282 // https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html 283 _buf = _lib->eh_frame.data; 284 while (true) { 285 uint64_t length = get_entry_length(); 286 unsigned char *next_entry = _buf + length; 287 unsigned char *start_of_entry = _buf; 288 uint32_t id = *(reinterpret_cast<uint32_t *>(_buf)); 289 _buf += 4; 290 if (id != 0) { // FDE 291 uintptr_t pc_begin = get_decoded_value() + _lib->eh_frame.library_base_addr; 292 uintptr_t pc_end = pc_begin + get_pc_range(); 293 294 if ((pc >= pc_begin) && (pc < pc_end)) { 295 // Process CIE 296 process_cie(start_of_entry, id); 297 298 // Skip Augumenation 299 uintptr_t augmentation_length = read_leb(false); 300 _buf += augmentation_length; // skip 301 302 // Process FDE 303 parse_dwarf_instructions(pc_begin, pc, next_entry); 304 break; 305 } 306 } 307 308 _buf = next_entry; 309 } 310 311 return 0L; 312 }