1 /* The implementation of exception handling primitives for Objective-C.
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GCC.
6 GCC is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 GCC is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING. If not, write to
18 the Free Software Foundation, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* As a special exception, if you link this library with files compiled
22 with GCC to produce an executable, this does not cause the resulting
23 executable to be covered by the GNU General Public License. This
24 exception does not however invalidate any other reasons why the
25 executable file might be covered by the GNU General Public License. */
29 #include "objc/objc-api.h"
31 #include "unwind-pe.h"
34 /* This is the exception class we report -- "GNUCOBJC". */
35 #define __objc_exception_class \
36 ((((((((_Unwind_Exception_Class) 'G' \
37 << 8 | (_Unwind_Exception_Class) 'N') \
38 << 8 | (_Unwind_Exception_Class) 'U') \
39 << 8 | (_Unwind_Exception_Class) 'C') \
40 << 8 | (_Unwind_Exception_Class) 'O') \
41 << 8 | (_Unwind_Exception_Class) 'B') \
42 << 8 | (_Unwind_Exception_Class) 'J') \
43 << 8 | (_Unwind_Exception_Class) 'C')
45 /* This is the object that is passed around by the Objective C runtime
46 to represent the exception in flight. */
50 /* This bit is needed in order to interact with the unwind runtime. */
51 struct _Unwind_Exception base
;
53 /* The actual object we want to throw. */
56 /* Cache some internal unwind data between phase 1 and phase 2. */
57 _Unwind_Ptr landingPad
;
58 int handlerSwitchValue
;
63 struct lsda_header_info
67 _Unwind_Ptr ttype_base
;
68 const unsigned char *TType
;
69 const unsigned char *action_table
;
70 unsigned char ttype_encoding
;
71 unsigned char call_site_encoding
;
74 static const unsigned char *
75 parse_lsda_header (struct _Unwind_Context
*context
, const unsigned char *p
,
76 struct lsda_header_info
*info
)
79 unsigned char lpstart_encoding
;
81 info
->Start
= (context
? _Unwind_GetRegionStart (context
) : 0);
83 /* Find @LPStart, the base to which landing pad offsets are relative. */
84 lpstart_encoding
= *p
++;
85 if (lpstart_encoding
!= DW_EH_PE_omit
)
86 p
= read_encoded_value (context
, lpstart_encoding
, p
, &info
->LPStart
);
88 info
->LPStart
= info
->Start
;
90 /* Find @TType, the base of the handler and exception spec type data. */
91 info
->ttype_encoding
= *p
++;
92 if (info
->ttype_encoding
!= DW_EH_PE_omit
)
94 p
= read_uleb128 (p
, &tmp
);
95 info
->TType
= p
+ tmp
;
100 /* The encoding and length of the call-site table; the action table
101 immediately follows. */
102 info
->call_site_encoding
= *p
++;
103 p
= read_uleb128 (p
, &tmp
);
104 info
->action_table
= p
+ tmp
;
110 get_ttype_entry (struct lsda_header_info
*info
, _Unwind_Word i
)
114 i
*= size_of_encoded_value (info
->ttype_encoding
);
115 read_encoded_value_with_base (info
->ttype_encoding
, info
->ttype_base
,
116 info
->TType
- i
, &ptr
);
118 /* NULL ptr means catch-all. */
120 return objc_get_class ((const char *) ptr
);
125 /* Like unto the method of the same name on Object, but takes an id. */
126 /* ??? Does this bork the meta-type system? Can/should we look up an
127 isKindOf method on the id? */
130 isKindOf (id value
, Class target
)
134 /* NULL target is catch-all. */
138 for (c
= value
->class_pointer
; c
; c
= class_get_super_class (c
))
144 /* Using a different personality function name causes link failures
145 when trying to mix code using different exception handling models. */
146 #ifdef SJLJ_EXCEPTIONS
147 #define PERSONALITY_FUNCTION __gnu_objc_personality_sj0
148 #define __builtin_eh_return_data_regno(x) x
150 #define PERSONALITY_FUNCTION __gnu_objc_personality_v0
154 PERSONALITY_FUNCTION (int version
,
155 _Unwind_Action actions
,
156 _Unwind_Exception_Class exception_class
,
157 struct _Unwind_Exception
*ue_header
,
158 struct _Unwind_Context
*context
)
160 struct ObjcException
*xh
= (struct ObjcException
*) ue_header
;
162 struct lsda_header_info info
;
163 const unsigned char *language_specific_data
;
164 const unsigned char *action_record
;
165 const unsigned char *p
;
166 _Unwind_Ptr landing_pad
, ip
;
167 int handler_switch_value
;
168 int saw_cleanup
, saw_handler
;
170 /* Interface version check. */
172 return _URC_FATAL_PHASE1_ERROR
;
174 /* Shortcut for phase 2 found handler for domestic exception. */
175 if (actions
== (_UA_CLEANUP_PHASE
| _UA_HANDLER_FRAME
)
176 && exception_class
== __objc_exception_class
)
178 handler_switch_value
= xh
->handlerSwitchValue
;
179 landing_pad
= xh
->landingPad
;
180 goto install_context
;
183 language_specific_data
= (const unsigned char *)
184 _Unwind_GetLanguageSpecificData (context
);
186 /* If no LSDA, then there are no handlers or cleanups. */
187 if (! language_specific_data
)
188 return _URC_CONTINUE_UNWIND
;
190 /* Parse the LSDA header. */
191 p
= parse_lsda_header (context
, language_specific_data
, &info
);
192 info
.ttype_base
= base_of_encoded_value (info
.ttype_encoding
, context
);
193 ip
= _Unwind_GetIP (context
) - 1;
196 handler_switch_value
= 0;
198 #ifdef SJLJ_EXCEPTIONS
199 /* The given "IP" is an index into the call-site table, with two
200 exceptions -- -1 means no-action, and 0 means terminate. But
201 since we're using uleb128 values, we've not got random access
204 return _URC_CONTINUE_UNWIND
;
207 _Unwind_Word cs_lp
, cs_action
;
210 p
= read_uleb128 (p
, &cs_lp
);
211 p
= read_uleb128 (p
, &cs_action
);
215 /* Can never have null landing pad for sjlj -- that would have
216 been indicated by a -1 call site index. */
217 landing_pad
= cs_lp
+ 1;
219 action_record
= info
.action_table
+ cs_action
- 1;
220 goto found_something
;
223 /* Search the call-site table for the action associated with this IP. */
224 while (p
< info
.action_table
)
226 _Unwind_Ptr cs_start
, cs_len
, cs_lp
;
227 _Unwind_Word cs_action
;
229 /* Note that all call-site encodings are "absolute" displacements. */
230 p
= read_encoded_value (0, info
.call_site_encoding
, p
, &cs_start
);
231 p
= read_encoded_value (0, info
.call_site_encoding
, p
, &cs_len
);
232 p
= read_encoded_value (0, info
.call_site_encoding
, p
, &cs_lp
);
233 p
= read_uleb128 (p
, &cs_action
);
235 /* The table is sorted, so if we've passed the ip, stop. */
236 if (ip
< info
.Start
+ cs_start
)
237 p
= info
.action_table
;
238 else if (ip
< info
.Start
+ cs_start
+ cs_len
)
241 landing_pad
= info
.LPStart
+ cs_lp
;
243 action_record
= info
.action_table
+ cs_action
- 1;
244 goto found_something
;
247 #endif /* SJLJ_EXCEPTIONS */
249 /* If ip is not present in the table, C++ would call terminate. */
250 /* ??? As with Java, it's perhaps better to tweek the LSDA to
251 that no-action is mapped to no-entry. */
252 return _URC_CONTINUE_UNWIND
;
258 if (landing_pad
== 0)
260 /* If ip is present, and has a null landing pad, there are
261 no cleanups or handlers to be run. */
263 else if (action_record
== 0)
265 /* If ip is present, has a non-null landing pad, and a null
266 action table offset, then there are only cleanups present.
267 Cleanups use a zero switch value, as set above. */
272 /* Otherwise we have a catch handler. */
273 _Unwind_Sword ar_filter
, ar_disp
;
278 p
= read_sleb128 (p
, &ar_filter
);
279 read_sleb128 (p
, &ar_disp
);
283 /* Zero filter values are cleanups. */
287 /* During forced unwinding, we only run cleanups. With a
288 foreign exception class, we have no class info to match. */
289 else if ((actions
& _UA_FORCE_UNWIND
)
290 || exception_class
!= __objc_exception_class
)
293 else if (ar_filter
> 0)
295 /* Positive filter values are handlers. */
297 Class catch_type
= get_ttype_entry (&info
, ar_filter
);
299 if (isKindOf (xh
->value
, catch_type
))
301 handler_switch_value
= ar_filter
;
308 /* Negative filter values are exception specifications,
309 which Objective-C does not use. */
315 action_record
= p
+ ar_disp
;
319 if (! saw_handler
&& ! saw_cleanup
)
320 return _URC_CONTINUE_UNWIND
;
322 if (actions
& _UA_SEARCH_PHASE
)
325 return _URC_CONTINUE_UNWIND
;
327 /* For domestic exceptions, we cache data from phase 1 for phase 2. */
328 if (exception_class
== __objc_exception_class
)
330 xh
->handlerSwitchValue
= handler_switch_value
;
331 xh
->landingPad
= landing_pad
;
333 return _URC_HANDLER_FOUND
;
337 _Unwind_SetGR (context
, __builtin_eh_return_data_regno (0),
338 __builtin_extend_pointer (xh
->value
));
339 _Unwind_SetGR (context
, __builtin_eh_return_data_regno (1),
340 handler_switch_value
);
341 _Unwind_SetIP (context
, landing_pad
);
342 return _URC_INSTALL_CONTEXT
;
346 __objc_exception_cleanup (_Unwind_Reason_Code code
__attribute__((unused
)),
347 struct _Unwind_Exception
*exc
)
353 objc_exception_throw (id value
)
355 struct ObjcException
*header
= calloc (1, sizeof (*header
));
356 header
->base
.exception_class
= __objc_exception_class
;
357 header
->base
.exception_cleanup
= __objc_exception_cleanup
;
358 header
->value
= value
;
360 #ifdef SJLJ_EXCEPTIONS
361 _Unwind_SjLj_RaiseException (&header
->base
);
363 _Unwind_RaiseException (&header
->base
);
366 /* Some sort of unwinding error. */