提交 b8abca32 编写于 作者: I iveresov

Merge

...@@ -53,6 +53,9 @@ sun.jvm.hotspot.code \ ...@@ -53,6 +53,9 @@ sun.jvm.hotspot.code \
sun.jvm.hotspot.compiler \ sun.jvm.hotspot.compiler \
sun.jvm.hotspot.debugger \ sun.jvm.hotspot.debugger \
sun.jvm.hotspot.debugger.amd64 \ sun.jvm.hotspot.debugger.amd64 \
sun.jvm.hotspot.debugger.bsd \
sun.jvm.hotspot.debugger.bsd.amd64 \
sun.jvm.hotspot.debugger.bsd.x86 \
sun.jvm.hotspot.debugger.cdbg \ sun.jvm.hotspot.debugger.cdbg \
sun.jvm.hotspot.debugger.cdbg.basic \ sun.jvm.hotspot.debugger.cdbg.basic \
sun.jvm.hotspot.debugger.cdbg.basic.amd64 \ sun.jvm.hotspot.debugger.cdbg.basic.amd64 \
...@@ -93,6 +96,9 @@ sun.jvm.hotspot.oops \ ...@@ -93,6 +96,9 @@ sun.jvm.hotspot.oops \
sun.jvm.hotspot.prims \ sun.jvm.hotspot.prims \
sun.jvm.hotspot.runtime \ sun.jvm.hotspot.runtime \
sun.jvm.hotspot.runtime.amd64 \ sun.jvm.hotspot.runtime.amd64 \
sun.jvm.hotspot.runtime.bsd \
sun.jvm.hotspot.runtime.bsd_amd64 \
sun.jvm.hotspot.runtime.bsd_x86 \
sun.jvm.hotspot.runtime.ia64 \ sun.jvm.hotspot.runtime.ia64 \
sun.jvm.hotspot.runtime.linux \ sun.jvm.hotspot.runtime.linux \
sun.jvm.hotspot.runtime.linux_amd64 \ sun.jvm.hotspot.runtime.linux_amd64 \
...@@ -143,6 +149,9 @@ sun/jvm/hotspot/code/*.java \ ...@@ -143,6 +149,9 @@ sun/jvm/hotspot/code/*.java \
sun/jvm/hotspot/compiler/*.java \ sun/jvm/hotspot/compiler/*.java \
sun/jvm/hotspot/debugger/*.java \ sun/jvm/hotspot/debugger/*.java \
sun/jvm/hotspot/debugger/amd64/*.java \ sun/jvm/hotspot/debugger/amd64/*.java \
sun/jvm/hotspot/debugger/bsd/*.java \
sun/jvm/hotspot/debugger/bsd/amd64/*.java \
sun/jvm/hotspot/debugger/bsd/x86/*.java \
sun/jvm/hotspot/debugger/cdbg/*.java \ sun/jvm/hotspot/debugger/cdbg/*.java \
sun/jvm/hotspot/debugger/cdbg/basic/*.java \ sun/jvm/hotspot/debugger/cdbg/basic/*.java \
sun/jvm/hotspot/debugger/cdbg/basic/amd64/*.java \ sun/jvm/hotspot/debugger/cdbg/basic/amd64/*.java \
...@@ -176,6 +185,9 @@ sun/jvm/hotspot/opto/*.java \ ...@@ -176,6 +185,9 @@ sun/jvm/hotspot/opto/*.java \
sun/jvm/hotspot/prims/*.java \ sun/jvm/hotspot/prims/*.java \
sun/jvm/hotspot/runtime/*.java \ sun/jvm/hotspot/runtime/*.java \
sun/jvm/hotspot/runtime/amd64/*.java \ sun/jvm/hotspot/runtime/amd64/*.java \
sun/jvm/hotspot/runtime/bsd/*.java \
sun/jvm/hotspot/runtime/bsd_amd64/*.java \
sun/jvm/hotspot/runtime/bsd_x86/*.java \
sun/jvm/hotspot/runtime/ia64/*.java \ sun/jvm/hotspot/runtime/ia64/*.java \
sun/jvm/hotspot/runtime/linux/*.java \ sun/jvm/hotspot/runtime/linux/*.java \
sun/jvm/hotspot/runtime/linux_amd64/*.java \ sun/jvm/hotspot/runtime/linux_amd64/*.java \
......
/*
* Copyright (c) 2002, 2007, 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
* 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 <stdlib.h>
#include <jni.h>
#include "libproc.h"
#if defined(x86_64) && !defined(amd64)
#define amd64 1
#endif
#ifdef i386
#include "sun_jvm_hotspot_debugger_x86_X86ThreadContext.h"
#endif
#ifdef amd64
#include "sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext.h"
#endif
#if defined(sparc) || defined(sparcv9)
#include "sun_jvm_hotspot_debugger_sparc_SPARCThreadContext.h"
#endif
static jfieldID p_ps_prochandle_ID = 0;
static jfieldID threadList_ID = 0;
static jfieldID loadObjectList_ID = 0;
static jmethodID createClosestSymbol_ID = 0;
static jmethodID createLoadObject_ID = 0;
static jmethodID getThreadForThreadId_ID = 0;
static jmethodID listAdd_ID = 0;
#define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; }
#define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;}
#define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; }
#define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;}
static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) {
(*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg);
}
static struct ps_prochandle* get_proc_handle(JNIEnv* env, jobject this_obj) {
jlong ptr = (*env)->GetLongField(env, this_obj, p_ps_prochandle_ID);
return (struct ps_prochandle*)(intptr_t)ptr;
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: init0
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0
(JNIEnv *env, jclass cls) {
jclass listClass;
if (init_libproc(getenv("LIBSAPROC_DEBUG") != NULL) != true) {
THROW_NEW_DEBUGGER_EXCEPTION("can't initialize libproc");
}
// fields we use
p_ps_prochandle_ID = (*env)->GetFieldID(env, cls, "p_ps_prochandle", "J");
CHECK_EXCEPTION;
threadList_ID = (*env)->GetFieldID(env, cls, "threadList", "Ljava/util/List;");
CHECK_EXCEPTION;
loadObjectList_ID = (*env)->GetFieldID(env, cls, "loadObjectList", "Ljava/util/List;");
CHECK_EXCEPTION;
// methods we use
createClosestSymbol_ID = (*env)->GetMethodID(env, cls, "createClosestSymbol",
"(Ljava/lang/String;J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;");
CHECK_EXCEPTION;
createLoadObject_ID = (*env)->GetMethodID(env, cls, "createLoadObject",
"(Ljava/lang/String;JJ)Lsun/jvm/hotspot/debugger/cdbg/LoadObject;");
CHECK_EXCEPTION;
getThreadForThreadId_ID = (*env)->GetMethodID(env, cls, "getThreadForThreadId",
"(J)Lsun/jvm/hotspot/debugger/ThreadProxy;");
CHECK_EXCEPTION;
// java.util.List method we call
listClass = (*env)->FindClass(env, "java/util/List");
CHECK_EXCEPTION;
listAdd_ID = (*env)->GetMethodID(env, listClass, "add", "(Ljava/lang/Object;)Z");
CHECK_EXCEPTION;
}
JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getAddressSize
(JNIEnv *env, jclass cls)
{
#ifdef _LP64
return 8;
#else
return 4;
#endif
}
static void fillThreadsAndLoadObjects(JNIEnv* env, jobject this_obj, struct ps_prochandle* ph) {
int n = 0, i = 0;
// add threads
n = get_num_threads(ph);
for (i = 0; i < n; i++) {
jobject thread;
jobject threadList;
lwpid_t lwpid;
lwpid = get_lwp_id(ph, i);
thread = (*env)->CallObjectMethod(env, this_obj, getThreadForThreadId_ID,
(jlong)lwpid);
CHECK_EXCEPTION;
threadList = (*env)->GetObjectField(env, this_obj, threadList_ID);
CHECK_EXCEPTION;
(*env)->CallBooleanMethod(env, threadList, listAdd_ID, thread);
CHECK_EXCEPTION;
}
// add load objects
n = get_num_libs(ph);
for (i = 0; i < n; i++) {
uintptr_t base;
const char* name;
jobject loadObject;
jobject loadObjectList;
base = get_lib_base(ph, i);
name = get_lib_name(ph, i);
loadObject = (*env)->CallObjectMethod(env, this_obj, createLoadObject_ID,
(*env)->NewStringUTF(env, name), (jlong)0, (jlong)base);
CHECK_EXCEPTION;
loadObjectList = (*env)->GetObjectField(env, this_obj, loadObjectList_ID);
CHECK_EXCEPTION;
(*env)->CallBooleanMethod(env, loadObjectList, listAdd_ID, loadObject);
CHECK_EXCEPTION;
}
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: attach0
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I
(JNIEnv *env, jobject this_obj, jint jpid) {
struct ps_prochandle* ph;
if ( (ph = Pgrab(jpid)) == NULL) {
THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process");
}
(*env)->SetLongField(env, this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph);
fillThreadsAndLoadObjects(env, this_obj, ph);
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: attach0
* Signature: (Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2
(JNIEnv *env, jobject this_obj, jstring execName, jstring coreName) {
const char *execName_cstr;
const char *coreName_cstr;
jboolean isCopy;
struct ps_prochandle* ph;
execName_cstr = (*env)->GetStringUTFChars(env, execName, &isCopy);
CHECK_EXCEPTION;
coreName_cstr = (*env)->GetStringUTFChars(env, coreName, &isCopy);
CHECK_EXCEPTION;
if ( (ph = Pgrab_core(execName_cstr, coreName_cstr)) == NULL) {
(*env)->ReleaseStringUTFChars(env, execName, execName_cstr);
(*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr);
THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the core file");
}
(*env)->SetLongField(env, this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph);
(*env)->ReleaseStringUTFChars(env, execName, execName_cstr);
(*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr);
fillThreadsAndLoadObjects(env, this_obj, ph);
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: detach0
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0
(JNIEnv *env, jobject this_obj) {
struct ps_prochandle* ph = get_proc_handle(env, this_obj);
if (ph != NULL) {
Prelease(ph);
}
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: lookupByName0
* Signature: (Ljava/lang/String;Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0
(JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) {
const char *objectName_cstr, *symbolName_cstr;
jlong addr;
jboolean isCopy;
struct ps_prochandle* ph = get_proc_handle(env, this_obj);
objectName_cstr = NULL;
if (objectName != NULL) {
objectName_cstr = (*env)->GetStringUTFChars(env, objectName, &isCopy);
CHECK_EXCEPTION_(0);
}
symbolName_cstr = (*env)->GetStringUTFChars(env, symbolName, &isCopy);
CHECK_EXCEPTION_(0);
addr = (jlong) lookup_symbol(ph, objectName_cstr, symbolName_cstr);
if (objectName_cstr != NULL) {
(*env)->ReleaseStringUTFChars(env, objectName, objectName_cstr);
}
(*env)->ReleaseStringUTFChars(env, symbolName, symbolName_cstr);
return addr;
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: lookupByAddress0
* Signature: (J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;
*/
JNIEXPORT jobject JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByAddress0
(JNIEnv *env, jobject this_obj, jlong addr) {
uintptr_t offset;
const char* sym = NULL;
struct ps_prochandle* ph = get_proc_handle(env, this_obj);
sym = symbol_for_pc(ph, (uintptr_t) addr, &offset);
if (sym == NULL) return 0;
return (*env)->CallObjectMethod(env, this_obj, createClosestSymbol_ID,
(*env)->NewStringUTF(env, sym), (jlong)offset);
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: readBytesFromProcess0
* Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult;
*/
JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0
(JNIEnv *env, jobject this_obj, jlong addr, jlong numBytes) {
jboolean isCopy;
jbyteArray array;
jbyte *bufPtr;
ps_err_e err;
array = (*env)->NewByteArray(env, numBytes);
CHECK_EXCEPTION_(0);
bufPtr = (*env)->GetByteArrayElements(env, array, &isCopy);
CHECK_EXCEPTION_(0);
err = ps_pread(get_proc_handle(env, this_obj), (psaddr_t) (uintptr_t)addr, bufPtr, numBytes);
(*env)->ReleaseByteArrayElements(env, array, bufPtr, 0);
return (err == PS_OK)? array : 0;
}
JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0
(JNIEnv *env, jobject this_obj, jint lwp_id) {
struct reg gregs;
jboolean isCopy;
jlongArray array;
jlong *regs;
struct ps_prochandle* ph = get_proc_handle(env, this_obj);
if (get_lwp_regs(ph, lwp_id, &gregs) != true) {
THROW_NEW_DEBUGGER_EXCEPTION_("get_thread_regs failed for a lwp", 0);
}
#undef NPRGREG
#ifdef i386
#define NPRGREG sun_jvm_hotspot_debugger_x86_X86ThreadContext_NPRGREG
#endif
#ifdef ia64
#define NPRGREG IA64_REG_COUNT
#endif
#ifdef amd64
#define NPRGREG sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_NPRGREG
#endif
#if defined(sparc) || defined(sparcv9)
#define NPRGREG sun_jvm_hotspot_debugger_sparc_SPARCThreadContext_NPRGREG
#endif
array = (*env)->NewLongArray(env, NPRGREG);
CHECK_EXCEPTION_(0);
regs = (*env)->GetLongArrayElements(env, array, &isCopy);
#undef REG_INDEX
#ifdef i386
#define REG_INDEX(reg) sun_jvm_hotspot_debugger_x86_X86ThreadContext_##reg
regs[REG_INDEX(GS)] = (uintptr_t) gregs.r_gs;
regs[REG_INDEX(FS)] = (uintptr_t) gregs.r_fs;
regs[REG_INDEX(ES)] = (uintptr_t) gregs.r_es;
regs[REG_INDEX(DS)] = (uintptr_t) gregs.r_ds;
regs[REG_INDEX(EDI)] = (uintptr_t) gregs.r_edi;
regs[REG_INDEX(ESI)] = (uintptr_t) gregs.r_esi;
regs[REG_INDEX(FP)] = (uintptr_t) gregs.r_ebp;
regs[REG_INDEX(SP)] = (uintptr_t) gregs.r_isp;
regs[REG_INDEX(EBX)] = (uintptr_t) gregs.r_ebx;
regs[REG_INDEX(EDX)] = (uintptr_t) gregs.r_edx;
regs[REG_INDEX(ECX)] = (uintptr_t) gregs.r_ecx;
regs[REG_INDEX(EAX)] = (uintptr_t) gregs.r_eax;
regs[REG_INDEX(PC)] = (uintptr_t) gregs.r_eip;
regs[REG_INDEX(CS)] = (uintptr_t) gregs.r_cs;
regs[REG_INDEX(SS)] = (uintptr_t) gregs.r_ss;
#endif /* i386 */
#if ia64
regs = (*env)->GetLongArrayElements(env, array, &isCopy);
int i;
for (i = 0; i < NPRGREG; i++ ) {
regs[i] = 0xDEADDEAD;
}
#endif /* ia64 */
#ifdef amd64
#define REG_INDEX(reg) sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_##reg
regs[REG_INDEX(R15)] = gregs.r_r15;
regs[REG_INDEX(R14)] = gregs.r_r14;
regs[REG_INDEX(R13)] = gregs.r_r13;
regs[REG_INDEX(R12)] = gregs.r_r12;
regs[REG_INDEX(RBP)] = gregs.r_rbp;
regs[REG_INDEX(RBX)] = gregs.r_rbx;
regs[REG_INDEX(R11)] = gregs.r_r11;
regs[REG_INDEX(R10)] = gregs.r_r10;
regs[REG_INDEX(R9)] = gregs.r_r9;
regs[REG_INDEX(R8)] = gregs.r_r8;
regs[REG_INDEX(RAX)] = gregs.r_rax;
regs[REG_INDEX(RCX)] = gregs.r_rcx;
regs[REG_INDEX(RDX)] = gregs.r_rdx;
regs[REG_INDEX(RSI)] = gregs.r_rsi;
regs[REG_INDEX(RDI)] = gregs.r_rdi;
regs[REG_INDEX(RIP)] = gregs.r_rip;
regs[REG_INDEX(CS)] = gregs.r_cs;
regs[REG_INDEX(RSP)] = gregs.r_rsp;
regs[REG_INDEX(SS)] = gregs.r_ss;
// regs[REG_INDEX(FSBASE)] = gregs.fs_base;
// regs[REG_INDEX(GSBASE)] = gregs.gs_base;
// regs[REG_INDEX(DS)] = gregs.ds;
// regs[REG_INDEX(ES)] = gregs.es;
// regs[REG_INDEX(FS)] = gregs.fs;
// regs[REG_INDEX(GS)] = gregs.gs;
#endif /* amd64 */
#if defined(sparc) || defined(sparcv9)
#define REG_INDEX(reg) sun_jvm_hotspot_debugger_sparc_SPARCThreadContext_##reg
#ifdef _LP64
regs[REG_INDEX(R_PSR)] = gregs.tstate;
regs[REG_INDEX(R_PC)] = gregs.tpc;
regs[REG_INDEX(R_nPC)] = gregs.tnpc;
regs[REG_INDEX(R_Y)] = gregs.y;
#else
regs[REG_INDEX(R_PSR)] = gregs.psr;
regs[REG_INDEX(R_PC)] = gregs.pc;
regs[REG_INDEX(R_nPC)] = gregs.npc;
regs[REG_INDEX(R_Y)] = gregs.y;
#endif
regs[REG_INDEX(R_G0)] = 0 ;
regs[REG_INDEX(R_G1)] = gregs.u_regs[0];
regs[REG_INDEX(R_G2)] = gregs.u_regs[1];
regs[REG_INDEX(R_G3)] = gregs.u_regs[2];
regs[REG_INDEX(R_G4)] = gregs.u_regs[3];
regs[REG_INDEX(R_G5)] = gregs.u_regs[4];
regs[REG_INDEX(R_G6)] = gregs.u_regs[5];
regs[REG_INDEX(R_G7)] = gregs.u_regs[6];
regs[REG_INDEX(R_O0)] = gregs.u_regs[7];
regs[REG_INDEX(R_O1)] = gregs.u_regs[8];
regs[REG_INDEX(R_O2)] = gregs.u_regs[ 9];
regs[REG_INDEX(R_O3)] = gregs.u_regs[10];
regs[REG_INDEX(R_O4)] = gregs.u_regs[11];
regs[REG_INDEX(R_O5)] = gregs.u_regs[12];
regs[REG_INDEX(R_O6)] = gregs.u_regs[13];
regs[REG_INDEX(R_O7)] = gregs.u_regs[14];
#endif /* sparc */
(*env)->ReleaseLongArrayElements(env, array, regs, JNI_COMMIT);
return array;
}
#
# Copyright (c) 2002, 2009, 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
# 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.
#
#
ARCH := $(shell if ([ `uname -m` = "ia64" ]) ; then echo ia64 ; elif ([ `uname -m` = "amd64" ]) ; then echo amd64; elif ([ `uname -m` = "sparc64" ]) ; then echo sparc; else echo i386 ; fi )
GCC = gcc
JAVAH = ${JAVA_HOME}/bin/javah
SOURCES = salibelf.c \
symtab.c \
libproc_impl.c \
ps_proc.c \
ps_core.c \
hsearch_r.c \
BsdDebuggerLocal.c
INCLUDES = -I${JAVA_HOME}/include -I${JAVA_HOME}/include/$(shell uname -s | tr "[:upper:]" "[:lower:]")
OBJS = $(SOURCES:.c=.o)
LIBS = -lutil -lthread_db
CFLAGS = -c -fPIC -g -Wall -D_ALLBSD_SOURCE -D_GNU_SOURCE -D$(ARCH) $(INCLUDES)
LIBSA = $(ARCH)/libsaproc.so
all: $(LIBSA)
BsdDebuggerLocal.o: BsdDebuggerLocal.c
$(JAVAH) -jni -classpath ../../../../../build/bsd-i586/hotspot/outputdir/bsd_i486_compiler2/generated/saclasses \
sun.jvm.hotspot.debugger.x86.X86ThreadContext \
sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext
$(GCC) $(CFLAGS) $<
.c.obj:
$(GCC) $(CFLAGS)
ifndef LDNOMAP
LFLAGS_LIBSA = -Xlinker --version-script=mapfile
endif
$(LIBSA): $(OBJS) mapfile
if [ ! -d $(ARCH) ] ; then mkdir $(ARCH) ; fi
$(GCC) -shared $(LFLAGS_LIBSA) -o $(LIBSA) $(OBJS) $(LIBS)
test.o: $(LIBSA) test.c
$(GCC) -c -o test.o -g -D_GNU_SOURCE -D$(ARCH) $(INCLUDES) test.c
test: test.o
$(GCC) -o test test.o -L$(ARCH) -lsaproc $(LIBS)
clean:
rm -f $(LIBSA)
rm -f $(OBJS)
rm -f test.o
-rmdir $(ARCH)
/*
* Copyright (c) 2009, 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
* 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 <stdlib.h>
#include <jni.h>
#define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; }
#define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;}
#define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; }
#define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;}
static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) {
(*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg);
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: init0
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0
(JNIEnv *env, jclass cls) {
}
JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getAddressSize
(JNIEnv *env, jclass cls)
{
#ifdef _LP64
return 8;
#else
return 4;
#endif
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: attach0
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I
(JNIEnv *env, jobject this_obj, jint jpid) {
THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process");
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: attach0
* Signature: (Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2
(JNIEnv *env, jobject this_obj, jstring execName, jstring coreName) {
THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the core file");
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: detach0
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0
(JNIEnv *env, jobject this_obj) {
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: lookupByName0
* Signature: (Ljava/lang/String;Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0
(JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) {
return 0;
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: lookupByAddress0
* Signature: (J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;
*/
JNIEXPORT jobject JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByAddress0
(JNIEnv *env, jobject this_obj, jlong addr) {
return 0;
}
/*
* Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
* Method: readBytesFromProcess0
* Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult;
*/
JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0
(JNIEnv *env, jobject this_obj, jlong addr, jlong numBytes) {
return 0;
}
JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0
(JNIEnv *env, jobject this_obj, jint lwp_id) {
return 0;
}
/*
* Copyright (c) 2003, 2006, 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
* 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 _ELFMACROS_H_
#define _ELFMACROS_H_
#define ELF_NHDR Elf_Note
#if defined(_LP64)
#define ELF_EHDR Elf64_Ehdr
#define ELF_SHDR Elf64_Shdr
#define ELF_PHDR Elf64_Phdr
#define ELF_SYM Elf64_Sym
#define ELF_DYN Elf64_Dyn
#define ELF_ADDR Elf64_Addr
#ifndef ELF_ST_TYPE
#define ELF_ST_TYPE ELF64_ST_TYPE
#endif
#else
#define ELF_EHDR Elf32_Ehdr
#define ELF_SHDR Elf32_Shdr
#define ELF_PHDR Elf32_Phdr
#define ELF_SYM Elf32_Sym
#define ELF_DYN Elf32_Dyn
#define ELF_ADDR Elf32_Addr
#ifndef ELF_ST_TYPE
#define ELF_ST_TYPE ELF32_ST_TYPE
#endif
#endif
#endif /* _ELFMACROS_H_ */
/*
* Copyright (c) 2003, 2007, 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
* 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 _LIBPROC_H_
#define _LIBPROC_H_
#include <unistd.h>
#include <stdint.h>
#include <machine/reg.h>
#include <proc_service.h>
#if defined(sparc) || defined(sparcv9)
/*
If _LP64 is defined ptrace.h should be taken from /usr/include/asm-sparc64
otherwise it should be from /usr/include/asm-sparc
These two files define pt_regs structure differently
*/
#ifdef _LP64
#include "asm-sparc64/ptrace.h"
#else
#include "asm-sparc/ptrace.h"
#endif
#endif //sparc or sparcv9
/************************************************************************************
0. This is very minimal subset of Solaris libproc just enough for current application.
Please note that the bulk of the functionality is from proc_service interface. This
adds Pgrab__ and some missing stuff. We hide the difference b/w live process and core
file by this interface.
1. pthread_id is unique. We store this in OSThread::_pthread_id in JVM code.
2. All threads see the same pid when they call getpid().
We used to save the result of ::getpid() call in OSThread::_thread_id.
Because gettid returns actual pid of thread (lwp id), this is
unique again. We therefore use OSThread::_thread_id as unique identifier.
3. There is a unique LWP id under both thread libraries. libthread_db maps pthread_id
to its underlying lwp_id under both the thread libraries. thread_info.lwp_id stores
lwp_id of the thread. The lwp id is nothing but the actual pid of clone'd processes. But
unfortunately libthread_db does not work very well for core dumps. So, we get pthread_id
only for processes. For core dumps, we don't use libthread_db at all (like gdb).
4. ptrace operates on this LWP id under both the thread libraries. When we say 'pid' for
ptrace call, we refer to lwp_id of the thread.
5. for core file, we parse ELF files and read data from them. For processes we use
combination of ptrace and /proc calls.
*************************************************************************************/
// This C bool type must be int for compatibility with BSD calls and
// it would be a mistake to equivalence it to C++ bool on many platforms
typedef int bool;
#define true 1
#define false 0
struct ps_prochandle;
// attach to a process
struct ps_prochandle* Pgrab(pid_t pid);
// attach to a core dump
struct ps_prochandle* Pgrab_core(const char* execfile, const char* corefile);
// release a process or core
void Prelease(struct ps_prochandle* ph);
// functions not directly available in Solaris libproc
// initialize libproc (call this only once per app)
// pass true to make library verbose
bool init_libproc(bool verbose);
// get number of threads
int get_num_threads(struct ps_prochandle* ph);
// get lwp_id of n'th thread
lwpid_t get_lwp_id(struct ps_prochandle* ph, int index);
// get regs for a given lwp
bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lid, struct reg* regs);
// get number of shared objects
int get_num_libs(struct ps_prochandle* ph);
// get name of n'th lib
const char* get_lib_name(struct ps_prochandle* ph, int index);
// get base of lib
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);
// symbol lookup
uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name,
const char* sym_name);
// address->nearest symbol lookup. return NULL for no symbol
const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset);
#endif //__LIBPROC_H_
/*
* Copyright (c) 2003, 2010, 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
* 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <thread_db.h>
#include "libproc_impl.h"
static const char* alt_root = NULL;
static int alt_root_len = -1;
#define SA_ALTROOT "SA_ALTROOT"
static void init_alt_root() {
if (alt_root_len == -1) {
alt_root = getenv(SA_ALTROOT);
if (alt_root) {
alt_root_len = strlen(alt_root);
} else {
alt_root_len = 0;
}
}
}
int pathmap_open(const char* name) {
int fd;
char alt_path[PATH_MAX + 1];
init_alt_root();
fd = open(name, O_RDONLY);
if (fd >= 0) {
return fd;
}
if (alt_root_len > 0) {
strcpy(alt_path, alt_root);
strcat(alt_path, name);
fd = open(alt_path, O_RDONLY);
if (fd >= 0) {
print_debug("path %s substituted for %s\n", alt_path, name);
return fd;
}
if (strrchr(name, '/')) {
strcpy(alt_path, alt_root);
strcat(alt_path, strrchr(name, '/'));
fd = open(alt_path, O_RDONLY);
if (fd >= 0) {
print_debug("path %s substituted for %s\n", alt_path, name);
return fd;
}
}
}
return -1;
}
static bool _libsaproc_debug;
void print_debug(const char* format,...) {
if (_libsaproc_debug) {
va_list alist;
va_start(alist, format);
fputs("libsaproc DEBUG: ", stderr);
vfprintf(stderr, format, alist);
va_end(alist);
}
}
bool is_debug() {
return _libsaproc_debug;
}
// initialize libproc
bool init_libproc(bool debug) {
// init debug mode
_libsaproc_debug = debug;
// initialize the thread_db library
if (td_init() != TD_OK) {
print_debug("libthread_db's td_init failed\n");
return false;
}
return true;
}
static void destroy_lib_info(struct ps_prochandle* ph) {
lib_info* lib = ph->libs;
while (lib) {
lib_info *next = lib->next;
if (lib->symtab) {
destroy_symtab(lib->symtab);
}
free(lib);
lib = next;
}
}
static void destroy_thread_info(struct ps_prochandle* ph) {
thread_info* thr = ph->threads;
while (thr) {
thread_info *next = thr->next;
free(thr);
thr = next;
}
}
// ps_prochandle cleanup
// ps_prochandle cleanup
void Prelease(struct ps_prochandle* ph) {
// do the "derived class" clean-up first
ph->ops->release(ph);
destroy_lib_info(ph);
destroy_thread_info(ph);
free(ph);
}
lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t base) {
return add_lib_info_fd(ph, libname, -1, base);
}
lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base) {
lib_info* newlib;
if ( (newlib = (lib_info*) calloc(1, sizeof(struct lib_info))) == NULL) {
print_debug("can't allocate memory for lib_info\n");
return NULL;
}
strncpy(newlib->name, libname, sizeof(newlib->name));
newlib->base = base;
if (fd == -1) {
if ( (newlib->fd = pathmap_open(newlib->name)) < 0) {
print_debug("can't open shared object %s\n", newlib->name);
free(newlib);
return NULL;
}
} else {
newlib->fd = fd;
}
// check whether we have got an ELF file. /proc/<pid>/map
// gives out all file mappings and not just shared objects
if (is_elf_file(newlib->fd) == false) {
close(newlib->fd);
free(newlib);
return NULL;
}
newlib->symtab = build_symtab(newlib->fd);
if (newlib->symtab == NULL) {
print_debug("symbol table build failed for %s\n", newlib->name);
}
else {
print_debug("built symbol table for %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.
if (ph->libs) {
ph->lib_tail->next = newlib;
ph->lib_tail = newlib;
} else {
ph->libs = ph->lib_tail = newlib;
}
ph->num_libs++;
return newlib;
}
// lookup for a specific symbol
uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name,
const char* sym_name) {
// ignore object_name. search in all libraries
// FIXME: what should we do with object_name?? The library names are obtained
// by parsing /proc/<pid>/maps, which may not be the same as object_name.
// What we need is a utility to map object_name to real file name, something
// dlopen() does by looking at LD_LIBRARY_PATH and /etc/ld.so.cache. For
// now, we just ignore object_name and do a global search for the symbol.
lib_info* lib = ph->libs;
while (lib) {
if (lib->symtab) {
uintptr_t res = search_symbol(lib->symtab, lib->base, sym_name, NULL);
if (res) return res;
}
lib = lib->next;
}
print_debug("lookup failed for symbol '%s' in obj '%s'\n",
sym_name, object_name);
return (uintptr_t) NULL;
}
const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset) {
const char* res = NULL;
lib_info* lib = ph->libs;
while (lib) {
if (lib->symtab && addr >= lib->base) {
res = nearest_symbol(lib->symtab, addr - lib->base, poffset);
if (res) return res;
}
lib = lib->next;
}
return NULL;
}
// add a thread to ps_prochandle
thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) {
thread_info* newthr;
if ( (newthr = (thread_info*) calloc(1, sizeof(thread_info))) == NULL) {
print_debug("can't allocate memory for thread_info\n");
return NULL;
}
// initialize thread info
newthr->pthread_id = pthread_id;
newthr->lwp_id = lwp_id;
// add new thread to the list
newthr->next = ph->threads;
ph->threads = newthr;
ph->num_threads++;
return newthr;
}
// struct used for client data from thread_db callback
struct thread_db_client_data {
struct ps_prochandle* ph;
thread_info_callback callback;
};
// callback function for libthread_db
static int thread_db_callback(const td_thrhandle_t *th_p, void *data) {
struct thread_db_client_data* ptr = (struct thread_db_client_data*) data;
td_thrinfo_t ti;
td_err_e err;
memset(&ti, 0, sizeof(ti));
err = td_thr_get_info(th_p, &ti);
if (err != TD_OK) {
print_debug("libthread_db : td_thr_get_info failed, can't get thread info\n");
return err;
}
print_debug("thread_db : pthread %d (lwp %d)\n", ti.ti_tid, ti.ti_lid);
if (ptr->callback(ptr->ph, (pthread_t)ti.ti_tid, ti.ti_lid) != true)
return TD_ERR;
return TD_OK;
}
// read thread_info using libthread_db
bool read_thread_info(struct ps_prochandle* ph, thread_info_callback cb) {
struct thread_db_client_data mydata;
td_thragent_t* thread_agent = NULL;
if (td_ta_new(ph, &thread_agent) != TD_OK) {
print_debug("can't create libthread_db agent\n");
return false;
}
mydata.ph = ph;
mydata.callback = cb;
// we use libthread_db iterator to iterate thru list of threads.
if (td_ta_thr_iter(thread_agent, thread_db_callback, &mydata,
TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS) != TD_OK) {
td_ta_delete(thread_agent);
return false;
}
// delete thread agent
td_ta_delete(thread_agent);
return true;
}
// get number of threads
int get_num_threads(struct ps_prochandle* ph) {
return ph->num_threads;
}
// get lwp_id of n'th thread
lwpid_t get_lwp_id(struct ps_prochandle* ph, int index) {
int count = 0;
thread_info* thr = ph->threads;
while (thr) {
if (count == index) {
return thr->lwp_id;
}
count++;
thr = thr->next;
}
return -1;
}
// get regs for a given lwp
bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lwp_id, struct reg* regs) {
return ph->ops->get_lwp_regs(ph, lwp_id, regs);
}
// get number of shared objects
int get_num_libs(struct ps_prochandle* ph) {
return ph->num_libs;
}
// get name of n'th solib
const char* get_lib_name(struct ps_prochandle* ph, int index) {
int count = 0;
lib_info* lib = ph->libs;
while (lib) {
if (count == index) {
return lib->name;
}
count++;
lib = lib->next;
}
return NULL;
}
// get base address of a lib
uintptr_t get_lib_base(struct ps_prochandle* ph, int index) {
int count = 0;
lib_info* lib = ph->libs;
while (lib) {
if (count == index) {
return lib->base;
}
count++;
lib = lib->next;
}
return (uintptr_t)NULL;
}
bool find_lib(struct ps_prochandle* ph, const char *lib_name) {
lib_info *p = ph->libs;
while (p) {
if (strcmp(p->name, lib_name) == 0) {
return true;
}
p = p->next;
}
return false;
}
//--------------------------------------------------------------------------
// proc service functions
// ps_pglobal_lookup() looks up the symbol sym_name in the symbol table
// of the load object object_name in the target process identified by ph.
// It returns the symbol's value as an address in the target process in
// *sym_addr.
ps_err_e ps_pglobal_lookup(struct ps_prochandle *ph, const char *object_name,
const char *sym_name, psaddr_t *sym_addr) {
*sym_addr = (psaddr_t) lookup_symbol(ph, object_name, sym_name);
return (*sym_addr ? PS_OK : PS_NOSYM);
}
// read "size" bytes info "buf" from address "addr"
ps_err_e ps_pread(struct ps_prochandle *ph, psaddr_t addr,
void *buf, size_t size) {
return ph->ops->p_pread(ph, (uintptr_t) addr, buf, size)? PS_OK: PS_ERR;
}
// write "size" bytes of data to debuggee at address "addr"
ps_err_e ps_pwrite(struct ps_prochandle *ph, psaddr_t addr,
const void *buf, size_t size) {
return ph->ops->p_pwrite(ph, (uintptr_t)addr, buf, size)? PS_OK: PS_ERR;
}
// fill in ptrace_lwpinfo for lid
ps_err_e ps_linfo(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo) {
return ph->ops->get_lwp_info(ph, lwp_id, linfo)? PS_OK: PS_ERR;
}
// needed for when libthread_db is compiled with TD_DEBUG defined
void
ps_plog (const char *format, ...)
{
va_list alist;
va_start(alist, format);
vfprintf(stderr, format, alist);
va_end(alist);
}
// ------------------------------------------------------------------------
// Functions below this point are not yet implemented. They are here only
// to make the linker happy.
ps_err_e ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lid, const prfpregset_t *fpregs) {
print_debug("ps_lsetfpregs not implemented\n");
return PS_OK;
}
ps_err_e ps_lsetregs(struct ps_prochandle *ph, lwpid_t lid, const prgregset_t gregset) {
print_debug("ps_lsetregs not implemented\n");
return PS_OK;
}
ps_err_e ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lid, prfpregset_t *fpregs) {
print_debug("ps_lgetfpregs not implemented\n");
return PS_OK;
}
ps_err_e ps_lgetregs(struct ps_prochandle *ph, lwpid_t lid, prgregset_t gregset) {
print_debug("ps_lgetfpregs not implemented\n");
return PS_OK;
}
ps_err_e ps_lstop(struct ps_prochandle *ph, lwpid_t lid) {
print_debug("ps_lstop not implemented\n");
return PS_OK;
}
ps_err_e ps_pcontinue(struct ps_prochandle *ph) {
print_debug("ps_pcontinue not implemented\n");
return PS_OK;
}
/*
* Copyright (c) 2003, 2005, 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
* 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 _LIBPROC_IMPL_H_
#define _LIBPROC_IMPL_H_
#include <unistd.h>
#include <limits.h>
#include "libproc.h"
#include "symtab.h"
// data structures in this file mimic those of Solaris 8.0 - libproc's Pcontrol.h
#define BUF_SIZE (PATH_MAX + NAME_MAX + 1)
// list of shared objects
typedef struct lib_info {
char name[BUF_SIZE];
uintptr_t base;
struct symtab* symtab;
int fd; // file descriptor for lib
struct lib_info* next;
} lib_info;
// list of threads
typedef struct thread_info {
lwpid_t lwp_id;
pthread_t pthread_id; // not used cores, always -1
struct reg regs; // not for process, core uses for caching regset
struct thread_info* next;
} thread_info;
// list of virtual memory maps
typedef struct map_info {
int fd; // file descriptor
off_t offset; // file offset of this mapping
uintptr_t vaddr; // starting virtual address
size_t memsz; // size of the mapping
struct map_info* next;
} map_info;
// vtable for ps_prochandle
typedef struct ps_prochandle_ops {
// "derived class" clean-up
void (*release)(struct ps_prochandle* ph);
// read from debuggee
bool (*p_pread)(struct ps_prochandle *ph,
uintptr_t addr, char *buf, size_t size);
// write into debuggee
bool (*p_pwrite)(struct ps_prochandle *ph,
uintptr_t addr, const char *buf , size_t size);
// get integer regset of a thread
bool (*get_lwp_regs)(struct ps_prochandle* ph, lwpid_t lwp_id, struct reg* regs);
// get info on thread
bool (*get_lwp_info)(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo);
} ps_prochandle_ops;
// the ps_prochandle
struct core_data {
int core_fd; // file descriptor of core file
int exec_fd; // file descriptor of exec file
int interp_fd; // file descriptor of interpreter (ld-elf.so.1)
// part of the class sharing workaround
int classes_jsa_fd; // file descriptor of class share archive
uintptr_t dynamic_addr; // address of dynamic section of a.out
uintptr_t ld_base_addr; // base address of ld.so
size_t num_maps; // number of maps.
map_info* maps; // maps in a linked list
// part of the class sharing workaround
map_info* class_share_maps;// class share maps in a linked list
map_info** map_array; // sorted (by vaddr) array of map_info pointers
};
struct ps_prochandle {
ps_prochandle_ops* ops; // vtable ptr
pid_t pid;
int num_libs;
lib_info* libs; // head of lib list
lib_info* lib_tail; // tail of lib list - to append at the end
int num_threads;
thread_info* threads; // head of thread list
struct core_data* core; // data only used for core dumps, NULL for process
};
int pathmap_open(const char* name);
void print_debug(const char* format,...);
bool is_debug();
typedef bool (*thread_info_callback)(struct ps_prochandle* ph, pthread_t pid, lwpid_t lwpid);
// reads thread info using libthread_db and calls above callback for each thread
bool read_thread_info(struct ps_prochandle* ph, thread_info_callback cb);
// adds a new shared object to lib list, returns NULL on failure
lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t base);
// adds a new shared object to lib list, supply open lib file descriptor as well
lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd,
uintptr_t base);
// adds a new thread to threads list, returns NULL on failure
thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id);
// a test for ELF signature without using libelf
bool is_elf_file(int fd);
#endif //_LIBPROC_IMPL_H_
#
#
# Copyright (c) 2003, 2006, 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
# 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.
#
#
# Define public interface.
SUNWprivate_1.1 {
global:
# native methods of BsdDebuggerLocal class
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0;
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getAddressSize;
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I;
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2;
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0;
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0;
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByAddress0;
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0;
Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0;
# proc_service.h functions - to be used by libthread_db
ps_getpid;
ps_pglobal_lookup;
ps_pread;
ps_pwrite;
ps_lsetfpregs;
ps_lsetregs;
ps_lgetfpregs;
ps_lgetregs;
ps_lcontinue;
ps_lgetxmmregs;
ps_lsetxmmregs;
ps_lstop;
ps_linfo;
# used by attach test program
init_libproc;
Pgrab;
Pgrab_core;
Prelease;
local:
*;
};
此差异已折叠。
/*
* Copyright (c) 2003, 2010, 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
* 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 <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/param.h>
#include <sys/user.h>
#include <elf.h>
#include <sys/elf_common.h>
#include <sys/link_elf.h>
#include <libutil.h>
#include "libproc_impl.h"
#include "elfmacros.h"
// This file has the libproc implementation specific to live process
// For core files, refer to ps_core.c
static inline uintptr_t align(uintptr_t ptr, size_t size) {
return (ptr & ~(size - 1));
}
// ---------------------------------------------
// ptrace functions
// ---------------------------------------------
// read "size" bytes of data from "addr" within the target process.
// unlike the standard ptrace() function, process_read_data() can handle
// unaligned address - alignment check, if required, should be done
// before calling process_read_data.
static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *buf, size_t size) {
int rslt;
size_t i, words;
uintptr_t end_addr = addr + size;
uintptr_t aligned_addr = align(addr, sizeof(int));
if (aligned_addr != addr) {
char *ptr = (char *)&rslt;
errno = 0;
rslt = ptrace(PT_READ_D, ph->pid, (caddr_t) aligned_addr, 0);
if (errno) {
print_debug("ptrace(PT_READ_D, ..) failed for %d bytes @ %lx\n", size, addr);
return false;
}
for (; aligned_addr != addr; aligned_addr++, ptr++);
for (; ((intptr_t)aligned_addr % sizeof(int)) && aligned_addr < end_addr;
aligned_addr++)
*(buf++) = *(ptr++);
}
words = (end_addr - aligned_addr) / sizeof(int);
// assert((intptr_t)aligned_addr % sizeof(int) == 0);
for (i = 0; i < words; i++) {
errno = 0;
rslt = ptrace(PT_READ_D, ph->pid, (caddr_t) aligned_addr, 0);
if (errno) {
print_debug("ptrace(PT_READ_D, ..) failed for %d bytes @ %lx\n", size, addr);
return false;
}
*(int *)buf = rslt;
buf += sizeof(int);
aligned_addr += sizeof(int);
}
if (aligned_addr != end_addr) {
char *ptr = (char *)&rslt;
errno = 0;
rslt = ptrace(PT_READ_D, ph->pid, (caddr_t) aligned_addr, 0);
if (errno) {
print_debug("ptrace(PT_READ_D, ..) failed for %d bytes @ %lx\n", size, addr);
return false;
}
for (; aligned_addr != end_addr; aligned_addr++)
*(buf++) = *(ptr++);
}
return true;
}
// null implementation for write
static bool process_write_data(struct ps_prochandle* ph,
uintptr_t addr, const char *buf , size_t size) {
return false;
}
// "user" should be a pointer to a reg
static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct reg *user) {
// we have already attached to all thread 'pid's, just use ptrace call
// to get regset now. Note that we don't cache regset upfront for processes.
if (ptrace(PT_GETREGS, pid, (caddr_t) user, 0) < 0) {
print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid);
return false;
}
return true;
}
// fill in ptrace_lwpinfo for lid
static bool process_get_lwp_info(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo) {
errno = 0;
ptrace(PT_LWPINFO, lwp_id, linfo, sizeof(struct ptrace_lwpinfo));
return (errno == 0)? true: false;
}
// attach to a process/thread specified by "pid"
static bool ptrace_attach(pid_t pid) {
if (ptrace(PT_ATTACH, pid, NULL, 0) < 0) {
print_debug("ptrace(PTRACE_ATTACH, ..) failed for %d\n", pid);
return false;
} else {
int ret;
int status;
do {
// Wait for debuggee to stop.
ret = waitpid(pid, &status, 0);
if (ret >= 0) {
if (WIFSTOPPED(status)) {
// Debuggee stopped.
return true;
} else {
print_debug("waitpid(): Child process exited/terminated (status = 0x%x)\n", status);
return false;
}
} else {
switch (errno) {
case EINTR:
continue;
break;
case ECHILD:
print_debug("waitpid() failed. Child process pid (%d) does not exist \n", pid);
break;
case EINVAL:
print_debug("waitpid() failed. Invalid options argument.\n");
break;
default:
print_debug("waitpid() failed. Unexpected error %d\n",errno);
}
return false;
}
} while(true);
}
}
// -------------------------------------------------------
// functions for obtaining library information
// -------------------------------------------------------
// callback for read_thread_info
static bool add_new_thread(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) {
return add_thread_info(ph, pthread_id, lwp_id) != NULL;
}
#if defined(__FreeBSD__) && __FreeBSD_version < 701000
/*
* TEXT_START_ADDR from binutils/ld/emulparams/<arch_spec>.sh
* Not the most robust but good enough.
*/
#if defined(amd64) || defined(x86_64)
#define TEXT_START_ADDR 0x400000
#elif defined(i386)
#define TEXT_START_ADDR 0x8048000
#else
#error TEXT_START_ADDR not defined
#endif
#define BUF_SIZE (PATH_MAX + NAME_MAX + 1)
uintptr_t linkmap_addr(struct ps_prochandle *ph) {
uintptr_t ehdr_addr, phdr_addr, dyn_addr, dmap_addr, lmap_addr;
ELF_EHDR ehdr;
ELF_PHDR *phdrs, *phdr;
ELF_DYN *dyns, *dyn;
struct r_debug dmap;
unsigned long hdrs_size;
unsigned int i;
/* read ELF_EHDR at TEXT_START_ADDR and validate */
ehdr_addr = (uintptr_t)TEXT_START_ADDR;
if (process_read_data(ph, ehdr_addr, (char *)&ehdr, sizeof(ehdr)) != true) {
print_debug("process_read_data failed for ehdr_addr %p\n", ehdr_addr);
return (0);
}
if (!IS_ELF(ehdr) ||
ehdr.e_ident[EI_CLASS] != ELF_TARG_CLASS ||
ehdr.e_ident[EI_DATA] != ELF_TARG_DATA ||
ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
ehdr.e_phentsize != sizeof(ELF_PHDR) ||
ehdr.e_version != ELF_TARG_VER ||
ehdr.e_machine != ELF_TARG_MACH) {
print_debug("not an ELF_EHDR at %p\n", ehdr_addr);
return (0);
}
/* allocate space for all ELF_PHDR's and read */
phdr_addr = ehdr_addr + ehdr.e_phoff;
hdrs_size = ehdr.e_phnum * sizeof(ELF_PHDR);
if ((phdrs = malloc(hdrs_size)) == NULL)
return (0);
if (process_read_data(ph, phdr_addr, (char *)phdrs, hdrs_size) != true) {
print_debug("process_read_data failed for phdr_addr %p\n", phdr_addr);
return (0);
}
/* find PT_DYNAMIC section */
for (i = 0, phdr = phdrs; i < ehdr.e_phnum; i++, phdr++) {
if (phdr->p_type == PT_DYNAMIC)
break;
}
if (i >= ehdr.e_phnum) {
print_debug("PT_DYNAMIC section not found!\n");
free(phdrs);
return (0);
}
/* allocate space and read in ELF_DYN headers */
dyn_addr = phdr->p_vaddr;
hdrs_size = phdr->p_memsz;
free(phdrs);
if ((dyns = malloc(hdrs_size)) == NULL)
return (0);
if (process_read_data(ph, dyn_addr, (char *)dyns, hdrs_size) != true) {
print_debug("process_read_data failed for dyn_addr %p\n", dyn_addr);
free(dyns);
return (0);
}
/* find DT_DEBUG */
dyn = dyns;
while (dyn->d_tag != DT_DEBUG && dyn->d_tag != DT_NULL) {
dyn++;
}
if (dyn->d_tag != DT_DEBUG) {
print_debug("failed to find DT_DEBUG\n");
free(dyns);
return (0);
}
/* read struct r_debug into dmap */
dmap_addr = (uintptr_t)dyn->d_un.d_ptr;
free(dyns);
if (process_read_data(ph, dmap_addr, (char *)&dmap, sizeof(dmap)) != true) {
print_debug("process_read_data failed for dmap_addr %p\n", dmap_addr);
return (0);
}
lmap_addr = (uintptr_t)dmap.r_map;
return (lmap_addr);
}
#endif // __FreeBSD__ && __FreeBSD_version < 701000
static bool read_lib_info(struct ps_prochandle* ph) {
#if defined(__FreeBSD__) && __FreeBSD_version >= 701000
struct kinfo_vmentry *freep, *kve;
int i, cnt;
freep = kinfo_getvmmap(ph->pid, &cnt);
if (freep == NULL) {
print_debug("can't get vm map for pid\n", ph->pid);
return false;
}
for (i = 0; i < cnt; i++) {
kve = &freep[i];
if ((kve->kve_flags & KVME_FLAG_COW) &&
kve->kve_path != NULL &&
strlen(kve->kve_path) > 0) {
if (find_lib(ph, kve->kve_path) == false) {
lib_info* lib;
if ((lib = add_lib_info(ph, kve->kve_path,
(uintptr_t) kve->kve_start)) == NULL)
continue; // ignore, add_lib_info prints error
// we don't need to keep the library open, symtab is already
// built. Only for core dump we need to keep the fd open.
close(lib->fd);
lib->fd = -1;
}
}
}
free(freep);
return true;
#else
char *l_name;
struct link_map *lmap;
uintptr_t lmap_addr;
if ((l_name = malloc(BUF_SIZE)) == NULL)
return false;
if ((lmap = malloc(sizeof(*lmap))) == NULL) {
free(l_name);
return false;
}
lmap_addr = linkmap_addr(ph);
if (lmap_addr == 0) {
free(l_name);
free(lmap);
return false;
}
do {
if (process_read_data(ph, lmap_addr, (char *)lmap, sizeof(*lmap)) != true) {
print_debug("process_read_data failed for lmap_addr %p\n", lmap_addr);
free (l_name);
free (lmap);
return false;
}
if (process_read_data(ph, (uintptr_t)lmap->l_name, l_name,
BUF_SIZE) != true) {
print_debug("process_read_data failed for lmap->l_name %p\n",
lmap->l_name);
free (l_name);
free (lmap);
return false;
}
if (find_lib(ph, l_name) == false) {
lib_info* lib;
if ((lib = add_lib_info(ph, l_name,
(uintptr_t) lmap->l_addr)) == NULL)
continue; // ignore, add_lib_info prints error
// we don't need to keep the library open, symtab is already
// built. Only for core dump we need to keep the fd open.
close(lib->fd);
lib->fd = -1;
}
lmap_addr = (uintptr_t)lmap->l_next;
} while (lmap->l_next != NULL);
free (l_name);
free (lmap);
return true;
#endif
}
// detach a given pid
static bool ptrace_detach(pid_t pid) {
if (pid && ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0) {
print_debug("ptrace(PTRACE_DETACH, ..) failed for %d\n", pid);
return false;
} else {
return true;
}
}
static void process_cleanup(struct ps_prochandle* ph) {
ptrace_detach(ph->pid);
}
static ps_prochandle_ops process_ops = {
.release= process_cleanup,
.p_pread= process_read_data,
.p_pwrite= process_write_data,
.get_lwp_regs= process_get_lwp_regs,
.get_lwp_info= process_get_lwp_info
};
// attach to the process. One and only one exposed stuff
struct ps_prochandle* Pgrab(pid_t pid) {
struct ps_prochandle* ph = NULL;
thread_info* thr = NULL;
if ( (ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle))) == NULL) {
print_debug("can't allocate memory for ps_prochandle\n");
return NULL;
}
if (ptrace_attach(pid) != true) {
free(ph);
return NULL;
}
// initialize ps_prochandle
ph->pid = pid;
// initialize vtable
ph->ops = &process_ops;
// read library info and symbol tables, must do this before attaching threads,
// as the symbols in the pthread library will be used to figure out
// the list of threads within the same process.
if (read_lib_info(ph) != true) {
ptrace_detach(pid);
free(ph);
return NULL;
}
// read thread info
read_thread_info(ph, add_new_thread);
return ph;
}
/*
* Copyright (c) 2003, 2006, 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
* 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 "salibelf.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
extern void print_debug(const char*,...);
// ELF file parsing helpers. Note that we do *not* use libelf here.
int read_elf_header(int fd, ELF_EHDR* ehdr) {
if (pread(fd, ehdr, sizeof (ELF_EHDR), 0) != sizeof (ELF_EHDR) ||
memcmp(&ehdr->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 ||
ehdr->e_version != EV_CURRENT) {
return 0;
}
return 1;
}
bool is_elf_file(int fd) {
ELF_EHDR ehdr;
return read_elf_header(fd, &ehdr);
}
// read program header table of an ELF file
ELF_PHDR* read_program_header_table(int fd, ELF_EHDR* hdr) {
ELF_PHDR* phbuf = 0;
// allocate memory for program header table
size_t nbytes = hdr->e_phnum * hdr->e_phentsize;
if ((phbuf = (ELF_PHDR*) malloc(nbytes)) == NULL) {
print_debug("can't allocate memory for reading program header table\n");
return NULL;
}
if (pread(fd, phbuf, nbytes, hdr->e_phoff) != nbytes) {
print_debug("ELF file is truncated! can't read program header table\n");
free(phbuf);
return NULL;
}
return phbuf;
}
// read section header table of an ELF file
ELF_SHDR* read_section_header_table(int fd, ELF_EHDR* hdr) {
ELF_SHDR* shbuf = 0;
// allocate memory for section header table
size_t nbytes = hdr->e_shnum * hdr->e_shentsize;
if ((shbuf = (ELF_SHDR*) malloc(nbytes)) == NULL) {
print_debug("can't allocate memory for reading section header table\n");
return NULL;
}
if (pread(fd, shbuf, nbytes, hdr->e_shoff) != nbytes) {
print_debug("ELF file is truncated! can't read section header table\n");
free(shbuf);
return NULL;
}
return shbuf;
}
// read a particular section's data
void* read_section_data(int fd, ELF_EHDR* ehdr, ELF_SHDR* shdr) {
void *buf = NULL;
if (shdr->sh_type == SHT_NOBITS || shdr->sh_size == 0) {
return buf;
}
if ((buf = calloc(shdr->sh_size, 1)) == NULL) {
print_debug("can't allocate memory for reading section data\n");
return NULL;
}
if (pread(fd, buf, shdr->sh_size, shdr->sh_offset) != shdr->sh_size) {
free(buf);
print_debug("section data read failed\n");
return NULL;
}
return buf;
}
uintptr_t find_base_address(int fd, ELF_EHDR* ehdr) {
uintptr_t baseaddr = (uintptr_t)-1;
int cnt;
ELF_PHDR *phbuf, *phdr;
// read program header table
if ((phbuf = read_program_header_table(fd, ehdr)) == NULL) {
goto quit;
}
// the base address of a shared object is the lowest vaddr of
// its loadable segments (PT_LOAD)
for (phdr = phbuf, cnt = 0; cnt < ehdr->e_phnum; cnt++, phdr++) {
if (phdr->p_type == PT_LOAD && phdr->p_vaddr < baseaddr) {
baseaddr = phdr->p_vaddr;
}
}
quit:
if (phbuf) free(phbuf);
return baseaddr;
}
/*
* Copyright (c) 2003, 2005, 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
* 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 _SALIBELF_H_
#define _SALIBELF_H_
#include <elf.h>
#include "elfmacros.h"
#include "libproc_impl.h"
// read ELF file header.
int read_elf_header(int fd, ELF_EHDR* ehdr);
// is given file descriptor corresponds to an ELF file?
bool is_elf_file(int fd);
// read program header table of an ELF file. caller has to
// free the result pointer after use. NULL on failure.
ELF_PHDR* read_program_header_table(int fd, ELF_EHDR* hdr);
// read section header table of an ELF file. caller has to
// free the result pointer after use. NULL on failure.
ELF_SHDR* read_section_header_table(int fd, ELF_EHDR* hdr);
// read a particular section's data. caller has to free the
// result pointer after use. NULL on failure.
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);
#endif /* _SALIBELF_H_ */
/*
* Copyright (c) 2003, 2010, 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
* 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 <unistd.h>
#include <search.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#include <fcntl.h>
#include "symtab.h"
#include "salibelf.h"
// ----------------------------------------------------
// functions for symbol lookups
// ----------------------------------------------------
struct elf_section {
ELF_SHDR *c_shdr;
void *c_data;
};
struct elf_symbol {
char *name;
uintptr_t offset;
uintptr_t size;
};
typedef struct symtab {
char *strs;
size_t num_symbols;
struct elf_symbol *symbols;
DB* hash_table;
} symtab_t;
// read symbol table from given fd.
struct symtab* build_symtab(int fd) {
ELF_EHDR ehdr;
struct symtab* symtab = NULL;
// Reading of elf header
struct elf_section *scn_cache = NULL;
int cnt = 0;
ELF_SHDR* shbuf = NULL;
ELF_SHDR* cursct = NULL;
ELF_PHDR* phbuf = NULL;
int symtab_found = 0;
int dynsym_found = 0;
uint32_t symsection = SHT_SYMTAB;
uintptr_t baseaddr = (uintptr_t)-1;
lseek(fd, (off_t)0L, SEEK_SET);
if (! read_elf_header(fd, &ehdr)) {
// not an elf
return NULL;
}
// read ELF header
if ((shbuf = read_section_header_table(fd, &ehdr)) == NULL) {
goto quit;
}
baseaddr = find_base_address(fd, &ehdr);
scn_cache = calloc(ehdr.e_shnum, sizeof(*scn_cache));
if (scn_cache == NULL) {
goto quit;
}
for (cursct = shbuf, cnt = 0; cnt < ehdr.e_shnum; cnt++) {
scn_cache[cnt].c_shdr = cursct;
if (cursct->sh_type == SHT_SYMTAB ||
cursct->sh_type == SHT_STRTAB ||
cursct->sh_type == SHT_DYNSYM) {
if ( (scn_cache[cnt].c_data = read_section_data(fd, &ehdr, cursct)) == NULL) {
goto quit;
}
}
if (cursct->sh_type == SHT_SYMTAB)
symtab_found++;
if (cursct->sh_type == SHT_DYNSYM)
dynsym_found++;
cursct++;
}
if (!symtab_found && dynsym_found)
symsection = SHT_DYNSYM;
for (cnt = 1; cnt < ehdr.e_shnum; cnt++) {
ELF_SHDR *shdr = scn_cache[cnt].c_shdr;
if (shdr->sh_type == symsection) {
ELF_SYM *syms;
int j, n, rslt;
size_t size;
// FIXME: there could be multiple data buffers associated with the
// same ELF section. Here we can handle only one buffer. See man page
// for elf_getdata on Solaris.
// guarantee(symtab == NULL, "multiple symtab");
symtab = calloc(1, sizeof(*symtab));
if (symtab == NULL) {
goto quit;
}
// the symbol table
syms = (ELF_SYM *)scn_cache[cnt].c_data;
// number of symbols
n = shdr->sh_size / shdr->sh_entsize;
// create hash table, we use berkeley db to
// manipulate the hash table.
symtab->hash_table = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL);
// guarantee(symtab->hash_table, "unexpected failure: dbopen");
// shdr->sh_link points to the section that contains the actual strings
// for symbol names. the st_name field in ELF_SYM is just the
// string table index. we make a copy of the string table so the
// strings will not be destroyed by elf_end.
size = scn_cache[shdr->sh_link].c_shdr->sh_size;
symtab->strs = malloc(size);
memcpy(symtab->strs, scn_cache[shdr->sh_link].c_data, size);
// allocate memory for storing symbol offset and size;
symtab->num_symbols = n;
symtab->symbols = calloc(n , sizeof(*symtab->symbols));
// copy symbols info our symtab and enter them info the hash table
for (j = 0; j < n; j++, syms++) {
DBT key, value;
char *sym_name = symtab->strs + syms->st_name;
// skip non-object and non-function symbols
int st_type = ELF_ST_TYPE(syms->st_info);
if ( st_type != STT_FUNC && st_type != STT_OBJECT)
continue;
// skip empty strings and undefined symbols
if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue;
symtab->symbols[j].name = sym_name;
symtab->symbols[j].offset = syms->st_value - baseaddr;
symtab->symbols[j].size = syms->st_size;
key.data = sym_name;
key.size = strlen(sym_name) + 1;
value.data = &(symtab->symbols[j]);
value.size = sizeof(void *);
(*symtab->hash_table->put)(symtab->hash_table, &key, &value, 0);
}
}
}
quit:
if (shbuf) free(shbuf);
if (phbuf) free(phbuf);
if (scn_cache) {
for (cnt = 0; cnt < ehdr.e_shnum; cnt++) {
if (scn_cache[cnt].c_data != NULL) {
free(scn_cache[cnt].c_data);
}
}
free(scn_cache);
}
return symtab;
}
void destroy_symtab(struct symtab* symtab) {
if (!symtab) return;
if (symtab->strs) free(symtab->strs);
if (symtab->symbols) free(symtab->symbols);
if (symtab->hash_table) {
symtab->hash_table->close(symtab->hash_table);
}
free(symtab);
}
uintptr_t search_symbol(struct symtab* symtab, uintptr_t base,
const char *sym_name, int *sym_size) {
DBT key, value;
int ret;
// library does not have symbol table
if (!symtab || !symtab->hash_table)
return 0;
key.data = (char*)(uintptr_t)sym_name;
key.size = strlen(sym_name) + 1;
ret = (*symtab->hash_table->get)(symtab->hash_table, &key, &value, 0);
if (ret == 0) {
struct elf_symbol *sym = value.data;
uintptr_t rslt = (uintptr_t) ((char*)base + sym->offset);
if (sym_size) *sym_size = sym->size;
return rslt;
}
quit:
return 0;
}
const char* nearest_symbol(struct symtab* symtab, uintptr_t offset,
uintptr_t* poffset) {
int n = 0;
if (!symtab) return NULL;
for (; n < symtab->num_symbols; n++) {
struct elf_symbol* sym = &(symtab->symbols[n]);
if (sym->name != NULL &&
offset >= sym->offset && offset < sym->offset + sym->size) {
if (poffset) *poffset = (offset - sym->offset);
return sym->name;
}
}
return NULL;
}
/*
* Copyright (c) 2003, 2010, 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
* 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 _SYMTAB_H_
#define _SYMTAB_H_
#include <stdint.h>
// interface to manage ELF symbol tables
struct symtab;
// build symbol table for a given ELF file descriptor
struct symtab* build_symtab(int fd);
// destroy the symbol table
void destroy_symtab(struct symtab* symtab);
// search for symbol in the given symbol table. Adds offset
// to the base uintptr_t supplied. Returns NULL if not found.
uintptr_t search_symbol(struct symtab* symtab, uintptr_t base,
const char *sym_name, int *sym_size);
// look for nearest symbol for a given offset (not address - base
// subtraction done by caller
const char* nearest_symbol(struct symtab* symtab, uintptr_t offset,
uintptr_t* poffset);
#endif /*_SYMTAB_H_*/
/*
* Copyright (c) 2003, 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
* 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 <stdio.h>
#include <stdlib.h>
#include "libproc.h"
int main(int argc, char** argv) {
struct ps_prochandle* ph;
init_libproc(true);
switch (argc) {
case 2: {
// process
ph = Pgrab(atoi(argv[1]));
break;
}
case 3: {
// core
ph = Pgrab_core(argv[1], argv[2]);
break;
}
default: {
fprintf(stderr, "usage %s <pid> or %s <exec file> <core file>\n", argv[0], argv[0]);
return 1;
}
}
if (ph) {
Prelease(ph);
return 0;
} else {
printf("can't connect to debuggee\n");
return 1;
}
}
/*
* Copyright (c) 2002, 2003, 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
* 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;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.types.*;
import sun.jvm.hotspot.types.basic.*;
public class BsdVtblAccess extends BasicVtblAccess {
private String vt;
public BsdVtblAccess(SymbolLookup symbolLookup,
String[] dllNames) {
super(symbolLookup, dllNames);
if (symbolLookup.lookup("libjvm.so", "__vt_10JavaThread") != null ||
symbolLookup.lookup("libjvm_g.so", "__vt_10JavaThread") != null) {
// old C++ ABI
vt = "__vt_";
} else {
// new C++ ABI
vt = "_ZTV";
}
}
protected String vtblSymbolForType(Type type) {
return vt + type.getName().length() + type;
}
}
...@@ -28,6 +28,7 @@ import java.io.PrintStream; ...@@ -28,6 +28,7 @@ import java.io.PrintStream;
import java.net.*; import java.net.*;
import java.rmi.*; import java.rmi.*;
import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.debugger.bsd.*;
import sun.jvm.hotspot.debugger.proc.*; import sun.jvm.hotspot.debugger.proc.*;
import sun.jvm.hotspot.debugger.remote.*; import sun.jvm.hotspot.debugger.remote.*;
import sun.jvm.hotspot.debugger.windbg.*; import sun.jvm.hotspot.debugger.windbg.*;
...@@ -335,6 +336,8 @@ public class HotSpotAgent { ...@@ -335,6 +336,8 @@ public class HotSpotAgent {
setupDebuggerWin32(); setupDebuggerWin32();
} else if (os.equals("linux")) { } else if (os.equals("linux")) {
setupDebuggerLinux(); setupDebuggerLinux();
} else if (os.equals("bsd")) {
setupDebuggerBsd();
} else { } else {
// Add support for more operating systems here // Add support for more operating systems here
throw new DebuggerException("Operating system " + os + " not yet supported"); throw new DebuggerException("Operating system " + os + " not yet supported");
...@@ -390,6 +393,10 @@ public class HotSpotAgent { ...@@ -390,6 +393,10 @@ public class HotSpotAgent {
db = new HotSpotTypeDataBase(machDesc, db = new HotSpotTypeDataBase(machDesc,
new LinuxVtblAccess(debugger, jvmLibNames), new LinuxVtblAccess(debugger, jvmLibNames),
debugger, jvmLibNames); debugger, jvmLibNames);
} else if (os.equals("bsd")) {
db = new HotSpotTypeDataBase(machDesc,
new BsdVtblAccess(debugger, jvmLibNames),
debugger, jvmLibNames);
} else { } else {
throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess yet)"); throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess yet)");
} }
...@@ -477,6 +484,8 @@ public class HotSpotAgent { ...@@ -477,6 +484,8 @@ public class HotSpotAgent {
setupJVMLibNamesWin32(); setupJVMLibNamesWin32();
} else if (os.equals("linux")) { } else if (os.equals("linux")) {
setupJVMLibNamesLinux(); setupJVMLibNamesLinux();
} else if (os.equals("bsd")) {
setupJVMLibNamesBsd();
} else { } else {
throw new RuntimeException("Unknown OS type"); throw new RuntimeException("Unknown OS type");
} }
...@@ -554,6 +563,31 @@ public class HotSpotAgent { ...@@ -554,6 +563,31 @@ public class HotSpotAgent {
jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" }; jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" };
} }
//
// BSD
//
private void setupDebuggerBsd() {
setupJVMLibNamesBsd();
if (cpu.equals("x86")) {
machDesc = new MachineDescriptionIntelX86();
} else if (cpu.equals("amd64")) {
machDesc = new MachineDescriptionAMD64();
} else {
throw new DebuggerException("BSD only supported on x86/amd64");
}
BsdDebuggerLocal dbg = new BsdDebuggerLocal(machDesc, !isServer);
debugger = dbg;
attachDebugger();
}
private void setupJVMLibNamesBsd() {
jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" };
}
/** Convenience routine which should be called by per-platform /** Convenience routine which should be called by per-platform
debugger setup. Should not be called when startupMode is debugger setup. Should not be called when startupMode is
REMOTE_MODE. */ REMOTE_MODE. */
......
...@@ -29,6 +29,7 @@ import java.net.*; ...@@ -29,6 +29,7 @@ import java.net.*;
import java.rmi.*; import java.rmi.*;
import sun.jvm.hotspot.*; import sun.jvm.hotspot.*;
import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.debugger.bsd.*;
import sun.jvm.hotspot.debugger.proc.*; import sun.jvm.hotspot.debugger.proc.*;
import sun.jvm.hotspot.debugger.cdbg.*; import sun.jvm.hotspot.debugger.cdbg.*;
import sun.jvm.hotspot.debugger.windbg.*; import sun.jvm.hotspot.debugger.windbg.*;
...@@ -514,6 +515,8 @@ public class BugSpotAgent { ...@@ -514,6 +515,8 @@ public class BugSpotAgent {
setupDebuggerWin32(); setupDebuggerWin32();
} else if (os.equals("linux")) { } else if (os.equals("linux")) {
setupDebuggerLinux(); setupDebuggerLinux();
} else if (os.equals("bsd")) {
setupDebuggerBsd();
} else { } else {
// Add support for more operating systems here // Add support for more operating systems here
throw new DebuggerException("Operating system " + os + " not yet supported"); throw new DebuggerException("Operating system " + os + " not yet supported");
...@@ -565,6 +568,9 @@ public class BugSpotAgent { ...@@ -565,6 +568,9 @@ public class BugSpotAgent {
} else if (os.equals("linux")) { } else if (os.equals("linux")) {
db = new HotSpotTypeDataBase(machDesc, new LinuxVtblAccess(debugger, jvmLibNames), db = new HotSpotTypeDataBase(machDesc, new LinuxVtblAccess(debugger, jvmLibNames),
debugger, jvmLibNames); debugger, jvmLibNames);
} else if (os.equals("bsd")) {
db = new HotSpotTypeDataBase(machDesc, new BsdVtblAccess(debugger, jvmLibNames),
debugger, jvmLibNames);
} else { } else {
throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess implemented yet)"); throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess implemented yet)");
} }
...@@ -666,6 +672,8 @@ public class BugSpotAgent { ...@@ -666,6 +672,8 @@ public class BugSpotAgent {
setupJVMLibNamesWin32(); setupJVMLibNamesWin32();
} else if (os.equals("linux")) { } else if (os.equals("linux")) {
setupJVMLibNamesLinux(); setupJVMLibNamesLinux();
} else if (os.equals("bsd")) {
setupJVMLibNamesBsd();
} else { } else {
throw new RuntimeException("Unknown OS type"); throw new RuntimeException("Unknown OS type");
} }
...@@ -745,6 +753,34 @@ public class BugSpotAgent { ...@@ -745,6 +753,34 @@ public class BugSpotAgent {
setupJVMLibNamesSolaris(); setupJVMLibNamesSolaris();
} }
//
// BSD
//
private void setupDebuggerBsd() {
setupJVMLibNamesBsd();
if (cpu.equals("x86")) {
machDesc = new MachineDescriptionIntelX86();
} else if (cpu.equals("amd64")) {
machDesc = new MachineDescriptionAMD64();
} else {
throw new DebuggerException("Bsd only supported on x86/amd64");
}
// Note we do not use a cache for the local debugger in server
// mode; it will be taken care of on the client side (once remote
// debugging is implemented).
debugger = new BsdDebuggerLocal(machDesc, !isServer);
attachDebugger();
}
private void setupJVMLibNamesBsd() {
// same as solaris
setupJVMLibNamesSolaris();
}
/** Convenience routine which should be called by per-platform /** Convenience routine which should be called by per-platform
debugger setup. Should not be called when startupMode is debugger setup. Should not be called when startupMode is
REMOTE_MODE. */ REMOTE_MODE. */
......
/*
* Copyright (c) 2002, 2008, 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
* 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.bsd;
import sun.jvm.hotspot.debugger.*;
class BsdAddress implements Address {
protected BsdDebugger debugger;
protected long addr;
BsdAddress(BsdDebugger debugger, long addr) {
this.debugger = debugger;
this.addr = addr;
}
//
// Basic Java routines
//
public boolean equals(Object arg) {
if (arg == null) {
return false;
}
if (!(arg instanceof BsdAddress)) {
return false;
}
return (addr == ((BsdAddress) arg).addr);
}
public int hashCode() {
// FIXME: suggestions on a better hash code?
return (int) addr;
}
public String toString() {
return debugger.addressValueToString(addr);
}
//
// C/C++-related routines
//
public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned)
throws UnalignedAddressException, UnmappedAddressException {
return debugger.readCInteger(addr + offset, numBytes, isUnsigned);
}
public Address getAddressAt(long offset)
throws UnalignedAddressException, UnmappedAddressException {
return debugger.readAddress(addr + offset);
}
public Address getCompOopAddressAt(long offset)
throws UnalignedAddressException, UnmappedAddressException {
return debugger.readCompOopAddress(addr + offset);
}
//
// Java-related routines
//
public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
return debugger.readJBoolean(addr + offset);
}
public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
return debugger.readJByte(addr + offset);
}
public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
return debugger.readJChar(addr + offset);
}
public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
return debugger.readJDouble(addr + offset);
}
public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
return debugger.readJFloat(addr + offset);
}
public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
return debugger.readJInt(addr + offset);
}
public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
return debugger.readJLong(addr + offset);
}
public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
return debugger.readJShort(addr + offset);
}
public OopHandle getOopHandleAt(long offset)
throws UnalignedAddressException, UnmappedAddressException, NotInHeapException {
return debugger.readOopHandle(addr + offset);
}
public OopHandle getCompOopHandleAt(long offset)
throws UnalignedAddressException, UnmappedAddressException, NotInHeapException {
return debugger.readCompOopHandle(addr + offset);
}
// Mutators -- not implemented for now (FIXME)
public void setCIntegerAt(long offset, long numBytes, long value) {
throw new DebuggerException("Unimplemented");
}
public void setAddressAt(long offset, Address value) {
throw new DebuggerException("Unimplemented");
}
public void setJBooleanAt (long offset, boolean value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
public void setJByteAt (long offset, byte value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
public void setJCharAt (long offset, char value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
public void setJDoubleAt (long offset, double value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
public void setJFloatAt (long offset, float value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
public void setJIntAt (long offset, int value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
public void setJLongAt (long offset, long value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
public void setJShortAt (long offset, short value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
public void setOopHandleAt (long offset, OopHandle value)
throws UnmappedAddressException, UnalignedAddressException {
throw new DebuggerException("Unimplemented");
}
//
// Arithmetic operations -- necessary evil.
//
public Address addOffsetTo (long offset) throws UnsupportedOperationException {
long value = addr + offset;
if (value == 0) {
return null;
}
return new BsdAddress(debugger, value);
}
public OopHandle addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException {
long value = addr + offset;
if (value == 0) {
return null;
}
return new BsdOopHandle(debugger, value);
}
/** (FIXME: any signed/unsigned issues? Should this work for
OopHandles?) */
public long minus(Address arg) {
if (arg == null) {
return addr;
}
return addr - ((BsdAddress) arg).addr;
}
// Two's complement representation.
// All negative numbers are larger than positive numbers.
// Numbers with the same sign can be compared normally.
// Test harness is below in main().
public boolean lessThan (Address a) {
if (a == null) {
return false;
}
BsdAddress arg = (BsdAddress) a;
if ((addr >= 0) && (arg.addr < 0)) {
return true;
}
if ((addr < 0) && (arg.addr >= 0)) {
return false;
}
return (addr < arg.addr);
}
public boolean lessThanOrEqual (Address a) {
if (a == null) {
return false;
}
BsdAddress arg = (BsdAddress) a;
if ((addr >= 0) && (arg.addr < 0)) {
return true;
}
if ((addr < 0) && (arg.addr >= 0)) {
return false;
}
return (addr <= arg.addr);
}
public boolean greaterThan (Address a) {
if (a == null) {
return true;
}
BsdAddress arg = (BsdAddress) a;
if ((addr >= 0) && (arg.addr < 0)) {
return false;
}
if ((addr < 0) && (arg.addr >= 0)) {
return true;
}
return (addr > arg.addr);
}
public boolean greaterThanOrEqual(Address a) {
if (a == null) {
return true;
}
BsdAddress arg = (BsdAddress) a;
if ((addr >= 0) && (arg.addr < 0)) {
return false;
}
if ((addr < 0) && (arg.addr >= 0)) {
return true;
}
return (addr >= arg.addr);
}
public Address andWithMask(long mask) throws UnsupportedOperationException {
long value = addr & mask;
if (value == 0) {
return null;
}
return new BsdAddress(debugger, value);
}
public Address orWithMask(long mask) throws UnsupportedOperationException {
long value = addr | mask;
if (value == 0) {
return null;
}
return new BsdAddress(debugger, value);
}
public Address xorWithMask(long mask) throws UnsupportedOperationException {
long value = addr ^ mask;
if (value == 0) {
return null;
}
return new BsdAddress(debugger, value);
}
//--------------------------------------------------------------------------------
// Internals only below this point
//
long getValue() {
return addr;
}
private static void check(boolean arg, String failMessage) {
if (!arg) {
System.err.println(failMessage + ": FAILED");
System.exit(1);
}
}
// Test harness
public static void main(String[] args) {
// p/n indicates whether the interior address is really positive
// or negative. In unsigned terms, p1 < p2 < n1 < n2.
BsdAddress p1 = new BsdAddress(null, 0x7FFFFFFFFFFFFFF0L);
BsdAddress p2 = (BsdAddress) p1.addOffsetTo(10);
BsdAddress n1 = (BsdAddress) p2.addOffsetTo(10);
BsdAddress n2 = (BsdAddress) n1.addOffsetTo(10);
// lessThan positive tests
check(p1.lessThan(p2), "lessThan 1");
check(p1.lessThan(n1), "lessThan 2");
check(p1.lessThan(n2), "lessThan 3");
check(p2.lessThan(n1), "lessThan 4");
check(p2.lessThan(n2), "lessThan 5");
check(n1.lessThan(n2), "lessThan 6");
// lessThan negative tests
check(!p1.lessThan(p1), "lessThan 7");
check(!p2.lessThan(p2), "lessThan 8");
check(!n1.lessThan(n1), "lessThan 9");
check(!n2.lessThan(n2), "lessThan 10");
check(!p2.lessThan(p1), "lessThan 11");
check(!n1.lessThan(p1), "lessThan 12");
check(!n2.lessThan(p1), "lessThan 13");
check(!n1.lessThan(p2), "lessThan 14");
check(!n2.lessThan(p2), "lessThan 15");
check(!n2.lessThan(n1), "lessThan 16");
// lessThanOrEqual positive tests
check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1");
check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2");
check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3");
check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4");
check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5");
check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6");
check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7");
check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8");
check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9");
check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10");
// lessThanOrEqual negative tests
check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11");
check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12");
check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13");
check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14");
check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15");
check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16");
// greaterThan positive tests
check(n2.greaterThan(p1), "greaterThan 1");
check(n2.greaterThan(p2), "greaterThan 2");
check(n2.greaterThan(n1), "greaterThan 3");
check(n1.greaterThan(p1), "greaterThan 4");
check(n1.greaterThan(p2), "greaterThan 5");
check(p2.greaterThan(p1), "greaterThan 6");
// greaterThan negative tests
check(!p1.greaterThan(p1), "greaterThan 7");
check(!p2.greaterThan(p2), "greaterThan 8");
check(!n1.greaterThan(n1), "greaterThan 9");
check(!n2.greaterThan(n2), "greaterThan 10");
check(!p1.greaterThan(n2), "greaterThan 11");
check(!p2.greaterThan(n2), "greaterThan 12");
check(!n1.greaterThan(n2), "greaterThan 13");
check(!p1.greaterThan(n1), "greaterThan 14");
check(!p2.greaterThan(n1), "greaterThan 15");
check(!p1.greaterThan(p2), "greaterThan 16");
// greaterThanOrEqual positive tests
check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1");
check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2");
check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3");
check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4");
check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5");
check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6");
check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7");
check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8");
check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9");
check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10");
// greaterThanOrEqual negative tests
check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11");
check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12");
check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13");
check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14");
check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15");
check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16");
System.err.println("BsdAddress: all tests passed successfully.");
}
}
/*
* Copyright (c) 2003, 2006, 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
* 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.bsd;
import java.io.*;
import java.util.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.debugger.cdbg.*;
import sun.jvm.hotspot.debugger.x86.*;
import sun.jvm.hotspot.debugger.amd64.*;
import sun.jvm.hotspot.debugger.bsd.x86.*;
import sun.jvm.hotspot.debugger.bsd.amd64.*;
import sun.jvm.hotspot.utilities.*;
class BsdCDebugger implements CDebugger {
private BsdDebugger dbg;
BsdCDebugger(BsdDebugger dbg) {
this.dbg = dbg;
}
public List getThreadList() throws DebuggerException {
return dbg.getThreadList();
}
public List/*<LoadObject>*/ getLoadObjectList() throws DebuggerException {
return dbg.getLoadObjectList();
}
public LoadObject loadObjectContainingPC(Address pc) throws DebuggerException {
if (pc == null) {
return null;
}
List objs = getLoadObjectList();
Object[] arr = objs.toArray();
// load objects are sorted by base address, do binary search
int mid = -1;
int low = 0;
int high = arr.length - 1;
while (low <= high) {
mid = (low + high) >> 1;
LoadObject midVal = (LoadObject) arr[mid];
long cmp = pc.minus(midVal.getBase());
if (cmp < 0) {
high = mid - 1;
} else if (cmp > 0) {
long size = midVal.getSize();
if (cmp >= size) {
low = mid + 1;
} else {
return (LoadObject) arr[mid];
}
} else { // match found
return (LoadObject) arr[mid];
}
}
// no match found.
return null;
}
public CFrame topFrameForThread(ThreadProxy thread) throws DebuggerException {
String cpu = dbg.getCPU();
if (cpu.equals("x86")) {
X86ThreadContext context = (X86ThreadContext) thread.getContext();
Address ebp = context.getRegisterAsAddress(X86ThreadContext.EBP);
if (ebp == null) return null;
Address pc = context.getRegisterAsAddress(X86ThreadContext.EIP);
if (pc == null) return null;
return new BsdX86CFrame(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 BsdAMD64CFrame(dbg, rbp, pc);
} else {
throw new DebuggerException(cpu + " is not yet supported");
}
}
public String getNameOfFile(String fileName) {
return new File(fileName).getName();
}
public ProcessControl getProcessControl() throws DebuggerException {
// FIXME: after stabs parser
return null;
}
public boolean canDemangle() {
return false;
}
public String demangle(String sym) {
throw new UnsupportedOperationException();
}
}
/*
* Copyright (c) 2002, 2008, 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
* 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.bsd;
import java.util.List;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.debugger.cdbg.*;
/** An extension of the JVMDebugger interface with a few additions to
support 32-bit vs. 64-bit debugging as well as features required
by the architecture-specific subpackages. */
public interface BsdDebugger extends JVMDebugger {
public String addressValueToString(long address) throws DebuggerException;
public boolean readJBoolean(long address) throws DebuggerException;
public byte readJByte(long address) throws DebuggerException;
public char readJChar(long address) throws DebuggerException;
public double readJDouble(long address) throws DebuggerException;
public float readJFloat(long address) throws DebuggerException;
public int readJInt(long address) throws DebuggerException;
public long readJLong(long address) throws DebuggerException;
public short readJShort(long address) throws DebuggerException;
public long readCInteger(long address, long numBytes, boolean isUnsigned)
throws DebuggerException;
public BsdAddress readAddress(long address) throws DebuggerException;
public BsdAddress readCompOopAddress(long address) throws DebuggerException;
public BsdOopHandle readOopHandle(long address) throws DebuggerException;
public BsdOopHandle readCompOopHandle(long address) throws DebuggerException;
public long[] getThreadIntegerRegisterSet(int lwp_id) throws DebuggerException;
public long getAddressValue(Address addr) throws DebuggerException;
public Address newAddress(long value) throws DebuggerException;
// For BsdCDebugger
public List getThreadList();
public List getLoadObjectList();
public ClosestSymbol lookup(long address);
// NOTE: this interface implicitly contains the following methods:
// From the Debugger interface via JVMDebugger
// public void attach(int processID) throws DebuggerException;
// public void attach(String executableName, String coreFileName) throws DebuggerException;
// public boolean detach();
// public Address parseAddress(String addressString) throws NumberFormatException;
// public String getOS();
// public String getCPU();
// From the SymbolLookup interface via Debugger and JVMDebugger
// public Address lookup(String objectName, String symbol);
// public OopHandle lookupOop(String objectName, String symbol);
// From the JVMDebugger interface
// public void configureJavaPrimitiveTypeSizes(long jbooleanSize,
// long jbyteSize,
// long jcharSize,
// long jdoubleSize,
// long jfloatSize,
// long jintSize,
// long jlongSize,
// long jshortSize);
// From the ThreadAccess interface via Debugger and JVMDebugger
// public ThreadProxy getThreadForIdentifierAddress(Address addr);
}
/*
* Copyright (c) 2002, 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
* 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.bsd;
import sun.jvm.hotspot.debugger.*;
class BsdOopHandle extends BsdAddress implements OopHandle {
BsdOopHandle(BsdDebugger debugger, long addr) {
super(debugger, addr);
}
public boolean equals(Object arg) {
if (arg == null) {
return false;
}
if (!(arg instanceof BsdOopHandle)) {
return false;
}
return (addr == ((BsdAddress) arg).addr);
}
public Address addOffsetTo (long offset) throws UnsupportedOperationException {
throw new UnsupportedOperationException("addOffsetTo not applicable to OopHandles (interior object pointers not allowed)");
}
public Address andWithMask(long mask) throws UnsupportedOperationException {
throw new UnsupportedOperationException("andWithMask not applicable to OopHandles (i.e., anything but C addresses)");
}
public Address orWithMask(long mask) throws UnsupportedOperationException {
throw new UnsupportedOperationException("orWithMask not applicable to OopHandles (i.e., anything but C addresses)");
}
public Address xorWithMask(long mask) throws UnsupportedOperationException {
throw new UnsupportedOperationException("xorWithMask not applicable to OopHandles (i.e., anything but C addresses)");
}
}
/*
* Copyright (c) 2002, 2003, 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
* 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.bsd;
import sun.jvm.hotspot.debugger.*;
class BsdThread implements ThreadProxy {
private BsdDebugger debugger;
private int lwp_id;
/** The address argument must be the address of the _thread_id in the
OSThread. It's value is result ::gettid() call. */
BsdThread(BsdDebugger debugger, Address addr) {
this.debugger = debugger;
// FIXME: size of data fetched here should be configurable.
// However, making it so would produce a dependency on the "types"
// package from the debugger package, which is not desired.
this.lwp_id = (int) addr.getCIntegerAt(0, 4, true);
}
BsdThread(BsdDebugger debugger, long id) {
this.debugger = debugger;
this.lwp_id = (int) id;
}
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof BsdThread)) {
return false;
}
return (((BsdThread) obj).lwp_id == lwp_id);
}
public int hashCode() {
return lwp_id;
}
public String toString() {
return Integer.toString(lwp_id);
}
public ThreadContext getContext() throws IllegalThreadStateException {
long[] data = debugger.getThreadIntegerRegisterSet(lwp_id);
ThreadContext context = BsdThreadContextFactory.createThreadContext(debugger);
for (int i = 0; i < data.length; i++) {
context.setRegister(i, data[i]);
}
return context;
}
public boolean canSetContext() throws DebuggerException {
return false;
}
public void setContext(ThreadContext context)
throws IllegalThreadStateException, DebuggerException {
throw new DebuggerException("Unimplemented");
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册