2
0

exceptions 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. Exception Implementation in the Mono Runtime
  2. Dietmar Maurer ([email protected])
  3. (C) 2001 Ximian, Inc.
  4. Exception implementation (jit):
  5. ===============================
  6. Stack unwinding:
  7. ================
  8. We record the code address (start_address, size) of all methods. That way it is
  9. possible to map an instruction pointer (IP) to the method information needed
  10. for unwinding the stack:
  11. We also save a Last Managed Frame (LMF) structure at each call from managed to
  12. unmanaged code. That way we can recover from exceptions inside unmanaged code.
  13. void handle_exception ((struct sigcontext *ctx, gpointer obj)
  14. {
  15. if (ctx->bp < mono_end_of_stack) {
  16. /* unhandled exception */
  17. abort ();
  18. }
  19. info = mono_jit_info_table_find (mono_jit_info_table, ctx->ip);
  20. if (info) { // we are inside managed code
  21. if (ch = find_catch_handler ())
  22. execute_catch_handler (ch, ctx, obj);
  23. execute_all_finally_handler ();
  24. // restore register, including IP and Frame pointer
  25. ctx = restore_caller_saved_registers_from_ctx (ji, ctx);
  26. // continue unwinding
  27. handle_exception (ctx, obj);
  28. } else {
  29. lmf = get_last_managed_frame ();
  30. // restore register, including IP and Frame pointer
  31. ctx = restore_caller_saved_registers_from_lmf (ji, lmf);
  32. // continue unwinding
  33. handle_exception (ctx, obj);
  34. }
  35. }
  36. Code generation:
  37. ================
  38. leave: is simply translated into a branch to the target. If the leave
  39. instruction is inside a finally block (but not inside another handler)
  40. we call the finally handler before we branch to the target.
  41. finally/endfinally, filter/endfilter: is translated into subroutine ending with
  42. a "return" statement. The subroutine does not save EBP, because we need access
  43. to the local variables of the enclosing method. Its is possible that
  44. instructions inside those handlers modify the stack pointer, thus we save the
  45. stack pointer at the start of the handler, and restore it at the end. We have
  46. to use a "call" instruction to execute such finally handlers. This makes it
  47. also possible to execute them inside the stack unwinding code. The exception
  48. object for filters is passed in a local variable (cfg->exvar).
  49. throw: we first save all regs into a sigcontext struct and then call the stack
  50. unwinding code.
  51. catch handler: catch hanlders are always called from the stack unwinding
  52. code. The exception object is passed in a local variable (cfg->exvar).
  53. gcc support for Exceptions
  54. ==========================
  55. gcc supports exceptions in files compiled with the -fexception option. gcc
  56. generates DWARF exceptions tables in that case, so it is possible to unwind the
  57. stack. The method to read those exception tables is contained in libgcc.a, and
  58. in newer versions of glibc (glibc 2.2.5 for example), and it is called
  59. __frame_state_for(). Another usable glibc function is backtrace_symbols() which
  60. returns the function name corresponding to a code address.
  61. We dynamically check if those features are available using g_module_symbol(),
  62. and we use them only when available. If not available we use the LMF as
  63. fallback.
  64. Using gcc exception information prevents us from saving the LMF at each native
  65. call, so this is a way to speed up native calls. This is especially valuable
  66. for internal calls, because we can make sure that all internal calls are
  67. compiled with -fexceptions (we compile the whole mono runtime with that
  68. option).
  69. All native function are able to call function without exception tables, and so
  70. we are unable to restore all caller saved registers if an exception is raised
  71. in such function. Well, its possible if the previous function already saves all
  72. registers. So we only omit the the LMF if a function has an exception table
  73. able to restore all caller saved registers.
  74. One problem is that gcc almost never saves all caller saved registers, because
  75. it is just unnecessary in normal situations. But there is a trick forcing gcc
  76. to save all register, we just need to call __builtin_unwind_init() at the
  77. beginning of a function. That way gcc generates code to save all caller saved
  78. register on the stack.