Path: blob/master/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp
41149 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2020, NTT DATA.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#include <cstring>2627#include "dwarf.hpp"28#include "libproc_impl.h"2930/* from read_leb128() in dwarf.c in binutils */31uintptr_t DwarfParser::read_leb(bool sign) {32uintptr_t result = 0L;33unsigned char b;34unsigned int shift = 0;3536while (true) {37b = *_buf++;38result |= static_cast<uintptr_t>(b & 0x7f) << shift;39shift += 7;40if ((b & 0x80) == 0) {41break;42}43}4445if (sign && (shift < (8 * sizeof(result))) && (b & 0x40)) {46result |= static_cast<uintptr_t>(-1L) << shift;47}4849return result;50}5152uint64_t DwarfParser::get_entry_length() {53uint64_t length = *(reinterpret_cast<uint32_t *>(_buf));54_buf += 4;55if (length == 0xffffffff) {56length = *(reinterpret_cast<uint64_t *>(_buf));57_buf += 8;58}59return length;60}6162bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) {63unsigned char *orig_pos = _buf;64_buf = start_of_entry - id;6566uint64_t length = get_entry_length();67if (length == 0L) {68return false;69}70unsigned char *end = _buf + length;7172_buf += 4; // Skip ID (This value of CIE would be always 0)73_buf++; // Skip version (assume to be "1")7475char *augmentation_string = reinterpret_cast<char *>(_buf);76bool has_ehdata = (strcmp("eh", augmentation_string) == 0);77_buf += strlen(augmentation_string) + 1; // includes '\0'78if (has_ehdata) {79_buf += sizeof(void *); // Skip EH data80}8182_code_factor = read_leb(false);83_data_factor = static_cast<int>(read_leb(true));84_return_address_reg = static_cast<enum DWARF_Register>(*_buf++);8586if (strpbrk(augmentation_string, "LP") != NULL) {87// Language personality routine (P) and Language Specific Data Area (LSDA:L)88// are not supported because we need compliant Unwind Library Interface,89// but we want to unwind without it.90//91// Unwind Library Interface (SysV ABI AMD64 6.2)92// https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf93return false;94} else if (strchr(augmentation_string, 'R') != NULL) {95read_leb(false); // augmentation length96_encoding = *_buf++;97}9899// Clear state100_current_pc = 0L;101_cfa_reg = RSP;102_return_address_reg = RA;103_cfa_offset = 0;104_ra_cfa_offset = 0;105_bp_cfa_offset = 0;106_bp_offset_available = false;107108parse_dwarf_instructions(0L, static_cast<uintptr_t>(-1L), end);109110_buf = orig_pos;111return true;112}113114void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const unsigned char *end) {115uintptr_t operand1;116_current_pc = begin;117118/* for remember state */119enum DWARF_Register rem_cfa_reg = MAX_VALUE;120int rem_cfa_offset = 0;121int rem_ra_cfa_offset = 0;122int rem_bp_cfa_offset = 0;123124while ((_buf < end) && (_current_pc < pc)) {125unsigned char op = *_buf++;126unsigned char opa = op & 0x3f;127if (op & 0xc0) {128op &= 0xc0;129}130131switch (op) {132case 0x0: // DW_CFA_nop133return;134case 0x01: // DW_CFA_set_loc135operand1 = get_decoded_value();136if (_current_pc != 0L) {137_current_pc = operand1;138}139break;140case 0x0c: // DW_CFA_def_cfa141_cfa_reg = static_cast<enum DWARF_Register>(read_leb(false));142_cfa_offset = read_leb(false);143break;144case 0x80: {// DW_CFA_offset145operand1 = read_leb(false);146enum DWARF_Register reg = static_cast<enum DWARF_Register>(opa);147if (reg == RBP) {148_bp_cfa_offset = operand1 * _data_factor;149_bp_offset_available = true;150} else if (reg == RA) {151_ra_cfa_offset = operand1 * _data_factor;152}153break;154}155case 0xe: // DW_CFA_def_cfa_offset156_cfa_offset = read_leb(false);157break;158case 0x40: // DW_CFA_advance_loc159if (_current_pc != 0L) {160_current_pc += opa * _code_factor;161}162break;163case 0x02: { // DW_CFA_advance_loc1164unsigned char ofs = *_buf++;165if (_current_pc != 0L) {166_current_pc += ofs * _code_factor;167}168break;169}170case 0x03: { // DW_CFA_advance_loc2171unsigned short ofs = *(reinterpret_cast<unsigned short *>(_buf));172_buf += 2;173if (_current_pc != 0L) {174_current_pc += ofs * _code_factor;175}176break;177}178case 0x04: { // DW_CFA_advance_loc4179unsigned int ofs = *(reinterpret_cast<unsigned int *>(_buf));180_buf += 4;181if (_current_pc != 0L) {182_current_pc += ofs * _code_factor;183}184break;185}186case 0x0d: {// DW_CFA_def_cfa_register187_cfa_reg = static_cast<enum DWARF_Register>(read_leb(false));188break;189}190case 0x0a: // DW_CFA_remember_state191rem_cfa_reg = _cfa_reg;192rem_cfa_offset = _cfa_offset;193rem_ra_cfa_offset = _ra_cfa_offset;194rem_bp_cfa_offset = _bp_cfa_offset;195break;196case 0x0b: // DW_CFA_restore_state197_cfa_reg = rem_cfa_reg;198_cfa_offset = rem_cfa_offset;199_ra_cfa_offset = rem_ra_cfa_offset;200_bp_cfa_offset = rem_bp_cfa_offset;201break;202default:203print_debug("DWARF: Unknown opcode: 0x%x\n", op);204return;205}206}207}208209/* from dwarf.c in binutils */210uint32_t DwarfParser::get_decoded_value() {211int size;212uintptr_t result;213214switch (_encoding & 0x7) {215case 0: // DW_EH_PE_absptr216size = sizeof(void *);217result = *(reinterpret_cast<uintptr_t *>(_buf));218break;219case 2: // DW_EH_PE_udata2220size = 2;221result = *(reinterpret_cast<unsigned int *>(_buf));222break;223case 3: // DW_EH_PE_udata4224size = 4;225result = *(reinterpret_cast<uint32_t *>(_buf));226break;227case 4: // DW_EH_PE_udata8228size = 8;229result = *(reinterpret_cast<uint64_t *>(_buf));230break;231default:232return 0;233}234235// On x86-64, we have to handle it as 32 bit value, and it is PC relative.236// https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html237#if defined(_LP64)238if (size == 8) {239result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);240size = 4;241} else242#endif243if ((_encoding & 0x70) == 0x10) { // 0x10 = DW_EH_PE_pcrel244result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);245} else if (size == 2) {246result = static_cast<int>(result) + _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);247size = 4;248}249250_buf += size;251return static_cast<uint32_t>(result);252}253254unsigned int DwarfParser::get_pc_range() {255int size;256uintptr_t result;257258switch (_encoding & 0x7) {259case 0: // DW_EH_PE_absptr260size = sizeof(void *);261result = *(reinterpret_cast<uintptr_t *>(_buf));262break;263case 2: // DW_EH_PE_udata2264size = 2;265result = *(reinterpret_cast<unsigned int *>(_buf));266break;267case 3: // DW_EH_PE_udata4268size = 4;269result = *(reinterpret_cast<uint32_t *>(_buf));270break;271case 4: // DW_EH_PE_udata8272size = 8;273result = *(reinterpret_cast<uint64_t *>(_buf));274break;275default:276return 0;277}278279// On x86-64, we have to handle it as 32 bit value, and it is PC relative.280// https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html281#if defined(_LP64)282if ((size == 8) || (size == 2)) {283size = 4;284}285#endif286287_buf += size;288return static_cast<unsigned int>(result);289}290291bool DwarfParser::process_dwarf(const uintptr_t pc) {292// https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html293_buf = _lib->eh_frame.data;294unsigned char *end = _lib->eh_frame.data + _lib->eh_frame.size;295while (_buf <= end) {296uint64_t length = get_entry_length();297if (length == 0L) {298return false;299}300unsigned char *next_entry = _buf + length;301unsigned char *start_of_entry = _buf;302uint32_t id = *(reinterpret_cast<uint32_t *>(_buf));303_buf += 4;304if (id != 0) { // FDE305uintptr_t pc_begin = get_decoded_value() + _lib->eh_frame.library_base_addr;306uintptr_t pc_end = pc_begin + get_pc_range();307308if ((pc >= pc_begin) && (pc < pc_end)) {309// Process CIE310if (!process_cie(start_of_entry, id)) {311return false;312}313314// Skip Augumenation315uintptr_t augmentation_length = read_leb(false);316_buf += augmentation_length; // skip317318// Process FDE319parse_dwarf_instructions(pc_begin, pc, next_entry);320return true;321}322}323324_buf = next_entry;325}326327return false;328}329330331