Index: ChangeLog from Ranjit Mathew * gcj/javaprims.h (_Jv_uintptr): New typedef similar to uintptr_t in C99. * include/java-stack.h: Include stdlib.h. (_Jv_AddrInfo): New structure to hold address information. (_Jv_platform_dladdr): Declare. * 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. (_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. Index: gcj/javaprims.h =================================================================== --- gcj/javaprims.h (revision 114838) +++ gcj/javaprims.h (working copy) @@ -624,6 +624,10 @@ typedef unsigned short _Jv_ushort __attr 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 __attribute__((__mode__(__pointer__))); + class _Jv_Utf8Const { _Jv_ushort hash; Index: include/java-stack.h =================================================================== --- include/java-stack.h (revision 114838) +++ include/java-stack.h (working copy) @@ -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,30 @@ public: }; +// Information about a given address. +struct _Jv_AddrInfo +{ + // File name of the defining module. + char *file_name; + // Base address of the loaded module. + void *base; + // Name of the nearest symbol. + char *sym_name; + // Address of the nearest symbol. + void *sym_addr; + + ~_Jv_AddrInfo (void) + { + if (file_name) + free (file_name); + + if (sym_name) + free (sym_name); + } +}; + +// 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_STACKTRACE_H__ */ Index: posix.cc =================================================================== --- posix.cc (revision 114838) +++ posix.cc (working copy) @@ -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_s 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; +} Index: win32.cc =================================================================== --- win32.cc (revision 114838) +++ win32.cc (working copy) @@ -12,8 +12,11 @@ details. */ #include #include #include +#include #include +#include + #include #include #include @@ -442,28 +445,6 @@ _Jv_platform_initProperties (java::util: } } -/* 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,41 @@ _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; + } + + info->file_name = (char *)(malloc (strlen (moduleName) + 1)); + strcpy (info->file_name, moduleName); + + // FIXME. + info->base = NULL; + info->sym_name = NULL; + info->sym_addr = NULL; + + return 1; +} Index: sysdep/i386/backtrace.h =================================================================== --- sysdep/i386/backtrace.h (revision 114838) +++ sysdep/i386/backtrace.h (working copy) @@ -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 *sta { register void *_ebp __asm__ ("ebp"); register void *_esp __asm__ ("esp"); - unsigned int *rfp; + _Jv_uintptr *rfp; int i = state->pos; - for (rfp = *(unsigned int**)_ebp; + for (rfp = *(_Jv_uintptr **)_ebp; rfp && i < state->length; - rfp = *(unsigned int **)rfp) + rfp = *(_Jv_uintptr **)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)rfp; + if (((_Jv_uintptr)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)ip - 1) == 0xE8 && i > state->pos + && state->frames[i-1].type == frame_native) + { + state->frames[i-1].start_ip + = (void *)((_Jv_uintptr)ip + 4 + *(_Jv_uintptr *)ip); + } + state->frames[i].type = frame_native; - state->frames[i].ip = (void*)(rfp[1]-4); + state->frames[i].ip = ip; + i++; } state->pos = i; Index: stacktrace.cc =================================================================== --- stacktrace.cc (revision 114838) +++ stacktrace.cc (working copy) @@ -15,10 +15,6 @@ details. */ #include #include -#ifdef HAVE_DLFCN_H -#include -#endif - #include #include @@ -184,41 +180,36 @@ _Jv_StackTrace::getLineNumberForFrame(_J 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 +225,6 @@ _Jv_StackTrace::getLineNumberForFrame(_J *sourceFileName = t->toString(); } } -#endif } // Look up class and method info for the given stack frame, setting @@ -283,7 +273,7 @@ _Jv_StackTrace::GetStackTraceElements (_ { 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 +292,7 @@ _Jv_StackTrace::GetStackTraceElements (_ list->add (element); } -#else /* SJLJ_EXCEPTIONS */ +#else /* SJLJ_EXCEPTIONS && !WIN32 */ //JvSynchronized (ncodeMap); UpdateNCodeMap (); @@ -370,7 +360,7 @@ _Jv_StackTrace::GetStackTraceElements (_ } finder->close(); -#endif /* SJLJ_EXCEPTIONS */ +#endif /* SJLJ_EXCEPTIONS && !WIN32 */ JArray *array = JvNewObjectArray (list->size (), &StackTraceElement::class$, NULL); @@ -472,7 +462,13 @@ _Jv_StackTrace::GetClassContext (jclass //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 +539,13 @@ _Jv_StackTrace::GetFirstNonSystemClassLo //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;