From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 2585 invoked by alias); 27 May 2008 02:06:59 -0000 Received: (qmail 2378 invoked by uid 367); 27 May 2008 02:06:59 -0000 Date: Tue, 27 May 2008 02:06:00 -0000 Message-ID: <20080527020659.2346.qmail@sourceware.org> From: cagney@sourceware.org To: frysk-cvs@sourceware.org Subject: [SCM] master: Implement jni libunwind. X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: ea1db5aa17cab4e9616eb07a86737d76560d6904 X-Git-Newrev: 435b6331ffaa61dab0feb8e2c988f0f062ee96f3 Mailing-List: contact frysk-cvs-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: frysk-cvs-owner@sourceware.org Reply-To: frysk@sourceware.org X-SW-Source: 2008-q2/txt/msg00294.txt.bz2 The branch, master has been updated via 435b6331ffaa61dab0feb8e2c988f0f062ee96f3 (commit) from ea1db5aa17cab4e9616eb07a86737d76560d6904 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email. - Log ----------------------------------------------------------------- commit 435b6331ffaa61dab0feb8e2c988f0f062ee96f3 Author: Andrew Cagney Date: Mon May 26 22:04:33 2008 -0400 Implement jni libunwind. frysk-sys/ChangeLog 2008-05-26 Andrew Cagney * Makefile.am (JNIXX_CLASSES): Add UnwindRegistersX86 et.al. frysk-sys/lib/unwind/ChangeLog 2008-05-26 Andrew Cagney * jni/ElfImage.cxx: Delete. * jni/UnwindH.hxx: Implement. * jni/UnwindX86.cxx: Implement. * jni/UnwindX8664.cxx: Implement. * jni/UnwindPPC32.cxx: Implement. * jni/UnwindPPC64.cxx: Implement. ----------------------------------------------------------------------- Summary of changes: frysk-sys/ChangeLog | 2 + frysk-sys/Makefile.am | 18 +- frysk-sys/lib/unwind/ChangeLog | 9 + frysk-sys/lib/unwind/jni/ElfImage.cxx | 40 -- frysk-sys/lib/unwind/jni/UnwindH.hxx | 862 ++++++++++++++++++++++++++++++ frysk-sys/lib/unwind/jni/UnwindPPC32.cxx | 12 +- frysk-sys/lib/unwind/jni/UnwindPPC64.cxx | 12 +- frysk-sys/lib/unwind/jni/UnwindX86.cxx | 12 +- frysk-sys/lib/unwind/jni/UnwindX8664.cxx | 12 +- 9 files changed, 922 insertions(+), 57 deletions(-) delete mode 100644 frysk-sys/lib/unwind/jni/ElfImage.cxx create mode 100644 frysk-sys/lib/unwind/jni/UnwindH.hxx First 500 lines of diff: diff --git a/frysk-sys/ChangeLog b/frysk-sys/ChangeLog index 16a563b..08aad82 100644 --- a/frysk-sys/ChangeLog +++ b/frysk-sys/ChangeLog @@ -1,5 +1,7 @@ 2008-05-26 Andrew Cagney + * Makefile.am (JNIXX_CLASSES): Add UnwindRegistersX86 et.al. + * Makefile.am (JniRunner): Load libdw and libelf. * Makefile.am (JNIXX_CLASSES): Add DwAttributeNotFoundException, diff --git a/frysk-sys/Makefile.am b/frysk-sys/Makefile.am index 2e342ed..4efa836 100644 --- a/frysk-sys/Makefile.am +++ b/frysk-sys/Makefile.am @@ -79,6 +79,10 @@ JNIXX_CLASSES += frysk.sys.Errno$$Esrch JNIXX_CLASSES += lib.dwfl.DwAttributeNotFoundException JNIXX_CLASSES += java.lang.Long JNIXX_CLASSES += lib.dwfl.ElfException +JNIXX_CLASSES += lib.unwind.UnwindRegistersPPC32 +JNIXX_CLASSES += lib.unwind.UnwindRegistersPPC64 +JNIXX_CLASSES += lib.unwind.UnwindRegistersX86 +JNIXX_CLASSES += lib.unwind.UnwindRegistersX8664 # Quick hack to get a test JNI program up-and-running; as a package is @@ -89,12 +93,24 @@ CLEANFILES += JniRunner JniRunner: | frysk-sys.jar libfrysk-sys-jni.so rm -f $@.tmp echo "#!/bin/sh" >> $@.tmp - echo "export LD_PRELOAD=\"libstdc++.so.6 libdw.so libelf.so\"" >> $@.tmp + echo "export LD_PRELOAD=\"\\" >> $@.tmp + echo "libstdc++.so.6 \\" >> $@.tmp + echo "libdw.so \\" >> $@.tmp + echo "libelf.so \\" >> $@.tmp + echo "libunwind-x86.so \\" >> $@.tmp + echo "libunwind-ppc32.so \\" >> $@.tmp + echo "libunwind-ppc64.so \\" >> $@.tmp + echo "libunwind-x86_64.so \\" >> $@.tmp + echo "\"" >> $@.tmp echo "# hack to hopefully find the right libstdc++.so" >> $@.tmp echo "export LD_LIBRARY_PATH=.\\" >> $@.tmp echo ":$$(cd ../frysk-imports/elfutils/libdw && pwd)\\" >> $@.tmp echo ":$$(cd ../frysk-imports/elfutils/libelf && pwd)\\" >> $@.tmp echo ":$$(cd ../frysk-imports/elfutils/backends && pwd)\\" >> $@.tmp + echo ":$$(cd ../frysk-imports/libunwind-i386/src/.libs && pwd)\\" >> $@.tmp + echo ":$$(cd ../frysk-imports/libunwind-ppc32/src/.libs && pwd)\\" >> $@.tmp + echo ":$$(cd ../frysk-imports/libunwind-ppc64/src/.libs && pwd)\\" >> $@.tmp + echo ":$$(cd ../frysk-imports/libunwind-x86_64/src/.libs && pwd)\\" >> $@.tmp echo ":/usr/lib64\\" >> $@.tmp echo ":/usr/lib" >> $@.tmp echo "export CLASSPATH=\\" >> $@.tmp diff --git a/frysk-sys/lib/unwind/ChangeLog b/frysk-sys/lib/unwind/ChangeLog index d17582d..39bcd79 100644 --- a/frysk-sys/lib/unwind/ChangeLog +++ b/frysk-sys/lib/unwind/ChangeLog @@ -1,3 +1,12 @@ +2008-05-26 Andrew Cagney + + * jni/ElfImage.cxx: Delete. + * jni/UnwindH.hxx: Implement. + * jni/UnwindX86.cxx: Implement. + * jni/UnwindX8664.cxx: Implement. + * jni/UnwindPPC32.cxx: Implement. + * jni/UnwindPPC64.cxx: Implement. + 2008-05-24 Andrew Cagney * cni/UnwindH.hxx: Fix 32-bit type casts. diff --git a/frysk-sys/lib/unwind/jni/ElfImage.cxx b/frysk-sys/lib/unwind/jni/ElfImage.cxx deleted file mode 100644 index b358932..0000000 --- a/frysk-sys/lib/unwind/jni/ElfImage.cxx +++ /dev/null @@ -1,40 +0,0 @@ -// This file is part of the program FRYSK. -// -// Copyright 2008, Red Hat Inc. -// -// FRYSK is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by -// the Free Software Foundation; version 2 of the License. -// -// FRYSK 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 for more details. -// -// You should have received a copy of the GNU General Public License -// along with FRYSK; if not, write to the Free Software Foundation, -// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -// -// In addition, as a special exception, Red Hat, Inc. gives You the -// additional right to link the code of FRYSK with code not covered -// under the GNU General Public License ("Non-GPL Code") and to -// distribute linked combinations including the two, subject to the -// limitations in this paragraph. Non-GPL Code permitted under this -// exception must only link to the code of FRYSK through those well -// defined interfaces identified in the file named EXCEPTION found in -// the source code files (the "Approved Interfaces"). The files of -// Non-GPL Code may instantiate templates or use macros or inline -// functions from the Approved Interfaces without causing the -// resulting work to be covered by the GNU General Public -// License. Only Red Hat, Inc. may make changes or additions to the -// list of Approved Interfaces. You must obey the GNU General Public -// License in all respects for all of the FRYSK code and other code -// used in conjunction with FRYSK except the Non-GPL Code covered by -// this exception. If you modify this file, you may extend this -// exception to your version of the file, but you are not obligated to -// do so. If you do not wish to provide this exception without -// modification, you must delete this exception statement from your -// version and license this file solely under the GPL without -// exception. - -#include "jni.hxx" diff --git a/frysk-sys/lib/unwind/jni/UnwindH.hxx b/frysk-sys/lib/unwind/jni/UnwindH.hxx new file mode 100644 index 0000000..3f42723 --- /dev/null +++ b/frysk-sys/lib/unwind/jni/UnwindH.hxx @@ -0,0 +1,862 @@ +// This file is part of the program FRYSK. +// +// Copyright 2007, 2008, Red Hat Inc. +// +// FRYSK is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// FRYSK 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 for more details. +// +// You should have received a copy of the GNU General Public License +// along with FRYSK; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// In addition, as a special exception, Red Hat, Inc. gives You the +// additional right to link the code of FRYSK with code not covered +// under the GNU General Public License ("Non-GPL Code") and to +// distribute linked combinations including the two, subject to the +// limitations in this paragraph. Non-GPL Code permitted under this +// exception must only link to the code of FRYSK through those well +// defined interfaces identified in the file named EXCEPTION found in +// the source code files (the "Approved Interfaces"). The files of +// Non-GPL Code may instantiate templates or use macros or inline +// functions from the Approved Interfaces without causing the +// resulting work to be covered by the GNU General Public +// License. Only Red Hat, Inc. may make changes or additions to the +// list of Approved Interfaces. You must obey the GNU General Public +// License in all respects for all of the FRYSK code and other code +// used in conjunction with FRYSK except the Non-GPL Code covered by +// this exception. If you modify this file, you may extend this +// exception to your version of the file, but you are not obligated to +// do so. If you do not wish to provide this exception without +// modification, you must delete this exception statement from your +// version and license this file solely under the GPL without +// exception. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include LIBUNWIND_TARGET_H + +#include +#include + +#include "jni.hxx" + +#include "jnixx/exceptions.hxx" +#include "jnixx/logging.hxx" +#include "jnixx/elements.hxx" +#include "jnixx/bounds.hxx" + +using namespace java::lang; +using namespace lib::unwind; + +#define UNW_PROC_INFO ((unw_proc_info_t*)unwProcInfo) +#define UNW_ADDRESS_SPACE ((unw_addr_space_t)unwAddressSpace) +#define UNW_CURSOR ((unw_cursor_t*)unwCursor) +#define FINE env, (GetFine(env)) + +#ifndef MAX_VDSO_SIZE +# define MAX_VDSO_SIZE ((size_t) sysconf (_SC_PAGESIZE)) +#endif + +/** + * The address space is going to be accessed in different JNI contexts + * to when it is passed to libunwind. Consequently, need to get the + * ENV from JNI and can't rely on it being passed in. + */ +#define UNW_CONTEXT \ + jnixx::env env = Object::_env_(); \ + AddressSpace addressSpace = AddressSpace(env, (jobject)contextArg) + + +/* + * Callback: Get misc. proc info + */ +static int +find_proc_info(::unw_addr_space_t as, ::unw_word_t ip, + ::unw_proc_info_t *pip, int need_unwind_info, + void *contextArg) { + UNW_CONTEXT; + ProcInfo procInfo = ProcInfo::New(env, + addressSpace.GetUnwinder(env), + (long) ::malloc(sizeof(unw_proc_info_t))); + int ok = addressSpace.findProcInfo(env, (jlong)ip, + (jboolean)need_unwind_info, + procInfo); + if (ok < 0) + return ok; + // Extract the info. + memcpy(pip, (void*) procInfo.GetUnwProcInfo(env), sizeof (unw_proc_info_t)); + procInfo.DeleteLocalRef(env); + return 0; +} + +/* + * Callback: Free space allocated during find_proc_info + */ +static void +put_unwind_info(::unw_addr_space_t as, ::unw_proc_info_t *proc_info, + void *contextArg) { + UNW_CONTEXT; + // This is passing up a stack pointer, which may then be freed. + ProcInfo procInfo = ProcInfo::New(env, addressSpace.GetUnwinder(env), + (jlong) proc_info); + addressSpace.putUnwindInfo(env, procInfo); +} + +/* + * Callback: Get the head of the dynamic unwind registration list. + * There is never any dynamic info in our case. + */ +static int +get_dyn_info_list_addr(::unw_addr_space_t as, ::unw_word_t *dilap, + void *contextArg) { + return -UNW_ENOINFO; +} + +/* + * Callback: Perform memory read/write. Implement as copy-in, + * copy-out. + */ +static int +access_mem(::unw_addr_space_t as, ::unw_word_t addr, + ::unw_word_t *valp, int write, void *contextArg) { + UNW_CONTEXT; + try { + jnixx::jbyteArray jtmp + = jnixx::jbyteArray::NewByteArray(env, sizeof(unw_word_t)); + jbyteArrayElements tmp = jbyteArrayElements(env, jtmp); + memcpy(tmp.elements(), valp, sizeof(unw_word_t)); + tmp.release(); + int ret = addressSpace.accessMem(env, (jlong) addr, + jtmp, (jboolean) write); + memcpy(valp, tmp.elements(), sizeof(unw_word_t)); + tmp.release(); + jtmp.DeleteLocalRef(env); + return ret; + } catch (RuntimeException *t) { + // We have to catch all RuntimeExceptions here since there + // is no indicator for just "invalid memory location". + // Core files might have "holes" in their memory. + return -UNW_EINVAL; + } +} + +/* + * Callback: perform register read/write + */ +static int +access_reg(::unw_addr_space_t as, ::unw_regnum_t regnum, + ::unw_word_t *valp, int write, void *contextArg) { + UNW_CONTEXT; + jnixx::jbyteArray jtmp + = jnixx::jbyteArray::NewByteArray(env, sizeof(unw_word_t)); + jbyteArrayElements tmp = jbyteArrayElements(env, jtmp); + // Map the REGNUM back to the published ENUM. + Number num = TARGET_REGISTERS::valueOf(env, regnum); + ::memcpy(tmp.elements(), valp, sizeof(unw_word_t)); + tmp.release(); + if (write) + addressSpace.setReg(env, num, *valp); + else + *valp = addressSpace.getReg(env, num); + num.DeleteLocalRef(env); + jtmp.DeleteLocalRef(env); + return 0; +} + +/* + * Callback: Perform a floating point register read/write + */ +static int +access_fpreg(::unw_addr_space_t as, ::unw_regnum_t regnum, + ::unw_fpreg_t *fpvalp, int write, void *contextArg) { + UNW_CONTEXT; + jnixx::jbyteArray jtmp + = jnixx::jbyteArray::NewByteArray(env, sizeof (unw_fpreg_t)); + jbyteArrayElements tmp = jbyteArrayElements(env, jtmp); + // Map the REGNUM back to the published ENUM. + Number num = TARGET_REGISTERS::valueOf(env, regnum); + // Implement read/modify/write style op. + ::memcpy(tmp.elements(), fpvalp, sizeof(unw_fpreg_t)); + tmp.release(); + int ret = addressSpace.accessReg(env, num, jtmp, (jboolean) write); + ::memcpy(fpvalp, tmp.elements(), sizeof(unw_fpreg_t)); + tmp.release(); + num.DeleteLocalRef(env); + jtmp.DeleteLocalRef(env); + return ret; +} + +/* + * Callback: Resumes the process at the provided stack level. We + * never resume a process through libunwind. + */ +static int +resume(::unw_addr_space_t as, ::unw_cursor_t *cp, void *contextArg) { + return -UNW_EINVAL; +} + +/* + * Callback: Returns the name of the procedure that the provided + * address is in as well as the offset from the start of the + * procedure. + */ +static int +get_proc_name(::unw_addr_space_t as, + ::unw_word_t addr, char *bufp, + size_t buf_len, ::unw_word_t *offp, void *contextArg) { + // This should never be called, always return an error. + return -UNW_ENOMEM; +} + +jlong +TARGET::createCursor(jnixx::env env, + AddressSpace addressSpace, + jlong unwAddressSpace) { + logf(FINE, "createCursor from address-space %lx", (long) UNW_ADDRESS_SPACE); + jlong unwCursor = (jlong)::malloc(sizeof(::unw_cursor_t)); + // XXX: Need to zero out the cursor, as unw_init_remote doesn't seem + // to do it. + memset(UNW_CURSOR, 0, sizeof(*UNW_CURSOR)); + unw_init_remote(UNW_CURSOR, UNW_ADDRESS_SPACE, addressSpace._object); + logf(FINE, "createCursor at %lx", (long) UNW_CURSOR); + return (jlong) UNW_CURSOR; +} + +void +TARGET::destroyCursor(jnixx::env env, jlong unwCursor) { + logf(FINE, "destroyCursor at %lx", (long) UNW_CURSOR); + ::free(UNW_CURSOR); +} + +jlong +TARGET::createAddressSpace(jnixx::env env, ByteOrder byteOrder) { + logf(FINE, "createAddressSpace, byteOrder %d", + (int) byteOrder.hashCode(env)); + static unw_accessors_t accessors = { + find_proc_info , + put_unwind_info, + get_dyn_info_list_addr, + access_mem, + access_reg, + access_fpreg, + resume, + get_proc_name + }; + jlong unwAddressSpace + = (jlong) unw_create_addr_space(&accessors, (int) byteOrder.hashCode(env)); + logf(FINE, "createAddressSpace at %lx", (long)unwAddressSpace); + return unwAddressSpace; +} + +void +TARGET::destroyAddressSpace(jnixx::env env, jlong unwAddressSpace) { + logf(FINE, "destroyAddressSpace %lx", (long)UNW_ADDRESS_SPACE); + unw_destroy_addr_space(UNW_ADDRESS_SPACE); +} + +void +TARGET::setCachingPolicy(jnixx::env env, jlong unwAddressSpace, + CachingPolicy cachingPolicy) { + log(FINE, "setCachingPolicy, cachingPolicy:", cachingPolicy); + unw_set_caching_policy(UNW_ADDRESS_SPACE, + (unw_caching_policy_t) cachingPolicy.hashCode(env)); +} + +jint +TARGET::isSignalFrame(jnixx::env env, jlong unwCursor) { + logf(FINE, "isSignalFrame"); + return unw_is_signal_frame(UNW_CURSOR); +} + +jint +TARGET::step(jnixx::env env, jlong unwCursor) { + logf(FINE, "step cursor: %lx", (long) UNW_CURSOR); + return unw_step(UNW_CURSOR); +} + +static void +verifyBounds(jnixx::env env, jlong offset, jint length, + jnixx::jbyteArray bytes, jint start, int size) { + verifyBounds(env, bytes, start, length); + if (offset < 0) + ArrayIndexOutOfBoundsException::New(env, offset).Throw(env); + if (offset + length > size) + ArrayIndexOutOfBoundsException::New(env, offset + length).Throw(env); +} + +void +TARGET::getRegister(jnixx::env env, jlong unwCursor, + Number num, jlong offset, jint length, + jnixx::jbyteArray jbytes, jint start) { + int regNum = num.intValue(env); + logf(FINE, "getRegister %d from %lx, offset %ld length %d start %d", + regNum, (long)UNW_CURSOR, (long) offset, (int)length, (int)start); + int status; + union { + unw_word_t w; + unw_fpreg_t fp; + } word; + int size; + if (unw_is_fpreg(regNum)) + size = sizeof(word.fp); + else + size = sizeof(word.w); + verifyBounds(env, offset, length, jbytes, start, size); + if (unw_is_fpreg(regNum)) { + status = unw_get_fpreg(UNW_CURSOR, + (::unw_regnum_t) regNum, + &word.fp); + } else { + status = unw_get_reg(UNW_CURSOR, + (::unw_regnum_t) regNum, + &word.w); + logf(FINE, "getRegister status %d %lx", status, (long)word.w); + } + if (status != 0) + RuntimeException::ThrowNew(env, "get register failed"); + jbyteArrayElements bytes = jbyteArrayElements(env, jbytes); + memcpy(bytes.elements() + start, (uint8_t*)&word + offset, length); +} + +void +TARGET::setRegister(jnixx::env env, jlong unwCursor, + Number num, + jlong offset, jint length, + jnixx::jbyteArray jbytes, jint start) { + int regNum = num.intValue(env); + int status; + union { + unw_word_t w; + unw_fpreg_t fp; + } word; + int size; + if (unw_is_fpreg(regNum)) + size = sizeof(word.fp); + else + size = sizeof(word.w); + verifyBounds(env, offset, length, jbytes, start, size); + if (unw_is_fpreg(regNum)) + status = unw_get_fpreg(UNW_CURSOR, (::unw_regnum_t) regNum, &word.fp); + else + status = unw_get_reg(UNW_CURSOR, (::unw_regnum_t) regNum, &word.w); + if (status != 0) + RuntimeException::ThrowNew(env, "set register failed"); + jbyteArrayElements bytes = jbyteArrayElements(env, jbytes); + memcpy((uint8_t*)&word + offset, bytes.elements() + start, length); + bytes.release(); + if (unw_is_fpreg(regNum)) + status = unw_set_fpreg(UNW_CURSOR, regNum, word.fp); + else + status = unw_set_reg(UNW_CURSOR, regNum, word.w); + if (status != 0) + RuntimeException::ThrowNew(env, "set register failed"); +} + +jlong +TARGET::getSP(jnixx::env env, jlong unwCursor) { + unw_word_t sp; + int status = unw_get_reg(UNW_CURSOR, UNW_REG_SP, &sp); + if (status < 0) + return 0; // bottom of stack. + else + return sp; +} hooks/post-receive -- frysk system monitor/debugger