1/** NSException - Object encapsulation of a general exception handler
2   Copyright (C) 1993-2013 Free Software Foundation, Inc.
3
4   Written by:  Adam Fedor <fedor@boulder.colorado.edu>
5   Date: Mar 1995
6
7   This file is part of the GNUstep Base Library.
8
9   This library is free software; you can redistribute it and/or
10   modify it under the terms of the GNU Lesser General Public
11   License as published by the Free Software Foundation; either
12   version 2 of the License, or (at your option) any later version.
13
14   This library is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   Lesser General Public License for more details.
18
19   You should have received a copy of the GNU Lesser General Public
20   License along with this library; if not, write to the Free
21   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22   Boston, MA 02110 USA.
23
24   $Date$ $Revision$
25*/
26
27#import "common.h"
28#define	EXPOSE_NSException_IVARS	1
29#define	EXPOSE_NSThread_IVARS	1
30#import "GSPrivate.h"
31#import "Foundation/NSEnumerator.h"
32#import "Foundation/NSException.h"
33#import "Foundation/NSArray.h"
34#import "Foundation/NSCoder.h"
35#import "Foundation/NSData.h"
36#import "Foundation/NSLock.h"
37#import "Foundation/NSNull.h"
38#import "Foundation/NSThread.h"
39#import "Foundation/NSLock.h"
40#import "Foundation/NSDictionary.h"
41#import "Foundation/NSValue.h"
42#import "GNUstepBase/NSString+GNUstepBase.h"
43
44#import "GSPThread.h"
45
46#ifdef __GNUSTEP_RUNTIME__
47#include <objc/hooks.h>
48#endif
49
50#ifdef HAVE_SET_UNCAUGHT_EXCEPTION_HANDLER
51#include <objc/objc-exception.h>
52#endif
53
54#ifdef HAVE_MALLOC_H
55#if !defined(__OpenBSD__)
56#include <malloc.h>
57#endif
58#endif
59#ifdef HAVE_ALLOCA_H
60#include <alloca.h>
61#endif
62
63#include <stdio.h>
64
65#ifdef HAVE_BACKTRACE
66#include <execinfo.h>
67#endif
68
69/*
70 * Turn off USE_BFD if we don't have bfd support for it.
71 */
72#if !(defined(HAVE_BFD_H) && defined(HAVE_LIBBFD) && defined(HAVE_LIBIBERTY))
73# if defined(USE_BFD)
74#   undef USE_BFD
75# endif
76#endif
77
78#if	defined(_WIN32) && !defined(USE_BFD)
79#include <windows.h>
80#if	defined(HAVE_DBGHELP_H)
81#include <dbghelp.h>
82#else
83/* Supply the relevant bits from dbghelp.h if we could't find the header.
84 */
85#define	SYMOPT_UNDNAME		0x00000002
86#define	SYMOPT_DEFERRED_LOADS	0x00000004
87typedef struct _SYMBOL_INFO {
88  ULONG   SizeOfStruct;
89  ULONG   TypeIndex;
90  uint64_t Reserved[2];
91  ULONG   Index;
92  ULONG   Size;
93  uint64_t ModBase;
94  ULONG   Flags;
95  uint64_t Value;
96  uint64_t Address;
97  ULONG   Register;
98  ULONG   Scope;
99  ULONG   Tag;
100  ULONG   NameLen;
101  ULONG   MaxNameLen;
102  TCHAR   Name[1];
103} SYMBOL_INFO;
104#endif
105#endif
106
107static  NSUncaughtExceptionHandler *_NSUncaughtExceptionHandler = 0;
108
109#define _e_info (((id*)_reserved)[0])
110#define _e_stack (((id*)_reserved)[1])
111
112
113@interface NSException (GSPrivate)
114- (GSStackTrace*) _callStack;
115@end
116
117
118#if	defined(_WIN32)
119#if	defined(USE_BFD)
120static NSString *
121GSPrivateBaseAddress(void *addr, void **base)
122{
123  MEMORY_BASIC_INFORMATION info;
124
125  /* Found a note saying that according to Matt Pietrek's "Under the Hood"
126   * column for the April 1997 issue of Microsoft Systems Journal, the
127   * allocation base returned by VirtualQuery can be used as the handle
128   * to obtain  module information for a loaded library.
129   */
130  if (VirtualQuery (addr, &info, sizeof(info)) != 0)
131    {
132      HMODULE	handle = (HMODULE) info.AllocationBase;
133      unichar	path[MAX_PATH+1];
134
135      if (GetModuleFileNameW(handle, path, sizeof(path)-1) != 0)
136	{
137	  path[sizeof(path)-1] = '\0';
138
139	  *base = info.BaseAddress;
140	  return [NSString stringWithCharacters: path length: wcslen(path)];
141	}
142    }
143  return nil;
144}
145#endif  /* USE_BFD */
146#else	/* _WIN32 */
147
148#include <dlfcn.h>
149
150#if	defined(USE_BFD)
151static NSString *
152GSPrivateBaseAddress(void *addr, void **base)
153{
154#ifdef HAVE_DLADDR
155  Dl_info     info;
156
157  if (!dladdr(addr, &info))
158    return nil;
159
160  *base = info.dli_fbase;
161
162  return [NSString stringWithUTF8String: info.dli_fname];
163#else
164  return nil;
165#endif
166}
167#endif  /* USE_BFD */
168#endif	/* _WIN32 */
169
170#if	defined(USE_BFD)
171
172// GSStackTrace inspired by  FYStackTrace.m
173// created by Wim Oudshoorn on Mon 11-Apr-2006
174// reworked by Lloyd Dupont @ NovaMind.com  on 4-May-2006
175
176#include <bfd.h>
177
178@class GSBinaryFileInfo;
179
180@interface GSFunctionInfo : NSObject
181{
182  void			*_address;
183  NSString		*_fileName;
184  NSString		*_functionName;
185  int			_lineNo;
186  GSBinaryFileInfo	*_module;
187}
188- (void*) address;
189- (NSString *) fileName;
190- (NSString *) function;
191- (id) initWithModule: (GSBinaryFileInfo*)module
192	      address: (void*)address
193		 file: (NSString*)file
194	     function: (NSString*)function
195		 line: (int)lineNo;
196- (int) lineNumber;
197- (GSBinaryFileInfo*) module;
198
199@end
200
201
202@interface GSBinaryFileInfo : NSObject
203{
204  NSString	*_fileName;
205  bfd		*_abfd;
206  asymbol	**_symbols;
207  long		_symbolCount;
208}
209- (NSString *) fileName;
210- (GSFunctionInfo *) functionForAddress: (void*) address;
211- (id) initWithBinaryFile: (NSString *)fileName;
212- (id) init; // return info for the current executing process
213
214@end
215
216
217
218@implementation GSFunctionInfo
219
220- (void*) address
221{
222  return _address;
223}
224
225- (oneway void) dealloc
226{
227  DESTROY(_module);
228  DESTROY(_fileName);
229  DESTROY(_functionName);
230  [super dealloc];
231}
232
233- (NSString *) description
234{
235  return [NSString stringWithFormat: @"(%@: %p) %@  %@: %d",
236    [_module fileName], _address, _functionName, _fileName, _lineNo];
237}
238
239- (NSString *) fileName
240{
241  return _fileName;
242}
243
244- (NSString *) function
245{
246  return _functionName;
247}
248
249- (id) init
250{
251  DESTROY(self);
252  return nil;
253}
254
255- (id) initWithModule: (GSBinaryFileInfo*)module
256	      address: (void*)address
257		 file: (NSString*)file
258	     function: (NSString*)function
259		 line: (int)lineNo
260{
261  _module = RETAIN(module);
262  _address = address;
263  _fileName = [file copy];
264  _functionName = [function copy];
265  _lineNo = lineNo;
266
267  return self;
268}
269
270- (int) lineNumber
271{
272  return _lineNo;
273}
274
275- (GSBinaryFileInfo *) module
276{
277  return _module;
278}
279
280@end
281
282
283
284@implementation GSBinaryFileInfo
285
286+ (GSBinaryFileInfo*) infoWithBinaryFile: (NSString *)fileName
287{
288  return [[[self alloc] initWithBinaryFile: fileName] autorelease];
289}
290
291+ (void) initialize
292{
293  static BOOL first = YES;
294
295  if (first == NO)
296    {
297      return;
298    }
299  first = NO;
300  bfd_init ();
301}
302
303- (oneway void) dealloc
304{
305  DESTROY(_fileName);
306  if (_abfd)
307    {
308      bfd_close (_abfd);
309      _abfd = NULL;
310    }
311  if (_symbols)
312    {
313      free(_symbols);
314      _symbols = NULL;
315    }
316  [super dealloc];
317}
318
319- (NSString *) fileName
320{
321  return _fileName;
322}
323
324- (id) init
325{
326  NSString *processName;
327
328  processName = [[[NSProcessInfo processInfo] arguments] objectAtIndex: 0];
329  return [self initWithBinaryFile: processName];
330}
331
332- (id) initWithBinaryFile: (NSString *)fileName
333{
334  int neededSpace;
335
336  // 1st initialize the bfd
337  if ([fileName length] == 0)
338    {
339      //NSLog (@"GSBinaryFileInfo: No File");
340      DESTROY(self);
341      return nil;
342    }
343  _fileName = [fileName copy];
344  _abfd = bfd_openr ([fileName cString], NULL);
345  if (!_abfd)
346    {
347      //NSLog (@"GSBinaryFileInfo: No Binary Info");
348      DESTROY(self);
349      return nil;
350    }
351  if (!bfd_check_format_matches (_abfd, bfd_object, NULL))
352    {
353      //NSLog (@"GSBinaryFileInfo: BFD format object error");
354      DESTROY(self);
355      return nil;
356    }
357
358  // second read the symbols from it
359  if (!(bfd_get_file_flags (_abfd) & HAS_SYMS))
360    {
361      //NSLog (@"GSBinaryFileInfo: BFD does not contain any symbols");
362      DESTROY(self);
363      return nil;
364    }
365
366  neededSpace = bfd_get_symtab_upper_bound (_abfd);
367  if (neededSpace < 0)
368    {
369      //NSLog (@"GSBinaryFileInfo: BFD error while deducing needed space");
370      DESTROY(self);
371      return nil;
372    }
373  if (neededSpace == 0)
374    {
375      //NSLog (@"GSBinaryFileInfo: BFD no space for symbols needed");
376      DESTROY(self);
377      return nil;
378    }
379  _symbols = malloc (neededSpace);
380  if (!_symbols)
381    {
382      //NSLog (@"GSBinaryFileInfo: Can't allocate buffer");
383      DESTROY(self);
384      return nil;
385    }
386  _symbolCount = bfd_canonicalize_symtab (_abfd, _symbols);
387  if (_symbolCount < 0)
388    {
389      //NSLog (@"GSBinaryFileInfo: BFD error while reading symbols");
390      DESTROY(self);
391      return nil;
392    }
393
394  return self;
395}
396
397struct SearchAddressStruct
398{
399  void			*theAddress;
400  GSBinaryFileInfo	*module;
401  asymbol		**symbols;
402  GSFunctionInfo	*theInfo;
403};
404
405static void find_address (bfd *abfd, asection *section,
406  struct SearchAddressStruct *info)
407{
408  bfd_vma	address;
409  bfd_vma	vma;
410  unsigned	size;
411  const char	*fileName = 0;
412  const char	*functionName = 0;
413  unsigned	line = 0;
414
415  if (info->theInfo)
416    {
417      return;
418    }
419  if (!(bfd_get_section_flags (abfd, section) & SEC_ALLOC))
420    {
421      return;	// Only debug in this section
422    }
423  if (bfd_get_section_flags (abfd, section) & SEC_DATA)
424    {
425      return;	// Only data in this section
426    }
427
428  address = (bfd_vma) (uintptr_t)info->theAddress;
429
430  vma = bfd_get_section_vma (abfd, section);
431
432#if     defined(bfd_get_section_size_before_reloc)
433  size = bfd_get_section_size_before_reloc (section);        // recent
434#elif     defined(bfd_get_section_size)
435  size = bfd_get_section_size (section);        // less recent
436#else
437  size = bfd_section_size (abfd, section);      // older version
438#endif
439
440  if (address < vma || address >= vma + size)
441    {
442      return;
443    }
444
445
446  if (bfd_find_nearest_line (abfd, section, info->symbols,
447    address - vma, &fileName, &functionName, &line))
448    {
449      GSFunctionInfo	*fi;
450      NSString		*file = nil;
451      NSString		*func = nil;
452
453      if (fileName != 0)
454        {
455	  file = [NSString stringWithCString: fileName
456	    encoding: [NSString defaultCStringEncoding]];
457	}
458      if (functionName != 0)
459        {
460	  func = [NSString stringWithCString: functionName
461	    encoding: [NSString defaultCStringEncoding]];
462	}
463      fi = [GSFunctionInfo alloc];
464      fi = [fi initWithModule: info->module
465		      address: info->theAddress
466			 file: file
467		     function: func
468			 line: line];
469      [fi autorelease];
470      info->theInfo = fi;
471    }
472}
473
474- (GSFunctionInfo *) functionForAddress: (void*) address
475{
476  struct SearchAddressStruct searchInfo =
477    { address, self, _symbols, nil };
478
479  bfd_map_over_sections (_abfd,
480    (void (*) (bfd *, asection *, void *)) find_address, &searchInfo);
481  return searchInfo.theInfo;
482}
483
484@end
485
486static pthread_mutex_t	        modLock;
487static NSMutableDictionary	*stackModules = nil;
488
489// initialize stack trace info
490static id
491GSLoadModule(NSString *fileName)
492{
493  GSBinaryFileInfo	*module = nil;
494
495  (void)pthread_mutex_lock(&modLock);
496
497  if (stackModules == nil)
498    {
499      NSEnumerator	*enumerator;
500      NSBundle		*bundle;
501
502      stackModules = [NSMutableDictionary new];
503
504      /*
505       * Try to ensure we have the main, base and gui library bundles.
506       */
507      [NSBundle mainBundle];
508      [NSBundle bundleForClass: [NSObject class]];
509      [NSBundle bundleForClass: NSClassFromString(@"NSView")];
510
511      /*
512       * Add file info for all bundles with code.
513       */
514      enumerator = [[NSBundle allBundles] objectEnumerator];
515      while ((bundle = [enumerator nextObject]) != nil)
516	{
517	  if ([bundle load] == YES)
518	    {
519	      GSLoadModule([bundle executablePath]);
520	    }
521	}
522    }
523
524  if ([fileName length] > 0)
525    {
526      module = [stackModules objectForKey: fileName];
527      if (module == nil)
528	{
529	  module = [GSBinaryFileInfo infoWithBinaryFile: fileName];
530	  if (module == nil)
531	    {
532	      module = (id)[NSNull null];
533	    }
534	  if ([stackModules objectForKey: fileName] == nil)
535	    {
536	      [stackModules setObject: module forKey: fileName];
537	    }
538	  else
539	    {
540	      module = [stackModules objectForKey: fileName];
541	    }
542	}
543    }
544  (void)pthread_mutex_unlock(&modLock);
545
546  if (module == (id)[NSNull null])
547    {
548      module = nil;
549    }
550  return module;
551}
552
553static NSArray*
554GSListModules()
555{
556  NSArray	*result;
557
558  GSLoadModule(nil);	// initialise
559  (void)pthread_mutex_lock(&modLock);
560  result = [stackModules allValues];
561  (void)pthread_mutex_unlock(&modLock);
562  return result;
563}
564
565#endif	/* USE_BFD */
566
567
568#if defined(HAVE_UNWIND_H) && !defined(HAVE_BACKTRACE)
569
570#include <unwind.h>
571#if	!defined(_WIN32)
572#include <dlfcn.h>
573#endif
574
575struct GSBacktraceState
576{
577  void **current;
578  void **end;
579};
580
581static _Unwind_Reason_Code
582GSUnwindCallback(struct _Unwind_Context* context, void* arg)
583{
584    struct GSBacktraceState *state = (struct GSBacktraceState*)arg;
585    uintptr_t pc = _Unwind_GetIP(context);
586    if (pc) {
587      if (state->current == state->end) {
588        return _URC_END_OF_STACK;
589      } else {
590        *state->current++ = (void*)pc;
591      }
592    }
593    return 0; //_URC_OK/_URC_NO_REASON
594}
595
596#endif	/* HAVE_UNWIND_H && !HAVE_BACKTRACE */
597
598
599#if	defined(_WIN32) && !defined(USE_BFD)
600typedef USHORT (WINAPI *CaptureStackBackTraceType)(ULONG,ULONG,PVOID*,PULONG);
601typedef BOOL (WINAPI *SymInitializeType)(HANDLE,char*,BOOL);
602typedef DWORD (WINAPI *SymSetOptionsType)(DWORD);
603typedef BOOL (WINAPI *SymFromAddrType)(HANDLE,DWORD64,PDWORD64,SYMBOL_INFO*);
604
605static CaptureStackBackTraceType capture = 0;
606static SymInitializeType initSym = 0;
607static SymSetOptionsType optSym = 0;
608static SymFromAddrType fromSym = 0;
609static HANDLE	hProcess = 0;
610static	pthread_mutex_t	traceLock;
611#define	MAXFRAMES 62	/* Limitation of windows-xp */
612#else
613#define MAXFRAMES 128   /* 1KB buffer on 64bit machine */
614#endif
615
616
617#if	!defined(HAVE_BUILTIN_EXTRACT_RETURN_ADDRESS)
618# define	__builtin_extract_return_address(X)	X
619#endif
620
621#define _NS_FRAME_HACK(a) \
622case a: env->addr = __builtin_frame_address(a + 1); break;
623#define _NS_RETURN_HACK(a) \
624case a: env->addr = (__builtin_frame_address(a + 1) ? \
625__builtin_extract_return_address(__builtin_return_address(a + 1)) : 0); break;
626
627/*
628 * The following horrible signal handling code is a workaround for the fact
629 * that the __builtin_frame_address() and __builtin_return_address()
630 * functions are not reliable (at least not on my EM64T based system) and
631 * will sometimes walk off the stack and access illegal memory locations.
632 * In order to prevent such an occurrance from crashing the application,
633 * we use sigsetjmp() and siglongjmp() to ensure that we can recover, and
634 * we keep the jump buffer in thread-local memory to avoid possible thread
635 * safety issues.
636 * Of course this will fail horribly if an exception occurs in one of the
637 * few methods we use to manage the per-thread jump buffer.
638 */
639#if	defined(HAVE_SYS_SIGNAL_H)
640#  include	<sys/signal.h>
641#elif	defined(HAVE_SIGNAL_H)
642#  include	<signal.h>
643#endif
644
645#if	defined(_WIN32)
646#ifndef SIGBUS
647#define SIGBUS  SIGILL
648#endif
649#endif
650
651/* sigsetjmp may be a function or a macro.  The test for the function is
652 * done at configure time so we can tell here if either is available.
653 */
654#if	!defined(HAVE_SIGSETJMP) && !defined(sigsetjmp)
655#define	siglongjmp(A,B)	longjmp(A,B)
656#define	sigsetjmp(A,B)	setjmp(A)
657#define	sigjmp_buf	jmp_buf
658#endif
659
660typedef struct {
661  sigjmp_buf    buf;
662  void          *addr;
663  void          (*bus)(int);
664  void          (*segv)(int);
665} jbuf_type;
666
667static jbuf_type *
668jbuf()
669{
670  NSMutableData	*d;
671  NSMutableDictionary	*dict;
672
673  dict = [[NSThread currentThread] threadDictionary];
674  d = [dict objectForKey: @"GSjbuf"];
675  if (d == nil)
676    {
677      d = [[NSMutableData alloc] initWithLength: sizeof(jbuf_type)];
678      [dict setObject: d forKey: @"GSjbuf"];
679      RELEASE(d);
680    }
681  return (jbuf_type*)[d mutableBytes];
682}
683
684static void
685recover(int sig)
686{
687  siglongjmp(jbuf()->buf, 1);
688}
689
690void *
691NSFrameAddress(NSUInteger offset)
692{
693  jbuf_type     *env;
694
695  env = jbuf();
696  if (sigsetjmp(env->buf, 1) == 0)
697    {
698      env->segv = signal(SIGSEGV, recover);
699      env->bus = signal(SIGBUS, recover);
700      switch (offset)
701	{
702	  _NS_FRAME_HACK(0); _NS_FRAME_HACK(1); _NS_FRAME_HACK(2);
703	  _NS_FRAME_HACK(3); _NS_FRAME_HACK(4); _NS_FRAME_HACK(5);
704	  _NS_FRAME_HACK(6); _NS_FRAME_HACK(7); _NS_FRAME_HACK(8);
705	  _NS_FRAME_HACK(9); _NS_FRAME_HACK(10); _NS_FRAME_HACK(11);
706	  _NS_FRAME_HACK(12); _NS_FRAME_HACK(13); _NS_FRAME_HACK(14);
707	  _NS_FRAME_HACK(15); _NS_FRAME_HACK(16); _NS_FRAME_HACK(17);
708	  _NS_FRAME_HACK(18); _NS_FRAME_HACK(19); _NS_FRAME_HACK(20);
709	  _NS_FRAME_HACK(21); _NS_FRAME_HACK(22); _NS_FRAME_HACK(23);
710	  _NS_FRAME_HACK(24); _NS_FRAME_HACK(25); _NS_FRAME_HACK(26);
711	  _NS_FRAME_HACK(27); _NS_FRAME_HACK(28); _NS_FRAME_HACK(29);
712	  _NS_FRAME_HACK(30); _NS_FRAME_HACK(31); _NS_FRAME_HACK(32);
713	  _NS_FRAME_HACK(33); _NS_FRAME_HACK(34); _NS_FRAME_HACK(35);
714	  _NS_FRAME_HACK(36); _NS_FRAME_HACK(37); _NS_FRAME_HACK(38);
715	  _NS_FRAME_HACK(39); _NS_FRAME_HACK(40); _NS_FRAME_HACK(41);
716	  _NS_FRAME_HACK(42); _NS_FRAME_HACK(43); _NS_FRAME_HACK(44);
717	  _NS_FRAME_HACK(45); _NS_FRAME_HACK(46); _NS_FRAME_HACK(47);
718	  _NS_FRAME_HACK(48); _NS_FRAME_HACK(49); _NS_FRAME_HACK(50);
719	  _NS_FRAME_HACK(51); _NS_FRAME_HACK(52); _NS_FRAME_HACK(53);
720	  _NS_FRAME_HACK(54); _NS_FRAME_HACK(55); _NS_FRAME_HACK(56);
721	  _NS_FRAME_HACK(57); _NS_FRAME_HACK(58); _NS_FRAME_HACK(59);
722	  _NS_FRAME_HACK(60); _NS_FRAME_HACK(61); _NS_FRAME_HACK(62);
723	  _NS_FRAME_HACK(63); _NS_FRAME_HACK(64); _NS_FRAME_HACK(65);
724	  _NS_FRAME_HACK(66); _NS_FRAME_HACK(67); _NS_FRAME_HACK(68);
725	  _NS_FRAME_HACK(69); _NS_FRAME_HACK(70); _NS_FRAME_HACK(71);
726	  _NS_FRAME_HACK(72); _NS_FRAME_HACK(73); _NS_FRAME_HACK(74);
727	  _NS_FRAME_HACK(75); _NS_FRAME_HACK(76); _NS_FRAME_HACK(77);
728	  _NS_FRAME_HACK(78); _NS_FRAME_HACK(79); _NS_FRAME_HACK(80);
729	  _NS_FRAME_HACK(81); _NS_FRAME_HACK(82); _NS_FRAME_HACK(83);
730	  _NS_FRAME_HACK(84); _NS_FRAME_HACK(85); _NS_FRAME_HACK(86);
731	  _NS_FRAME_HACK(87); _NS_FRAME_HACK(88); _NS_FRAME_HACK(89);
732	  _NS_FRAME_HACK(90); _NS_FRAME_HACK(91); _NS_FRAME_HACK(92);
733	  _NS_FRAME_HACK(93); _NS_FRAME_HACK(94); _NS_FRAME_HACK(95);
734	  _NS_FRAME_HACK(96); _NS_FRAME_HACK(97); _NS_FRAME_HACK(98);
735	  _NS_FRAME_HACK(99);
736	  default: env->addr = NULL; break;
737	}
738      signal(SIGSEGV, env->segv);
739      signal(SIGBUS, env->bus);
740    }
741  else
742    {
743      env = jbuf();
744      signal(SIGSEGV, env->segv);
745      signal(SIGBUS, env->bus);
746      env->addr = NULL;
747    }
748  return env->addr;
749}
750
751NSUInteger NSCountFrames(void)
752{
753  jbuf_type	*env;
754
755  env = jbuf();
756  if (sigsetjmp(env->buf, 1) == 0)
757    {
758      env->segv = signal(SIGSEGV, recover);
759      env->bus = signal(SIGBUS, recover);
760      env->addr = 0;
761
762#define _NS_COUNT_HACK(X) if (__builtin_frame_address(X + 1) == 0) \
763        goto done; else env->addr = (void*)(X + 1);
764
765      _NS_COUNT_HACK(0); _NS_COUNT_HACK(1); _NS_COUNT_HACK(2);
766      _NS_COUNT_HACK(3); _NS_COUNT_HACK(4); _NS_COUNT_HACK(5);
767      _NS_COUNT_HACK(6); _NS_COUNT_HACK(7); _NS_COUNT_HACK(8);
768      _NS_COUNT_HACK(9); _NS_COUNT_HACK(10); _NS_COUNT_HACK(11);
769      _NS_COUNT_HACK(12); _NS_COUNT_HACK(13); _NS_COUNT_HACK(14);
770      _NS_COUNT_HACK(15); _NS_COUNT_HACK(16); _NS_COUNT_HACK(17);
771      _NS_COUNT_HACK(18); _NS_COUNT_HACK(19); _NS_COUNT_HACK(20);
772      _NS_COUNT_HACK(21); _NS_COUNT_HACK(22); _NS_COUNT_HACK(23);
773      _NS_COUNT_HACK(24); _NS_COUNT_HACK(25); _NS_COUNT_HACK(26);
774      _NS_COUNT_HACK(27); _NS_COUNT_HACK(28); _NS_COUNT_HACK(29);
775      _NS_COUNT_HACK(30); _NS_COUNT_HACK(31); _NS_COUNT_HACK(32);
776      _NS_COUNT_HACK(33); _NS_COUNT_HACK(34); _NS_COUNT_HACK(35);
777      _NS_COUNT_HACK(36); _NS_COUNT_HACK(37); _NS_COUNT_HACK(38);
778      _NS_COUNT_HACK(39); _NS_COUNT_HACK(40); _NS_COUNT_HACK(41);
779      _NS_COUNT_HACK(42); _NS_COUNT_HACK(43); _NS_COUNT_HACK(44);
780      _NS_COUNT_HACK(45); _NS_COUNT_HACK(46); _NS_COUNT_HACK(47);
781      _NS_COUNT_HACK(48); _NS_COUNT_HACK(49); _NS_COUNT_HACK(50);
782      _NS_COUNT_HACK(51); _NS_COUNT_HACK(52); _NS_COUNT_HACK(53);
783      _NS_COUNT_HACK(54); _NS_COUNT_HACK(55); _NS_COUNT_HACK(56);
784      _NS_COUNT_HACK(57); _NS_COUNT_HACK(58); _NS_COUNT_HACK(59);
785      _NS_COUNT_HACK(60); _NS_COUNT_HACK(61); _NS_COUNT_HACK(62);
786      _NS_COUNT_HACK(63); _NS_COUNT_HACK(64); _NS_COUNT_HACK(65);
787      _NS_COUNT_HACK(66); _NS_COUNT_HACK(67); _NS_COUNT_HACK(68);
788      _NS_COUNT_HACK(69); _NS_COUNT_HACK(70); _NS_COUNT_HACK(71);
789      _NS_COUNT_HACK(72); _NS_COUNT_HACK(73); _NS_COUNT_HACK(74);
790      _NS_COUNT_HACK(75); _NS_COUNT_HACK(76); _NS_COUNT_HACK(77);
791      _NS_COUNT_HACK(78); _NS_COUNT_HACK(79); _NS_COUNT_HACK(80);
792      _NS_COUNT_HACK(81); _NS_COUNT_HACK(82); _NS_COUNT_HACK(83);
793      _NS_COUNT_HACK(84); _NS_COUNT_HACK(85); _NS_COUNT_HACK(86);
794      _NS_COUNT_HACK(87); _NS_COUNT_HACK(88); _NS_COUNT_HACK(89);
795      _NS_COUNT_HACK(90); _NS_COUNT_HACK(91); _NS_COUNT_HACK(92);
796      _NS_COUNT_HACK(93); _NS_COUNT_HACK(94); _NS_COUNT_HACK(95);
797      _NS_COUNT_HACK(96); _NS_COUNT_HACK(97); _NS_COUNT_HACK(98);
798      _NS_COUNT_HACK(99);
799
800done:
801      signal(SIGSEGV, env->segv);
802      signal(SIGBUS, env->bus);
803    }
804  else
805    {
806      env = jbuf();
807      signal(SIGSEGV, env->segv);
808      signal(SIGBUS, env->bus);
809    }
810
811  return (uintptr_t)env->addr;
812}
813
814void *
815NSReturnAddress(NSUInteger offset)
816{
817  jbuf_type	*env;
818
819  env = jbuf();
820  if (sigsetjmp(env->buf, 1) == 0)
821    {
822      env->segv = signal(SIGSEGV, recover);
823      env->bus = signal(SIGBUS, recover);
824      switch (offset)
825	{
826	  _NS_RETURN_HACK(0); _NS_RETURN_HACK(1); _NS_RETURN_HACK(2);
827	  _NS_RETURN_HACK(3); _NS_RETURN_HACK(4); _NS_RETURN_HACK(5);
828	  _NS_RETURN_HACK(6); _NS_RETURN_HACK(7); _NS_RETURN_HACK(8);
829	  _NS_RETURN_HACK(9); _NS_RETURN_HACK(10); _NS_RETURN_HACK(11);
830	  _NS_RETURN_HACK(12); _NS_RETURN_HACK(13); _NS_RETURN_HACK(14);
831	  _NS_RETURN_HACK(15); _NS_RETURN_HACK(16); _NS_RETURN_HACK(17);
832	  _NS_RETURN_HACK(18); _NS_RETURN_HACK(19); _NS_RETURN_HACK(20);
833	  _NS_RETURN_HACK(21); _NS_RETURN_HACK(22); _NS_RETURN_HACK(23);
834	  _NS_RETURN_HACK(24); _NS_RETURN_HACK(25); _NS_RETURN_HACK(26);
835	  _NS_RETURN_HACK(27); _NS_RETURN_HACK(28); _NS_RETURN_HACK(29);
836	  _NS_RETURN_HACK(30); _NS_RETURN_HACK(31); _NS_RETURN_HACK(32);
837	  _NS_RETURN_HACK(33); _NS_RETURN_HACK(34); _NS_RETURN_HACK(35);
838	  _NS_RETURN_HACK(36); _NS_RETURN_HACK(37); _NS_RETURN_HACK(38);
839	  _NS_RETURN_HACK(39); _NS_RETURN_HACK(40); _NS_RETURN_HACK(41);
840	  _NS_RETURN_HACK(42); _NS_RETURN_HACK(43); _NS_RETURN_HACK(44);
841	  _NS_RETURN_HACK(45); _NS_RETURN_HACK(46); _NS_RETURN_HACK(47);
842	  _NS_RETURN_HACK(48); _NS_RETURN_HACK(49); _NS_RETURN_HACK(50);
843	  _NS_RETURN_HACK(51); _NS_RETURN_HACK(52); _NS_RETURN_HACK(53);
844	  _NS_RETURN_HACK(54); _NS_RETURN_HACK(55); _NS_RETURN_HACK(56);
845	  _NS_RETURN_HACK(57); _NS_RETURN_HACK(58); _NS_RETURN_HACK(59);
846	  _NS_RETURN_HACK(60); _NS_RETURN_HACK(61); _NS_RETURN_HACK(62);
847	  _NS_RETURN_HACK(63); _NS_RETURN_HACK(64); _NS_RETURN_HACK(65);
848	  _NS_RETURN_HACK(66); _NS_RETURN_HACK(67); _NS_RETURN_HACK(68);
849	  _NS_RETURN_HACK(69); _NS_RETURN_HACK(70); _NS_RETURN_HACK(71);
850	  _NS_RETURN_HACK(72); _NS_RETURN_HACK(73); _NS_RETURN_HACK(74);
851	  _NS_RETURN_HACK(75); _NS_RETURN_HACK(76); _NS_RETURN_HACK(77);
852	  _NS_RETURN_HACK(78); _NS_RETURN_HACK(79); _NS_RETURN_HACK(80);
853	  _NS_RETURN_HACK(81); _NS_RETURN_HACK(82); _NS_RETURN_HACK(83);
854	  _NS_RETURN_HACK(84); _NS_RETURN_HACK(85); _NS_RETURN_HACK(86);
855	  _NS_RETURN_HACK(87); _NS_RETURN_HACK(88); _NS_RETURN_HACK(89);
856	  _NS_RETURN_HACK(90); _NS_RETURN_HACK(91); _NS_RETURN_HACK(92);
857	  _NS_RETURN_HACK(93); _NS_RETURN_HACK(94); _NS_RETURN_HACK(95);
858	  _NS_RETURN_HACK(96); _NS_RETURN_HACK(97); _NS_RETURN_HACK(98);
859	  _NS_RETURN_HACK(99);
860	  default: env->addr = NULL; break;
861	}
862      signal(SIGSEGV, env->segv);
863      signal(SIGBUS, env->bus);
864    }
865  else
866    {
867      env = jbuf();
868      signal(SIGSEGV, env->segv);
869      signal(SIGBUS, env->bus);
870      env->addr = NULL;
871    }
872
873  return env->addr;
874}
875
876unsigned
877GSPrivateReturnAddresses(NSUInteger **returns)
878{
879  unsigned      numReturns;
880#if	defined(HAVE_BACKTRACE)
881  void          *addr[MAXFRAMES*sizeof(void*)];
882
883  numReturns = backtrace(addr, MAXFRAMES);
884  if (numReturns > 0)
885    {
886      *returns = malloc(numReturns * sizeof(void*));
887      memcpy(*returns, addr, numReturns * sizeof(void*));
888    }
889#elif	defined(HAVE_UNWIND_H)
890  void          *addr[MAXFRAMES];
891
892  struct GSBacktraceState state = {addr, addr + MAXFRAMES};
893  _Unwind_Backtrace(GSUnwindCallback, &state);
894
895  numReturns = state.current - addr;
896  if (numReturns > 0)
897    {
898      *returns = malloc(numReturns * sizeof(void*));
899      memcpy(*returns, addr, numReturns * sizeof(void*));
900    }
901#elif	defined(_WIN32) && !defined(USE_BFD)
902  NSUInteger	addr[MAXFRAMES];
903
904  (void)pthread_mutex_lock(&traceLock);
905  if (0 == hProcess)
906    {
907      hProcess = GetCurrentProcess();
908
909      if (0 == capture)
910	{
911	  HANDLE	hModule;
912
913	  hModule = LoadLibrary("kernel32.dll");
914	  if (0 == hModule)
915	    {
916	      fprintf(stderr, "Failed to load kernel32.dll with error: %d\n",
917		(int)GetLastError());
918	      (void)pthread_mutex_unlock(&traceLock);
919	      return 0;
920	    }
921	  capture = (CaptureStackBackTraceType)GetProcAddress(
922	    hModule, "RtlCaptureStackBackTrace");
923	  if (0 == capture)
924	    {
925	      fprintf(stderr, "Failed to find RtlCaptureStackBackTrace: %d\n",
926		(int)GetLastError());
927	      (void)pthread_mutex_unlock(&traceLock);
928	      return 0;
929	    }
930	  hModule = LoadLibrary("dbghelp.dll");
931	  if (0 == hModule)
932	    {
933	      fprintf(stderr, "Failed to load dbghelp.dll with error: %d\n",
934		(int)GetLastError());
935	      (void)pthread_mutex_unlock(&traceLock);
936	      return 0;
937	    }
938	  optSym = (SymSetOptionsType)GetProcAddress(
939	    hModule, "SymSetOptions");
940	  if (0 == optSym)
941	    {
942	      fprintf(stderr, "Failed to find SymSetOptions: %d\n",
943		(int)GetLastError());
944	      (void)pthread_mutex_unlock(&traceLock);
945	      return 0;
946	    }
947	  initSym = (SymInitializeType)GetProcAddress(
948	    hModule, "SymInitialize");
949	  if (0 == initSym)
950	    {
951	      fprintf(stderr, "Failed to find SymInitialize: %d\n",
952		(int)GetLastError());
953	      (void)pthread_mutex_unlock(&traceLock);
954	      return 0;
955	    }
956	  fromSym = (SymFromAddrType)GetProcAddress(
957	    hModule, "SymFromAddr");
958	  if (0 == fromSym)
959	    {
960	      fprintf(stderr, "Failed to find SymFromAddr: %d\n",
961		(int)GetLastError());
962	      (void)pthread_mutex_unlock(&traceLock);
963	      return 0;
964	    }
965	}
966
967      (optSym)(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
968
969      if (!(initSym)(hProcess, NULL, TRUE))
970	{
971	  fprintf(stderr, "SymInitialize failed with error: %d\n",
972	    (int)GetLastError());
973	  fromSym = 0;
974	  (void)pthread_mutex_unlock(&traceLock);
975	  return 0;
976	}
977    }
978  if (0 == capture)
979    {
980      (void)pthread_mutex_unlock(&traceLock);
981      return 0;
982    }
983
984  numReturns = (capture)(0, MAXFRAMES, (void**)addr, NULL);
985  if (numReturns > 0)
986    {
987      *returns = malloc(numReturns * sizeof(void*));
988      memcpy(*returns, addr, numReturns * sizeof(void*));
989    }
990
991  (void)pthread_mutex_unlock(&traceLock);
992
993#else
994  int   n;
995
996  n = NSCountFrames();
997  /* There should be more frame addresses than return addresses.
998   */
999  if (n > 0)
1000    {
1001      n--;
1002    }
1003  if (n > 0)
1004    {
1005      n--;
1006    }
1007
1008  if ((numReturns = n) > 0)
1009    {
1010      jbuf_type *env;
1011
1012      *returns = malloc(numReturns * sizeof(void*));
1013
1014      env = jbuf();
1015      if (sigsetjmp(env->buf, 1) == 0)
1016        {
1017          unsigned      i;
1018
1019          env->segv = signal(SIGSEGV, recover);
1020          env->bus = signal(SIGBUS, recover);
1021
1022          for (i = 0; i < n; i++)
1023            {
1024              switch (i)
1025                {
1026                  _NS_RETURN_HACK(0); _NS_RETURN_HACK(1); _NS_RETURN_HACK(2);
1027                  _NS_RETURN_HACK(3); _NS_RETURN_HACK(4); _NS_RETURN_HACK(5);
1028                  _NS_RETURN_HACK(6); _NS_RETURN_HACK(7); _NS_RETURN_HACK(8);
1029                  _NS_RETURN_HACK(9); _NS_RETURN_HACK(10); _NS_RETURN_HACK(11);
1030                  _NS_RETURN_HACK(12); _NS_RETURN_HACK(13); _NS_RETURN_HACK(14);
1031                  _NS_RETURN_HACK(15); _NS_RETURN_HACK(16); _NS_RETURN_HACK(17);
1032                  _NS_RETURN_HACK(18); _NS_RETURN_HACK(19); _NS_RETURN_HACK(20);
1033                  _NS_RETURN_HACK(21); _NS_RETURN_HACK(22); _NS_RETURN_HACK(23);
1034                  _NS_RETURN_HACK(24); _NS_RETURN_HACK(25); _NS_RETURN_HACK(26);
1035                  _NS_RETURN_HACK(27); _NS_RETURN_HACK(28); _NS_RETURN_HACK(29);
1036                  _NS_RETURN_HACK(30); _NS_RETURN_HACK(31); _NS_RETURN_HACK(32);
1037                  _NS_RETURN_HACK(33); _NS_RETURN_HACK(34); _NS_RETURN_HACK(35);
1038                  _NS_RETURN_HACK(36); _NS_RETURN_HACK(37); _NS_RETURN_HACK(38);
1039                  _NS_RETURN_HACK(39); _NS_RETURN_HACK(40); _NS_RETURN_HACK(41);
1040                  _NS_RETURN_HACK(42); _NS_RETURN_HACK(43); _NS_RETURN_HACK(44);
1041                  _NS_RETURN_HACK(45); _NS_RETURN_HACK(46); _NS_RETURN_HACK(47);
1042                  _NS_RETURN_HACK(48); _NS_RETURN_HACK(49); _NS_RETURN_HACK(50);
1043                  _NS_RETURN_HACK(51); _NS_RETURN_HACK(52); _NS_RETURN_HACK(53);
1044                  _NS_RETURN_HACK(54); _NS_RETURN_HACK(55); _NS_RETURN_HACK(56);
1045                  _NS_RETURN_HACK(57); _NS_RETURN_HACK(58); _NS_RETURN_HACK(59);
1046                  _NS_RETURN_HACK(60); _NS_RETURN_HACK(61); _NS_RETURN_HACK(62);
1047                  _NS_RETURN_HACK(63); _NS_RETURN_HACK(64); _NS_RETURN_HACK(65);
1048                  _NS_RETURN_HACK(66); _NS_RETURN_HACK(67); _NS_RETURN_HACK(68);
1049                  _NS_RETURN_HACK(69); _NS_RETURN_HACK(70); _NS_RETURN_HACK(71);
1050                  _NS_RETURN_HACK(72); _NS_RETURN_HACK(73); _NS_RETURN_HACK(74);
1051                  _NS_RETURN_HACK(75); _NS_RETURN_HACK(76); _NS_RETURN_HACK(77);
1052                  _NS_RETURN_HACK(78); _NS_RETURN_HACK(79); _NS_RETURN_HACK(80);
1053                  _NS_RETURN_HACK(81); _NS_RETURN_HACK(82); _NS_RETURN_HACK(83);
1054                  _NS_RETURN_HACK(84); _NS_RETURN_HACK(85); _NS_RETURN_HACK(86);
1055                  _NS_RETURN_HACK(87); _NS_RETURN_HACK(88); _NS_RETURN_HACK(89);
1056                  _NS_RETURN_HACK(90); _NS_RETURN_HACK(91); _NS_RETURN_HACK(92);
1057                  _NS_RETURN_HACK(93); _NS_RETURN_HACK(94); _NS_RETURN_HACK(95);
1058                  _NS_RETURN_HACK(96); _NS_RETURN_HACK(97); _NS_RETURN_HACK(98);
1059                  _NS_RETURN_HACK(99);
1060                  default: env->addr = 0; break;
1061                }
1062              if (env->addr == 0)
1063                {
1064                  break;
1065                }
1066              memcpy(&(*returns)[i], env->addr, sizeof(void*));
1067            }
1068          signal(SIGSEGV, env->segv);
1069          signal(SIGBUS, env->bus);
1070        }
1071      else
1072        {
1073          env = jbuf();
1074          signal(SIGSEGV, env->segv);
1075          signal(SIGBUS, env->bus);
1076        }
1077    }
1078#endif
1079  return numReturns;
1080}
1081
1082
1083@implementation GSStackTrace : NSObject
1084
1085/** Offset from the top of the stack (when we generate a trace) to the
1086 * first frame likely to be of interest for debugging.
1087 */
1088#define FrameOffset     4
1089
1090+ (void) initialize
1091{
1092#if	defined(_WIN32) && !defined(USE_BFD)
1093  GS_INIT_RECURSIVE_MUTEX(traceLock);
1094#endif
1095#if     defined(USE_BFD)
1096  GS_INIT_RECURSIVE_MUTEX(modLock);
1097#endif
1098}
1099
1100- (NSArray*) addresses
1101{
1102  if (nil == addresses && numReturns > FrameOffset)
1103    {
1104      ENTER_POOL
1105      NSInteger         count = numReturns - FrameOffset;
1106      NSValue           *objects[count];
1107      NSUInteger        index;
1108      void              **ptrs = (void **)returns;
1109
1110      for (index = 0; index < count; index++)
1111        {
1112          objects[index] = [NSValue valueWithPointer: ptrs[FrameOffset+index]];
1113        }
1114      addresses = [[NSArray alloc] initWithObjects: objects count: count];
1115      LEAVE_POOL
1116    }
1117  return addresses;
1118}
1119
1120- (oneway void) dealloc
1121{
1122  DESTROY(addresses);
1123  DESTROY(symbols);
1124  if (returns != NULL)
1125    {
1126      free(returns);
1127      returns = NULL;
1128    }
1129  [super dealloc];
1130}
1131
1132- (NSString*) description
1133{
1134  NSMutableString *result;
1135  NSArray *s;
1136  int i;
1137  int n;
1138
1139  result = [NSMutableString string];
1140  s = [self symbols];
1141  n = [s count];
1142  for (i = 0; i < n; i++)
1143    {
1144      NSString	*line = [s objectAtIndex: i];
1145
1146      [result appendFormat: @"%3d: %@\n", i, line];
1147    }
1148  return result;
1149}
1150
1151- (id) init
1152{
1153  return self;
1154}
1155
1156- (NSArray*) symbols
1157{
1158  if (nil == symbols && numReturns > FrameOffset)
1159    {
1160      NSInteger	        count = numReturns - FrameOffset;
1161      NSUInteger        i;
1162
1163#if	defined(USE_BFD)
1164      void              **ptrs = (void**)&returns[FrameOffset];
1165      NSMutableArray	*a;
1166
1167      a = [[NSMutableArray alloc] initWithCapacity: count];
1168
1169      for (i = 0; i < count; i++)
1170        {
1171          GSFunctionInfo	*aFrame = nil;
1172          void		        *address = (void*)*ptrs++;
1173          void		        *base;
1174          NSString		*modulePath;
1175          GSBinaryFileInfo	*bfi;
1176
1177          modulePath = GSPrivateBaseAddress(address, &base);
1178          if (modulePath != nil && (bfi = GSLoadModule(modulePath)) != nil)
1179            {
1180              aFrame = [bfi functionForAddress: (void*)(address - base)];
1181              if (aFrame == nil)
1182                {
1183                  /* We know we have the right module but function lookup
1184                   * failed ... perhaps we need to use the absolute
1185                   * address rather than offest by 'base' in this case.
1186                   */
1187                  aFrame = [bfi functionForAddress: address];
1188                }
1189            }
1190          else
1191            {
1192              NSArray	*modules;
1193              int	j;
1194              int	m;
1195
1196              modules = GSListModules();
1197              m = [modules count];
1198              for (j = 0; j < m; j++)
1199                {
1200                  bfi = [modules objectAtIndex: j];
1201
1202                  if ((id)bfi != (id)[NSNull null])
1203                    {
1204                      aFrame = [bfi functionForAddress: address];
1205                      if (aFrame != nil)
1206                        {
1207                          break;
1208                        }
1209                    }
1210                }
1211            }
1212
1213          // not found (?!), add an 'unknown' function
1214          if (aFrame == nil)
1215            {
1216              aFrame = [GSFunctionInfo alloc];
1217              [aFrame initWithModule: nil
1218                             address: address
1219                                file: nil
1220                            function: nil
1221                                line: 0];
1222              [aFrame autorelease];
1223            }
1224          [a addObject: [aFrame description]];
1225        }
1226      symbols = [a copy];
1227      [a release];
1228#elif	defined(_WIN32)
1229      void              **ptrs = (void**)&returns[FrameOffset];
1230      SYMBOL_INFO	*symbol;
1231      NSString	        *syms[MAXFRAMES];
1232
1233      symbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO)
1234        + 1024 * sizeof(char), 1);
1235      symbol->MaxNameLen = 1024;
1236      symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
1237
1238      (void)pthread_mutex_lock(&traceLock);
1239      for (i = 0; i < count; i++)
1240        {
1241          NSUInteger	addr = (NSUInteger)*ptrs++;
1242
1243          if ((fromSym)(hProcess, (DWORD64)addr, 0, symbol))
1244            {
1245              syms[i] = [NSString stringWithFormat:
1246                @"%s - %p", symbol->Name, addr];
1247            }
1248          else
1249            {
1250              syms[i] = [NSString stringWithFormat:
1251                @"unknown - %p", symbol->Name, addr];
1252            }
1253        }
1254      (void)pthread_mutex_unlock(&traceLock);
1255      free(symbol);
1256
1257      symbols = [[NSArray alloc] initWithObjects: syms count: count];
1258#elif	defined(HAVE_BACKTRACE)
1259      void              **ptrs = (void**)&returns[FrameOffset];
1260      char		**strs;
1261      NSString	        **symbolArray;
1262
1263      strs = backtrace_symbols(ptrs, count);
1264      symbolArray = alloca(count * sizeof(NSString*));
1265      for (i = 0; i < count; i++)
1266        {
1267          symbolArray[i] = [NSString stringWithUTF8String: strs[i]];
1268        }
1269      symbols = [[NSArray alloc] initWithObjects: symbolArray count: count];
1270      free(strs);
1271#elif	defined(HAVE_UNWIND_H)
1272      void              **ptrs = (void**)&returns[FrameOffset];
1273      NSString	        **symbolArray;
1274
1275      symbolArray = alloca(count * sizeof(NSString*));
1276      for (i = 0; i < count; i++)
1277        {
1278          const void *addr = ptrs[i];
1279          Dl_info info;
1280          if (dladdr(addr, &info)) {
1281            const char *libname = "unknown";
1282            if (info.dli_fname) {
1283              // strip library path
1284              char *delim = strrchr(info.dli_fname, '/');
1285              libname = delim ? delim + 1 : info.dli_fname;
1286            }
1287            if (info.dli_sname) {
1288              symbolArray[i] = [NSString stringWithFormat:
1289                @"%lu: %p %s %s + %d", (unsigned long)i, addr, libname,
1290                info.dli_sname, (int)(addr - info.dli_saddr)];
1291            } else {
1292              symbolArray[i] = [NSString stringWithFormat:
1293                @"%lu: %p %s unknown", (unsigned long)i, addr, libname];
1294            }
1295          } else {
1296            symbolArray[i] = [NSString stringWithFormat:
1297              @"%lu: %p unknown", (unsigned long)i, addr];
1298          }
1299        }
1300      symbols = [[NSArray alloc] initWithObjects: symbolArray count: count];
1301#else
1302      NSMutableArray	*a;
1303
1304      symbols = a = [[self addresses] mutableCopy];
1305      for (i = 0; i < count; i++)
1306        {
1307          NSString      *s;
1308
1309          s = [[NSString alloc] initWithFormat: @"%p: symbol not available",
1310            [[a objectAtIndex: i] pointerValue]];
1311          [a replaceObjectAtIndex: i withObject: s];
1312          RELEASE(s);
1313        }
1314#endif
1315    }
1316  return symbols;
1317}
1318
1319- (void) trace
1320{
1321  DESTROY(addresses);
1322  DESTROY(symbols);
1323  if (returns != NULL)
1324    {
1325      free(returns);
1326      returns = NULL;
1327    }
1328  numReturns = GSPrivateReturnAddresses(&returns);
1329}
1330
1331@end
1332
1333
1334NSString* const NSCharacterConversionException
1335  = @"NSCharacterConversionException";
1336
1337NSString* const NSGenericException
1338  = @"NSGenericException";
1339
1340NSString* const NSInternalInconsistencyException
1341  = @"NSInternalInconsistencyException";
1342
1343NSString* const NSInvalidArgumentException
1344  = @"NSInvalidArgumentException";
1345
1346NSString* const NSMallocException
1347  = @"NSMallocException";
1348
1349NSString* const NSOldStyleException
1350  = @"NSOldStyleException";
1351
1352NSString* const NSParseErrorException
1353  = @"NSParseErrorException";
1354
1355NSString* const NSRangeException
1356 = @"NSRangeException";
1357
1358static void _terminate()
1359{
1360  BOOL			shouldAbort;
1361
1362#ifdef	DEBUG
1363  shouldAbort = YES;		// abort() by default.
1364#else
1365  shouldAbort = NO;		// exit() by default.
1366#endif
1367  shouldAbort = GSPrivateEnvironmentFlag("CRASH_ON_ABORT", shouldAbort);
1368  if (shouldAbort == YES)
1369    {
1370      abort();
1371    }
1372  else
1373    {
1374      exit(1);
1375    }
1376}
1377
1378static void
1379_NSFoundationUncaughtExceptionHandler (NSException *exception)
1380{
1381  NSAutoreleasePool	*pool = [NSAutoreleasePool new];
1382
1383  fprintf(stderr, "%s: Uncaught exception %s, reason: %s\n",
1384    GSPrivateArgZero(),
1385    [[exception name] lossyCString], [[exception reason] lossyCString]);
1386  fflush(stderr);	/* NEEDED UNDER MINGW */
1387  if (GSPrivateEnvironmentFlag("GNUSTEP_STACK_TRACE", NO) == YES
1388    || GSPrivateDefaultsFlag(GSExceptionStackTrace) == YES)
1389    {
1390      fprintf(stderr, "Stack\n%s\n",
1391	[[[exception _callStack] description] lossyCString]);
1392    }
1393  fflush(stderr);	/* NEEDED UNDER MINGW */
1394  [pool drain];
1395  _terminate();
1396}
1397
1398static void
1399callUncaughtHandler(id value)
1400{
1401  if (_NSUncaughtExceptionHandler != NULL)
1402    {
1403      (*_NSUncaughtExceptionHandler)(value);
1404    }
1405  /* The uncaught exception handler which is set has not exited,
1406   * so we MUST call the builtin handler, (normal behavior of MacOS-X).
1407   * The standard handler is guaranteed to exit/abort, which is the
1408   * required behavior for OSX compatibility.
1409   * NB Cocoa's Exception Handling framework might bypass this behavior
1410   * somehow (it's not clear if it does that or simply wraps various
1411   * things with its own exception handlers thus preventing the
1412   * uncaught handler from ever being needed) ... if anyone contributes
1413   * an implementation, perhaps we could integrate it here.
1414   */
1415  _NSFoundationUncaughtExceptionHandler(value);
1416}
1417
1418@implementation NSException
1419
1420+ (void) initialize
1421{
1422  if (self == [NSException class])
1423    {
1424#if defined(_NATIVE_OBJC_EXCEPTIONS)
1425#  ifdef HAVE_SET_UNCAUGHT_EXCEPTION_HANDLER
1426      objc_setUncaughtExceptionHandler(callUncaughtHandler);
1427#  elif defined(HAVE_UNEXPECTED)
1428      _objc_unexpected_exception = callUncaughtHandler;
1429#  elif defined(HAVE_SET_UNEXPECTED)
1430      objc_set_unexpected(callUncaughtHandler);
1431#  endif
1432#endif
1433    }
1434}
1435
1436+ (NSException*) exceptionWithName: (NSString*)name
1437			    reason: (NSString*)reason
1438			  userInfo: (NSDictionary*)userInfo
1439{
1440  return AUTORELEASE([[self alloc] initWithName: name reason: reason
1441				   userInfo: userInfo]);
1442}
1443
1444+ (void) raise: (NSString*)name
1445	format: (NSString*)format,...
1446{
1447  va_list args;
1448
1449  va_start(args, format);
1450  [self raise: name format: format arguments: args];
1451  // This probably doesn't matter, but va_end won't get called
1452  va_end(args);
1453  while (1);    // does not return
1454}
1455
1456+ (void) raise: (NSString*)name
1457	format: (NSString*)format
1458     arguments: (va_list)argList
1459{
1460  NSString	*reason;
1461  NSException	*except;
1462
1463  reason = [NSString stringWithFormat: format arguments: argList];
1464  except = [self exceptionWithName: name reason: reason userInfo: nil];
1465  [except raise];
1466  while (1);    // does not return
1467}
1468
1469/* For OSX compatibility -init returns nil.
1470 */
1471- (id) init
1472{
1473  DESTROY(self);
1474  return nil;
1475}
1476
1477- (id) initWithName: (NSString*)name
1478	     reason: (NSString*)reason
1479	   userInfo: (NSDictionary*)userInfo
1480{
1481  ASSIGN(_e_name, name);
1482  ASSIGN(_e_reason, reason);
1483  if (userInfo != nil)
1484    {
1485      if (_reserved == 0)
1486        {
1487          _reserved = NSZoneCalloc([self zone], 2, sizeof(id));
1488        }
1489      ASSIGN(_e_info, userInfo);
1490    }
1491  return self;
1492}
1493
1494- (NSArray*) callStackReturnAddresses
1495{
1496  if (_reserved == 0)
1497    {
1498      return nil;
1499    }
1500  return [_e_stack addresses];
1501}
1502
1503- (NSArray *) callStackSymbols
1504{
1505  if (_reserved == 0)
1506    {
1507      return nil;
1508    }
1509  return [_e_stack symbols];
1510}
1511
1512- (void) dealloc
1513{
1514  DESTROY(_e_name);
1515  DESTROY(_e_reason);
1516  if (_reserved != 0)
1517    {
1518      DESTROY(_e_info);
1519      DESTROY(_e_stack);
1520      NSZoneFree([self zone], _reserved);
1521      _reserved = 0;
1522    }
1523  [super dealloc];
1524}
1525
1526- (NSString*) description
1527{
1528  NSAutoreleasePool	*pool = [NSAutoreleasePool new];
1529  NSString      	*result;
1530
1531  if (_e_name == nil)
1532    {
1533      [NSException raise: NSInvalidArgumentException
1534		  format: @"Atttempt to use uninitialised NSException"];
1535    }
1536  if (_reserved != 0)
1537    {
1538      if (_e_stack != nil
1539        && (GSPrivateEnvironmentFlag("GNUSTEP_STACK_TRACE", NO) == YES
1540          || GSPrivateDefaultsFlag(GSExceptionStackTrace) == YES))
1541        {
1542          if (_e_info != nil)
1543            {
1544              result = [NSString stringWithFormat:
1545                @"%@ NAME:%@ REASON:%@ INFO:%@ STACK:%@",
1546                [super description], _e_name, _e_reason, _e_info, _e_stack];
1547            }
1548          else
1549            {
1550              result = [NSString stringWithFormat:
1551                @"%@ NAME:%@ REASON:%@ STACK:%@",
1552                [super description], _e_name, _e_reason, _e_stack];
1553            }
1554        }
1555      else
1556        {
1557          result = [NSString stringWithFormat:
1558            @"%@ NAME:%@ REASON:%@ INFO:%@",
1559            [super description], _e_name, _e_reason, _e_info];
1560        }
1561    }
1562  else
1563    {
1564      result = [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@",
1565        [super description], _e_name, _e_reason];
1566    }
1567  [result retain];
1568  [pool drain];
1569  return [result autorelease];
1570}
1571
1572- (void) raise
1573{
1574  if (_reserved == 0)
1575    {
1576      _reserved = NSZoneCalloc([self zone], 2, sizeof(id));
1577    }
1578  if (nil == _e_stack)
1579    {
1580      // Only set the stack when first raised
1581      _e_stack = [GSStackTrace new];
1582      [_e_stack trace];
1583    }
1584
1585#if     defined(_NATIVE_OBJC_EXCEPTIONS)
1586  @throw self;
1587#else
1588{
1589  NSThread      *thread;
1590  NSHandler	*handler;
1591
1592  thread = GSCurrentThread();
1593  handler = thread->_exception_handler;
1594  if (NULL == handler)
1595    {
1596      static	int	recursion = 0;
1597
1598      /*
1599       * Set/check a counter to prevent recursive uncaught exceptions.
1600       * Allow a little recursion in case we have different handlers
1601       * being tried.
1602       */
1603      if (recursion++ > 3)
1604	{
1605	  fprintf(stderr,
1606	    "recursion encountered handling uncaught exception\n");
1607	  fflush(stderr);	/* NEEDED UNDER MINGW */
1608	  _terminate();
1609	}
1610
1611      /*
1612       * Call the uncaught exception handler (if there is one).
1613       * The calls the built-in default handler to terminate the program!
1614       */
1615      callUncaughtHandler(self);
1616    }
1617  else
1618    {
1619      thread->_exception_handler = handler->next;
1620      handler->exception = self;
1621      longjmp(handler->jumpState, 1);
1622    }
1623}
1624#endif
1625  while (1);    // does not return
1626}
1627
1628- (NSString*) name
1629{
1630  if (_e_name != nil)
1631    {
1632      return _e_name;
1633    }
1634  else
1635    {
1636      return NSStringFromClass([self class]);
1637    }
1638}
1639
1640- (NSString*) reason
1641{
1642  if (_e_reason != nil)
1643    {
1644      return _e_reason;
1645    }
1646  else
1647    {
1648      return @"unspecified reason";
1649    }
1650}
1651
1652- (NSDictionary*) userInfo
1653{
1654  if (_reserved == 0)
1655    {
1656      return nil;
1657    }
1658  return _e_info;
1659}
1660
1661- (Class) classForPortCoder
1662{
1663  return [self class];
1664}
1665
1666- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
1667{
1668  return self;
1669}
1670
1671- (void) encodeWithCoder: (NSCoder*)aCoder
1672{
1673  id    info = (_reserved == 0) ? nil : _e_info;
1674
1675  [aCoder encodeValueOfObjCType: @encode(id) at: &_e_name];
1676  [aCoder encodeValueOfObjCType: @encode(id) at: &_e_reason];
1677  [aCoder encodeValueOfObjCType: @encode(id) at: &info];
1678}
1679
1680- (id) initWithCoder: (NSCoder*)aDecoder
1681{
1682  id    info;
1683
1684  [aDecoder decodeValueOfObjCType: @encode(id) at: &_e_name];
1685  [aDecoder decodeValueOfObjCType: @encode(id) at: &_e_reason];
1686  [aDecoder decodeValueOfObjCType: @encode(id) at: &info];
1687  if (info != nil)
1688    {
1689      if (_reserved == 0)
1690        {
1691          _reserved = NSZoneCalloc([self zone], 2, sizeof(id));
1692        }
1693      _e_info = info;
1694    }
1695  return self;
1696}
1697
1698- (id) copyWithZone: (NSZone*)zone
1699{
1700  if (NSShouldRetainWithZone(self, zone))
1701    {
1702      return RETAIN(self);
1703    }
1704  else
1705    {
1706      return [[[self class] alloc] initWithName: [self name]
1707                                         reason: [self reason]
1708                                       userInfo: [self userInfo]];
1709    }
1710}
1711
1712@end
1713
1714@implementation	NSException (GSPrivate)
1715
1716- (GSStackTrace*) _callStack
1717{
1718  if (_reserved == 0)
1719    {
1720      return nil;
1721    }
1722  return _e_stack;
1723}
1724
1725@end
1726
1727@implementation NSThread (CallStackSymbols)
1728
1729+ (NSArray *) callStackSymbols
1730{
1731  GSStackTrace *stackTrace = AUTORELEASE([GSStackTrace new]);
1732  [stackTrace trace];
1733  return [stackTrace symbols];
1734}
1735
1736@end
1737
1738void
1739_NSAddHandler (NSHandler* handler)
1740{
1741  NSThread *thread;
1742
1743  thread = GSCurrentThread();
1744#if defined(_WIN32) && defined(DEBUG)
1745  if (thread->_exception_handler
1746    && IsBadReadPtr(thread->_exception_handler, sizeof(NSHandler)))
1747    {
1748      fprintf(stderr, "ERROR: Current exception handler is bogus.\n");
1749    }
1750#endif
1751  handler->next = thread->_exception_handler;
1752  thread->_exception_handler = handler;
1753}
1754
1755void
1756_NSRemoveHandler (NSHandler* handler)
1757{
1758  NSThread	*thread;
1759
1760  thread = GSCurrentThread();
1761#if defined(DEBUG)
1762  if (thread->_exception_handler != handler)
1763    {
1764      fprintf(stderr, "ERROR: Removing exception handler that is not on top "
1765	"of the stack. (You probably called return in an NS_DURING block.)\n");
1766    }
1767#if defined(_WIN32)
1768  if (IsBadReadPtr(handler, sizeof(NSHandler)))
1769    {
1770      fprintf(stderr, "ERROR: Could not remove exception handler, "
1771	"handler is bad pointer.\n");
1772      thread->_exception_handler = 0;
1773      return;
1774    }
1775  if (handler->next && IsBadReadPtr(handler->next, sizeof(NSHandler)))
1776    {
1777      fprintf(stderr, "ERROR: Could not restore exception handler, "
1778	"handler->next is bad pointer.\n");
1779      thread->_exception_handler = 0;
1780      return;
1781    }
1782#endif
1783#endif
1784  thread->_exception_handler = handler->next;
1785}
1786
1787NSUncaughtExceptionHandler *
1788NSGetUncaughtExceptionHandler()
1789{
1790  return _NSUncaughtExceptionHandler;
1791}
1792
1793void
1794NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler *handler)
1795{
1796  _NSUncaughtExceptionHandler = handler;
1797}
1798