8234624
: jstack mixed mode should refer DWARF
Reviewed-by: sspitsyn, kevinw
This commit is contained in:
parent
5b9a09cb9c
commit
069d9e792e
231
src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp
Normal file
231
src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, NTT DATA.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "dwarf.hpp"
|
||||
#include "libproc.h"
|
||||
|
||||
#define CHECK_EXCEPTION if (env->ExceptionOccurred()) { return; }
|
||||
|
||||
static jfieldID p_dwarf_context_ID = 0;
|
||||
static jint sa_RAX = -1;
|
||||
static jint sa_RDX = -1;
|
||||
static jint sa_RCX = -1;
|
||||
static jint sa_RBX = -1;
|
||||
static jint sa_RSI = -1;
|
||||
static jint sa_RDI = -1;
|
||||
static jint sa_RBP = -1;
|
||||
static jint sa_RSP = -1;
|
||||
static jint sa_R8 = -1;
|
||||
static jint sa_R9 = -1;
|
||||
static jint sa_R10 = -1;
|
||||
static jint sa_R11 = -1;
|
||||
static jint sa_R12 = -1;
|
||||
static jint sa_R13 = -1;
|
||||
static jint sa_R14 = -1;
|
||||
static jint sa_R15 = -1;
|
||||
|
||||
static jlong get_dwarf_context(JNIEnv *env, jobject obj) {
|
||||
return env->GetLongField(obj, p_dwarf_context_ID);
|
||||
}
|
||||
|
||||
#define SET_REG(env, reg, reg_cls) \
|
||||
jfieldID reg##_ID = env->GetStaticFieldID(reg_cls, #reg, "I"); \
|
||||
CHECK_EXCEPTION \
|
||||
sa_##reg = env->GetStaticIntField(reg_cls, reg##_ID); \
|
||||
CHECK_EXCEPTION
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: init0
|
||||
* Signature: ()V
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_init0
|
||||
(JNIEnv *env, jclass this_cls) {
|
||||
jclass cls = env->FindClass("sun/jvm/hotspot/debugger/linux/amd64/DwarfParser");
|
||||
CHECK_EXCEPTION
|
||||
p_dwarf_context_ID = env->GetFieldID(cls, "p_dwarf_context", "J");
|
||||
CHECK_EXCEPTION
|
||||
|
||||
jclass reg_cls = env->FindClass("sun/jvm/hotspot/debugger/amd64/AMD64ThreadContext");
|
||||
CHECK_EXCEPTION
|
||||
SET_REG(env, RAX, reg_cls);
|
||||
SET_REG(env, RDX, reg_cls);
|
||||
SET_REG(env, RCX, reg_cls);
|
||||
SET_REG(env, RBX, reg_cls);
|
||||
SET_REG(env, RSI, reg_cls);
|
||||
SET_REG(env, RDI, reg_cls);
|
||||
SET_REG(env, RBP, reg_cls);
|
||||
SET_REG(env, RSP, reg_cls);
|
||||
SET_REG(env, R8, reg_cls);
|
||||
SET_REG(env, R9, reg_cls);
|
||||
SET_REG(env, R10, reg_cls);
|
||||
SET_REG(env, R11, reg_cls);
|
||||
SET_REG(env, R12, reg_cls);
|
||||
SET_REG(env, R13, reg_cls);
|
||||
SET_REG(env, R14, reg_cls);
|
||||
SET_REG(env, R15, reg_cls);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: createDwarfContext
|
||||
* Signature: (J)J
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_createDwarfContext
|
||||
(JNIEnv *env, jclass this_cls, jlong lib) {
|
||||
jlong result = 0L;
|
||||
|
||||
DwarfParser *parser = new DwarfParser(reinterpret_cast<lib_info *>(lib));
|
||||
if (!parser->is_parseable()) {
|
||||
jclass ex_cls = env->FindClass("sun/jvm/hotspot/debugger/DebuggerException");
|
||||
env->ThrowNew(ex_cls, "DWARF not found");
|
||||
return 0L;
|
||||
}
|
||||
|
||||
return reinterpret_cast<jlong>(parser);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: destroyDwarfContext
|
||||
* Signature: (J)V
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_destroyDwarfContext
|
||||
(JNIEnv *env, jclass this_cls, jlong context) {
|
||||
DwarfParser *parser = reinterpret_cast<DwarfParser *>(context);
|
||||
delete parser;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: isIn0
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_isIn0
|
||||
(JNIEnv *env, jobject this_obj, jlong pc) {
|
||||
DwarfParser *parser = reinterpret_cast<DwarfParser *>(get_dwarf_context(env, this_obj));
|
||||
return static_cast<jboolean>(parser->is_in(pc));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: processDwarf0
|
||||
* Signature: (J)V
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_processDwarf0
|
||||
(JNIEnv *env, jobject this_obj, jlong pc) {
|
||||
DwarfParser *parser = reinterpret_cast<DwarfParser *>(get_dwarf_context(env, this_obj));
|
||||
if (!parser->process_dwarf(pc)) {
|
||||
jclass ex_cls = env->FindClass("sun/jvm/hotspot/debugger/DebuggerException");
|
||||
env->ThrowNew(ex_cls, "Could not find PC in DWARF");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: getCFARegister
|
||||
* Signature: ()I
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getCFARegister
|
||||
(JNIEnv *env, jobject this_obj) {
|
||||
DwarfParser *parser = reinterpret_cast<DwarfParser *>(get_dwarf_context(env, this_obj));
|
||||
switch (parser->get_cfa_register()) {
|
||||
case RAX: return sa_RAX;
|
||||
case RDX: return sa_RDX;
|
||||
case RCX: return sa_RCX;
|
||||
case RBX: return sa_RBX;
|
||||
case RSI: return sa_RSI;
|
||||
case RDI: return sa_RDI;
|
||||
case RBP: return sa_RBP;
|
||||
case RSP: return sa_RSP;
|
||||
case R8: return sa_R8;
|
||||
case R9: return sa_R9;
|
||||
case R10: return sa_R10;
|
||||
case R11: return sa_R11;
|
||||
case R12: return sa_R12;
|
||||
case R13: return sa_R13;
|
||||
case R14: return sa_R14;
|
||||
case R15: return sa_R15;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: getCFAOffset
|
||||
* Signature: ()I
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getCFAOffset
|
||||
(JNIEnv *env, jobject this_obj) {
|
||||
DwarfParser *parser = reinterpret_cast<DwarfParser *>(get_dwarf_context(env, this_obj));
|
||||
return parser->get_cfa_offset();
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: getReturnAddressOffsetFromCFA
|
||||
* Signature: ()I
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getReturnAddressOffsetFromCFA
|
||||
(JNIEnv *env, jobject this_obj) {
|
||||
DwarfParser *parser = reinterpret_cast<DwarfParser *>(get_dwarf_context(env, this_obj));
|
||||
return parser->get_ra_cfa_offset();
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: getBasePointerOffsetFromCFA
|
||||
* Signature: ()I
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getBasePointerOffsetFromCFA
|
||||
(JNIEnv *env, jobject this_obj) {
|
||||
DwarfParser *parser = reinterpret_cast<DwarfParser *>(get_dwarf_context(env, this_obj));
|
||||
return parser->get_bp_cfa_offset();
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
|
||||
* Method: isBPOffsetAvailable
|
||||
* Signature: ()Z
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_isBPOffsetAvailable
|
||||
(JNIEnv *env, jobject this_obj) {
|
||||
DwarfParser *parser = reinterpret_cast<DwarfParser *>(get_dwarf_context(env, this_obj));
|
||||
return parser->is_bp_offset_available();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, NTT DATA.
|
||||
* Copyright (c) 2019, 2020, NTT DATA.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -616,3 +616,15 @@ JNIEXPORT jstring JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal
|
||||
* Method: findLibPtrByAddress0
|
||||
* Signature: (J)J
|
||||
*/
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_findLibPtrByAddress0
|
||||
(JNIEnv *env, jobject this_obj, jlong pc) {
|
||||
struct ps_prochandle* ph = get_proc_handle(env, this_obj);
|
||||
return reinterpret_cast<jlong>(find_lib_by_address(ph, pc));
|
||||
}
|
||||
|
321
src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp
Normal file
321
src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, NTT DATA.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "dwarf.hpp"
|
||||
#include "libproc_impl.h"
|
||||
|
||||
/* from read_leb128() in dwarf.c in binutils */
|
||||
uintptr_t DwarfParser::read_leb(bool sign) {
|
||||
uintptr_t result = 0L;
|
||||
unsigned char b;
|
||||
unsigned int shift = 0;
|
||||
|
||||
while (true) {
|
||||
b = *_buf++;
|
||||
result |= static_cast<uintptr_t>(b & 0x7f) << shift;
|
||||
shift += 7;
|
||||
if ((b & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sign && (shift < (8 * sizeof(result))) && (b & 0x40)) {
|
||||
result |= static_cast<uintptr_t>(-1L) << shift;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t DwarfParser::get_entry_length() {
|
||||
uint64_t length = *(reinterpret_cast<uint32_t *>(_buf));
|
||||
_buf += 4;
|
||||
if (length == 0xffffffff) {
|
||||
length = *(reinterpret_cast<uint64_t *>(_buf));
|
||||
_buf += 8;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) {
|
||||
unsigned char *orig_pos = _buf;
|
||||
_buf = start_of_entry - id;
|
||||
|
||||
uint64_t length = get_entry_length();
|
||||
if (length == 0L) {
|
||||
return false;
|
||||
}
|
||||
unsigned char *end = _buf + length;
|
||||
|
||||
_buf += 4; // Skip ID (This value of CIE would be always 0)
|
||||
_buf++; // Skip version (assume to be "1")
|
||||
|
||||
char *augmentation_string = reinterpret_cast<char *>(_buf);
|
||||
bool has_ehdata = (strcmp("eh", augmentation_string) == 0);
|
||||
bool fde_encoded = (strchr(augmentation_string, 'R') != NULL);
|
||||
_buf += strlen(augmentation_string) + 1; // includes '\0'
|
||||
if (has_ehdata) {
|
||||
_buf += sizeof(void *); // Skip EH data
|
||||
}
|
||||
|
||||
_code_factor = read_leb(false);
|
||||
_data_factor = static_cast<int>(read_leb(true));
|
||||
_return_address_reg = static_cast<enum DWARF_Register>(*_buf++);
|
||||
|
||||
if (fde_encoded) {
|
||||
uintptr_t augmentation_length = read_leb(false);
|
||||
_encoding = *_buf++;
|
||||
}
|
||||
|
||||
// Clear state
|
||||
_current_pc = 0L;
|
||||
_cfa_reg = RSP;
|
||||
_return_address_reg = RA;
|
||||
_cfa_offset = 0;
|
||||
_ra_cfa_offset = 0;
|
||||
_bp_cfa_offset = 0;
|
||||
_bp_offset_available = false;
|
||||
|
||||
parse_dwarf_instructions(0L, static_cast<uintptr_t>(-1L), end);
|
||||
|
||||
_buf = orig_pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const unsigned char *end) {
|
||||
uintptr_t operand1;
|
||||
_current_pc = begin;
|
||||
|
||||
/* for remember state */
|
||||
enum DWARF_Register rem_cfa_reg = MAX_VALUE;
|
||||
int rem_cfa_offset = 0;
|
||||
int rem_ra_cfa_offset = 0;
|
||||
int rem_bp_cfa_offset = 0;
|
||||
|
||||
while ((_buf < end) && (_current_pc < pc)) {
|
||||
unsigned char op = *_buf++;
|
||||
unsigned char opa = op & 0x3f;
|
||||
if (op & 0xc0) {
|
||||
op &= 0xc0;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case 0x0: // DW_CFA_nop
|
||||
return;
|
||||
case 0x01: // DW_CFA_set_loc
|
||||
operand1 = get_decoded_value();
|
||||
if (_current_pc != 0L) {
|
||||
_current_pc = operand1;
|
||||
}
|
||||
break;
|
||||
case 0x0c: // DW_CFA_def_cfa
|
||||
_cfa_reg = static_cast<enum DWARF_Register>(read_leb(false));
|
||||
_cfa_offset = read_leb(false);
|
||||
break;
|
||||
case 0x80: {// DW_CFA_offset
|
||||
operand1 = read_leb(false);
|
||||
enum DWARF_Register reg = static_cast<enum DWARF_Register>(opa);
|
||||
if (reg == RBP) {
|
||||
_bp_cfa_offset = operand1 * _data_factor;
|
||||
_bp_offset_available = true;
|
||||
} else if (reg == RA) {
|
||||
_ra_cfa_offset = operand1 * _data_factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xe: // DW_CFA_def_cfa_offset
|
||||
_cfa_offset = read_leb(false);
|
||||
break;
|
||||
case 0x40: // DW_CFA_advance_loc
|
||||
if (_current_pc != 0L) {
|
||||
_current_pc += opa * _code_factor;
|
||||
}
|
||||
break;
|
||||
case 0x02: { // DW_CFA_advance_loc1
|
||||
unsigned char ofs = *_buf++;
|
||||
if (_current_pc != 0L) {
|
||||
_current_pc += ofs * _code_factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x03: { // DW_CFA_advance_loc2
|
||||
unsigned short ofs = *(reinterpret_cast<unsigned short *>(_buf));
|
||||
_buf += 2;
|
||||
if (_current_pc != 0L) {
|
||||
_current_pc += ofs * _code_factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x04: { // DW_CFA_advance_loc4
|
||||
unsigned int ofs = *(reinterpret_cast<unsigned int *>(_buf));
|
||||
_buf += 4;
|
||||
if (_current_pc != 0L) {
|
||||
_current_pc += ofs * _code_factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0d: {// DW_CFA_def_cfa_register
|
||||
_cfa_reg = static_cast<enum DWARF_Register>(read_leb(false));
|
||||
break;
|
||||
}
|
||||
case 0x0a: // DW_CFA_remember_state
|
||||
rem_cfa_reg = _cfa_reg;
|
||||
rem_cfa_offset = _cfa_offset;
|
||||
rem_ra_cfa_offset = _ra_cfa_offset;
|
||||
rem_bp_cfa_offset = _bp_cfa_offset;
|
||||
break;
|
||||
case 0x0b: // DW_CFA_restore_state
|
||||
_cfa_reg = rem_cfa_reg;
|
||||
_cfa_offset = rem_cfa_offset;
|
||||
_ra_cfa_offset = rem_ra_cfa_offset;
|
||||
_bp_cfa_offset = rem_bp_cfa_offset;
|
||||
break;
|
||||
default:
|
||||
print_debug("DWARF: Unknown opcode: 0x%x\n", op);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* from dwarf.c in binutils */
|
||||
uint32_t DwarfParser::get_decoded_value() {
|
||||
int size;
|
||||
uintptr_t result;
|
||||
|
||||
switch (_encoding & 0x7) {
|
||||
case 0: // DW_EH_PE_absptr
|
||||
size = sizeof(void *);
|
||||
result = *(reinterpret_cast<uintptr_t *>(_buf));
|
||||
break;
|
||||
case 2: // DW_EH_PE_udata2
|
||||
size = 2;
|
||||
result = *(reinterpret_cast<unsigned int *>(_buf));
|
||||
break;
|
||||
case 3: // DW_EH_PE_udata4
|
||||
size = 4;
|
||||
result = *(reinterpret_cast<uint32_t *>(_buf));
|
||||
break;
|
||||
case 4: // DW_EH_PE_udata8
|
||||
size = 8;
|
||||
result = *(reinterpret_cast<uint64_t *>(_buf));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// On x86-64, we have to handle it as 32 bit value, and it is PC relative.
|
||||
// https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html
|
||||
#if defined(_LP64)
|
||||
if (size == 8) {
|
||||
result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
|
||||
size = 4;
|
||||
} else
|
||||
#endif
|
||||
if ((_encoding & 0x70) == 0x10) { // 0x10 = DW_EH_PE_pcrel
|
||||
result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
|
||||
} else if (size == 2) {
|
||||
result = static_cast<int>(result) + _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
|
||||
size = 4;
|
||||
}
|
||||
|
||||
_buf += size;
|
||||
return static_cast<uint32_t>(result);
|
||||
}
|
||||
|
||||
unsigned int DwarfParser::get_pc_range() {
|
||||
int size;
|
||||
uintptr_t result;
|
||||
|
||||
switch (_encoding & 0x7) {
|
||||
case 0: // DW_EH_PE_absptr
|
||||
size = sizeof(void *);
|
||||
result = *(reinterpret_cast<uintptr_t *>(_buf));
|
||||
break;
|
||||
case 2: // DW_EH_PE_udata2
|
||||
size = 2;
|
||||
result = *(reinterpret_cast<unsigned int *>(_buf));
|
||||
break;
|
||||
case 3: // DW_EH_PE_udata4
|
||||
size = 4;
|
||||
result = *(reinterpret_cast<uint32_t *>(_buf));
|
||||
break;
|
||||
case 4: // DW_EH_PE_udata8
|
||||
size = 8;
|
||||
result = *(reinterpret_cast<uint64_t *>(_buf));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// On x86-64, we have to handle it as 32 bit value, and it is PC relative.
|
||||
// https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html
|
||||
#if defined(_LP64)
|
||||
if ((size == 8) || (size == 2)) {
|
||||
size = 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
_buf += size;
|
||||
return static_cast<unsigned int>(result);
|
||||
}
|
||||
|
||||
bool DwarfParser::process_dwarf(const uintptr_t pc) {
|
||||
// https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
|
||||
_buf = _lib->eh_frame.data;
|
||||
while (true) {
|
||||
uint64_t length = get_entry_length();
|
||||
if (length == 0L) {
|
||||
return false;
|
||||
}
|
||||
unsigned char *next_entry = _buf + length;
|
||||
unsigned char *start_of_entry = _buf;
|
||||
uint32_t id = *(reinterpret_cast<uint32_t *>(_buf));
|
||||
_buf += 4;
|
||||
if (id != 0) { // FDE
|
||||
uintptr_t pc_begin = get_decoded_value() + _lib->eh_frame.library_base_addr;
|
||||
uintptr_t pc_end = pc_begin + get_pc_range();
|
||||
|
||||
if ((pc >= pc_begin) && (pc < pc_end)) {
|
||||
// Process CIE
|
||||
if (!process_cie(start_of_entry, id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip Augumenation
|
||||
uintptr_t augmentation_length = read_leb(false);
|
||||
_buf += augmentation_length; // skip
|
||||
|
||||
// Process FDE
|
||||
parse_dwarf_instructions(pc_begin, pc, next_entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_buf = next_entry;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
116
src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp
Normal file
116
src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, NTT DATA.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DWARF_HPP_
|
||||
#define _DWARF_HPP_
|
||||
|
||||
#include "libproc_impl.h"
|
||||
|
||||
/*
|
||||
* from System V Application Binary Interface
|
||||
* AMD64 Architecture Processor Supplement
|
||||
* Figure 3.38: DWARF Register Number Mapping
|
||||
* https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf
|
||||
*/
|
||||
enum DWARF_Register {
|
||||
RAX,
|
||||
RDX,
|
||||
RCX,
|
||||
RBX,
|
||||
RSI,
|
||||
RDI,
|
||||
RBP,
|
||||
RSP,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15,
|
||||
RA,
|
||||
MAX_VALUE
|
||||
};
|
||||
|
||||
/*
|
||||
* DwarfParser finds out CFA (Canonical Frame Address) from DWARF in ELF binary.
|
||||
* Also Return Address (RA) and Base Pointer (BP) are calculated from CFA.
|
||||
*/
|
||||
class DwarfParser {
|
||||
private:
|
||||
const lib_info *_lib;
|
||||
unsigned char *_buf;
|
||||
unsigned char _encoding;
|
||||
enum DWARF_Register _cfa_reg;
|
||||
enum DWARF_Register _return_address_reg;
|
||||
unsigned int _code_factor;
|
||||
int _data_factor;
|
||||
|
||||
uintptr_t _current_pc;
|
||||
int _cfa_offset;
|
||||
int _ra_cfa_offset;
|
||||
int _bp_cfa_offset;
|
||||
bool _bp_offset_available;
|
||||
|
||||
uintptr_t read_leb(bool sign);
|
||||
uint64_t get_entry_length();
|
||||
bool process_cie(unsigned char *start_of_entry, uint32_t id);
|
||||
void parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const unsigned char *end);
|
||||
uint32_t get_decoded_value();
|
||||
unsigned int get_pc_range();
|
||||
|
||||
public:
|
||||
DwarfParser(lib_info *lib) : _lib(lib),
|
||||
_buf(NULL),
|
||||
_encoding(0),
|
||||
_cfa_reg(RSP),
|
||||
_return_address_reg(RA),
|
||||
_code_factor(0),
|
||||
_data_factor(0),
|
||||
_current_pc(0L),
|
||||
_cfa_offset(0),
|
||||
_ra_cfa_offset(0),
|
||||
_bp_cfa_offset(0),
|
||||
_bp_offset_available(false) {};
|
||||
|
||||
~DwarfParser() {}
|
||||
bool process_dwarf(const uintptr_t pc);
|
||||
enum DWARF_Register get_cfa_register() { return _cfa_reg; }
|
||||
int get_cfa_offset() { return _cfa_offset; }
|
||||
int get_ra_cfa_offset() { return _ra_cfa_offset; }
|
||||
int get_bp_cfa_offset() { return _bp_cfa_offset; }
|
||||
bool is_bp_offset_available() { return _bp_offset_available; }
|
||||
|
||||
bool is_in(long pc) {
|
||||
return (_lib->exec_start <= pc) && (pc < _lib->exec_end);
|
||||
}
|
||||
|
||||
bool is_parseable() {
|
||||
return _lib->eh_frame.data != NULL;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_DWARF_HPP_
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -54,6 +54,7 @@ typedef int bool;
|
||||
#endif
|
||||
|
||||
struct ps_prochandle;
|
||||
struct lib_info;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -99,6 +100,9 @@ uintptr_t get_lib_base(struct ps_prochandle* ph, int index);
|
||||
// returns true if given library is found in lib list
|
||||
bool find_lib(struct ps_prochandle* ph, const char *lib_name);
|
||||
|
||||
// returns lib which contains pc
|
||||
struct lib_info *find_lib_by_address(struct ps_prochandle* ph, uintptr_t pc);
|
||||
|
||||
// symbol lookup
|
||||
uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name,
|
||||
const char* sym_name);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <sys/procfs.h>
|
||||
#include "libproc_impl.h"
|
||||
#include "proc_service.h"
|
||||
#include "salibelf.h"
|
||||
|
||||
#define SA_ALTROOT "SA_ALTROOT"
|
||||
|
||||
@ -127,6 +128,7 @@ static void destroy_lib_info(struct ps_prochandle* ph) {
|
||||
if (lib->symtab) {
|
||||
destroy_symtab(lib->symtab);
|
||||
}
|
||||
free(lib->eh_frame.data);
|
||||
free(lib);
|
||||
lib = next;
|
||||
}
|
||||
@ -157,6 +159,81 @@ lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t
|
||||
return add_lib_info_fd(ph, libname, -1, base);
|
||||
}
|
||||
|
||||
static bool fill_instr_info(lib_info* lib) {
|
||||
off_t current_pos;
|
||||
ELF_EHDR ehdr;
|
||||
ELF_PHDR* phbuf = NULL;
|
||||
ELF_PHDR* ph = NULL;
|
||||
int cnt;
|
||||
long align = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
current_pos = lseek(lib->fd, (off_t)0L, SEEK_CUR);
|
||||
lseek(lib->fd, (off_t)0L, SEEK_SET);
|
||||
read_elf_header(lib->fd, &ehdr);
|
||||
if ((phbuf = read_program_header_table(lib->fd, &ehdr)) == NULL) {
|
||||
lseek(lib->fd, current_pos, SEEK_SET);
|
||||
return false;
|
||||
}
|
||||
|
||||
lib->exec_start = (uintptr_t)-1L;
|
||||
lib->exec_end = (uintptr_t)-1L;
|
||||
for (ph = phbuf, cnt = 0; cnt < ehdr.e_phnum; cnt++, ph++) {
|
||||
if ((ph->p_type == PT_LOAD) && (ph->p_flags & PF_X)) {
|
||||
print_debug("[%d] vaddr = 0x%lx, memsz = 0x%lx, filesz = 0x%lx\n", cnt, ph->p_vaddr, ph->p_memsz, ph->p_filesz);
|
||||
if ((lib->exec_start == -1L) || (lib->exec_start > ph->p_vaddr)) {
|
||||
lib->exec_start = ph->p_vaddr;
|
||||
}
|
||||
if ((lib->exec_end == (uintptr_t)-1L) || (lib->exec_end < (ph->p_vaddr + ph->p_memsz))) {
|
||||
lib->exec_end = ph->p_vaddr + ph->p_memsz;
|
||||
}
|
||||
align = ph->p_align;
|
||||
}
|
||||
}
|
||||
|
||||
free(phbuf);
|
||||
lseek(lib->fd, current_pos, SEEK_SET);
|
||||
|
||||
if ((lib->exec_start == -1L) || (lib->exec_end == -1L)) {
|
||||
return false;
|
||||
} else {
|
||||
lib->exec_start = (lib->exec_start + lib->base) & ~(align - 1);
|
||||
lib->exec_end = (lib->exec_end + lib->base + align) & ~(align - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool read_eh_frame(struct ps_prochandle* ph, lib_info* lib) {
|
||||
off_t current_pos = -1;
|
||||
ELF_EHDR ehdr;
|
||||
ELF_SHDR* shbuf = NULL;
|
||||
ELF_SHDR* sh = NULL;
|
||||
char* strtab = NULL;
|
||||
void* result = NULL;
|
||||
int cnt;
|
||||
|
||||
current_pos = lseek(lib->fd, (off_t)0L, SEEK_CUR);
|
||||
lseek(lib->fd, (off_t)0L, SEEK_SET);
|
||||
|
||||
read_elf_header(lib->fd, &ehdr);
|
||||
shbuf = read_section_header_table(lib->fd, &ehdr);
|
||||
strtab = read_section_data(lib->fd, &ehdr, &shbuf[ehdr.e_shstrndx]);
|
||||
|
||||
for (cnt = 0, sh = shbuf; cnt < ehdr.e_shnum; cnt++, sh++) {
|
||||
if (strcmp(".eh_frame", sh->sh_name + strtab) == 0) {
|
||||
lib->eh_frame.library_base_addr = lib->base;
|
||||
lib->eh_frame.v_addr = sh->sh_addr;
|
||||
lib->eh_frame.data = read_section_data(lib->fd, &ehdr, sh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(strtab);
|
||||
free(shbuf);
|
||||
lseek(lib->fd, current_pos, SEEK_SET);
|
||||
return lib->eh_frame.data != NULL;
|
||||
}
|
||||
|
||||
lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base) {
|
||||
lib_info* newlib;
|
||||
|
||||
@ -197,6 +274,14 @@ lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd,
|
||||
print_debug("symbol table build failed for %s\n", newlib->name);
|
||||
}
|
||||
|
||||
if (fill_instr_info(newlib)) {
|
||||
if (!read_eh_frame(ph, newlib)) {
|
||||
print_debug("Could not find .eh_frame section in %s\n", newlib->name);
|
||||
}
|
||||
} else {
|
||||
print_debug("Could not find executable section in %s\n", newlib->name);
|
||||
}
|
||||
|
||||
// even if symbol table building fails, we add the lib_info.
|
||||
// This is because we may need to read from the ELF file for core file
|
||||
// address read functionality. lookup_symbol checks for NULL symtab.
|
||||
@ -356,6 +441,17 @@ bool find_lib(struct ps_prochandle* ph, const char *lib_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct lib_info *find_lib_by_address(struct ps_prochandle* ph, uintptr_t pc) {
|
||||
lib_info *p = ph->libs;
|
||||
while (p) {
|
||||
if ((p->exec_start <= pc) && (pc < p->exec_end)) {
|
||||
return p;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// proc service functions
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -34,10 +34,20 @@
|
||||
|
||||
#define BUF_SIZE (PATH_MAX + NAME_MAX + 1)
|
||||
|
||||
// .eh_frame data
|
||||
typedef struct eh_frame_info {
|
||||
uintptr_t library_base_addr;
|
||||
uintptr_t v_addr;
|
||||
unsigned char* data;
|
||||
} eh_frame_info;
|
||||
|
||||
// list of shared objects
|
||||
typedef struct lib_info {
|
||||
char name[BUF_SIZE];
|
||||
uintptr_t base;
|
||||
uintptr_t exec_start;
|
||||
uintptr_t exec_end;
|
||||
eh_frame_info eh_frame;
|
||||
struct symtab* symtab;
|
||||
int fd; // file descriptor for lib
|
||||
struct lib_info* next;
|
||||
@ -101,6 +111,10 @@ struct ps_prochandle {
|
||||
struct core_data* core; // data only used for core dumps, NULL for process
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int pathmap_open(const char* name);
|
||||
|
||||
void print_debug(const char* format,...);
|
||||
@ -123,4 +137,8 @@ thread_info* add_thread_info(struct ps_prochandle* ph, lwpid_t lwp_id);
|
||||
// a test for ELF signature without using libelf
|
||||
bool is_elf_file(int fd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //_LIBPROC_IMPL_H_
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -124,3 +124,32 @@ quit:
|
||||
if (phbuf) free(phbuf);
|
||||
return baseaddr;
|
||||
}
|
||||
|
||||
struct elf_section *find_section_by_name(char *name,
|
||||
int fd,
|
||||
ELF_EHDR *ehdr,
|
||||
struct elf_section *scn_cache) {
|
||||
char *strtab;
|
||||
int cnt;
|
||||
int strtab_size;
|
||||
|
||||
// Section cache have to already contain data for e_shstrndx section.
|
||||
// If it's not true - elf file is broken, so just bail out
|
||||
if (scn_cache[ehdr->e_shstrndx].c_data == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strtab = scn_cache[ehdr->e_shstrndx].c_data;
|
||||
strtab_size = scn_cache[ehdr->e_shstrndx].c_shdr->sh_size;
|
||||
|
||||
for (cnt = 0; cnt < ehdr->e_shnum; ++cnt) {
|
||||
if (scn_cache[cnt].c_shdr->sh_name < strtab_size) {
|
||||
if (strcmp(scn_cache[cnt].c_shdr->sh_name + strtab, name) == 0) {
|
||||
scn_cache[cnt].c_data = read_section_data(fd, ehdr, scn_cache[cnt].c_shdr);
|
||||
return &scn_cache[cnt];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,6 +29,11 @@
|
||||
#include "elfmacros.h"
|
||||
#include "libproc_impl.h"
|
||||
|
||||
struct elf_section {
|
||||
ELF_SHDR *c_shdr;
|
||||
void *c_data;
|
||||
};
|
||||
|
||||
// read ELF file header.
|
||||
int read_elf_header(int fd, ELF_EHDR* ehdr);
|
||||
|
||||
@ -49,4 +54,10 @@ void* read_section_data(int fd, ELF_EHDR* ehdr, ELF_SHDR* shdr);
|
||||
|
||||
// find the base address at which the library wants to load itself
|
||||
uintptr_t find_base_address(int fd, ELF_EHDR* ehdr);
|
||||
|
||||
// Find an ELF section.
|
||||
struct elf_section *find_section_by_name(char *name,
|
||||
int fd,
|
||||
ELF_EHDR *ehdr,
|
||||
struct elf_section *scn_cache);
|
||||
#endif /* _SALIBELF_H_ */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,11 +35,6 @@
|
||||
// functions for symbol lookups
|
||||
// ----------------------------------------------------
|
||||
|
||||
struct elf_section {
|
||||
ELF_SHDR *c_shdr;
|
||||
void *c_data;
|
||||
};
|
||||
|
||||
struct elf_symbol {
|
||||
char *name;
|
||||
uintptr_t offset;
|
||||
@ -158,37 +153,6 @@ open_debug_file (const char *pathname, unsigned int crc)
|
||||
}
|
||||
}
|
||||
|
||||
/* Find an ELF section. */
|
||||
static struct elf_section *find_section_by_name(char *name,
|
||||
int fd,
|
||||
ELF_EHDR *ehdr,
|
||||
struct elf_section *scn_cache)
|
||||
{
|
||||
char *strtab;
|
||||
int cnt;
|
||||
int strtab_size;
|
||||
|
||||
// Section cache have to already contain data for e_shstrndx section.
|
||||
// If it's not true - elf file is broken, so just bail out
|
||||
if (scn_cache[ehdr->e_shstrndx].c_data == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strtab = scn_cache[ehdr->e_shstrndx].c_data;
|
||||
strtab_size = scn_cache[ehdr->e_shstrndx].c_shdr->sh_size;
|
||||
|
||||
for (cnt = 0; cnt < ehdr->e_shnum; ++cnt) {
|
||||
if (scn_cache[cnt].c_shdr->sh_name < strtab_size) {
|
||||
if (strcmp(scn_cache[cnt].c_shdr->sh_name + strtab, name) == 0) {
|
||||
scn_cache[cnt].c_data = read_section_data(fd, ehdr, scn_cache[cnt].c_shdr);
|
||||
return &scn_cache[cnt];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Look for a ".gnu_debuglink" section. If one exists, try to open a
|
||||
suitable debuginfo file. */
|
||||
static int open_file_from_debug_link(const char *name,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, Red Hat Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -90,11 +90,9 @@ class LinuxCDebugger implements CDebugger {
|
||||
return new LinuxX86CFrame(dbg, ebp, pc);
|
||||
} else if (cpu.equals("amd64")) {
|
||||
AMD64ThreadContext context = (AMD64ThreadContext) thread.getContext();
|
||||
Address rbp = context.getRegisterAsAddress(AMD64ThreadContext.RBP);
|
||||
if (rbp == null) return null;
|
||||
Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP);
|
||||
if (pc == null) return null;
|
||||
return new LinuxAMD64CFrame(dbg, rbp, pc);
|
||||
return LinuxAMD64CFrame.getTopFrame(dbg, pc, context);
|
||||
} else if (cpu.equals("sparc")) {
|
||||
SPARCThreadContext context = (SPARCThreadContext) thread.getContext();
|
||||
Address sp = context.getRegisterAsAddress(SPARCThreadContext.R_SP);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -52,6 +52,7 @@ public interface LinuxDebugger extends JVMDebugger {
|
||||
public long[] getThreadIntegerRegisterSet(int lwp_id) throws DebuggerException;
|
||||
public long getAddressValue(Address addr) throws DebuggerException;
|
||||
public Address newAddress(long value) throws DebuggerException;
|
||||
public Address findLibPtrByAddress(Address pc);
|
||||
|
||||
// For LinuxCDebugger
|
||||
public List getThreadList();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -121,6 +121,15 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger {
|
||||
@Override
|
||||
public native String demangle(String sym);
|
||||
|
||||
public native long findLibPtrByAddress0(long pc);
|
||||
|
||||
@Override
|
||||
public Address findLibPtrByAddress(Address pc) {
|
||||
long ptr = findLibPtrByAddress0(pc.asLongValue());
|
||||
return (ptr == 0L) ? null
|
||||
: new LinuxAddress(this, ptr);
|
||||
}
|
||||
|
||||
// Note on Linux threads are really processes. When target process is
|
||||
// attached by a serviceability agent thread, only that thread can do
|
||||
// ptrace operations on the target. This is because from kernel's point
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, NTT DATA.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package sun.jvm.hotspot.debugger.linux.amd64;
|
||||
|
||||
import java.lang.ref.Cleaner;
|
||||
import sun.jvm.hotspot.debugger.Address;
|
||||
import sun.jvm.hotspot.debugger.DebuggerException;
|
||||
|
||||
public class DwarfParser {
|
||||
private final long p_dwarf_context; // native dwarf context handle
|
||||
|
||||
private static native void init0();
|
||||
private static native long createDwarfContext(long lib);
|
||||
private static native void destroyDwarfContext(long context);
|
||||
private native boolean isIn0(long pc);
|
||||
|
||||
static {
|
||||
init0();
|
||||
}
|
||||
|
||||
public DwarfParser(Address lib) {
|
||||
p_dwarf_context = createDwarfContext(lib.asLongValue());
|
||||
|
||||
if (p_dwarf_context == 0L) {
|
||||
throw new DebuggerException("Could not create DWARF context");
|
||||
}
|
||||
|
||||
Cleaner.create()
|
||||
.register(this, () -> DwarfParser.destroyDwarfContext(p_dwarf_context));
|
||||
}
|
||||
|
||||
public boolean isIn(Address pc) {
|
||||
return isIn0(pc.asLongValue());
|
||||
}
|
||||
|
||||
private native void processDwarf0(long pc);
|
||||
|
||||
public void processDwarf(Address pc) {
|
||||
processDwarf0(pc.asLongValue());
|
||||
}
|
||||
|
||||
public native int getCFARegister();
|
||||
public native int getCFAOffset();
|
||||
public native int getReturnAddressOffsetFromCFA();
|
||||
public native int getBasePointerOffsetFromCFA();
|
||||
public native boolean isBPOffsetAvailable();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,11 +31,36 @@ import sun.jvm.hotspot.debugger.cdbg.*;
|
||||
import sun.jvm.hotspot.debugger.cdbg.basic.*;
|
||||
|
||||
final public class LinuxAMD64CFrame extends BasicCFrame {
|
||||
public LinuxAMD64CFrame(LinuxDebugger dbg, Address rbp, Address rip) {
|
||||
|
||||
public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, Address rip, ThreadContext context) {
|
||||
Address libptr = dbg.findLibPtrByAddress(rip);
|
||||
Address cfa = context.getRegisterAsAddress(AMD64ThreadContext.RBP);
|
||||
DwarfParser dwarf = null;
|
||||
|
||||
if (libptr != null) { // Native frame
|
||||
try {
|
||||
dwarf = new DwarfParser(libptr);
|
||||
dwarf.processDwarf(rip);
|
||||
cfa = ((dwarf.getCFARegister() == AMD64ThreadContext.RBP) &&
|
||||
!dwarf.isBPOffsetAvailable())
|
||||
? context.getRegisterAsAddress(AMD64ThreadContext.RBP)
|
||||
: context.getRegisterAsAddress(dwarf.getCFARegister())
|
||||
.addOffsetTo(dwarf.getCFAOffset());
|
||||
} catch (DebuggerException e) {
|
||||
// Bail out to Java frame case
|
||||
}
|
||||
}
|
||||
|
||||
return (cfa == null) ? null
|
||||
: new LinuxAMD64CFrame(dbg, cfa, rip, dwarf);
|
||||
}
|
||||
|
||||
private LinuxAMD64CFrame(LinuxDebugger dbg, Address cfa, Address rip, DwarfParser dwarf) {
|
||||
super(dbg.getCDebugger());
|
||||
this.rbp = rbp;
|
||||
this.cfa = cfa;
|
||||
this.rip = rip;
|
||||
this.dbg = dbg;
|
||||
this.dwarf = dwarf;
|
||||
}
|
||||
|
||||
// override base class impl to avoid ELF parsing
|
||||
@ -49,36 +74,97 @@ final public class LinuxAMD64CFrame extends BasicCFrame {
|
||||
}
|
||||
|
||||
public Address localVariableBase() {
|
||||
return rbp;
|
||||
return cfa;
|
||||
}
|
||||
|
||||
private Address getNextPC(boolean useDwarf) {
|
||||
try {
|
||||
long offs = useDwarf ? dwarf.getReturnAddressOffsetFromCFA()
|
||||
: ADDRESS_SIZE;
|
||||
return cfa.getAddressAt(offs);
|
||||
} catch (UnmappedAddressException | UnalignedAddressException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidFrame(Address nextCFA, ThreadContext context) {
|
||||
return (nextCFA != null) &&
|
||||
!nextCFA.lessThan(context.getRegisterAsAddress(AMD64ThreadContext.RSP));
|
||||
}
|
||||
|
||||
private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context) {
|
||||
Address nextCFA;
|
||||
|
||||
if (nextDwarf == null) { // Next frame is Java
|
||||
nextCFA = (dwarf == null) ? cfa.getAddressAt(0) // Current frame is Java (Use RBP)
|
||||
: cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()); // Current frame is Native
|
||||
} else { // Next frame is Native
|
||||
if (dwarf == null) { // Current frame is Java (Use RBP)
|
||||
nextCFA = cfa.getAddressAt(0);
|
||||
} else { // Current frame is Native
|
||||
int nextCFAReg = nextDwarf.getCFARegister();
|
||||
if (!dwarf.isBPOffsetAvailable() && // Use RBP as CFA
|
||||
(nextCFAReg == AMD64ThreadContext.RBP) &&
|
||||
(nextCFAReg != dwarf.getCFARegister())) {
|
||||
nextCFA = context.getRegisterAsAddress(AMD64ThreadContext.RBP);
|
||||
if (nextCFA == null) {
|
||||
return null;
|
||||
}
|
||||
nextCFA = nextCFA.getAddressAt(0);
|
||||
} else {
|
||||
nextCFA = cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA());
|
||||
}
|
||||
}
|
||||
if (nextCFA != null) {
|
||||
nextCFA = nextCFA.addOffsetTo(-nextDwarf.getBasePointerOffsetFromCFA());
|
||||
}
|
||||
}
|
||||
|
||||
return isValidFrame(nextCFA, context) ? nextCFA : null;
|
||||
}
|
||||
|
||||
private DwarfParser getNextDwarf(Address nextPC) {
|
||||
DwarfParser nextDwarf = null;
|
||||
|
||||
if ((dwarf != null) && dwarf.isIn(nextPC)) {
|
||||
nextDwarf = dwarf;
|
||||
} else {
|
||||
Address libptr = dbg.findLibPtrByAddress(nextPC);
|
||||
if (libptr != null) {
|
||||
try {
|
||||
nextDwarf = new DwarfParser(libptr);
|
||||
} catch (DebuggerException e) {
|
||||
// Bail out to Java frame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextDwarf != null) {
|
||||
nextDwarf.processDwarf(nextPC);
|
||||
}
|
||||
|
||||
return nextDwarf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CFrame sender(ThreadProxy thread) {
|
||||
AMD64ThreadContext context = (AMD64ThreadContext) thread.getContext();
|
||||
Address rsp = context.getRegisterAsAddress(AMD64ThreadContext.RSP);
|
||||
ThreadContext context = thread.getContext();
|
||||
|
||||
if ( (rbp == null) || rbp.lessThan(rsp) ) {
|
||||
return null;
|
||||
}
|
||||
Address nextPC = getNextPC(dwarf != null);
|
||||
if (nextPC == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check alignment of rbp
|
||||
if ( dbg.getAddressValue(rbp) % ADDRESS_SIZE != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Address nextRBP = rbp.getAddressAt( 0 * ADDRESS_SIZE);
|
||||
if (nextRBP == null || nextRBP.lessThanOrEqual(rbp)) {
|
||||
return null;
|
||||
}
|
||||
Address nextPC = rbp.getAddressAt( 1 * ADDRESS_SIZE);
|
||||
if (nextPC == null) {
|
||||
return null;
|
||||
}
|
||||
return new LinuxAMD64CFrame(dbg, nextRBP, nextPC);
|
||||
DwarfParser nextDwarf = getNextDwarf(nextPC);
|
||||
Address nextCFA = getNextCFA(nextDwarf, context);
|
||||
return isValidFrame(nextCFA, context) ? new LinuxAMD64CFrame(dbg, nextCFA, nextPC, nextDwarf)
|
||||
: null;
|
||||
}
|
||||
|
||||
// package/class internals only
|
||||
private static final int ADDRESS_SIZE = 8;
|
||||
private Address rip;
|
||||
private Address rbp;
|
||||
private Address cfa;
|
||||
private LinuxDebugger dbg;
|
||||
private DwarfParser dwarf;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user