Fix another corner case with C/asm symtable
[tinycc.git] / tccrun.c
blobd0501a0b85e05bfdd07c4358aa4d7adafd4a6262
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 = "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 #define RUN_SECTION_ALIGNMENT 63
173 #else
174 #define RUN_SECTION_ALIGNMENT 15
175 #endif
177 /* relocate code. Return -1 on error, required size if ptr is NULL,
178 otherwise copy code into buffer passed by the caller */
179 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
181 Section *s;
182 unsigned offset, length, fill, i, k;
183 addr_t mem;
185 if (NULL == ptr) {
186 s1->nb_errors = 0;
187 #ifdef TCC_TARGET_PE
188 pe_output_file(s1, NULL);
189 #else
190 tcc_add_runtime(s1);
191 relocate_common_syms();
192 tcc_add_linker_symbols(s1);
193 build_got_entries(s1);
194 #endif
195 if (s1->nb_errors)
196 return -1;
199 offset = 0, mem = (addr_t)ptr;
200 fill = -mem & RUN_SECTION_ALIGNMENT;
201 #ifdef _WIN64
202 offset += sizeof (void*);
203 #endif
204 for (k = 0; k < 2; ++k) {
205 for(i = 1; i < s1->nb_sections; i++) {
206 s = s1->sections[i];
207 if (0 == (s->sh_flags & SHF_ALLOC))
208 continue;
209 if (k != !(s->sh_flags & SHF_EXECINSTR))
210 continue;
211 offset += fill;
212 if (!mem)
213 s->sh_addr = 0;
214 else if (s->sh_flags & SHF_EXECINSTR)
215 s->sh_addr = mem + offset + ptr_diff;
216 else
217 s->sh_addr = mem + offset;
218 #if 0
219 if (mem)
220 printf("%-16s +%02lx %p %04x\n",
221 s->name, fill, (void*)s->sh_addr, (unsigned)s->data_offset);
222 #endif
223 offset += s->data_offset;
224 fill = -(mem + offset) & 15;
226 #if RUN_SECTION_ALIGNMENT > 15
227 /* To avoid that x86 processors would reload cached instructions each time
228 when data is written in the near, we need to make sure that code and data
229 do not share the same 64 byte unit */
230 fill = -(mem + offset) & RUN_SECTION_ALIGNMENT;
231 #endif
234 /* relocate symbols */
235 relocate_syms(s1, s1->symtab, 1);
236 if (s1->nb_errors)
237 return -1;
239 if (0 == mem)
240 return offset + RUN_SECTION_ALIGNMENT;
242 /* relocate each section */
243 for(i = 1; i < s1->nb_sections; i++) {
244 s = s1->sections[i];
245 if (s->reloc)
246 relocate_section(s1, s);
248 relocate_plt(s1);
250 for(i = 1; i < s1->nb_sections; i++) {
251 s = s1->sections[i];
252 if (0 == (s->sh_flags & SHF_ALLOC))
253 continue;
254 length = s->data_offset;
255 ptr = (void*)s->sh_addr;
256 if (s->sh_flags & SHF_EXECINSTR)
257 ptr = (char*)ptr - ptr_diff;
258 if (NULL == s->data || s->sh_type == SHT_NOBITS)
259 memset(ptr, 0, length);
260 else
261 memcpy(ptr, s->data, length);
262 /* mark executable sections as executable in memory */
263 if (s->sh_flags & SHF_EXECINSTR)
264 set_pages_executable((char*)ptr + ptr_diff, length);
267 #ifdef _WIN64
268 *(void**)mem = win64_add_function_table(s1);
269 #endif
271 return 0;
274 /* ------------------------------------------------------------- */
275 /* allow to run code in memory */
277 static void set_pages_executable(void *ptr, unsigned long length)
279 #ifdef _WIN32
280 unsigned long old_protect;
281 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
282 #else
283 void __clear_cache(void *beginning, void *end);
284 # ifndef HAVE_SELINUX
285 addr_t start, end;
286 # ifndef PAGESIZE
287 # define PAGESIZE 4096
288 # endif
289 start = (addr_t)ptr & ~(PAGESIZE - 1);
290 end = (addr_t)ptr + length;
291 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
292 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
293 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
294 # endif
295 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
296 __clear_cache(ptr, (char *)ptr + length);
297 # endif
298 #endif
301 #ifdef _WIN64
302 static void *win64_add_function_table(TCCState *s1)
304 void *p = NULL;
305 if (s1->uw_pdata) {
306 p = (void*)s1->uw_pdata->sh_addr;
307 RtlAddFunctionTable(
308 (RUNTIME_FUNCTION*)p,
309 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
310 text_section->sh_addr
312 s1->uw_pdata = NULL;
314 return p;;
317 static void win64_del_function_table(void *p)
319 if (p) {
320 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
323 #endif
325 /* ------------------------------------------------------------- */
326 #ifdef CONFIG_TCC_BACKTRACE
328 ST_FUNC void tcc_set_num_callers(int n)
330 rt_num_callers = n;
333 /* print the position in the source file of PC value 'pc' by reading
334 the stabs debug information */
335 static addr_t rt_printline(addr_t wanted_pc, const char *msg)
337 char func_name[128], last_func_name[128];
338 addr_t func_addr, last_pc, pc;
339 const char *incl_files[INCLUDE_STACK_SIZE];
340 int incl_index, len, last_line_num, i;
341 const char *str, *p;
343 Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
344 int stab_len = 0;
345 char *stab_str = NULL;
347 if (stab_section) {
348 stab_len = stab_section->data_offset;
349 stab_sym = (Stab_Sym *)stab_section->data;
350 stab_str = (char *) stabstr_section->data;
353 func_name[0] = '\0';
354 func_addr = 0;
355 incl_index = 0;
356 last_func_name[0] = '\0';
357 last_pc = (addr_t)-1;
358 last_line_num = 1;
360 if (!stab_sym)
361 goto no_stabs;
363 stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
364 for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
365 switch(sym->n_type) {
366 /* function start or end */
367 case N_FUN:
368 if (sym->n_strx == 0) {
369 /* we test if between last line and end of function */
370 pc = sym->n_value + func_addr;
371 if (wanted_pc >= last_pc && wanted_pc < pc)
372 goto found;
373 func_name[0] = '\0';
374 func_addr = 0;
375 } else {
376 str = stab_str + sym->n_strx;
377 p = strchr(str, ':');
378 if (!p) {
379 pstrcpy(func_name, sizeof(func_name), str);
380 } else {
381 len = p - str;
382 if (len > sizeof(func_name) - 1)
383 len = sizeof(func_name) - 1;
384 memcpy(func_name, str, len);
385 func_name[len] = '\0';
387 func_addr = sym->n_value;
389 break;
390 /* line number info */
391 case N_SLINE:
392 pc = sym->n_value + func_addr;
393 if (wanted_pc >= last_pc && wanted_pc < pc)
394 goto found;
395 last_pc = pc;
396 last_line_num = sym->n_desc;
397 /* XXX: slow! */
398 strcpy(last_func_name, func_name);
399 break;
400 /* include files */
401 case N_BINCL:
402 str = stab_str + sym->n_strx;
403 add_incl:
404 if (incl_index < INCLUDE_STACK_SIZE) {
405 incl_files[incl_index++] = str;
407 break;
408 case N_EINCL:
409 if (incl_index > 1)
410 incl_index--;
411 break;
412 case N_SO:
413 if (sym->n_strx == 0) {
414 incl_index = 0; /* end of translation unit */
415 } else {
416 str = stab_str + sym->n_strx;
417 /* do not add path */
418 len = strlen(str);
419 if (len > 0 && str[len - 1] != '/')
420 goto add_incl;
422 break;
426 no_stabs:
427 /* second pass: we try symtab symbols (no line number info) */
428 incl_index = 0;
429 if (symtab_section)
431 ElfW(Sym) *sym, *sym_end;
432 int type;
434 sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
435 for(sym = (ElfW(Sym) *)symtab_section->data + 1;
436 sym < sym_end;
437 sym++) {
438 type = ELFW(ST_TYPE)(sym->st_info);
439 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
440 if (wanted_pc >= sym->st_value &&
441 wanted_pc < sym->st_value + sym->st_size) {
442 pstrcpy(last_func_name, sizeof(last_func_name),
443 (char *) strtab_section->data + sym->st_name);
444 func_addr = sym->st_value;
445 goto found;
450 /* did not find any info: */
451 fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
452 fflush(stderr);
453 return 0;
454 found:
455 i = incl_index;
456 if (i > 0)
457 fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
458 fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
459 if (last_func_name[0] != '\0')
460 fprintf(stderr, " %s()", last_func_name);
461 if (--i >= 0) {
462 fprintf(stderr, " (included from ");
463 for (;;) {
464 fprintf(stderr, "%s", incl_files[i]);
465 if (--i < 0)
466 break;
467 fprintf(stderr, ", ");
469 fprintf(stderr, ")");
471 fprintf(stderr, "\n");
472 fflush(stderr);
473 return func_addr;
476 /* emit a run time error at position 'pc' */
477 static void rt_error(ucontext_t *uc, const char *fmt, ...)
479 va_list ap;
480 addr_t pc;
481 int i;
483 fprintf(stderr, "Runtime error: ");
484 va_start(ap, fmt);
485 vfprintf(stderr, fmt, ap);
486 va_end(ap);
487 fprintf(stderr, "\n");
489 for(i=0;i<rt_num_callers;i++) {
490 if (rt_get_caller_pc(&pc, uc, i) < 0)
491 break;
492 pc = rt_printline(pc, i ? "by" : "at");
493 if (pc == (addr_t)rt_prog_main && pc)
494 break;
498 /* ------------------------------------------------------------- */
499 #ifndef _WIN32
501 /* signal handler for fatal errors */
502 static void sig_error(int signum, siginfo_t *siginf, void *puc)
504 ucontext_t *uc = puc;
506 switch(signum) {
507 case SIGFPE:
508 switch(siginf->si_code) {
509 case FPE_INTDIV:
510 case FPE_FLTDIV:
511 rt_error(uc, "division by zero");
512 break;
513 default:
514 rt_error(uc, "floating point exception");
515 break;
517 break;
518 case SIGBUS:
519 case SIGSEGV:
520 if (rt_bound_error_msg && *rt_bound_error_msg)
521 rt_error(uc, *rt_bound_error_msg);
522 else
523 rt_error(uc, "dereferencing invalid pointer");
524 break;
525 case SIGILL:
526 rt_error(uc, "illegal instruction");
527 break;
528 case SIGABRT:
529 rt_error(uc, "abort() called");
530 break;
531 default:
532 rt_error(uc, "caught signal %d", signum);
533 break;
535 exit(255);
538 #ifndef SA_SIGINFO
539 # define SA_SIGINFO 0x00000004u
540 #endif
542 /* Generate a stack backtrace when a CPU exception occurs. */
543 static void set_exception_handler(void)
545 struct sigaction sigact;
546 /* install TCC signal handlers to print debug info on fatal
547 runtime errors */
548 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
549 sigact.sa_sigaction = sig_error;
550 sigemptyset(&sigact.sa_mask);
551 sigaction(SIGFPE, &sigact, NULL);
552 sigaction(SIGILL, &sigact, NULL);
553 sigaction(SIGSEGV, &sigact, NULL);
554 sigaction(SIGBUS, &sigact, NULL);
555 sigaction(SIGABRT, &sigact, NULL);
558 /* ------------------------------------------------------------- */
559 #ifdef __i386__
561 /* fix for glibc 2.1 */
562 #ifndef REG_EIP
563 #define REG_EIP EIP
564 #define REG_EBP EBP
565 #endif
567 /* return the PC at frame level 'level'. Return negative if not found */
568 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
570 addr_t fp;
571 int i;
573 if (level == 0) {
574 #if defined(__APPLE__)
575 *paddr = uc->uc_mcontext->__ss.__eip;
576 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
577 *paddr = uc->uc_mcontext.mc_eip;
578 #elif defined(__dietlibc__)
579 *paddr = uc->uc_mcontext.eip;
580 #elif defined(__NetBSD__)
581 *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
582 #elif defined(__OpenBSD__)
583 *paddr = uc->sc_eip;
584 #else
585 *paddr = uc->uc_mcontext.gregs[REG_EIP];
586 #endif
587 return 0;
588 } else {
589 #if defined(__APPLE__)
590 fp = uc->uc_mcontext->__ss.__ebp;
591 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
592 fp = uc->uc_mcontext.mc_ebp;
593 #elif defined(__dietlibc__)
594 fp = uc->uc_mcontext.ebp;
595 #elif defined(__NetBSD__)
596 fp = uc->uc_mcontext.__gregs[_REG_EBP];
597 #elif defined(__OpenBSD__)
598 *paddr = uc->sc_ebp;
599 #else
600 fp = uc->uc_mcontext.gregs[REG_EBP];
601 #endif
602 for(i=1;i<level;i++) {
603 /* XXX: check address validity with program info */
604 if (fp <= 0x1000 || fp >= 0xc0000000)
605 return -1;
606 fp = ((addr_t *)fp)[0];
608 *paddr = ((addr_t *)fp)[1];
609 return 0;
613 /* ------------------------------------------------------------- */
614 #elif defined(__x86_64__)
616 /* return the PC at frame level 'level'. Return negative if not found */
617 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
619 addr_t fp;
620 int i;
622 if (level == 0) {
623 /* XXX: only support linux */
624 #if defined(__APPLE__)
625 *paddr = uc->uc_mcontext->__ss.__rip;
626 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
627 *paddr = uc->uc_mcontext.mc_rip;
628 #elif defined(__NetBSD__)
629 *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
630 #else
631 *paddr = uc->uc_mcontext.gregs[REG_RIP];
632 #endif
633 return 0;
634 } else {
635 #if defined(__APPLE__)
636 fp = uc->uc_mcontext->__ss.__rbp;
637 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
638 fp = uc->uc_mcontext.mc_rbp;
639 #elif defined(__NetBSD__)
640 fp = uc->uc_mcontext.__gregs[_REG_RBP];
641 #else
642 fp = uc->uc_mcontext.gregs[REG_RBP];
643 #endif
644 for(i=1;i<level;i++) {
645 /* XXX: check address validity with program info */
646 if (fp <= 0x1000)
647 return -1;
648 fp = ((addr_t *)fp)[0];
650 *paddr = ((addr_t *)fp)[1];
651 return 0;
655 /* ------------------------------------------------------------- */
656 #elif defined(__arm__)
658 /* return the PC at frame level 'level'. Return negative if not found */
659 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
661 addr_t fp, sp;
662 int i;
664 if (level == 0) {
665 /* XXX: only supports linux */
666 #if defined(__linux__)
667 *paddr = uc->uc_mcontext.arm_pc;
668 #else
669 return -1;
670 #endif
671 return 0;
672 } else {
673 #if defined(__linux__)
674 fp = uc->uc_mcontext.arm_fp;
675 sp = uc->uc_mcontext.arm_sp;
676 if (sp < 0x1000)
677 sp = 0x1000;
678 #else
679 return -1;
680 #endif
681 /* XXX: specific to tinycc stack frames */
682 if (fp < sp + 12 || fp & 3)
683 return -1;
684 for(i = 1; i < level; i++) {
685 sp = ((addr_t *)fp)[-2];
686 if (sp < fp || sp - fp > 16 || sp & 3)
687 return -1;
688 fp = ((addr_t *)fp)[-3];
689 if (fp <= sp || fp - sp < 12 || fp & 3)
690 return -1;
692 /* XXX: check address validity with program info */
693 *paddr = ((addr_t *)fp)[-1];
694 return 0;
698 /* ------------------------------------------------------------- */
699 #elif defined(__aarch64__)
701 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
703 if (level < 0)
704 return -1;
705 else if (level == 0) {
706 *paddr = uc->uc_mcontext.pc;
707 return 0;
709 else {
710 addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
711 int i;
712 for (i = 1; i < level; i++)
713 fp = (addr_t *)fp[0];
714 *paddr = fp[1];
715 return 0;
719 /* ------------------------------------------------------------- */
720 #else
722 #warning add arch specific rt_get_caller_pc()
723 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
725 return -1;
728 #endif /* !__i386__ */
730 /* ------------------------------------------------------------- */
731 #else /* WIN32 */
733 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
735 EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
736 CONTEXT *uc = ex_info->ContextRecord;
737 switch (er->ExceptionCode) {
738 case EXCEPTION_ACCESS_VIOLATION:
739 if (rt_bound_error_msg && *rt_bound_error_msg)
740 rt_error(uc, *rt_bound_error_msg);
741 else
742 rt_error(uc, "access violation");
743 break;
744 case EXCEPTION_STACK_OVERFLOW:
745 rt_error(uc, "stack overflow");
746 break;
747 case EXCEPTION_INT_DIVIDE_BY_ZERO:
748 rt_error(uc, "division by zero");
749 break;
750 default:
751 rt_error(uc, "exception caught");
752 break;
754 return EXCEPTION_EXECUTE_HANDLER;
757 /* Generate a stack backtrace when a CPU exception occurs. */
758 static void set_exception_handler(void)
760 SetUnhandledExceptionFilter(cpu_exception_handler);
763 /* return the PC at frame level 'level'. Return non zero if not found */
764 static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
766 addr_t fp, pc;
767 int i;
768 #ifdef _WIN64
769 pc = uc->Rip;
770 fp = uc->Rbp;
771 #else
772 pc = uc->Eip;
773 fp = uc->Ebp;
774 #endif
775 if (level > 0) {
776 for(i=1;i<level;i++) {
777 /* XXX: check address validity with program info */
778 if (fp <= 0x1000 || fp >= 0xc0000000)
779 return -1;
780 fp = ((addr_t*)fp)[0];
782 pc = ((addr_t*)fp)[1];
784 *paddr = pc;
785 return 0;
788 #endif /* _WIN32 */
789 #endif /* CONFIG_TCC_BACKTRACE */
790 /* ------------------------------------------------------------- */
791 #ifdef CONFIG_TCC_STATIC
793 /* dummy function for profiling */
794 ST_FUNC void *dlopen(const char *filename, int flag)
796 return NULL;
799 ST_FUNC void dlclose(void *p)
803 ST_FUNC const char *dlerror(void)
805 return "error";
808 typedef struct TCCSyms {
809 char *str;
810 void *ptr;
811 } TCCSyms;
814 /* add the symbol you want here if no dynamic linking is done */
815 static TCCSyms tcc_syms[] = {
816 #if !defined(CONFIG_TCCBOOT)
817 #define TCCSYM(a) { #a, &a, },
818 TCCSYM(printf)
819 TCCSYM(fprintf)
820 TCCSYM(fopen)
821 TCCSYM(fclose)
822 #undef TCCSYM
823 #endif
824 { NULL, NULL },
827 ST_FUNC void *dlsym(void *handle, const char *symbol)
829 TCCSyms *p;
830 p = tcc_syms;
831 while (p->str != NULL) {
832 if (!strcmp(p->str, symbol))
833 return p->ptr;
834 p++;
836 return NULL;
839 #endif /* CONFIG_TCC_STATIC */
840 #endif /* TCC_IS_NATIVE */
841 /* ------------------------------------------------------------- */