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 bool 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 if (length == 0L) { 69 return false; 70 } 71 unsigned char *end = _buf + length; 72 73 _buf += 4; // Skip ID (This value of CIE would be always 0) 74 _buf++; // Skip version (assume to be "1") 75 76 char *augmentation_string = reinterpret_cast<char *>(_buf); 77 bool has_ehdata = (strcmp("eh", augmentation_string) == 0); 78 bool fde_encoded = (strchr(augmentation_string, 'R') != NULL); 79 _buf += strlen(augmentation_string) + 1; // includes '\0' 80 if (has_ehdata) { 81 _buf += sizeof(void *); // Skip EH data 82 } 83 84 _code_factor = read_leb(false); 85 _data_factor = static_cast<int>(read_leb(true)); 86 _return_address_reg = static_cast<enum DWARF_Register>(*_buf++); 87 88 if (fde_encoded) { 89 uintptr_t augmentation_length = read_leb(false); 90 _encoding = *_buf++; 91 } 92 93 // Clear state 94 _current_pc = 0L; 95 _cfa_reg = RSP; 96 _return_address_reg = RA; 97 _cfa_offset = 0; 98 _ra_cfa_offset = 0; 99 _bp_cfa_offset = 0; 100 _bp_offset_available = false; 101 102 parse_dwarf_instructions(0L, static_cast<uintptr_t>(-1L), end); 103 104 _buf = orig_pos; 105 return true; 106 } 107 108 void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const unsigned char *end) { 109 uintptr_t operand1; 110 _current_pc = begin; 111 112 /* for remember state */ 113 enum DWARF_Register rem_cfa_reg = MAX_VALUE; 114 int rem_cfa_offset = 0; 115 int rem_ra_cfa_offset = 0; 116 int rem_bp_cfa_offset = 0; 117 118 while ((_buf < end) && (_current_pc < pc)) { 119 unsigned char op = *_buf++; 120 unsigned char opa = op & 0x3f; 121 if (op & 0xc0) { 122 op &= 0xc0; 123 } 124 125 switch (op) { 126 case 0x0: // DW_CFA_nop 127 return; 128 case 0x01: // DW_CFA_set_loc 129 operand1 = get_decoded_value(); 130 if (_current_pc != 0L) { 131 _current_pc = operand1; 132 } 133 break; 134 case 0x0c: // DW_CFA_def_cfa 135 _cfa_reg = static_cast<enum DWARF_Register>(read_leb(false)); 136 _cfa_offset = read_leb(false); 137 break; 138 case 0x80: {// DW_CFA_offset 139 operand1 = read_leb(false); 140 enum DWARF_Register reg = static_cast<enum DWARF_Register>(opa); 141 if (reg == RBP) { 142 _bp_cfa_offset = operand1 * _data_factor; 143 _bp_offset_available = true; 144 } else if (reg == RA) { 145 _ra_cfa_offset = operand1 * _data_factor; 146 } 147 break; 148 } 149 case 0xe: // DW_CFA_def_cfa_offset 150 _cfa_offset = read_leb(false); 151 break; 152 case 0x40: // DW_CFA_advance_loc 153 if (_current_pc != 0L) { 154 _current_pc += opa * _code_factor; 155 } 156 break; 157 case 0x02: { // DW_CFA_advance_loc1 158 unsigned char ofs = *_buf++; 159 if (_current_pc != 0L) { 160 _current_pc += ofs * _code_factor; 161 } 162 break; 163 } 164 case 0x03: { // DW_CFA_advance_loc2 165 unsigned short ofs = *(reinterpret_cast<unsigned short *>(_buf)); 166 _buf += 2; 167 if (_current_pc != 0L) { 168 _current_pc += ofs * _code_factor; 169 } 170 break; 171 } 172 case 0x04: { // DW_CFA_advance_loc4 173 unsigned int ofs = *(reinterpret_cast<unsigned int *>(_buf)); 174 _buf += 4; 175 if (_current_pc != 0L) { 176 _current_pc += ofs * _code_factor; 177 } 178 break; 179 } 180 case 0x0d: {// DW_CFA_def_cfa_register 181 _cfa_reg = static_cast<enum DWARF_Register>(read_leb(false)); 182 break; 183 } 184 case 0x0a: // DW_CFA_remember_state 185 rem_cfa_reg = _cfa_reg; 186 rem_cfa_offset = _cfa_offset; 187 rem_ra_cfa_offset = _ra_cfa_offset; 188 rem_bp_cfa_offset = _bp_cfa_offset; 189 break; 190 case 0x0b: // DW_CFA_restore_state 191 _cfa_reg = rem_cfa_reg; 192 _cfa_offset = rem_cfa_offset; 193 _ra_cfa_offset = rem_ra_cfa_offset; 194 _bp_cfa_offset = rem_bp_cfa_offset; 195 break; 196 default: 197 print_debug("DWARF: Unknown opcode: 0x%x\n", op); 198 return; 199 } 200 } 201 } 202 203 /* from dwarf.c in binutils */ 204 uint32_t DwarfParser::get_decoded_value() { 205 int size; 206 uintptr_t result; 207 208 switch (_encoding & 0x7) { 209 case 0: // DW_EH_PE_absptr 210 size = sizeof(void *); 211 result = *(reinterpret_cast<uintptr_t *>(_buf)); 212 break; 213 case 2: // DW_EH_PE_udata2 214 size = 2; 215 result = *(reinterpret_cast<unsigned int *>(_buf)); 216 break; 217 case 3: // DW_EH_PE_udata4 218 size = 4; 219 result = *(reinterpret_cast<uint32_t *>(_buf)); 220 break; 221 case 4: // DW_EH_PE_udata8 222 size = 8; 223 result = *(reinterpret_cast<uint64_t *>(_buf)); 224 break; 225 default: 226 return 0; 227 } 228 229 // On x86-64, we have to handle it as 32 bit value, and it is PC relative. 230 // https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html 231 #if defined(_LP64) 232 if (size == 8) { 233 result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data); 234 size = 4; 235 } else 236 #endif 237 if ((_encoding & 0x70) == 0x10) { // 0x10 = DW_EH_PE_pcrel 238 result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data); 239 } else if (size == 2) { 240 result = static_cast<int>(result) + _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data); 241 size = 4; 242 } 243 244 _buf += size; 245 return static_cast<uint32_t>(result); 246 } 247 248 unsigned int DwarfParser::get_pc_range() { 249 int size; 250 uintptr_t result; 251 252 switch (_encoding & 0x7) { 253 case 0: // DW_EH_PE_absptr 254 size = sizeof(void *); 255 result = *(reinterpret_cast<uintptr_t *>(_buf)); 256 break; 257 case 2: // DW_EH_PE_udata2 258 size = 2; 259 result = *(reinterpret_cast<unsigned int *>(_buf)); 260 break; 261 case 3: // DW_EH_PE_udata4 262 size = 4; 263 result = *(reinterpret_cast<uint32_t *>(_buf)); 264 break; 265 case 4: // DW_EH_PE_udata8 266 size = 8; 267 result = *(reinterpret_cast<uint64_t *>(_buf)); 268 break; 269 default: 270 return 0; 271 } 272 273 // On x86-64, we have to handle it as 32 bit value, and it is PC relative. 274 // https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html 275 #if defined(_LP64) 276 if ((size == 8) || (size == 2)) { 277 size = 4; 278 } 279 #endif 280 281 _buf += size; 282 return static_cast<unsigned int>(result); 283 } 284 285 bool DwarfParser::process_dwarf(const uintptr_t pc) { 286 // https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html 287 _buf = _lib->eh_frame.data; 288 while (true) { 289 uint64_t length = get_entry_length(); 290 if (length == 0L) { 291 return false; 292 } 293 unsigned char *next_entry = _buf + length; 294 unsigned char *start_of_entry = _buf; 295 uint32_t id = *(reinterpret_cast<uint32_t *>(_buf)); 296 _buf += 4; 297 if (id != 0) { // FDE 298 uintptr_t pc_begin = get_decoded_value() + _lib->eh_frame.library_base_addr; 299 uintptr_t pc_end = pc_begin + get_pc_range(); 300 301 if ((pc >= pc_begin) && (pc < pc_end)) { 302 // Process CIE 303 if (!process_cie(start_of_entry, id)) { 304 return false; 305 } 306 307 // Skip Augumenation 308 uintptr_t augmentation_length = read_leb(false); 309 _buf += augmentation_length; // skip 310 311 // Process FDE 312 parse_dwarf_instructions(pc_begin, pc, next_entry); 313 break; 314 } 315 } 316 317 _buf = next_entry; 318 } 319 320 return true; 321 }