1 module d.rt.eh; 2 3 import d.rt.dwarf; 4 import d.rt.unwind; 5 6 private enum ExceptionRegno = 0; 7 private enum SelectorRegno = 1; 8 9 private auto getExceptionClass() { 10 ulong ec; 11 12 auto s = "SDC\0D2\0\0"; 13 for (uint i = 0; i < s.length; i++) { 14 ec = ec << 8 | s[i]; 15 } 16 17 return ec; 18 } 19 20 enum ExceptionClass = getExceptionClass(); 21 22 private Throwable inFlight; 23 private _Unwind_Exception ue; 24 25 extern(C) void __sd_eh_delete(_Unwind_Reason_Code reason, 26 _Unwind_Exception* exceptionObject) { 27 inFlight = null; 28 } 29 30 /** 31 * Throws a exception. 32 */ 33 extern(C) void __sd_eh_throw(Throwable t) { 34 if (inFlight !is null) { 35 // TODO: chain 36 } 37 38 inFlight = t; 39 40 ue.exception_class = ExceptionClass; 41 ue.exception_cleanup = &__sd_eh_delete; 42 43 auto f = _Unwind_RaiseException(&ue); 44 45 import core.stdc.stdlib, core.stdc.stdio; 46 printf("FAILED TO RAISE EXCEPTION %i\n".ptr, f); 47 exit(-1); 48 } 49 50 extern(C) _Unwind_Reason_Code __sd_eh_personality( 51 int ver, 52 _Unwind_Action actions, 53 ulong exceptionClass, 54 _Unwind_Exception* exceptionObject, 55 _Unwind_Context* ctx 56 ) { 57 if (ver != 1) { 58 return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; 59 } 60 61 auto p = cast(const(ubyte)*) _Unwind_GetLanguageSpecificData(ctx); 62 if (p is null) { 63 return _Unwind_Reason_Code.CONTINUE_UNWIND; 64 } 65 66 // XXX: GCC mention: 67 // Shortcut for phase 2 found handler for domestic exception. 68 // Consider adding this when it is understood :D 69 70 // get the instruction pointer 71 // will be used to find the right entry in the callsite_table 72 // -1 because it will point past the last instruction 73 auto ip = _Unwind_GetIP(ctx) - 1; 74 75 // printf("ip:%p\n".ptr, ip); 76 77 auto headers = parseLsdHeader(p, ctx); 78 /+ 79 printf("start:\t%p\n".ptr, headers.start); 80 printf("lpStart:\t%p\n".ptr, headers.lpStart); 81 printf("typeTable:\t%p\n".ptr, headers.typeTable); 82 printf("actionTable:\t%p\n".ptr, headers.actionTable); 83 +/ 84 _Unwind_Ptr landingPad = null; 85 const(ubyte)* actionPtr = null; 86 87 while (p < headers.actionTable) { 88 auto start = read_encoded(p, ctx, headers.callSiteEncoding); 89 auto len = read_encoded(p, ctx, headers.callSiteEncoding); 90 auto lp = read_encoded(p, ctx, headers.callSiteEncoding); 91 auto action = read_uleb128(p); 92 93 // printf("start: %ld\tlen: %ld\tlp: %ld\n".ptr, start, len, lp); 94 95 // The table is sorted, so if we've passed the ip, stop. 96 if (ip < headers.start + start) { 97 return _Unwind_Reason_Code.CONTINUE_UNWIND; 98 } 99 100 // We found something ! 101 if (ip < headers.start + start + len) { 102 if (lp) { 103 landingPad = headers.lpStart + lp; 104 } 105 106 if (action) { 107 actionPtr = headers.actionTable + action - 1; 108 } 109 110 break; 111 } 112 } 113 114 if (landingPad is null) { 115 return _Unwind_Reason_Code.CONTINUE_UNWIND; 116 } 117 118 // We have landing pad, but no action. This is a cleanup. 119 if (actionPtr is null) { 120 return setupCleanup(ctx, actions, landingPad, exceptionObject); 121 } 122 123 // We do not catch foreign exceptions and if we have to force unwind. 124 bool doCatch = (exceptionClass == ExceptionClass) 125 && !(actions & _Unwind_Action.FORCE_UNWIND); 126 127 ptrdiff_t nextOffset = -1; 128 while (nextOffset) { 129 auto switchval = read_sleb128(actionPtr); 130 auto prev = actionPtr; 131 nextOffset = read_sleb128(actionPtr); 132 133 if (switchval < 0) { 134 import core.stdc.stdlib, core.stdc.stdio; 135 printf("FILTER NOT SUPPORTED\n".ptr); 136 exit(-1); 137 } 138 139 if (switchval == 0) { 140 // XXX: Ensure that nextOffset is 0 as cleanup must come last. 141 return setupCleanup(ctx, actions, landingPad, exceptionObject); 142 } 143 144 p = headers.typeTable - switchval * headers.typeEncoding.getSize(); 145 auto tmp = read_encoded(p, ctx, headers.typeEncoding); 146 auto candidate = *(cast(ClassInfo*) &tmp); 147 148 // Null is a special case that always catches. 149 if (candidate !is null && !doCatch) { 150 continue; 151 } 152 153 // null is a wildcard for catch all. 154 // XXX: We don't need to recompute all downcast every time. 155 if (candidate is null 156 || __sd_class_downcast(inFlight, candidate) !is null) { 157 return setupCatch(ctx, actions, switchval, landingPad, 158 exceptionObject); 159 } 160 161 actionPtr = prev + nextOffset; 162 } 163 164 // No action found. 165 return _Unwind_Reason_Code.CONTINUE_UNWIND; 166 } 167 168 private _Unwind_Reason_Code setupCatch( 169 _Unwind_Context* ctx, 170 _Unwind_Action actions, 171 ptrdiff_t switchval, 172 _Unwind_Ptr landingPad, 173 _Unwind_Exception* exceptionObject 174 ) { 175 if (actions & _Unwind_Action.SEARCH_PHASE) { 176 return _Unwind_Reason_Code.HANDLER_FOUND; 177 } 178 179 if (actions & _Unwind_Action.CLEANUP_PHASE) { 180 _Unwind_SetGR(ctx, ExceptionRegno, 181 *(cast(_Unwind_Word*) &exceptionObject)); 182 _Unwind_SetGR(ctx, SelectorRegno, switchval); 183 _Unwind_SetIP(ctx, landingPad); 184 185 return _Unwind_Reason_Code.INSTALL_CONTEXT; 186 } 187 188 return _Unwind_Reason_Code.FATAL_PHASE2_ERROR; 189 } 190 191 _Unwind_Reason_Code setupCleanup( 192 _Unwind_Context* ctx, 193 _Unwind_Action actions, 194 _Unwind_Ptr landingPad, 195 _Unwind_Exception* exceptionObject 196 ) { 197 // If we're merely in search phase, continue 198 if (actions & _Unwind_Action.SEARCH_PHASE) { 199 return _Unwind_Reason_Code.CONTINUE_UNWIND; 200 } 201 202 _Unwind_SetGR(ctx, ExceptionRegno, *(cast(_Unwind_Word*) &exceptionObject)); 203 _Unwind_SetGR(ctx, SelectorRegno, 0); 204 _Unwind_SetIP(ctx, landingPad); 205 206 return _Unwind_Reason_Code.INSTALL_CONTEXT; 207 } 208 209 // This is specific from SDC's runtime, so DMD doesn't know about it. 210 private extern(C) Object __sd_class_downcast(Object o, ClassInfo c);