From 33792684238fe1f68be7f6f953c9198b9d200f52 Mon Sep 17 00:00:00 2001 From: Ranjit Mathew Date: Thu, 29 Jun 2006 14:57:39 +0000 Subject: [PATCH] javaprims.h (_Jv_uintptr_t): New typedef similar to uintptr_t in C99. * gcj/javaprims.h (_Jv_uintptr_t): New typedef similar to uintptr_t in C99. * include/java-stack.h: Include stdlib.h. (_Jv_AddrInfo): New structure to hold address information. * include/posix.h (_Jv_platform_dladdr): Declare. * include/win32.h (_Jv_platform_dladdr): Declare. (backtrace): Remove declaration. * posix.cc: Include dlfcn.h if available. Include java-stack.h. (_Jv_platform_dladdr): Define. * win32.cc: Include string.h. Include java-stack.h. (backtrace): Remove. (_Jv_platform_dladdr): Define. * sysdep/i386/backtrace.h (fallback_backtrace): Check that a potential frame pointer value is 32-bit word-aligned. Use operand of the CALL instruction calling the current function to find its starting address. * stacktrace.cc: Do not include dlfcn.h. Include platform.h. (_Jv_StackTrace::getLineNumberForFrame): Use _Jv_platform_dladdr() instead of dladdr(). (_Jv_StackTrace::GetStackTraceElements): Use nCodeMap even for Windows. (_Jv_StackTrace::GetClassContext): Use fallback_backtrace() for targets with SJLJ exceptions instead of using _Unwind_Backtrace(). (_Jv_StackTrace::GetFirstNonSystemClassLoader): Likewise. From-SVN: r115069 --- libjava/ChangeLog | 25 +++++++++++++ libjava/gcj/javaprims.h | 4 +++ libjava/include/java-stack.h | 33 ++++++++++++++++- libjava/include/posix.h | 7 ++++ libjava/include/win32.h | 11 +++--- libjava/posix.cc | 33 +++++++++++++++++ libjava/stacktrace.cc | 51 +++++++++++++------------- libjava/sysdep/i386/backtrace.h | 39 ++++++++++++++++---- libjava/win32.cc | 64 +++++++++++++++++++++------------ 9 files changed, 209 insertions(+), 58 deletions(-) diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 55c07620a9b..9076505c5cf 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,28 @@ +2006-06-29 Ranjit Mathew + + * gcj/javaprims.h (_Jv_uintptr_t): New typedef similar to uintptr_t in + C99. + * include/java-stack.h: Include stdlib.h. + (_Jv_AddrInfo): New structure to hold address information. + * include/posix.h (_Jv_platform_dladdr): Declare. + * include/win32.h (_Jv_platform_dladdr): Declare. + (backtrace): Remove declaration. + * posix.cc: Include dlfcn.h if available. Include java-stack.h. + (_Jv_platform_dladdr): Define. + * win32.cc: Include string.h. Include java-stack.h. + (backtrace): Remove. + (_Jv_platform_dladdr): Define. + * sysdep/i386/backtrace.h (fallback_backtrace): Check that a potential + frame pointer value is 32-bit word-aligned. Use operand of the CALL + instruction calling the current function to find its starting address. + * stacktrace.cc: Do not include dlfcn.h. Include platform.h. + (_Jv_StackTrace::getLineNumberForFrame): Use _Jv_platform_dladdr() + instead of dladdr(). + (_Jv_StackTrace::GetStackTraceElements): Use nCodeMap even for Windows. + (_Jv_StackTrace::GetClassContext): Use fallback_backtrace() for + targets with SJLJ exceptions instead of using _Unwind_Backtrace(). + (_Jv_StackTrace::GetFirstNonSystemClassLoader): Likewise. + 2006-06-27 Tom Tromey * java/io/OutputStreamWriter.java (writeChars): Use a 'do' loop. diff --git a/libjava/gcj/javaprims.h b/libjava/gcj/javaprims.h index 04f99edd68c..61f5276c022 100644 --- a/libjava/gcj/javaprims.h +++ b/libjava/gcj/javaprims.h @@ -624,6 +624,10 @@ typedef unsigned short _Jv_ushort __attribute__((__mode__(__HI__))); typedef unsigned int _Jv_uint __attribute__((__mode__(__SI__))); typedef unsigned int _Jv_ulong __attribute__((__mode__(__DI__))); +// The type to use when treating a pointer as an integer. Similar to +// uintptr_t in C99. +typedef unsigned int _Jv_uintptr_t __attribute__((__mode__(__pointer__))); + class _Jv_Utf8Const { _Jv_ushort hash; diff --git a/libjava/include/java-stack.h b/libjava/include/java-stack.h index 7bf4d7b39d0..eb1ddcce4c7 100644 --- a/libjava/include/java-stack.h +++ b/libjava/include/java-stack.h @@ -1,6 +1,6 @@ // java-stack.h - Definitions for unwinding & inspecting the call stack. -/* Copyright (C) 2005 Free Software Foundation +/* Copyright (C) 2005, 2006 Free Software Foundation This file is part of libgcj. @@ -11,6 +11,7 @@ details. */ #ifndef __JV_STACKTRACE_H__ #define __JV_STACKTRACE_H__ +#include #include #include @@ -126,5 +127,35 @@ public: }; +// Information about a given address. +struct _Jv_AddrInfo +{ + // File name of the defining module. + const char *file_name; + + // Base address of the loaded module. + void *base; + + // Name of the nearest symbol. + const char *sym_name; + + // Address of the nearest symbol. + void *sym_addr; + + ~_Jv_AddrInfo (void) + { + // On systems with a real dladdr(), the file and symbol names given by + // _Jv_platform_dladdr() are not dynamically allocated. On Windows, + // they are. + +#ifdef WIN32 + if (file_name) + free ((void *)file_name); + + if (sym_name) + free ((void *)sym_name); +#endif /* WIN32 */ + } +}; #endif /* __JV_STACKTRACE_H__ */ diff --git a/libjava/include/posix.h b/libjava/include/posix.h index 5b74eb7da77..15795ddfdaf 100644 --- a/libjava/include/posix.h +++ b/libjava/include/posix.h @@ -194,4 +194,11 @@ _Jv_pipe (int filedes[2]) return ::pipe (filedes); } +// Forward declaration. See java-stack.h for definition. +struct _Jv_AddrInfo; + +// Given an address, determine the executable or shared object that defines +// it and the nearest named symbol. +extern int _Jv_platform_dladdr (const void *addr, _Jv_AddrInfo *info); + #endif /* __JV_POSIX_H__ */ diff --git a/libjava/include/win32.h b/libjava/include/win32.h index 3e2beabe5d3..26c307c1d47 100644 --- a/libjava/include/win32.h +++ b/libjava/include/win32.h @@ -11,7 +11,7 @@ details. */ #ifndef __JV_WIN32_H__ #define __JV_WIN32_H__ -// Enable UNICODE Support.? +// Enable UNICODE support? #ifdef MINGW_LIBGCJ_UNICODE #define UNICODE @@ -175,8 +175,11 @@ _Jv_platform_usleep (unsigned long usecs) } #endif /* JV_HASH_SYNCHRONIZATION */ -/* Store up to SIZE return address of the current program state in - ARRAY and return the exact number of values stored. */ -extern int backtrace (void **__array, int __size); +// Forward declaration. See java-stack.h for definition. +struct _Jv_AddrInfo; + +// Given an address, determine the executable or shared object that defines +// it and the nearest named symbol. +extern int _Jv_platform_dladdr (const void *addr, _Jv_AddrInfo *info); #endif /* __JV_WIN32_H__ */ diff --git a/libjava/posix.cc b/libjava/posix.cc index 608fd5dad90..41702dfffb9 100644 --- a/libjava/posix.cc +++ b/libjava/posix.cc @@ -17,7 +17,12 @@ details. */ #include #include +#ifdef HAVE_DLFCN_H +#include +#endif + #include +#include #include #include #include @@ -203,3 +208,31 @@ _Jv_select (int n, fd_set *readfds, fd_set *writefds, return 0; #endif } + +// Given an address, find the object that defines it and the nearest +// defined symbol to that address. Returns 0 if no object defines this +// address. +int +_Jv_platform_dladdr (const void *addr, _Jv_AddrInfo *info) +{ + int ret_val = 0; + +#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR) + Dl_info addr_info; + ret_val = dladdr (addr, &addr_info); + if (ret_val != 0) + { + info->file_name = addr_info.dli_fname; + info->base = addr_info.dli_fbase; + info->sym_name = addr_info.dli_sname; + info->sym_addr = addr_info.dli_saddr; + } +#else + info->file_name = NULL; + info->base = NULL; + info->sym_name = NULL; + info->sym_addr = NULL; +#endif + + return ret_val; +} diff --git a/libjava/stacktrace.cc b/libjava/stacktrace.cc index 5d429e67d14..06a4dfadb98 100644 --- a/libjava/stacktrace.cc +++ b/libjava/stacktrace.cc @@ -9,16 +9,13 @@ Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ #include +#include #include #include #include #include -#ifdef HAVE_DLFCN_H -#include -#endif - #include #include @@ -184,41 +181,36 @@ _Jv_StackTrace::getLineNumberForFrame(_Jv_StackFrame *frame, NameFinder *finder, return; } #endif - // Use dladdr() to determine in which binary the address IP resides. -#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR) - Dl_info info; + + // Use _Jv_platform_dladdr() to determine in which binary the address IP + // resides. + _Jv_AddrInfo info; jstring binaryName = NULL; const char *argv0 = _Jv_GetSafeArg(0); void *ip = frame->ip; _Unwind_Ptr offset = 0; - if (dladdr (ip, &info)) + if (_Jv_platform_dladdr (ip, &info)) { - if (info.dli_fname) - binaryName = JvNewStringUTF (info.dli_fname); + if (info.file_name) + binaryName = JvNewStringUTF (info.file_name); else return; - if (*methodName == NULL && info.dli_sname) - *methodName = JvNewStringUTF (info.dli_sname); + if (*methodName == NULL && info.sym_name) + *methodName = JvNewStringUTF (info.sym_name); // addr2line expects relative addresses for shared libraries. - if (strcmp (info.dli_fname, argv0) == 0) + if (strcmp (info.file_name, argv0) == 0) offset = (_Unwind_Ptr) ip; else - offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.dli_fbase; + offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.base; - //printf ("linenum ip: %p\n", ip); - //printf ("%s: 0x%x\n", info.dli_fname, offset); - //offset -= sizeof(void *); - // The unwinder gives us the return address. In order to get the right // line number for the stack trace, roll it back a little. offset -= 1; - // printf ("%s: 0x%x\n", info.dli_fname, offset); - finder->lookup (binaryName, (jlong) offset); *sourceFileName = finder->getSourceFile(); *lineNum = finder->getLineNum(); @@ -234,7 +226,6 @@ _Jv_StackTrace::getLineNumberForFrame(_Jv_StackFrame *frame, NameFinder *finder, *sourceFileName = t->toString(); } } -#endif } // Look up class and method info for the given stack frame, setting @@ -283,7 +274,7 @@ _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, { ArrayList *list = new ArrayList (); -#ifdef SJLJ_EXCEPTIONS +#if defined (SJLJ_EXCEPTIONS) && ! defined (WIN32) // We can't use the nCodeMap without unwinder support. Instead, // fake the method name by giving the IP in hex - better than nothing. jstring hex = JvNewStringUTF ("0x"); @@ -302,7 +293,7 @@ _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, list->add (element); } -#else /* SJLJ_EXCEPTIONS */ +#else /* SJLJ_EXCEPTIONS && !WIN32 */ //JvSynchronized (ncodeMap); UpdateNCodeMap (); @@ -370,7 +361,7 @@ _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, } finder->close(); -#endif /* SJLJ_EXCEPTIONS */ +#endif /* SJLJ_EXCEPTIONS && !WIN32 */ JArray *array = JvNewObjectArray (list->size (), &StackTraceElement::class$, NULL); @@ -472,7 +463,13 @@ _Jv_StackTrace::GetClassContext (jclass checkClass) //JvSynchronized (ncodeMap); UpdateNCodeMap (); +#ifdef SJLJ_EXCEPTIONS + // The Unwind interface doesn't work with the SJLJ exception model. + // Fall back to a platform-specific unwinder. + fallback_backtrace (&state); +#else /* SJLJ_EXCEPTIONS */ _Unwind_Backtrace (UnwindTraceFn, &state); +#endif /* SJLJ_EXCEPTIONS */ // Count the number of Java frames on the stack. int jframe_count = 0; @@ -543,7 +540,13 @@ _Jv_StackTrace::GetFirstNonSystemClassLoader () //JvSynchronized (ncodeMap); UpdateNCodeMap (); +#ifdef SJLJ_EXCEPTIONS + // The Unwind interface doesn't work with the SJLJ exception model. + // Fall back to a platform-specific unwinder. + fallback_backtrace (&state); +#else /* SJLJ_EXCEPTIONS */ _Unwind_Backtrace (UnwindTraceFn, &state); +#endif /* SJLJ_EXCEPTIONS */ if (state.trace_data) return (ClassLoader *) state.trace_data; diff --git a/libjava/sysdep/i386/backtrace.h b/libjava/sysdep/i386/backtrace.h index b10840213a4..8d46cbf1702 100644 --- a/libjava/sysdep/i386/backtrace.h +++ b/libjava/sysdep/i386/backtrace.h @@ -1,6 +1,6 @@ // backtrace.h - Fallback backtrace implementation. i386 implementation. -/* Copyright (C) 2005 Free Software Foundation +/* Copyright (C) 2005, 2006 Free Software Foundation This file is part of libgcj. @@ -22,19 +22,44 @@ fallback_backtrace (_Jv_UnwindState *state) { register void *_ebp __asm__ ("ebp"); register void *_esp __asm__ ("esp"); - unsigned int *rfp; + _Jv_uintptr_t *rfp; int i = state->pos; - for (rfp = *(unsigned int**)_ebp; + for (rfp = *(_Jv_uintptr_t **)_ebp; rfp && i < state->length; - rfp = *(unsigned int **)rfp) + rfp = *(_Jv_uintptr_t **)rfp) { - int diff = *rfp - (unsigned int)rfp; - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) + /* Sanity checks to eliminate dubious-looking frame pointer chains. + The frame pointer should be a 32-bit word-aligned stack address. + Since the stack grows downwards on x86, the frame pointer must have + a value greater than the current value of the stack pointer, it + should not be below the supposed next frame pointer and it should + not be too far off from the supposed next frame pointer. */ + int diff = *rfp - (_Jv_uintptr_t)rfp; + if (((_Jv_uintptr_t)rfp & 0x00000003) != 0 || (void*)rfp < _esp + || diff > 4 * 1024 || diff < 0) break; + /* Use the return address in the calling function stored just before + the current frame pointer to locate the address operand part of the + "CALL " instruction in the calling function that called this + function. */ + void *ip = (void*)(rfp[1] - 4); + + /* Verify that the instruction at this position is a "CALL " and + use its operand to determine the starting address of the function + that this function had called. 0xE8 is the opcode for this CALL + instruction variant. */ + if (*(unsigned char *)((_Jv_uintptr_t)ip - 1) == 0xE8 && i > state->pos + && state->frames[i-1].type == frame_native) + { + state->frames[i-1].start_ip + = (void *)((_Jv_uintptr_t)ip + 4 + *(_Jv_uintptr_t *)ip); + } + state->frames[i].type = frame_native; - state->frames[i].ip = (void*)(rfp[1]-4); + state->frames[i].ip = ip; + i++; } state->pos = i; diff --git a/libjava/win32.cc b/libjava/win32.cc index a0ae0f0f9cb..a78f814c687 100644 --- a/libjava/win32.cc +++ b/libjava/win32.cc @@ -12,8 +12,11 @@ details. */ #include #include #include +#include #include +#include + #include #include #include @@ -442,28 +445,6 @@ _Jv_platform_initProperties (java::util::Properties* newprops) } } -/* Store up to SIZE return address of the current program state in - ARRAY and return the exact number of values stored. */ -int -backtrace (void **__array, int __size) -{ - register void *_ebp __asm__ ("ebp"); - register void *_esp __asm__ ("esp"); - unsigned int *rfp; - - int i=0; - for (rfp = *(unsigned int**)_ebp; - rfp && i < __size; - rfp = *(unsigned int **)rfp) - { - int diff = *rfp - (unsigned int)rfp; - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) break; - - __array[i++] = (void*)(rfp[1]-4); - } - return i; -} - int _Jv_pipe (int filedes[2]) { @@ -477,3 +458,42 @@ _Jv_platform_close_on_exec (HANDLE h) // no effect under Win9X. SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0); } + +// Given an address, find the object that defines it and the nearest +// defined symbol to that address. Returns 0 if no object defines this +// address. +int +_Jv_platform_dladdr (const void *addr, _Jv_AddrInfo *info) +{ + // Since we do not have dladdr() on Windows, we use a trick involving + // VirtualQuery() to find the module (EXE or DLL) that contains a given + // address. This was taken from Matt Pietrek's "Under the Hood" column + // for the April 1997 issue of Microsoft Systems Journal. + + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery (addr, &mbi, sizeof (mbi))) + { + return 0; + } + + HMODULE hMod = (HMODULE) mbi.AllocationBase; + + char moduleName[MAX_PATH]; + + // FIXME: We explicitly use the ANSI variant of the function here. + if (!GetModuleFileNameA (hMod, moduleName, sizeof (moduleName))) + { + return 0; + } + + char *file_name = (char *)(malloc (strlen (moduleName) + 1)); + strcpy (file_name, moduleName); + info->file_name = file_name; + + // FIXME. + info->base = NULL; + info->sym_name = NULL; + info->sym_addr = NULL; + + return 1; +}