Don't emit applied .rel sections
[tinycc.git] / tccrun.c
blob936016455c02ddfe6943905d5193fddd54de8461
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 resolve_common_syms(s1);
192 build_got_entries(s1);
193 #endif
194 if (s1->nb_errors)
195 return -1;
198 offset = 0, mem = (addr_t)ptr;
199 fill = -mem & RUN_SECTION_ALIGNMENT;
200 #ifdef _WIN64
201 offset += sizeof (void*);
202 #endif
203 for (k = 0; k < 2; ++k) {
204 for(i = 1; i < s1->nb_sections; i++) {
205 s = s1->sections[i];
206 if (0 == (s->sh_flags & SHF_ALLOC))
207 continue;
208 if (k != !(s->sh_flags & SHF_EXECINSTR))
209 continue;
210 offset += fill;
211 if (!mem)
212 s->sh_addr = 0;
213 else if (s->sh_flags & SHF_EXECINSTR)
214 s->sh_addr = mem + offset + ptr_diff;
215 else
216 s->sh_addr = mem + offset;
217 #if 0
218 if (mem)
219 printf("%-16s +%02lx %p %04x\n",
220 s->name, fill, (void*)s->sh_addr, (unsigned)s->data_offset);
221 #endif
222 offset += s->data_offset;
223 fill = -(mem + offset) & 15;
225 #if RUN_SECTION_ALIGNMENT > 15
226 /* To avoid that x86 processors would reload cached instructions each time
227 when data is written in the near, we need to make sure that code and data
228 do not share the same 64 byte unit */
229 fill = -(mem + offset) & RUN_SECTION_ALIGNMENT;
230 #endif
233 /* relocate symbols */
234 relocate_syms(s1, s1->symtab, 1);
235 if (s1->nb_errors)
236 return -1;
238 if (0 == mem)
239 return offset + RUN_SECTION_ALIGNMENT;
241 #ifdef TCC_TARGET_PE
242 s1->pe_imagebase = mem;
243 #endif
245 /* relocate each section */
246 for(i = 1; i < s1->nb_sections; i++) {
247 s = s1->sections[i];
248 if (s->reloc)
249 relocate_section(s1, s);
251 relocate_plt(s1);
253 for(i = 1; i < s1->nb_sections; i++) {
254 s = s1->sections[i];
255 if (0 == (s->sh_flags & SHF_ALLOC))
256 continue;
257 length = s->data_offset;
258 ptr = (void*)s->sh_addr;
259 if (s->sh_flags & SHF_EXECINSTR)
260 ptr = (char*)ptr - ptr_diff;
261 if (NULL == s->data || s->sh_type == SHT_NOBITS)
262 memset(ptr, 0, length);
263 else
264 memcpy(ptr, s->data, length);
265 /* mark executable sections as executable in memory */
266 if (s->sh_flags & SHF_EXECINSTR)
267 set_pages_executable((char*)ptr + ptr_diff, length);
270 #ifdef _WIN64
271 *(void**)mem = win64_add_function_table(s1);
272 #endif
274 return 0;
277 /* ------------------------------------------------------------- */
278 /* allow to run code in memory */
280 static void set_pages_executable(void *ptr, unsigned long length)
282 #ifdef _WIN32
283 unsigned long old_protect;
284 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
285 #else
286 void __clear_cache(void *beginning, void *end);
287 # ifndef HAVE_SELINUX
288 addr_t start, end;
289 # ifndef PAGESIZE
290 # define PAGESIZE 4096
291 # endif
292 start = (addr_t)ptr & ~(PAGESIZE - 1);
293 end = (addr_t)ptr + length;
294 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
295 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
296 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
297 # endif
298 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
299 __clear_cache(ptr, (char *)ptr + length);
300 # endif
301 #endif
304 #ifdef _WIN64
305 static void *win64_add_function_table(TCCState *s1)
307 void *p = NULL;
308 if (s1->uw_pdata) {
309 p = (void*)s1->uw_pdata->sh_addr;
310 RtlAddFunctionTable(
311 (RUNTIME_FUNCTION*)p,
312 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
313 s1->pe_imagebase
315 s1->uw_pdata = NULL;
317 return p;
320 static void win64_del_function_table(void *p)
322 if (p) {
323 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
326 #endif
328 /* ------------------------------------------------------------- */
329 #ifdef CONFIG_TCC_BACKTRACE
331 ST_FUNC void tcc_set_num_callers(int n)
333 rt_num_callers = n;
336 /* print the position in the source file of PC value 'pc' by reading
337 the stabs debug information */
338 static addr_t rt_printline(addr_t wanted_pc, const char *msg)
340 char func_name[128], last_func_name[128];
341 addr_t func_addr, last_pc, pc;
342 const char *incl_files[INCLUDE_STACK_SIZE];
343 int incl_index, len, last_line_num, i;
344 const char *str, *p;
346 Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
347 int stab_len = 0;
348 char *stab_str = NULL;
350 if (stab_section) {
351 stab_len = stab_section->data_offset;
352 stab_sym = (Stab_Sym *)stab_section->data;
353 stab_str = (char *) stabstr_section->data;
356 func_name[0] = '\0';
357 func_addr = 0;
358 incl_index = 0;
359 last_func_name[0] = '\0';
360 last_pc = (addr_t)-1;
361 last_line_num = 1;
363 if (!stab_sym)
364 goto no_stabs;
366 stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
367 for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
368 switch(sym->n_type) {
369 /* function start or end */
370 case N_FUN:
371 if (sym->n_strx == 0) {
372 /* we test if between last line and end of function */
373 pc = sym->n_value + func_addr;
374 if (wanted_pc >= last_pc && wanted_pc < pc)
375 goto found;
376 func_name[0] = '\0';
377 func_addr = 0;
378 } else {
379 str = stab_str + sym->n_strx;
380 p = strchr(str, ':');
381 if (!p) {
382 pstrcpy(func_name, sizeof(func_name), str);
383 } else {
384 len = p - str;
385 if (len > sizeof(func_name) - 1)
386 len = sizeof(func_name) - 1;
387 memcpy(func_name, str, len);
388 func_name[len] = '\0';
390 func_addr = sym->n_value;
392 break;
393 /* line number info */
394 case N_SLINE:
395 pc = sym->n_value + func_addr;
396 if (wanted_pc >= last_pc && wanted_pc < pc)
397 goto found;
398 last_pc = pc;
399 last_line_num = sym->n_desc;
400 /* XXX: slow! */
401 strcpy(last_func_name, func_name);
402 break;
403 /* include files */
404 case N_BINCL:
405 str = stab_str + sym->n_strx;
406 add_incl:
407 if (incl_index < INCLUDE_STACK_SIZE) {
408 incl_files[incl_index++] = str;
410 break;
411 case N_EINCL:
412 if (incl_index > 1)
413 incl_index--;
414 break;
415 case N_SO:
416 if (sym->n_strx == 0) {
417 incl_index = 0; /* end of translation unit */
418 } else {
419 str = stab_str + sym->n_strx;
420 /* do not add path */
421 len = strlen(str);
422 if (len > 0 && str[len - 1] != '/')
423 goto add_incl;
425 break;
429 no_stabs:
430 /* second pass: we try symtab symbols (no line number info) */
431 incl_index = 0;
432 if (symtab_section)
434 ElfW(Sym) *sym, *sym_end;
435 int type;
437 sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
438 for(sym = (ElfW(Sym) *)symtab_section->data + 1;
439 sym < sym_end;
440 sym++) {
441 type = ELFW(ST_TYPE)(sym->st_info);
442 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
443 if (wanted_pc >= sym->st_value &&
444 wanted_pc < sym->st_value + sym->st_size) {
445 pstrcpy(last_func_name, sizeof(last_func_name),
446 (char *) symtab_section->link->data + sym->st_name);
447 func_addr = sym->st_value;
448 goto found;
453 /* did not find any info: */
454 fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
455 fflush(stderr);
456 return 0;
457 found:
458 i = incl_index;
459 if (i > 0)
460 fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
461 fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
462 if (last_func_name[0] != '\0')
463 fprintf(stderr, " %s()", last_func_name);
464 if (--i >= 0) {
465 fprintf(stderr, " (included from ");
466 for (;;) {
467 fprintf(stderr, "%s", incl_files[i]);
468 if (--i < 0)
469 break;
470 fprintf(stderr, ", ");
472 fprintf(stderr, ")");
474 fprintf(stderr, "\n");
475 fflush(stderr);
476 return func_addr;
479 /* emit a run time error at position 'pc' */
480 static void rt_error(ucontext_t *uc, const char *fmt, ...)
482 va_list ap;
483 addr_t pc;
484 int i;
486 fprintf(stderr, "Runtime error: ");
487 va_start(ap, fmt);
488 vfprintf(stderr, fmt, ap);
489 va_end(ap);
490 fprintf(stderr, "\n");
492 for(i=0;i<rt_num_callers;i++) {
493 if (rt_get_caller_pc(&pc, uc, i) < 0)
494 break;
495 pc = rt_printline(pc, i ? "by" : "at");
496 if (pc == (addr_t)rt_prog_main && pc)
497 break;
501 /* ------------------------------------------------------------- */
502 #ifndef _WIN32
504 /* signal handler for fatal errors */
505 static void sig_error(int signum, siginfo_t *siginf, void *puc)
507 ucontext_t *uc = puc;
509 switch(signum) {
510 case SIGFPE:
511 switch(siginf->si_code) {
512 case FPE_INTDIV:
513 case FPE_FLTDIV:
514 rt_error(uc, "division by zero");
515 break;
516 default:
517 rt_error(uc, "floating point exception");
518 break;
520 break;
521 case SIGBUS:
522 case SIGSEGV:
523 if (rt_bound_error_msg && *rt_bound_error_msg)
524 rt_error(uc, *rt_bound_error_msg);
525 else
526 rt_error(uc, "dereferencing invalid pointer");
527 break;
528 case SIGILL:
529 rt_error(uc, "illegal instruction");
530 break;
531 case SIGABRT:
532 rt_error(uc, "abort() called");
533 break;
534 default:
535 rt_error(uc, "caught signal %d", signum);
536 break;
538 exit(255);
541 #ifndef SA_SIGINFO
542 # define SA_SIGINFO 0x00000004u
543 #endif
545 /* Generate a stack backtrace when a CPU exception occurs. */
546 static void set_exception_handler(void)
548 struct sigaction sigact;
549 /* install TCC signal handlers to print debug info on fatal
550 runtime errors */
551 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
552 sigact.sa_sigaction = sig_error;
553 sigemptyset(&sigact.sa_mask);
554 sigaction(SIGFPE, &sigact, NULL);
555 sigaction(SIGILL, &sigact, NULL);
556 sigaction(SIGSEGV, &sigact, NULL);
557 sigaction(SIGBUS, &sigact, NULL);
558 sigaction(SIGABRT, &sigact, NULL);
561 /* ------------------------------------------------------------- */
562 #ifdef __i386__
564 /* fix for glibc 2.1 */
565 #ifndef REG_EIP
566 #define REG_EIP EIP
567 #define REG_EBP EBP
568 #endif
570 /* return the PC at frame level 'level'. Return negative if not found */
571 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
573 addr_t fp;
574 int i;
576 if (level == 0) {
577 #if defined(__APPLE__)
578 *paddr = uc->uc_mcontext->__ss.__eip;
579 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
580 *paddr = uc->uc_mcontext.mc_eip;
581 #elif defined(__dietlibc__)
582 *paddr = uc->uc_mcontext.eip;
583 #elif defined(__NetBSD__)
584 *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
585 #elif defined(__OpenBSD__)
586 *paddr = uc->sc_eip;
587 #else
588 *paddr = uc->uc_mcontext.gregs[REG_EIP];
589 #endif
590 return 0;
591 } else {
592 #if defined(__APPLE__)
593 fp = uc->uc_mcontext->__ss.__ebp;
594 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
595 fp = uc->uc_mcontext.mc_ebp;
596 #elif defined(__dietlibc__)
597 fp = uc->uc_mcontext.ebp;
598 #elif defined(__NetBSD__)
599 fp = uc->uc_mcontext.__gregs[_REG_EBP];
600 #elif defined(__OpenBSD__)
601 *paddr = uc->sc_ebp;
602 #else
603 fp = uc->uc_mcontext.gregs[REG_EBP];
604 #endif
605 for(i=1;i<level;i++) {
606 /* XXX: check address validity with program info */
607 if (fp <= 0x1000 || fp >= 0xc0000000)
608 return -1;
609 fp = ((addr_t *)fp)[0];
611 *paddr = ((addr_t *)fp)[1];
612 return 0;
616 /* ------------------------------------------------------------- */
617 #elif defined(__x86_64__)
619 /* return the PC at frame level 'level'. Return negative if not found */
620 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
622 addr_t fp;
623 int i;
625 if (level == 0) {
626 /* XXX: only support linux */
627 #if defined(__APPLE__)
628 *paddr = uc->uc_mcontext->__ss.__rip;
629 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
630 *paddr = uc->uc_mcontext.mc_rip;
631 #elif defined(__NetBSD__)
632 *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
633 #else
634 *paddr = uc->uc_mcontext.gregs[REG_RIP];
635 #endif
636 return 0;
637 } else {
638 #if defined(__APPLE__)
639 fp = uc->uc_mcontext->__ss.__rbp;
640 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
641 fp = uc->uc_mcontext.mc_rbp;
642 #elif defined(__NetBSD__)
643 fp = uc->uc_mcontext.__gregs[_REG_RBP];
644 #else
645 fp = uc->uc_mcontext.gregs[REG_RBP];
646 #endif
647 for(i=1;i<level;i++) {
648 /* XXX: check address validity with program info */
649 if (fp <= 0x1000)
650 return -1;
651 fp = ((addr_t *)fp)[0];
653 *paddr = ((addr_t *)fp)[1];
654 return 0;
658 /* ------------------------------------------------------------- */
659 #elif defined(__arm__)
661 /* return the PC at frame level 'level'. Return negative if not found */
662 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
664 addr_t fp, sp;
665 int i;
667 if (level == 0) {
668 /* XXX: only supports linux */
669 #if defined(__linux__)
670 *paddr = uc->uc_mcontext.arm_pc;
671 #else
672 return -1;
673 #endif
674 return 0;
675 } else {
676 #if defined(__linux__)
677 fp = uc->uc_mcontext.arm_fp;
678 sp = uc->uc_mcontext.arm_sp;
679 if (sp < 0x1000)
680 sp = 0x1000;
681 #else
682 return -1;
683 #endif
684 /* XXX: specific to tinycc stack frames */
685 if (fp < sp + 12 || fp & 3)
686 return -1;
687 for(i = 1; i < level; i++) {
688 sp = ((addr_t *)fp)[-2];
689 if (sp < fp || sp - fp > 16 || sp & 3)
690 return -1;
691 fp = ((addr_t *)fp)[-3];
692 if (fp <= sp || fp - sp < 12 || fp & 3)
693 return -1;
695 /* XXX: check address validity with program info */
696 *paddr = ((addr_t *)fp)[-1];
697 return 0;
701 /* ------------------------------------------------------------- */
702 #elif defined(__aarch64__)
704 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
706 if (level < 0)
707 return -1;
708 else if (level == 0) {
709 *paddr = uc->uc_mcontext.pc;
710 return 0;
712 else {
713 addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
714 int i;
715 for (i = 1; i < level; i++)
716 fp = (addr_t *)fp[0];
717 *paddr = fp[1];
718 return 0;
722 /* ------------------------------------------------------------- */
723 #else
725 #warning add arch specific rt_get_caller_pc()
726 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
728 return -1;
731 #endif /* !__i386__ */
733 /* ------------------------------------------------------------- */
734 #else /* WIN32 */
736 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
738 EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
739 CONTEXT *uc = ex_info->ContextRecord;
740 switch (er->ExceptionCode) {
741 case EXCEPTION_ACCESS_VIOLATION:
742 if (rt_bound_error_msg && *rt_bound_error_msg)
743 rt_error(uc, *rt_bound_error_msg);
744 else
745 rt_error(uc, "access violation");
746 break;
747 case EXCEPTION_STACK_OVERFLOW:
748 rt_error(uc, "stack overflow");
749 break;
750 case EXCEPTION_INT_DIVIDE_BY_ZERO:
751 rt_error(uc, "division by zero");
752 break;
753 default:
754 rt_error(uc, "exception caught");
755 break;
757 return EXCEPTION_EXECUTE_HANDLER;
760 /* Generate a stack backtrace when a CPU exception occurs. */
761 static void set_exception_handler(void)
763 SetUnhandledExceptionFilter(cpu_exception_handler);
766 /* return the PC at frame level 'level'. Return non zero if not found */
767 static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
769 addr_t fp, pc;
770 int i;
771 #ifdef _WIN64
772 pc = uc->Rip;
773 fp = uc->Rbp;
774 #else
775 pc = uc->Eip;
776 fp = uc->Ebp;
777 #endif
778 if (level > 0) {
779 for(i=1;i<level;i++) {
780 /* XXX: check address validity with program info */
781 if (fp <= 0x1000 || fp >= 0xc0000000)
782 return -1;
783 fp = ((addr_t*)fp)[0];
785 pc = ((addr_t*)fp)[1];
787 *paddr = pc;
788 return 0;
791 #endif /* _WIN32 */
792 #endif /* CONFIG_TCC_BACKTRACE */
793 /* ------------------------------------------------------------- */
794 #ifdef CONFIG_TCC_STATIC
796 /* dummy function for profiling */
797 ST_FUNC void *dlopen(const char *filename, int flag)
799 return NULL;
802 ST_FUNC void dlclose(void *p)
806 ST_FUNC const char *dlerror(void)
808 return "error";
811 typedef struct TCCSyms {
812 char *str;
813 void *ptr;
814 } TCCSyms;
817 /* add the symbol you want here if no dynamic linking is done */
818 static TCCSyms tcc_syms[] = {
819 #if !defined(CONFIG_TCCBOOT)
820 #define TCCSYM(a) { #a, &a, },
821 TCCSYM(printf)
822 TCCSYM(fprintf)
823 TCCSYM(fopen)
824 TCCSYM(fclose)
825 #undef TCCSYM
826 #endif
827 { NULL, NULL },
830 ST_FUNC void *dlsym(void *handle, const char *symbol)
832 TCCSyms *p;
833 p = tcc_syms;
834 while (p->str != NULL) {
835 if (!strcmp(p->str, symbol))
836 return p->ptr;
837 p++;
839 return NULL;
842 #endif /* CONFIG_TCC_STATIC */
843 #endif /* TCC_IS_NATIVE */
844 /* ------------------------------------------------------------- */