PageRenderTime 42ms CodeModel.GetById 18ms app.highlight 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/rt/deh2.d

http://github.com/AlexeyProkhin/druntime
D | 503 lines | 367 code | 66 blank | 70 comment | 37 complexity | df6cdcb8520bd3f0f0f6ddeb05e2cee4 MD5 | raw file
  1/**
  2 * Written in the D programming language.
  3 * Implementation of exception handling support routines for Posix and Win64.
  4 *
  5 * Copyright: Copyright Digital Mars 2000 - 2012.
  6 * License: Distributed under the
  7 *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
  8 *    (See accompanying file LICENSE)
  9 * Authors:   Walter Bright, Sean Kelly
 10 * Source: $(DRUNTIMESRC src/rt/_deh2.d)
 11 */
 12
 13module rt.deh2;
 14
 15version (Posix)
 16{
 17    version = deh2;
 18}
 19else version (Win64)
 20{
 21    version = deh2;
 22}
 23
 24// Use deh.d for Win32
 25
 26version (deh2)
 27{
 28
 29//debug=1;
 30debug import core.stdc.stdio : printf;
 31
 32extern (C)
 33{
 34    version (OSX)
 35    {
 36        // Set by rt.memory_osx.onAddImage()
 37        __gshared ubyte[] _deh_eh_array;
 38    }
 39    else
 40    {
 41        extern __gshared
 42        {
 43            /* Symbols created by the compiler and inserted into the object file
 44             * that 'bracket' the __deh_eh segment
 45             */
 46            void* _deh_beg;
 47            void* _deh_end;
 48        }
 49    }
 50
 51    Throwable.TraceInfo _d_traceContext(void* ptr = null);
 52
 53    int _d_isbaseof(ClassInfo oc, ClassInfo c);
 54
 55    void _d_createTrace(Object*);
 56}
 57
 58alias int function() fp_t;   // function pointer in ambient memory model
 59
 60// DHandlerInfo table is generated by except_gentables() in eh.c
 61
 62struct DHandlerInfo
 63{
 64    uint offset;                // offset from function address to start of guarded section
 65    uint endoffset;             // offset of end of guarded section
 66    int prev_index;             // previous table index
 67    uint cioffset;              // offset to DCatchInfo data from start of table (!=0 if try-catch)
 68    size_t finally_offset;      // offset to finally code to execute
 69                                // (!=0 if try-finally)
 70}
 71
 72// Address of DHandlerTable, searched for by eh_finddata()
 73
 74struct DHandlerTable
 75{
 76    uint espoffset;             // offset of ESP from EBP
 77    uint retoffset;             // offset from start of function to return code
 78    size_t nhandlers;           // dimension of handler_info[] (use size_t to set alignment of handler_info[])
 79    DHandlerInfo handler_info[1];
 80}
 81
 82struct DCatchBlock
 83{
 84    ClassInfo type;             // catch type
 85    size_t bpoffset;            // EBP offset of catch var
 86    size_t codeoffset;          // catch handler offset
 87}
 88
 89// Create one of these for each try-catch
 90struct DCatchInfo
 91{
 92    size_t ncatches;                    // number of catch blocks
 93    DCatchBlock catch_block[1];         // data for each catch block
 94}
 95
 96// One of these is generated for each function with try-catch or try-finally
 97
 98struct FuncTable
 99{
100    void *fptr;                 // pointer to start of function
101    DHandlerTable *handlertable; // eh data for this function
102    uint fsize;         // size of function in bytes
103}
104
105private
106{
107    struct InFlight
108    {
109        InFlight*   next;
110        void*       addr;
111        Throwable   t;
112    }
113
114    InFlight* __inflight = null;
115}
116
117void terminate()
118{
119    asm
120    {
121        hlt ;
122    }
123}
124
125/*******************************************
126 * Given address that is inside a function,
127 * figure out which function it is in.
128 * Return DHandlerTable if there is one, NULL if not.
129 */
130
131FuncTable *__eh_finddata(void *address)
132{
133    debug printf("FuncTable.sizeof = %p\n", FuncTable.sizeof);
134    debug printf("__eh_finddata(address = %p)\n", address);
135
136    version (OSX)
137    {
138        auto pstart = cast(FuncTable *)_deh_eh_array.ptr;
139        auto pend   = cast(FuncTable *)(_deh_eh_array.ptr + _deh_eh_array.length);
140    }
141    else
142    {
143        auto pstart = cast(FuncTable *)&_deh_beg;
144        auto pend   = cast(FuncTable *)&_deh_end;
145    }
146    debug printf("_deh_beg = %p, _deh_end = %p\n", pstart, pend);
147
148    for (auto ft = pstart; 1; ft++)
149    {
150     Lagain:
151        if (ft >= pend)
152            break;
153
154        version (Win64)
155        {
156            /* The MS Linker has an inexplicable and erratic tendency to insert
157             * 8 zero bytes between sections generated from different .obj
158             * files. This kludge tries to skip over them.
159             */
160            if (ft.fptr == null)
161            {
162                ft = cast(FuncTable *)(cast(void**)ft + 1);
163                goto Lagain;
164            }
165        }
166
167        debug printf("  ft = %p, fptr = %p, handlertable = %p, fsize = x%03x\n",
168              ft, ft.fptr, ft.handlertable, ft.fsize);
169
170        void *fptr = ft.fptr;
171        version (Win64)
172        {
173            /* If linked with /DEBUG, the linker rewrites it so the function pointer points
174             * to a JMP to the actual code. The address will be in the actual code, so we
175             * need to follow the JMP.
176             */
177            if ((cast(ubyte*)fptr)[0] == 0xE9)
178            {   // JMP target = RIP of next instruction + signed 32 bit displacement
179                fptr = fptr + 5 + *cast(int*)(fptr + 1);
180            }
181        }
182
183        if (fptr <= address &&
184            address < cast(void *)(cast(char *)fptr + ft.fsize))
185        {
186            debug printf("\tfound handler table\n");
187            return ft;
188        }
189    }
190    debug printf("\tnot found\n");
191    return null;
192}
193
194
195/******************************
196 * Given EBP, find return address to caller, and caller's EBP.
197 * Input:
198 *   regbp       Value of EBP for current function
199 *   *pretaddr   Return address
200 * Output:
201 *   *pretaddr   return address to caller
202 * Returns:
203 *   caller's EBP
204 */
205
206size_t __eh_find_caller(size_t regbp, size_t *pretaddr)
207{
208    size_t bp = *cast(size_t *)regbp;
209
210    if (bp)         // if not end of call chain
211    {
212        // Perform sanity checks on new EBP.
213        // If it is screwed up, terminate() hopefully before we do more damage.
214        if (bp <= regbp)
215            // stack should grow to smaller values
216            terminate();
217
218        *pretaddr = *cast(size_t *)(regbp + size_t.sizeof);
219    }
220    return bp;
221}
222
223
224/***********************************
225 * Throw a D object.
226 */
227
228extern (C) void _d_throwc(Object *h)
229{
230    size_t regebp;
231
232    debug
233    {
234        printf("_d_throw(h = %p, &h = %p)\n", h, &h);
235        printf("\tvptr = %p\n", *cast(void **)h);
236    }
237
238    version (D_InlineAsm_X86)
239        asm
240        {
241            mov regebp,EBP  ;
242        }
243    else version (D_InlineAsm_X86_64)
244        asm
245        {
246            mov regebp,RBP  ;
247        }
248    else
249        static assert(0);
250
251    _d_createTrace(h);
252
253//static uint abc;
254//if (++abc == 2) *(char *)0=0;
255
256//int count = 0;
257    while (1)           // for each function on the stack
258    {
259        size_t retaddr;
260
261        regebp = __eh_find_caller(regebp,&retaddr);
262        if (!regebp)
263        {   // if end of call chain
264            debug printf("end of call chain\n");
265            break;
266        }
267
268        debug printf("found caller, EBP = %p, retaddr = %p\n", regebp, retaddr);
269//if (++count == 12) *(char*)0=0;
270        auto func_table = __eh_finddata(cast(void *)retaddr);   // find static data associated with function
271        auto handler_table = func_table ? func_table.handlertable : null;
272        if (!handler_table)         // if no static data
273        {
274            debug printf("no handler table\n");
275            continue;
276        }
277        auto funcoffset = cast(size_t)func_table.fptr;
278        version (Win64)
279        {
280            /* If linked with /DEBUG, the linker rewrites it so the function pointer points
281             * to a JMP to the actual code. The address will be in the actual code, so we
282             * need to follow the JMP.
283             */
284            if ((cast(ubyte*)funcoffset)[0] == 0xE9)
285            {   // JMP target = RIP of next instruction + signed 32 bit displacement
286                funcoffset = funcoffset + 5 + *cast(int*)(funcoffset + 1);
287            }
288        }
289        auto spoff = handler_table.espoffset;
290        auto retoffset = handler_table.retoffset;
291
292        debug
293        {
294            printf("retaddr = %p\n", retaddr);
295            printf("regebp=%p, funcoffset=%p, spoff=x%x, retoffset=x%x\n",
296            regebp,funcoffset,spoff,retoffset);
297        }
298
299        // Find start index for retaddr in static data
300        auto dim = handler_table.nhandlers;
301
302        debug
303        {
304            printf("handler_info[%d]:\n", dim);
305            for (int i = 0; i < dim; i++)
306            {
307                auto phi = &handler_table.handler_info.ptr[i];
308                printf("\t[%d]: offset = x%04x, endoffset = x%04x, prev_index = %d, cioffset = x%04x, finally_offset = %x\n",
309                        i, phi.offset, phi.endoffset, phi.prev_index, phi.cioffset, phi.finally_offset);
310            }
311        }
312
313        auto index = -1;
314        for (int i = 0; i < dim; i++)
315        {
316            auto phi = &handler_table.handler_info.ptr[i];
317
318            debug printf("i = %d, phi.offset = %04x\n", i, funcoffset + phi.offset);
319            if (retaddr > funcoffset + phi.offset &&
320                retaddr <= funcoffset + phi.endoffset)
321                index = i;
322        }
323        debug printf("index = %d\n", index);
324
325        if (dim)
326        {
327            auto phi = &handler_table.handler_info.ptr[index+1];
328            debug printf("next finally_offset %p\n", phi.finally_offset);
329            auto prev = cast(InFlight*) &__inflight;
330            auto curr = prev.next;
331
332            if (curr !is null && curr.addr == cast(void*)(funcoffset + phi.finally_offset))
333            {
334                auto e = cast(Error)(cast(Throwable) h);
335                if (e !is null && (cast(Error) curr.t) is null)
336                {
337                    debug printf("new error %p bypassing inflight %p\n", h, curr.t);
338
339                    e.bypassedException = curr.t;
340                    prev.next = curr.next;
341                    //h = cast(Object*) t;
342                }
343                else
344                {
345                    debug printf("replacing thrown %p with inflight %p\n", h, __inflight.t);
346
347                    auto t = curr.t;
348                    auto n = curr.t;
349
350                    while (n.next)
351                        n = n.next;
352                    n.next = cast(Throwable) h;
353                    prev.next = curr.next;
354                    h = cast(Object*) t;
355                }
356            }
357        }
358
359        // walk through handler table, checking each handler
360        // with an index smaller than the current table_index
361        int prev_ndx;
362        for (auto ndx = index; ndx != -1; ndx = prev_ndx)
363        {
364            auto phi = &handler_table.handler_info.ptr[ndx];
365            prev_ndx = phi.prev_index;
366            if (phi.cioffset)
367            {
368                // this is a catch handler (no finally)
369
370                auto pci = cast(DCatchInfo *)(cast(char *)handler_table + phi.cioffset);
371                auto ncatches = pci.ncatches;
372                for (int i = 0; i < ncatches; i++)
373                {
374                    auto ci = **cast(ClassInfo **)h;
375
376                    auto pcb = &pci.catch_block.ptr[i];
377
378                    if (_d_isbaseof(ci, pcb.type))
379                    {
380                        // Matched the catch type, so we've found the handler.
381
382                        // Initialize catch variable
383                        *cast(void **)(regebp + (pcb.bpoffset)) = h;
384
385                        // Jump to catch block. Does not return.
386                        {
387                            size_t catch_esp;
388                            fp_t catch_addr;
389
390                            catch_addr = cast(fp_t)(funcoffset + pcb.codeoffset);
391                            catch_esp = regebp - handler_table.espoffset - fp_t.sizeof;
392                            version (D_InlineAsm_X86)
393                                asm
394                                {
395                                    mov     EAX,catch_esp   ;
396                                    mov     ECX,catch_addr  ;
397                                    mov     [EAX],ECX       ;
398                                    mov     EBP,regebp      ;
399                                    mov     ESP,EAX         ; // reset stack
400                                    ret                     ; // jump to catch block
401                                }
402                            else version (D_InlineAsm_X86_64)
403                                asm
404                                {
405                                    mov     RAX,catch_esp   ;
406                                    mov     RCX,catch_esp   ;
407                                    mov     RCX,catch_addr  ;
408                                    mov     [RAX],RCX       ;
409                                    mov     RBP,regebp      ;
410                                    mov     RSP,RAX         ; // reset stack
411                                    ret                     ; // jump to catch block
412                                }
413                            else
414                                static assert(0);
415                        }
416                    }
417                }
418            }
419            else if (phi.finally_offset)
420            {
421                // Call finally block
422                // Note that it is unnecessary to adjust the ESP, as the finally block
423                // accesses all items on the stack as relative to EBP.
424                debug printf("calling finally_offset %p\n", phi.finally_offset);
425
426                auto     blockaddr = cast(void*)(funcoffset + phi.finally_offset);
427                InFlight inflight;
428
429                inflight.addr = blockaddr;
430                inflight.next = __inflight;
431                inflight.t    = cast(Throwable) h;
432                __inflight    = &inflight;
433
434                version (OSX)
435                {
436                    version (D_InlineAsm_X86)
437                        asm
438                        {
439                            sub     ESP,4           ;
440                            push    EBX             ;
441                            mov     EBX,blockaddr   ;
442                            push    EBP             ;
443                            mov     EBP,regebp      ;
444                            call    EBX             ;
445                            pop     EBP             ;
446                            pop     EBX             ;
447                            add     ESP,4           ;
448                        }
449                    else version (D_InlineAsm_X86_64)
450                        asm
451                        {
452                            sub     RSP,8           ;
453                            push    RBX             ;
454                            mov     RBX,blockaddr   ;
455                            push    RBP             ;
456                            mov     RBP,regebp      ;
457                            call    RBX             ;
458                            pop     RBP             ;
459                            pop     RBX             ;
460                            add     RSP,8           ;
461                        }
462                    else
463                        static assert(0);
464                }
465                else
466                {
467                    version (D_InlineAsm_X86)
468                        asm
469                        {
470                            push    EBX             ;
471                            mov     EBX,blockaddr   ;
472                            push    EBP             ;
473                            mov     EBP,regebp      ;
474                            call    EBX             ;
475                            pop     EBP             ;
476                            pop     EBX             ;
477                        }
478                    else version (D_InlineAsm_X86_64)
479                        asm
480                        {
481                            sub     RSP,8           ;
482                            push    RBX             ;
483                            mov     RBX,blockaddr   ;
484                            push    RBP             ;
485                            mov     RBP,regebp      ;
486                            call    RBX             ;
487                            pop     RBP             ;
488                            pop     RBX             ;
489                            add     RSP,8           ;
490                        }
491                    else
492                        static assert(0);
493                }
494
495                if (__inflight is &inflight)
496                    __inflight = __inflight.next;
497            }
498        }
499    }
500    terminate();
501}
502
503}