exception.c: Include tsystem.h for unwind.h.
[gcc.git] / libobjc / exception.c
1 /* The implementation of exception handling primitives for Objective-C.
2 Copyright (C) 2004 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
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
9 later version.
10
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.
15
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. */
20
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. */
26
27 #include <stdlib.h>
28 #include "config.h"
29 #include "objc/objc-api.h"
30 #include "tsystem.h"
31 #include "unwind.h"
32 #include "unwind-pe.h"
33
34 \f
35 /* This is the exception class we report -- "GNUCOBJC". */
36 #define __objc_exception_class \
37 ((((((((_Unwind_Exception_Class) 'G' \
38 << 8 | (_Unwind_Exception_Class) 'N') \
39 << 8 | (_Unwind_Exception_Class) 'U') \
40 << 8 | (_Unwind_Exception_Class) 'C') \
41 << 8 | (_Unwind_Exception_Class) 'O') \
42 << 8 | (_Unwind_Exception_Class) 'B') \
43 << 8 | (_Unwind_Exception_Class) 'J') \
44 << 8 | (_Unwind_Exception_Class) 'C')
45
46 /* This is the object that is passed around by the Objective C runtime
47 to represent the exception in flight. */
48
49 struct ObjcException
50 {
51 /* This bit is needed in order to interact with the unwind runtime. */
52 struct _Unwind_Exception base;
53
54 /* The actual object we want to throw. */
55 id value;
56
57 /* Cache some internal unwind data between phase 1 and phase 2. */
58 _Unwind_Ptr landingPad;
59 int handlerSwitchValue;
60 };
61
62 \f
63
64 struct lsda_header_info
65 {
66 _Unwind_Ptr Start;
67 _Unwind_Ptr LPStart;
68 _Unwind_Ptr ttype_base;
69 const unsigned char *TType;
70 const unsigned char *action_table;
71 unsigned char ttype_encoding;
72 unsigned char call_site_encoding;
73 };
74
75 static const unsigned char *
76 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
77 struct lsda_header_info *info)
78 {
79 _Unwind_Word tmp;
80 unsigned char lpstart_encoding;
81
82 info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
83
84 /* Find @LPStart, the base to which landing pad offsets are relative. */
85 lpstart_encoding = *p++;
86 if (lpstart_encoding != DW_EH_PE_omit)
87 p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
88 else
89 info->LPStart = info->Start;
90
91 /* Find @TType, the base of the handler and exception spec type data. */
92 info->ttype_encoding = *p++;
93 if (info->ttype_encoding != DW_EH_PE_omit)
94 {
95 p = read_uleb128 (p, &tmp);
96 info->TType = p + tmp;
97 }
98 else
99 info->TType = 0;
100
101 /* The encoding and length of the call-site table; the action table
102 immediately follows. */
103 info->call_site_encoding = *p++;
104 p = read_uleb128 (p, &tmp);
105 info->action_table = p + tmp;
106
107 return p;
108 }
109
110 static Class
111 get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i)
112 {
113 _Unwind_Ptr ptr;
114
115 i *= size_of_encoded_value (info->ttype_encoding);
116 read_encoded_value_with_base (info->ttype_encoding, info->ttype_base,
117 info->TType - i, &ptr);
118
119 /* NULL ptr means catch-all. */
120 if (ptr)
121 return objc_get_class ((const char *) ptr);
122 else
123 return 0;
124 }
125
126 /* Like unto the method of the same name on Object, but takes an id. */
127 /* ??? Does this bork the meta-type system? Can/should we look up an
128 isKindOf method on the id? */
129
130 static int
131 isKindOf (id value, Class target)
132 {
133 Class c;
134
135 /* NULL target is catch-all. */
136 if (target == 0)
137 return 1;
138
139 for (c = value->class_pointer; c; c = class_get_super_class (c))
140 if (c == target)
141 return 1;
142 return 0;
143 }
144
145 /* Using a different personality function name causes link failures
146 when trying to mix code using different exception handling models. */
147 #ifdef SJLJ_EXCEPTIONS
148 #define PERSONALITY_FUNCTION __gnu_objc_personality_sj0
149 #define __builtin_eh_return_data_regno(x) x
150 #else
151 #define PERSONALITY_FUNCTION __gnu_objc_personality_v0
152 #endif
153
154 _Unwind_Reason_Code
155 PERSONALITY_FUNCTION (int version,
156 _Unwind_Action actions,
157 _Unwind_Exception_Class exception_class,
158 struct _Unwind_Exception *ue_header,
159 struct _Unwind_Context *context)
160 {
161 struct ObjcException *xh = (struct ObjcException *) ue_header;
162
163 struct lsda_header_info info;
164 const unsigned char *language_specific_data;
165 const unsigned char *action_record;
166 const unsigned char *p;
167 _Unwind_Ptr landing_pad, ip;
168 int handler_switch_value;
169 int saw_cleanup, saw_handler;
170
171 /* Interface version check. */
172 if (version != 1)
173 return _URC_FATAL_PHASE1_ERROR;
174
175 /* Shortcut for phase 2 found handler for domestic exception. */
176 if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
177 && exception_class == __objc_exception_class)
178 {
179 handler_switch_value = xh->handlerSwitchValue;
180 landing_pad = xh->landingPad;
181 goto install_context;
182 }
183
184 language_specific_data = (const unsigned char *)
185 _Unwind_GetLanguageSpecificData (context);
186
187 /* If no LSDA, then there are no handlers or cleanups. */
188 if (! language_specific_data)
189 return _URC_CONTINUE_UNWIND;
190
191 /* Parse the LSDA header. */
192 p = parse_lsda_header (context, language_specific_data, &info);
193 info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
194 ip = _Unwind_GetIP (context) - 1;
195 landing_pad = 0;
196 action_record = 0;
197 handler_switch_value = 0;
198
199 #ifdef SJLJ_EXCEPTIONS
200 /* The given "IP" is an index into the call-site table, with two
201 exceptions -- -1 means no-action, and 0 means terminate. But
202 since we're using uleb128 values, we've not got random access
203 to the array. */
204 if ((int) ip < 0)
205 return _URC_CONTINUE_UNWIND;
206 else
207 {
208 _Unwind_Word cs_lp, cs_action;
209 do
210 {
211 p = read_uleb128 (p, &cs_lp);
212 p = read_uleb128 (p, &cs_action);
213 }
214 while (--ip);
215
216 /* Can never have null landing pad for sjlj -- that would have
217 been indicated by a -1 call site index. */
218 landing_pad = cs_lp + 1;
219 if (cs_action)
220 action_record = info.action_table + cs_action - 1;
221 goto found_something;
222 }
223 #else
224 /* Search the call-site table for the action associated with this IP. */
225 while (p < info.action_table)
226 {
227 _Unwind_Ptr cs_start, cs_len, cs_lp;
228 _Unwind_Word cs_action;
229
230 /* Note that all call-site encodings are "absolute" displacements. */
231 p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
232 p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
233 p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
234 p = read_uleb128 (p, &cs_action);
235
236 /* The table is sorted, so if we've passed the ip, stop. */
237 if (ip < info.Start + cs_start)
238 p = info.action_table;
239 else if (ip < info.Start + cs_start + cs_len)
240 {
241 if (cs_lp)
242 landing_pad = info.LPStart + cs_lp;
243 if (cs_action)
244 action_record = info.action_table + cs_action - 1;
245 goto found_something;
246 }
247 }
248 #endif /* SJLJ_EXCEPTIONS */
249
250 /* If ip is not present in the table, C++ would call terminate. */
251 /* ??? As with Java, it's perhaps better to tweek the LSDA to
252 that no-action is mapped to no-entry. */
253 return _URC_CONTINUE_UNWIND;
254
255 found_something:
256 saw_cleanup = 0;
257 saw_handler = 0;
258
259 if (landing_pad == 0)
260 {
261 /* If ip is present, and has a null landing pad, there are
262 no cleanups or handlers to be run. */
263 }
264 else if (action_record == 0)
265 {
266 /* If ip is present, has a non-null landing pad, and a null
267 action table offset, then there are only cleanups present.
268 Cleanups use a zero switch value, as set above. */
269 saw_cleanup = 1;
270 }
271 else
272 {
273 /* Otherwise we have a catch handler. */
274 _Unwind_Sword ar_filter, ar_disp;
275
276 while (1)
277 {
278 p = action_record;
279 p = read_sleb128 (p, &ar_filter);
280 read_sleb128 (p, &ar_disp);
281
282 if (ar_filter == 0)
283 {
284 /* Zero filter values are cleanups. */
285 saw_cleanup = 1;
286 }
287
288 /* During forced unwinding, we only run cleanups. With a
289 foreign exception class, we have no class info to match. */
290 else if ((actions & _UA_FORCE_UNWIND)
291 || exception_class != __objc_exception_class)
292 ;
293
294 else if (ar_filter > 0)
295 {
296 /* Positive filter values are handlers. */
297
298 Class catch_type = get_ttype_entry (&info, ar_filter);
299
300 if (isKindOf (xh->value, catch_type))
301 {
302 handler_switch_value = ar_filter;
303 saw_handler = 1;
304 break;
305 }
306 }
307 else
308 {
309 /* Negative filter values are exception specifications,
310 which Objective-C does not use. */
311 abort ();
312 }
313
314 if (ar_disp == 0)
315 break;
316 action_record = p + ar_disp;
317 }
318 }
319
320 if (! saw_handler && ! saw_cleanup)
321 return _URC_CONTINUE_UNWIND;
322
323 if (actions & _UA_SEARCH_PHASE)
324 {
325 if (!saw_handler)
326 return _URC_CONTINUE_UNWIND;
327
328 /* For domestic exceptions, we cache data from phase 1 for phase 2. */
329 if (exception_class == __objc_exception_class)
330 {
331 xh->handlerSwitchValue = handler_switch_value;
332 xh->landingPad = landing_pad;
333 }
334 return _URC_HANDLER_FOUND;
335 }
336
337 install_context:
338 _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
339 __builtin_extend_pointer (xh->value));
340 _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
341 handler_switch_value);
342 _Unwind_SetIP (context, landing_pad);
343 return _URC_INSTALL_CONTEXT;
344 }
345
346 static void
347 __objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)),
348 struct _Unwind_Exception *exc)
349 {
350 free (exc);
351 }
352
353 void
354 objc_exception_throw (id value)
355 {
356 struct ObjcException *header = calloc (1, sizeof (*header));
357 header->base.exception_class = __objc_exception_class;
358 header->base.exception_cleanup = __objc_exception_cleanup;
359 header->value = value;
360
361 #ifdef SJLJ_EXCEPTIONS
362 _Unwind_SjLj_RaiseException (&header->base);
363 #else
364 _Unwind_RaiseException (&header->base);
365 #endif
366
367 /* Some sort of unwinding error. */
368 abort ();
369 }