1 /*
2  *  Copyright (C) 1999-2005 by Digital Mars, www.digitalmars.com
3  *  Written by Walter Bright
4  *
5  *  This software is provided 'as-is', without any express or implied
6  *  warranty. In no event will the authors be held liable for any damages
7  *  arising from the use of this software.
8  *
9  *  Permission is granted to anyone to use this software for any purpose,
10  *  including commercial applications, and to alter it and redistribute it
11  *  freely, in both source and binary form, subject to the following
12  *  restrictions:
13  *
14  *  o  The origin of this software must not be misrepresented; you must not
15  *     claim that you wrote the original software. If you use this software
16  *     in a product, an acknowledgment in the product documentation would be
17  *     appreciated but is not required.
18  *  o  Altered source versions must be plainly marked as such, and must not
19  *     be misrepresented as being the original software.
20  *  o  This notice may not be removed or altered from any source
21  *     distribution.
22  */
23 
24 module rt.compiler.dmd.posix.deh;
25 
26 // Exception handling support for linux
27 
28 //debug=1;
29 import tango.stdc.stdio : printf;
30 import tango.stdc.stdlib : exit;
31 
32 version (darwin)
33     import rt.compiler.dmd.darwin.Image;
34 
35 extern (C)
36 {
37     version (darwin) {}
38 
39     else
40     {
41         extern void* _deh_beg;
42         extern void* _deh_end;
43     }
44 
45     int _d_isbaseof(ClassInfo oc, ClassInfo c);
46 }
47 
48 alias int (*fp_t)();   // function pointer in ambient memory model
49 
50 struct DHandlerInfo
51 {
52     uint offset;                // offset from function address to start of guarded section
53     uint endoffset;             // offset of end of guarded section
54     int prev_index;             // previous table index
55     uint cioffset;              // offset to DCatchInfo data from start of table (!=0 if try-catch)
56     void *finally_code;         // pointer to finally code to execute
57                                 // (!=0 if try-finally)
58 }
59 
60 // Address of DHandlerTable, searched for by eh_finddata()
61 
62 struct DHandlerTable
63 {
64     void *fptr;                 // pointer to start of function
65     uint espoffset;             // offset of ESP from EBP
66     uint retoffset;             // offset from start of function to return code
67     size_t nhandlers;             // dimension of handler_info[]
68     DHandlerInfo handler_info[1];
69 }
70 
71 struct DCatchBlock
72 {
73     ClassInfo type;             // catch type
74     size_t bpoffset;              // EBP offset of catch var
75     void *code;                 // catch handler code
76 }
77 
78 // Create one of these for each try-catch
79 struct DCatchInfo
80 {
81     size_t ncatches;                      // number of catch blocks
82     DCatchBlock catch_block[1];         // data for each catch block
83 }
84 
85 // One of these is generated for each function with try-catch or try-finally
86 
87 struct FuncTable
88 {
89     void *fptr;                 // pointer to start of function
90     DHandlerTable *handlertable; // eh data for this function
91     uint fsize;         // size of function in bytes
92 }
93 
94 void terminate()
95 {
96     asm
97     {
98         hlt ;
99     }
100 }
101 
102 /*******************************************
103  * Given address that is inside a function,
104  * figure out which function it is in.
105  * Return DHandlerTable if there is one, NULL if not.
106  */
107 
108 DHandlerTable *__eh_finddata(void *address)
109 {
110     version (darwin)
111     {
112         static FuncTable[] functionTables;
113         static bool hasFunctionTables;
114 
115         if (!hasFunctionTables)
116         {
117             functionTables = getSectionData!(FuncTable, "__DATA", "__deh_eh");
118             hasFunctionTables = true;
119         }
120 
121         foreach (ft ; functionTables)
122         {
123             if (ft.fptr <= address && address < cast(void *)(cast(char *)ft.fptr + ft.fsize))
124                 return ft.handlertable;
125         }
126 
127         return null;
128     }
129     else
130     {
131         FuncTable *ft;
132 
133     //      debug printf("__eh_finddata(address = x%x)\n", address);
134     //    debug printf("_deh_beg = x%x, _deh_end = x%x\n", &_deh_beg, &_deh_end);
135         for (ft = cast(FuncTable *)&_deh_beg;
136              ft < cast(FuncTable *)&_deh_end;
137              ft++)
138         {
139     //      debug printf("\tfptr = x%x, fsize = x%03x, handlertable = x%x\n",
140     //              ft.fptr, ft.fsize, ft.handlertable);
141 
142             if (ft.fptr <= address &&
143                 address < cast(void *)(cast(char *)ft.fptr + ft.fsize))
144             {
145     //          debug printf("\tfound handler table\n");
146                 return ft.handlertable;
147             }
148         }
149     //    debug printf("\tnot found\n");
150         return null;
151     }
152 }
153 
154 
155 /******************************
156  * Given EBP, find return address to caller, and caller's EBP.
157  * Input:
158  *   regbp       Value of EBP for current function
159  *   *pretaddr   Return address
160  * Output:
161  *   *pretaddr   return address to caller
162  * Returns:
163  *   caller's EBP
164  */
165 
166 size_t __eh_find_caller(size_t regbp, size_t *pretaddr)
167 {
168     size_t bp = *cast(size_t *)regbp;
169 
170     if (bp)         // if not end of call chain
171     {
172         // Perform sanity checks on new EBP.
173         // If it is screwed up, terminate() hopefully before we do more damage.
174         if (bp <= regbp)
175             // stack should grow to smaller values
176             terminate();
177 
178         *pretaddr = *cast(size_t *)(regbp + size_t.sizeof);
179     }
180     return bp;
181 }
182 
183 /***********************************
184 * Deprecated because of Bugzilla 4398,
185 * keep for the moment for backwards compatibility.
186 */
187 
188 extern (Windows) void _d_throw(Object *h)
189 {
190     _d_throwc(h);
191 }
192 
193 /***********************************
194 * Throw a D object.
195 */
196 
197 extern(C) void _d_throwc(Object *h)
198 {
199     size_t regebp;
200 
201     debug(deh)
202     {
203         printf("_d_throw(h = %p, &h = %p)\n", h, &h);
204         printf("\tvptr = %p\n", *cast(void **)h);
205     }
206 
207     version (D_InlineAsm_X86)
208         asm
209         {
210             mov regebp,EBP  ;
211         }
212     else version (D_InlineAsm_X86_64)
213         asm
214         {
215             mov regebp,RBP  ;
216         }
217     else
218         static assert(0);
219 
220 //static uint abc;
221 //if (++abc == 2) *(char *)0=0;
222 
223 //int count = 0;
224     while (1)           // for each function on the stack
225     {
226         DHandlerTable *handler_table;
227         FuncTable *pfunc;
228         DHandlerInfo *phi;
229         size_t retaddr;
230         size_t funcoffset;
231         uint spoff;
232         uint retoffset;
233         int index;
234         size_t dim;
235         int ndx;
236         int prev_ndx;
237 
238         regebp = __eh_find_caller(regebp,&retaddr);
239         if (!regebp)
240         {   // if end of call chain
241             debug(deh) printf("end of call chain\n");
242             printf("unhandled exception\n");
243             exit(1);
244             break;
245         }
246 
247         debug(deh) printf("found caller, EBP = x%x, retaddr = x%x\n", regebp, retaddr);
248 //if (++count == 12) *(char*)0=0;
249         handler_table = __eh_finddata(cast(void *)retaddr);   // find static data associated with function
250         if (!handler_table)         // if no static data
251         {
252             debug(deh) printf("no handler table\n");
253             continue;
254         }
255         funcoffset = cast(size_t)handler_table.fptr;
256         spoff = handler_table.espoffset;
257         retoffset = handler_table.retoffset;
258 
259         debug(deh)
260         {
261             printf("retaddr = x%x\n",cast(uint)retaddr);
262             printf("regebp=x%04x, funcoffset=x%04x, spoff=x%x, retoffset=x%x\n",
263             regebp,funcoffset,spoff,retoffset);
264         }
265 
266         // Find start index for retaddr in static data
267         dim = handler_table.nhandlers;
268 
269         debug(deh)
270         {
271             printf("handler_info[]:\n");
272             for (size_t i = 0; i < dim; i++)
273             {
274                 phi = handler_table.handler_info.ptr + i;
275                 printf("\t[%d]: offset = x%04x, endoffset = x%04x, prev_index = %d, cioffset = x%04x, finally_code = %x\n",
276                         i, phi.offset, phi.endoffset, phi.prev_index, phi.cioffset, phi.finally_code);
277             }
278         }
279 
280         index = -1;
281         for (size_t i = 0; i < dim; i++)
282         {
283             phi = handler_table.handler_info.ptr + i;
284 
285             debug(deh) printf("i = %d, phi.offset = %04x\n", i, funcoffset + phi.offset);
286             if (retaddr > funcoffset + phi.offset &&
287                 retaddr <= funcoffset + phi.endoffset)
288                 index = i;
289         }
290         debug(deh) printf("index = %d\n", index);
291 
292         // walk through handler table, checking each handler
293         // with an index smaller than the current table_index
294         for (ndx = index; ndx != -1; ndx = prev_ndx)
295         {
296             phi = handler_table.handler_info.ptr + ndx;
297             prev_ndx = phi.prev_index;
298 
299             if (phi.cioffset)
300             {
301                 // this is a catch handler (no finally)
302                 DCatchInfo *pci;
303                 size_t ncatches;
304                 size_t i;
305 
306                 pci = cast(DCatchInfo *)(cast(char *)handler_table + phi.cioffset);
307                 ncatches = pci.ncatches;
308 
309                 for (i = 0; i < ncatches; i++)
310                 {
311                     DCatchBlock *pcb;
312                     ClassInfo ci = **cast(ClassInfo **)h;
313 
314                     pcb = pci.catch_block.ptr + i;
315 
316                     if (_d_isbaseof(ci, pcb.type))
317                     {   // Matched the catch type, so we've found the handler.
318 
319                         // Initialize catch variable
320                         *cast(void **)(regebp + (pcb.bpoffset)) = h;
321 
322                         // Jump to catch block. Does not return.
323                         {
324                             size_t catch_esp;
325                             fp_t catch_addr;
326 
327                             catch_addr = cast(fp_t)(pcb.code);
328                             catch_esp = regebp - handler_table.espoffset - fp_t.sizeof;
329 
330                             version (D_InlineAsm_X86)
331                                 asm
332                                 {
333                                     mov     EAX,catch_esp   ;
334                                     mov     ECX,catch_addr  ;
335                                     mov     [EAX],ECX       ;
336                                     mov     EBP,regebp      ;
337                                     mov     ESP,EAX         ; // reset stack
338                                     ret                     ; // jump to catch block
339                                 }
340                             else version (D_InlineAsm_X86_64)
341                                 asm
342                                 {
343                                     mov     RAX,catch_esp   ;
344                                     mov     RCX,catch_esp   ;
345                                     mov     RCX,catch_addr  ;
346                                     mov     [RAX],RCX       ;
347                                     mov     RBP,regebp      ;
348                                     mov     RSP,RAX         ; // reset stack
349                                     ret                     ; // jump to catch block
350                                 }
351                             else
352                                 static assert(0);
353 
354                         }
355                     }
356                 }
357             }
358             else if (phi.finally_code)
359             {   // Call finally block
360                 // Note that it is unnecessary to adjust the ESP, as the finally block
361                 // accesses all items on the stack as relative to EBP.
362 
363                 void *blockaddr = phi.finally_code;
364 
365                 version (OSX)
366                 {
367                     version (D_InlineAsm_X86)
368                         asm
369                         {
370                             sub     ESP,4           ;
371                             push    EBX             ;
372                             mov     EBX,blockaddr   ;
373                             push    EBP             ;
374                             mov     EBP,regebp      ;
375                             call    EBX             ;
376                             pop     EBP             ;
377                             pop     EBX             ;
378                             add     ESP,4           ;
379                         }
380                     else version (D_InlineAsm_X86_64)
381                         asm
382                         {
383                             sub     RSP,8           ;
384                             push    RBX             ;
385                             mov     RBX,blockaddr   ;
386                             push    RBP             ;
387                             mov     RBP,regebp      ;
388                             call    RBX             ;
389                             pop     RBP             ;
390                             pop     RBX             ;
391                             add     RSP,8           ;
392                         }
393                     else
394                         static assert(0);
395                 }
396                 else
397                 {
398                     version (D_InlineAsm_X86)
399                         asm
400                         {
401                             push    EBX             ;
402                             mov     EBX,blockaddr   ;
403                             push    EBP             ;
404                             mov     EBP,regebp      ;
405                             call    EBX             ;
406                             pop     EBP             ;
407                             pop     EBX             ;
408                         }
409                     else version (D_InlineAsm_X86_64)
410                         asm
411                         {
412                             sub     RSP,8           ;
413                             push    RBX             ;
414                             mov     RBX,blockaddr   ;
415                             push    RBP             ;
416                             mov     RBP,regebp      ;
417                             call    RBX             ;
418                             pop     RBP             ;
419                             pop     RBX             ;
420                             add     RSP,8           ;
421                         }
422                     else
423                         static assert(0);
424                 }
425             }
426         }
427     }
428 }