github.com/goccy/go-jit@v0.0.0-20200514131505-ff78d45cf6af/internal/ccall/jit-except.c (about) 1 /* 2 * jit-except.c - Exception handling functions. 3 * 4 * Copyright (C) 2004 Southern Storm Software, Pty Ltd. 5 * 6 * This file is part of the libjit library. 7 * 8 * The libjit library is free software: you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public License 10 * as published by the Free Software Foundation, either version 2.1 of 11 * the License, or (at your option) any later version. 12 * 13 * The libjit library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with the libjit library. If not, see 20 * <http://www.gnu.org/licenses/>. 21 */ 22 23 #include "jit-internal.h" 24 #include "jit-rules.h" 25 #ifdef HAVE_STDLIB_H 26 # include <stdlib.h> 27 #endif 28 #if defined(JIT_BACKEND_INTERP) 29 # include "jit-interp.h" 30 #endif 31 #include <stdio.h> 32 #include "jit-setjmp.h" 33 34 /*@ 35 36 @cindex jit-except.h 37 38 @*/ 39 40 /*@ 41 * @deftypefun {void *} jit_exception_get_last (void) 42 * Get the last exception object that occurred on this thread, or NULL 43 * if there is no exception object on this thread. As far as @code{libjit} 44 * is concerned, an exception is just a pointer. The precise meaning of the 45 * data at the pointer is determined by the front end. 46 * @end deftypefun 47 @*/ 48 void *jit_exception_get_last(void) 49 { 50 jit_thread_control_t control = _jit_thread_get_control(); 51 if(control) 52 { 53 return control->last_exception; 54 } 55 else 56 { 57 return 0; 58 } 59 } 60 61 /*@ 62 * @deftypefun {void *} jit_exception_get_last_and_clear (void) 63 * Get the last exception object that occurred on this thread and also 64 * clear the exception state to NULL. This combines the effect of 65 * both @code{jit_exception_get_last} and @code{jit_exception_clear_last}. 66 * @end deftypefun 67 @*/ 68 void *jit_exception_get_last_and_clear(void) 69 { 70 jit_thread_control_t control = _jit_thread_get_control(); 71 if(control) 72 { 73 void *obj = control->last_exception; 74 control->last_exception = 0; 75 return obj; 76 } 77 else 78 { 79 return 0; 80 } 81 } 82 83 /*@ 84 * @deftypefun void jit_exception_set_last (void *@var{object}) 85 * Set the last exception object that occurred on this thread, so that 86 * it can be retrieved by a later call to @code{jit_exception_get_last}. 87 * This is normally used by @code{jit_function_apply} to save the 88 * exception object before returning to regular code. 89 * @end deftypefun 90 @*/ 91 void jit_exception_set_last(void *object) 92 { 93 jit_thread_control_t control = _jit_thread_get_control(); 94 if(control) 95 { 96 control->last_exception = object; 97 } 98 } 99 100 /*@ 101 * @deftypefun void jit_exception_clear_last (void) 102 * Clear the last exception object that occurred on this thread. 103 * This is equivalent to calling @code{jit_exception_set_last} 104 * with a parameter of NULL. 105 * @end deftypefun 106 @*/ 107 void jit_exception_clear_last(void) 108 { 109 jit_exception_set_last(0); 110 } 111 112 /*@ 113 * @deftypefun void jit_exception_throw (void *@var{object}) 114 * Throw an exception object within the current thread. As far as 115 * @code{libjit} is concerned, the exception object is just a pointer. 116 * The precise meaning of the data at the pointer is determined 117 * by the front end. 118 * 119 * Note: as an exception object works its way back up the stack, 120 * it may be temporarily stored in memory that is not normally visible 121 * to a garbage collector. The front-end is responsible for taking steps 122 * to "pin" the object so that it is uncollectable until explicitly 123 * copied back into a location that is visible to the collector once more. 124 * @end deftypefun 125 @*/ 126 void jit_exception_throw(void *object) 127 { 128 jit_thread_control_t control = _jit_thread_get_control(); 129 if(control) 130 { 131 control->last_exception = object; 132 if(control->setjmp_head) 133 { 134 control->backtrace_head = control->setjmp_head->trace; 135 longjmp(control->setjmp_head->buf, 1); 136 } 137 } 138 } 139 140 /*@ 141 * @deftypefun void jit_exception_builtin (int @var{exception_type}) 142 * This function is called to report a builtin exception. 143 * The JIT will automatically embed calls to this function wherever a 144 * builtin exception needs to be reported. 145 * 146 * When a builtin exception occurs, the current thread's exception 147 * handler is called to construct an appropriate object, which is 148 * then thrown. 149 * 150 * If there is no exception handler set, or the handler returns NULL, 151 * then @code{libjit} will print an error message to stderr and cause 152 * the program to exit with a status of 1. You normally don't want 153 * this behavior and you should override it if possible. 154 * 155 * The following builtin exception types are currently supported: 156 * 157 * @table @code 158 * @vindex JIT_RESULT_OK 159 * @item JIT_RESULT_OK 160 * The operation was performed successfully (value is 1). 161 * 162 * @vindex JIT_RESULT_OVERFLOW 163 * @item JIT_RESULT_OVERFLOW 164 * The operation resulted in an overflow exception (value is 0). 165 * 166 * @vindex JIT_RESULT_ARITHMETIC 167 * @item JIT_RESULT_ARITHMETIC 168 * The operation resulted in an arithmetic exception. i.e. an attempt was 169 * made to divide the minimum integer value by -1 (value is -1). 170 * 171 * @vindex JIT_RESULT_DIVISION_BY_ZERO 172 * @item JIT_RESULT_DIVISION_BY_ZERO 173 * The operation resulted in a division by zero exception (value is -2). 174 * 175 * @vindex JIT_RESULT_COMPILE_ERROR 176 * @item JIT_RESULT_COMPILE_ERROR 177 * An error occurred when attempting to dynamically compile a function 178 * (value is -3). 179 * 180 * @vindex JIT_RESULT_OUT_OF_MEMORY 181 * @item JIT_RESULT_OUT_OF_MEMORY 182 * The system ran out of memory while performing an operation (value is -4). 183 * 184 * @vindex JIT_RESULT_NULL_REFERENCE 185 * @item JIT_RESULT_NULL_REFERENCE 186 * An attempt was made to dereference a NULL pointer (value is -5). 187 * 188 * @vindex JIT_RESULT_NULL_FUNCTION 189 * @item JIT_RESULT_NULL_FUNCTION 190 * An attempt was made to call a function with a NULL function pointer 191 * (value is -6). 192 * 193 * @vindex JIT_RESULT_CALLED_NESTED 194 * @item JIT_RESULT_CALLED_NESTED 195 * An attempt was made to call a nested function from a non-nested context 196 * (value is -7). 197 * 198 * @vindex JIT_RESULT_OUT_OF_BOUNDS 199 * @item JIT_RESULT_OUT_OF_BOUNDS 200 * The operation resulted in an out of bounds array access (value is -8). 201 * 202 * @vindex JIT_RESULT_UNDEFINED_LABEL 203 * @item JIT_RESULT_UNDEFINED_LABEL 204 * A branch operation used a label that was not defined anywhere in the 205 * function (value is -9). 206 * @end table 207 * @end deftypefun 208 @*/ 209 void jit_exception_builtin(int exception_type) 210 { 211 jit_exception_func handler; 212 void *object; 213 static const char * const messages[11] = { 214 "Success", 215 "Overflow during checked arithmetic operation", 216 "Arithmetic exception (dividing the minimum integer by -1)", 217 "Division by zero", 218 "Error during function compilation", 219 "Out of memory", 220 "Null pointer dereferenced", 221 "Null function pointer called", 222 "Nested function called from non-nested context", 223 "Array index out of bounds", 224 "Undefined label" 225 }; 226 #define num_messages (sizeof(messages) / sizeof(const char *)) 227 228 /* Get the exception handler for this thread */ 229 handler = jit_exception_get_handler(); 230 231 /* Invoke the exception handler to create an appropriate object */ 232 if(handler) 233 { 234 object = (*handler)(exception_type); 235 if(object) 236 { 237 jit_exception_throw(object); 238 } 239 } 240 241 /* We don't have an exception handler, so print a message and exit */ 242 fputs("A builtin JIT exception could not be handled:\n", stderr); 243 exception_type = -(exception_type - 1); 244 if(exception_type >= 0 && exception_type < (int)num_messages) 245 { 246 fputs(messages[exception_type], stderr); 247 } 248 else 249 { 250 fprintf(stderr, "Unknown builtin exception %d", 251 (-exception_type) + 1); 252 } 253 putc('\n', stderr); 254 exit(1); 255 } 256 257 /*@ 258 * @deftypefun jit_exception_func jit_exception_set_handler (jit_exception_func @var{handler}) 259 * Set the builtin exception handler for the current thread. 260 * Returns the previous exception handler. 261 * @end deftypefun 262 @*/ 263 jit_exception_func jit_exception_set_handler 264 (jit_exception_func handler) 265 { 266 jit_exception_func previous; 267 jit_thread_control_t control = _jit_thread_get_control(); 268 if(control) 269 { 270 previous = control->exception_handler; 271 control->exception_handler = handler; 272 return previous; 273 } 274 else 275 { 276 return 0; 277 } 278 } 279 280 /*@ 281 * @deftypefun jit_exception_func jit_exception_get_handler (void) 282 * Get the builtin exception handler for the current thread. 283 * @end deftypefun 284 @*/ 285 jit_exception_func jit_exception_get_handler(void) 286 { 287 jit_thread_control_t control = _jit_thread_get_control(); 288 if(control) 289 { 290 return control->exception_handler; 291 } 292 else 293 { 294 return 0; 295 } 296 } 297 298 /* 299 * Structure of a stack trace. 300 */ 301 struct jit_stack_trace 302 { 303 unsigned int size; 304 void *items[1]; 305 }; 306 307 /*@ 308 * @deftypefun jit_stack_trace_t jit_exception_get_stack_trace (void) 309 * Create an object that represents the current call stack. 310 * This is normally used to indicate the location of an exception. 311 * Returns NULL if a stack trace is not available, or there is 312 * insufficient memory to create it. 313 * @end deftypefun 314 @*/ 315 jit_stack_trace_t jit_exception_get_stack_trace(void) 316 { 317 jit_stack_trace_t trace; 318 unsigned int size; 319 jit_unwind_context_t unwind; 320 321 /* Count the number of items in the current thread's call stack */ 322 size = 0; 323 if(jit_unwind_init(&unwind, NULL)) 324 { 325 do 326 { 327 size++; 328 } 329 while(jit_unwind_next_pc(&unwind)); 330 jit_unwind_free(&unwind); 331 } 332 333 /* Bail out if the stack is not available */ 334 if(size == 0) 335 { 336 return 0; 337 } 338 339 /* Allocate memory for the stack trace */ 340 trace = (jit_stack_trace_t) jit_malloc(sizeof(struct jit_stack_trace) 341 + size * sizeof(void *) 342 - sizeof(void *)); 343 if(!trace) 344 { 345 return 0; 346 } 347 trace->size = size; 348 349 /* Populate the stack trace with the items we counted earlier */ 350 size = 0; 351 if(jit_unwind_init(&unwind, NULL)) 352 { 353 do 354 { 355 trace->items[size] = jit_unwind_get_pc(&unwind); 356 size++; 357 } 358 while(jit_unwind_next_pc(&unwind)); 359 jit_unwind_free(&unwind); 360 } 361 else 362 { 363 jit_free(trace); 364 return 0; 365 } 366 367 return trace; 368 } 369 370 /*@ 371 * @deftypefun {unsigned int} jit_stack_trace_get_size (jit_stack_trace_t @var{trace}) 372 * Get the size of a stack trace. 373 * @end deftypefun 374 @*/ 375 unsigned int jit_stack_trace_get_size(jit_stack_trace_t trace) 376 { 377 if(trace) 378 { 379 return trace->size; 380 } 381 else 382 { 383 return 0; 384 } 385 } 386 387 /*@ 388 * @deftypefun jit_function_t jit_stack_trace_get_function (jit_context_t @var{context}, jit_stack_trace_t @var{trace}, unsigned int @var{posn}) 389 * Get the function that is at position @var{posn} within a stack trace. 390 * Position 0 is the function that created the stack trace. If this 391 * returns NULL, then it indicates that there is a native callout at 392 * @var{posn} within the stack trace. 393 * @end deftypefun 394 @*/ 395 jit_function_t 396 jit_stack_trace_get_function(jit_context_t context, jit_stack_trace_t trace, unsigned int posn) 397 { 398 if(trace && posn < trace->size) 399 { 400 void *func_info = _jit_memory_find_function_info(context, trace->items[posn]); 401 if(func_info) 402 { 403 return _jit_memory_get_function(context, func_info); 404 } 405 } 406 return 0; 407 } 408 409 /*@ 410 * @deftypefun {void *} jit_stack_trace_get_pc (jit_stack_trace_t @var{trace}, unsigned int @var{posn}) 411 * Get the program counter that corresponds to position @var{posn} 412 * within a stack trace. This is the point within the function 413 * where execution had reached at the time of the trace. 414 * @end deftypefun 415 @*/ 416 void *jit_stack_trace_get_pc 417 (jit_stack_trace_t trace, unsigned int posn) 418 { 419 if(trace && posn < trace->size) 420 { 421 return trace->items[posn]; 422 } 423 else 424 { 425 return 0; 426 } 427 } 428 429 /*@ 430 * @deftypefun {unsigned int} jit_stack_trace_get_offset (jit_stack_trace_t @var{trace}, unsigned int @var{posn}) 431 * Get the bytecode offset that is recorded for position @var{posn} 432 * within a stack trace. This will be @code{JIT_NO_OFFSET} if there 433 * is no bytecode offset associated with @var{posn}. 434 * @end deftypefun 435 @*/ 436 unsigned int 437 jit_stack_trace_get_offset(jit_context_t context, jit_stack_trace_t trace, unsigned int posn) 438 { 439 void *func_info; 440 jit_function_t func; 441 442 if(!trace || posn >= trace->size) 443 { 444 return JIT_NO_OFFSET; 445 } 446 447 func_info = _jit_memory_find_function_info(context, trace->items[posn]); 448 if(!func_info) 449 { 450 return JIT_NO_OFFSET; 451 } 452 func = _jit_memory_get_function(context, func_info); 453 if(!func) 454 { 455 return JIT_NO_OFFSET; 456 } 457 458 return _jit_function_get_bytecode(func, func_info, trace->items[posn], 0); 459 } 460 461 /*@ 462 * @deftypefun void jit_stack_trace_free (jit_stack_trace_t @var{trace}) 463 * Free the memory associated with a stack trace. 464 * @end deftypefun 465 @*/ 466 void jit_stack_trace_free(jit_stack_trace_t trace) 467 { 468 if(trace) 469 { 470 jit_free(trace); 471 } 472 } 473 474 void _jit_backtrace_push(jit_backtrace_t trace, void *pc) 475 { 476 jit_thread_control_t control = _jit_thread_get_control(); 477 if(control) 478 { 479 trace->parent = control->backtrace_head; 480 trace->pc = pc; 481 trace->security_object = 0; 482 trace->free_security_object = 0; 483 control->backtrace_head = trace; 484 } 485 else 486 { 487 trace->parent = 0; 488 trace->pc = pc; 489 trace->security_object = 0; 490 trace->free_security_object = 0; 491 } 492 } 493 494 void _jit_backtrace_pop(void) 495 { 496 jit_thread_control_t control = _jit_thread_get_control(); 497 jit_backtrace_t trace; 498 if(control) 499 { 500 trace = control->backtrace_head; 501 if(trace) 502 { 503 control->backtrace_head = trace->parent; 504 if(trace->security_object && trace->free_security_object) 505 { 506 (*(trace->free_security_object))(trace->security_object); 507 } 508 } 509 } 510 } 511 512 void _jit_backtrace_set(jit_backtrace_t trace) 513 { 514 jit_thread_control_t control = _jit_thread_get_control(); 515 if(control) 516 { 517 control->backtrace_head = trace; 518 } 519 } 520 521 void _jit_unwind_push_setjmp(jit_jmp_buf *jbuf) 522 { 523 jit_thread_control_t control = _jit_thread_get_control(); 524 if(control) 525 { 526 jbuf->trace = control->backtrace_head; 527 jbuf->catch_pc = 0; 528 jbuf->parent = control->setjmp_head; 529 control->setjmp_head = jbuf; 530 } 531 } 532 533 void _jit_unwind_pop_setjmp(void) 534 { 535 jit_thread_control_t control = _jit_thread_get_control(); 536 if(control && control->setjmp_head) 537 { 538 control->backtrace_head = control->setjmp_head->trace; 539 control->setjmp_head = control->setjmp_head->parent; 540 } 541 } 542 543 void _jit_unwind_pop_and_rethrow(void) 544 { 545 _jit_unwind_pop_setjmp(); 546 jit_exception_throw(jit_exception_get_last()); 547 }