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