Fix indentation and run_test type
[tinycc.git] / tccrun.c
blobfe7a757b0ddb366e42e16943bd143dbeb3ae37af
1 /*
2 * TCC - Tiny C Compiler - Support for -run switch
4 * Copyright (c) 2001-2004 Fabrice Bellard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "tcc.h"
23 /* only native compiler supports -run */
24 #ifdef TCC_IS_NATIVE
26 #ifndef _WIN32
27 # include <sys/mman.h>
28 #endif
30 #ifdef CONFIG_TCC_BACKTRACE
31 # ifndef _WIN32
32 # include <signal.h>
33 # ifndef __OpenBSD__
34 # include <sys/ucontext.h>
35 # endif
36 # else
37 # define ucontext_t CONTEXT
38 # endif
39 ST_DATA int rt_num_callers = 6;
40 ST_DATA const char **rt_bound_error_msg;
41 ST_DATA void *rt_prog_main;
42 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
43 static void rt_error(ucontext_t *uc, const char *fmt, ...);
44 static void set_exception_handler(void);
45 #endif
47 static void set_pages_executable(void *ptr, unsigned long length);
48 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff);
50 #ifdef _WIN64
51 static void *win64_add_function_table(TCCState *s1);
52 static void win64_del_function_table(void *);
53 #endif
55 /* ------------------------------------------------------------- */
56 /* Do all relocations (needed before using tcc_get_symbol())
57 Returns -1 on error. */
59 LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
61 int size;
62 addr_t ptr_diff = 0;
64 if (TCC_RELOCATE_AUTO != ptr)
65 return tcc_relocate_ex(s1, ptr, 0);
67 size = tcc_relocate_ex(s1, NULL, 0);
68 if (size < 0)
69 return -1;
71 #ifdef HAVE_SELINUX
73 /* Using mmap instead of malloc */
74 void *prx;
75 char tmpfname[] = "/tmp/.tccrunXXXXXX";
76 int fd = mkstemp(tmpfname);
77 unlink(tmpfname);
78 ftruncate(fd, size);
80 ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
81 prx = mmap (NULL, size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
82 if (ptr == MAP_FAILED || prx == MAP_FAILED)
83 tcc_error("tccrun: could not map memory");
84 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
85 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx);
86 ptr_diff = (char*)prx - (char*)ptr;
88 #else
89 ptr = tcc_malloc(size);
90 #endif
91 tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
92 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
93 return 0;
96 ST_FUNC void tcc_run_free(TCCState *s1)
98 int i;
100 for (i = 0; i < s1->nb_runtime_mem; ++i) {
101 #ifdef HAVE_SELINUX
102 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
103 munmap(s1->runtime_mem[i++], size);
104 munmap(s1->runtime_mem[i], size);
105 #else
106 #ifdef _WIN64
107 win64_del_function_table(*(void**)s1->runtime_mem[i]);
108 #endif
109 tcc_free(s1->runtime_mem[i]);
110 #endif
112 tcc_free(s1->runtime_mem);
115 /* launch the compiled program with the given arguments */
116 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
118 int (*prog_main)(int, char **);
120 s1->runtime_main = s1->nostdlib ? "_start" : "main";
121 if ((s1->dflag & 16) && !find_elf_sym(s1->symtab, s1->runtime_main))
122 return 0;
123 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
124 return -1;
125 prog_main = tcc_get_symbol_err(s1, s1->runtime_main);
127 #ifdef CONFIG_TCC_BACKTRACE
128 if (s1->do_debug) {
129 set_exception_handler();
130 rt_prog_main = prog_main;
132 #endif
134 errno = 0; /* clean errno value */
136 #ifdef CONFIG_TCC_BCHECK
137 if (s1->do_bounds_check) {
138 void (*bound_init)(void);
139 void (*bound_exit)(void);
140 void (*bound_new_region)(void *p, addr_t size);
141 int (*bound_delete_region)(void *p);
142 int i, ret;
144 /* set error function */
145 rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
146 /* XXX: use .init section so that it also work in binary ? */
147 bound_init = tcc_get_symbol_err(s1, "__bound_init");
148 bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
149 bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region");
150 bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region");
152 bound_init();
153 /* mark argv area as valid */
154 bound_new_region(argv, argc*sizeof(argv[0]));
155 for (i=0; i<argc; ++i)
156 bound_new_region(argv[i], strlen(argv[i]) + 1);
158 ret = (*prog_main)(argc, argv);
160 /* unmark argv area */
161 for (i=0; i<argc; ++i)
162 bound_delete_region(argv[i]);
163 bound_delete_region(argv);
164 bound_exit();
165 return ret;
167 #endif
168 return (*prog_main)(argc, argv);
171 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
172 /* To avoid that x86 processors would reload cached instructions
173 each time when data is written in the near, we need to make
174 sure that code and data do not share the same 64 byte unit */
175 #define RUN_SECTION_ALIGNMENT 63
176 #else
177 #define RUN_SECTION_ALIGNMENT 0
178 #endif
180 /* relocate code. Return -1 on error, required size if ptr is NULL,
181 otherwise copy code into buffer passed by the caller */
182 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
184 Section *s;
185 unsigned offset, length, align, max_align, i, k, f;
186 addr_t mem, addr;
188 if (NULL == ptr) {
189 s1->nb_errors = 0;
190 #ifdef TCC_TARGET_PE
191 pe_output_file(s1, NULL);
192 #else
193 tcc_add_runtime(s1);
194 resolve_common_syms(s1);
195 build_got_entries(s1);
196 #endif
197 if (s1->nb_errors)
198 return -1;
201 offset = max_align = 0, mem = (addr_t)ptr;
202 #ifdef _WIN64
203 offset += sizeof (void*); /* space for function_table pointer */
204 #endif
205 for (k = 0; k < 2; ++k) {
206 f = 0, addr = k ? mem : mem + ptr_diff;
207 for(i = 1; i < s1->nb_sections; i++) {
208 s = s1->sections[i];
209 if (0 == (s->sh_flags & SHF_ALLOC))
210 continue;
211 if (k != !(s->sh_flags & SHF_EXECINSTR))
212 continue;
213 align = s->sh_addralign - 1;
214 if (++f == 1 && align < RUN_SECTION_ALIGNMENT)
215 align = RUN_SECTION_ALIGNMENT;
216 if (max_align < align)
217 max_align = align;
218 offset += -(addr + offset) & align;
219 s->sh_addr = mem ? addr + offset : 0;
220 offset += s->data_offset;
221 #if 0
222 if (mem)
223 printf("%-16s %p len %04x align %2d\n",
224 s->name, (void*)s->sh_addr, (unsigned)s->data_offset, align + 1);
225 #endif
229 /* relocate symbols */
230 relocate_syms(s1, s1->symtab, 1);
231 if (s1->nb_errors)
232 return -1;
234 if (0 == mem)
235 return offset + max_align;
237 #ifdef TCC_TARGET_PE
238 s1->pe_imagebase = mem;
239 #endif
241 /* relocate each section */
242 for(i = 1; i < s1->nb_sections; i++) {
243 s = s1->sections[i];
244 if (s->reloc)
245 relocate_section(s1, s);
247 relocate_plt(s1);
249 for(i = 1; i < s1->nb_sections; i++) {
250 s = s1->sections[i];
251 if (0 == (s->sh_flags & SHF_ALLOC))
252 continue;
253 length = s->data_offset;
254 ptr = (void*)s->sh_addr;
255 if (s->sh_flags & SHF_EXECINSTR)
256 ptr = (char*)ptr - ptr_diff;
257 if (NULL == s->data || s->sh_type == SHT_NOBITS)
258 memset(ptr, 0, length);
259 else
260 memcpy(ptr, s->data, length);
261 /* mark executable sections as executable in memory */
262 if (s->sh_flags & SHF_EXECINSTR)
263 set_pages_executable((char*)ptr + ptr_diff, length);
266 #ifdef _WIN64
267 *(void**)mem = win64_add_function_table(s1);
268 #endif
270 return 0;
273 /* ------------------------------------------------------------- */
274 /* allow to run code in memory */
276 static void set_pages_executable(void *ptr, unsigned long length)
278 #ifdef _WIN32
279 unsigned long old_protect;
280 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
281 #else
282 void __clear_cache(void *beginning, void *end);
283 # ifndef HAVE_SELINUX
284 addr_t start, end;
285 # ifndef PAGESIZE
286 # define PAGESIZE 4096
287 # endif
288 start = (addr_t)ptr & ~(PAGESIZE - 1);
289 end = (addr_t)ptr + length;
290 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
291 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
292 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
293 # endif
294 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
295 __clear_cache(ptr, (char *)ptr + length);
296 # endif
297 #endif
300 #ifdef _WIN64
301 static void *win64_add_function_table(TCCState *s1)
303 void *p = NULL;
304 if (s1->uw_pdata) {
305 p = (void*)s1->uw_pdata->sh_addr;
306 RtlAddFunctionTable(
307 (RUNTIME_FUNCTION*)p,
308 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
309 s1->pe_imagebase
311 s1->uw_pdata = NULL;
313 return p;
316 static void win64_del_function_table(void *p)
318 if (p) {
319 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
322 #endif
324 /* ------------------------------------------------------------- */
325 #ifdef CONFIG_TCC_BACKTRACE
327 ST_FUNC void tcc_set_num_callers(int n)
329 rt_num_callers = n;
332 /* print the position in the source file of PC value 'pc' by reading
333 the stabs debug information */
334 static addr_t rt_printline(addr_t wanted_pc, const char *msg)
336 char func_name[128], last_func_name[128];
337 addr_t func_addr, last_pc, pc;
338 const char *incl_files[INCLUDE_STACK_SIZE];
339 int incl_index, len, last_line_num, i;
340 const char *str, *p;
342 Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
343 int stab_len = 0;
344 char *stab_str = NULL;
346 if (stab_section) {
347 stab_len = stab_section->data_offset;
348 stab_sym = (Stab_Sym *)stab_section->data;
349 stab_str = (char *) stabstr_section->data;
352 func_name[0] = '\0';
353 func_addr = 0;
354 incl_index = 0;
355 last_func_name[0] = '\0';
356 last_pc = (addr_t)-1;
357 last_line_num = 1;
359 if (!stab_sym)
360 goto no_stabs;
362 stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
363 for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
364 switch(sym->n_type) {
365 /* function start or end */
366 case N_FUN:
367 if (sym->n_strx == 0) {
368 /* we test if between last line and end of function */
369 pc = sym->n_value + func_addr;
370 if (wanted_pc >= last_pc && wanted_pc < pc)
371 goto found;
372 func_name[0] = '\0';
373 func_addr = 0;
374 } else {
375 str = stab_str + sym->n_strx;
376 p = strchr(str, ':');
377 if (!p) {
378 pstrcpy(func_name, sizeof(func_name), str);
379 } else {
380 len = p - str;
381 if (len > sizeof(func_name) - 1)
382 len = sizeof(func_name) - 1;
383 memcpy(func_name, str, len);
384 func_name[len] = '\0';
386 func_addr = sym->n_value;
388 break;
389 /* line number info */
390 case N_SLINE:
391 pc = sym->n_value + func_addr;
392 if (wanted_pc >= last_pc && wanted_pc < pc)
393 goto found;
394 last_pc = pc;
395 last_line_num = sym->n_desc;
396 /* XXX: slow! */
397 strcpy(last_func_name, func_name);
398 break;
399 /* include files */
400 case N_BINCL:
401 str = stab_str + sym->n_strx;
402 add_incl:
403 if (incl_index < INCLUDE_STACK_SIZE) {
404 incl_files[incl_index++] = str;
406 break;
407 case N_EINCL:
408 if (incl_index > 1)
409 incl_index--;
410 break;
411 case N_SO:
412 if (sym->n_strx == 0) {
413 incl_index = 0; /* end of translation unit */
414 } else {
415 str = stab_str + sym->n_strx;
416 /* do not add path */
417 len = strlen(str);
418 if (len > 0 && str[len - 1] != '/')
419 goto add_incl;
421 break;
425 no_stabs:
426 /* second pass: we try symtab symbols (no line number info) */
427 incl_index = 0;
428 if (symtab_section)
430 ElfW(Sym) *sym, *sym_end;
431 int type;
433 sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
434 for(sym = (ElfW(Sym) *)symtab_section->data + 1;
435 sym < sym_end;
436 sym++) {
437 type = ELFW(ST_TYPE)(sym->st_info);
438 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
439 if (wanted_pc >= sym->st_value &&
440 wanted_pc < sym->st_value + sym->st_size) {
441 pstrcpy(last_func_name, sizeof(last_func_name),
442 (char *) symtab_section->link->data + sym->st_name);
443 func_addr = sym->st_value;
444 goto found;
449 /* did not find any info: */
450 fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
451 fflush(stderr);
452 return 0;
453 found:
454 i = incl_index;
455 if (i > 0)
456 fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
457 fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
458 if (last_func_name[0] != '\0')
459 fprintf(stderr, " %s()", last_func_name);
460 if (--i >= 0) {
461 fprintf(stderr, " (included from ");
462 for (;;) {
463 fprintf(stderr, "%s", incl_files[i]);
464 if (--i < 0)
465 break;
466 fprintf(stderr, ", ");
468 fprintf(stderr, ")");
470 fprintf(stderr, "\n");
471 fflush(stderr);
472 return func_addr;
475 /* emit a run time error at position 'pc' */
476 static void rt_error(ucontext_t *uc, const char *fmt, ...)
478 va_list ap;
479 addr_t pc;
480 int i;
482 fprintf(stderr, "Runtime error: ");
483 va_start(ap, fmt);
484 vfprintf(stderr, fmt, ap);
485 va_end(ap);
486 fprintf(stderr, "\n");
488 for(i=0;i<rt_num_callers;i++) {
489 if (rt_get_caller_pc(&pc, uc, i) < 0)
490 break;
491 pc = rt_printline(pc, i ? "by" : "at");
492 if (pc == (addr_t)rt_prog_main && pc)
493 break;
497 /* ------------------------------------------------------------- */
498 #ifndef _WIN32
500 /* signal handler for fatal errors */
501 static void sig_error(int signum, siginfo_t *siginf, void *puc)
503 ucontext_t *uc = puc;
505 switch(signum) {
506 case SIGFPE:
507 switch(siginf->si_code) {
508 case FPE_INTDIV:
509 case FPE_FLTDIV:
510 rt_error(uc, "division by zero");
511 break;
512 default:
513 rt_error(uc, "floating point exception");
514 break;
516 break;
517 case SIGBUS:
518 case SIGSEGV:
519 if (rt_bound_error_msg && *rt_bound_error_msg)
520 rt_error(uc, *rt_bound_error_msg);
521 else
522 rt_error(uc, "dereferencing invalid pointer");
523 break;
524 case SIGILL:
525 rt_error(uc, "illegal instruction");
526 break;
527 case SIGABRT:
528 rt_error(uc, "abort() called");
529 break;
530 default:
531 rt_error(uc, "caught signal %d", signum);
532 break;
534 exit(255);
537 #ifndef SA_SIGINFO
538 # define SA_SIGINFO 0x00000004u
539 #endif
541 /* Generate a stack backtrace when a CPU exception occurs. */
542 static void set_exception_handler(void)
544 struct sigaction sigact;
545 /* install TCC signal handlers to print debug info on fatal
546 runtime errors */
547 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
548 sigact.sa_sigaction = sig_error;
549 sigemptyset(&sigact.sa_mask);
550 sigaction(SIGFPE, &sigact, NULL);
551 sigaction(SIGILL, &sigact, NULL);
552 sigaction(SIGSEGV, &sigact, NULL);
553 sigaction(SIGBUS, &sigact, NULL);
554 sigaction(SIGABRT, &sigact, NULL);
557 /* ------------------------------------------------------------- */
558 #ifdef __i386__
560 /* fix for glibc 2.1 */
561 #ifndef REG_EIP
562 #define REG_EIP EIP
563 #define REG_EBP EBP
564 #endif
566 /* return the PC at frame level 'level'. Return negative if not found */
567 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
569 addr_t fp;
570 int i;
572 if (level == 0) {
573 #if defined(__APPLE__)
574 *paddr = uc->uc_mcontext->__ss.__eip;
575 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
576 *paddr = uc->uc_mcontext.mc_eip;
577 #elif defined(__dietlibc__)
578 *paddr = uc->uc_mcontext.eip;
579 #elif defined(__NetBSD__)
580 *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
581 #elif defined(__OpenBSD__)
582 *paddr = uc->sc_eip;
583 #else
584 *paddr = uc->uc_mcontext.gregs[REG_EIP];
585 #endif
586 return 0;
587 } else {
588 #if defined(__APPLE__)
589 fp = uc->uc_mcontext->__ss.__ebp;
590 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
591 fp = uc->uc_mcontext.mc_ebp;
592 #elif defined(__dietlibc__)
593 fp = uc->uc_mcontext.ebp;
594 #elif defined(__NetBSD__)
595 fp = uc->uc_mcontext.__gregs[_REG_EBP];
596 #elif defined(__OpenBSD__)
597 *paddr = uc->sc_ebp;
598 #else
599 fp = uc->uc_mcontext.gregs[REG_EBP];
600 #endif
601 for(i=1;i<level;i++) {
602 /* XXX: check address validity with program info */
603 if (fp <= 0x1000 || fp >= 0xc0000000)
604 return -1;
605 fp = ((addr_t *)fp)[0];
607 *paddr = ((addr_t *)fp)[1];
608 return 0;
612 /* ------------------------------------------------------------- */
613 #elif defined(__x86_64__)
615 /* return the PC at frame level 'level'. Return negative if not found */
616 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
618 addr_t fp;
619 int i;
621 if (level == 0) {
622 /* XXX: only support linux */
623 #if defined(__APPLE__)
624 *paddr = uc->uc_mcontext->__ss.__rip;
625 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
626 *paddr = uc->uc_mcontext.mc_rip;
627 #elif defined(__NetBSD__)
628 *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
629 #else
630 *paddr = uc->uc_mcontext.gregs[REG_RIP];
631 #endif
632 return 0;
633 } else {
634 #if defined(__APPLE__)
635 fp = uc->uc_mcontext->__ss.__rbp;
636 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
637 fp = uc->uc_mcontext.mc_rbp;
638 #elif defined(__NetBSD__)
639 fp = uc->uc_mcontext.__gregs[_REG_RBP];
640 #else
641 fp = uc->uc_mcontext.gregs[REG_RBP];
642 #endif
643 for(i=1;i<level;i++) {
644 /* XXX: check address validity with program info */
645 if (fp <= 0x1000)
646 return -1;
647 fp = ((addr_t *)fp)[0];
649 *paddr = ((addr_t *)fp)[1];
650 return 0;
654 /* ------------------------------------------------------------- */
655 #elif defined(__arm__)
657 /* return the PC at frame level 'level'. Return negative if not found */
658 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
660 addr_t fp, sp;
661 int i;
663 if (level == 0) {
664 /* XXX: only supports linux */
665 #if defined(__linux__)
666 *paddr = uc->uc_mcontext.arm_pc;
667 #else
668 return -1;
669 #endif
670 return 0;
671 } else {
672 #if defined(__linux__)
673 fp = uc->uc_mcontext.arm_fp;
674 sp = uc->uc_mcontext.arm_sp;
675 if (sp < 0x1000)
676 sp = 0x1000;
677 #else
678 return -1;
679 #endif
680 /* XXX: specific to tinycc stack frames */
681 if (fp < sp + 12 || fp & 3)
682 return -1;
683 for(i = 1; i < level; i++) {
684 sp = ((addr_t *)fp)[-2];
685 if (sp < fp || sp - fp > 16 || sp & 3)
686 return -1;
687 fp = ((addr_t *)fp)[-3];
688 if (fp <= sp || fp - sp < 12 || fp & 3)
689 return -1;
691 /* XXX: check address validity with program info */
692 *paddr = ((addr_t *)fp)[-1];
693 return 0;
697 /* ------------------------------------------------------------- */
698 #elif defined(__aarch64__)
700 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
702 if (level < 0)
703 return -1;
704 else if (level == 0) {
705 *paddr = uc->uc_mcontext.pc;
706 return 0;
708 else {
709 addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
710 int i;
711 for (i = 1; i < level; i++)
712 fp = (addr_t *)fp[0];
713 *paddr = fp[1];
714 return 0;
718 /* ------------------------------------------------------------- */
719 #else
721 #warning add arch specific rt_get_caller_pc()
722 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
724 return -1;
727 #endif /* !__i386__ */
729 /* ------------------------------------------------------------- */
730 #else /* WIN32 */
732 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
734 EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
735 CONTEXT *uc = ex_info->ContextRecord;
736 switch (er->ExceptionCode) {
737 case EXCEPTION_ACCESS_VIOLATION:
738 if (rt_bound_error_msg && *rt_bound_error_msg)
739 rt_error(uc, *rt_bound_error_msg);
740 else
741 rt_error(uc, "access violation");
742 break;
743 case EXCEPTION_STACK_OVERFLOW:
744 rt_error(uc, "stack overflow");
745 break;
746 case EXCEPTION_INT_DIVIDE_BY_ZERO:
747 rt_error(uc, "division by zero");
748 break;
749 default:
750 rt_error(uc, "exception caught");
751 break;
753 return EXCEPTION_EXECUTE_HANDLER;
756 /* Generate a stack backtrace when a CPU exception occurs. */
757 static void set_exception_handler(void)
759 SetUnhandledExceptionFilter(cpu_exception_handler);
762 /* return the PC at frame level 'level'. Return non zero if not found */
763 static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
765 addr_t fp, pc;
766 int i;
767 #ifdef _WIN64
768 pc = uc->Rip;
769 fp = uc->Rbp;
770 #else
771 pc = uc->Eip;
772 fp = uc->Ebp;
773 #endif
774 if (level > 0) {
775 for(i=1;i<level;i++) {
776 /* XXX: check address validity with program info */
777 if (fp <= 0x1000 || fp >= 0xc0000000)
778 return -1;
779 fp = ((addr_t*)fp)[0];
781 pc = ((addr_t*)fp)[1];
783 *paddr = pc;
784 return 0;
787 #endif /* _WIN32 */
788 #endif /* CONFIG_TCC_BACKTRACE */
789 /* ------------------------------------------------------------- */
790 #ifdef CONFIG_TCC_STATIC
792 /* dummy function for profiling */
793 ST_FUNC void *dlopen(const char *filename, int flag)
795 return NULL;
798 ST_FUNC void dlclose(void *p)
802 ST_FUNC const char *dlerror(void)
804 return "error";
807 typedef struct TCCSyms {
808 char *str;
809 void *ptr;
810 } TCCSyms;
813 /* add the symbol you want here if no dynamic linking is done */
814 static TCCSyms tcc_syms[] = {
815 #if !defined(CONFIG_TCCBOOT)
816 #define TCCSYM(a) { #a, &a, },
817 TCCSYM(printf)
818 TCCSYM(fprintf)
819 TCCSYM(fopen)
820 TCCSYM(fclose)
821 #undef TCCSYM
822 #endif
823 { NULL, NULL },
826 ST_FUNC void *dlsym(void *handle, const char *symbol)
828 TCCSyms *p;
829 p = tcc_syms;
830 while (p->str != NULL) {
831 if (!strcmp(p->str, symbol))
832 return p->ptr;
833 p++;
835 return NULL;
838 #endif /* CONFIG_TCC_STATIC */
839 #endif /* TCC_IS_NATIVE */
840 /* ------------------------------------------------------------- */