8234624: jstack mixed mode should refer DWARF

Reviewed-by: sspitsyn, kevinw
This commit is contained in:
Yasumasa Suenaga 2020-03-12 09:23:05 +09:00
parent 5b9a09cb9c
commit 069d9e792e
15 changed files with 1038 additions and 72 deletions

View 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();
}

View File

@ -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));
}

View 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;
}

View 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_

View File

@ -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);

View File

@ -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

View File

@ -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_

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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,

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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();
}

View File

@ -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;
}