Disable thumb code generation on tcctest.gcc
[tinycc.git] / tccrun.c
bloba733e9aef07043adcac1e5cf8dde796ce1ea2cdf
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
40 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
41 static void rt_error(ucontext_t *uc, const char *fmt, ...);
42 static void set_exception_handler(void);
44 #ifdef _WIN32
45 static DWORD s1_for_run_idx;
46 void set_s1_for_run(TCCState *s)
48 if (!s1_for_run_idx)
49 s1_for_run_idx = TlsAlloc();
50 TlsSetValue(s1_for_run_idx, s);
52 #define get_s1_for_run() ((TCCState*)TlsGetValue(s1_for_run_idx))
53 #else
54 /* XXX: add tls support for linux */
55 static TCCState *s1_for_run;
56 #define set_s1_for_run(s) (s1_for_run = s)
57 #define get_s1_for_run() s1_for_run
58 #endif
59 #endif
61 static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length);
62 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff);
64 #ifdef _WIN64
65 static void *win64_add_function_table(TCCState *s1);
66 static void win64_del_function_table(void *);
67 #endif
69 /* ------------------------------------------------------------- */
70 /* Do all relocations (needed before using tcc_get_symbol())
71 Returns -1 on error. */
73 LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
75 int size;
76 addr_t ptr_diff = 0;
78 if (TCC_RELOCATE_AUTO != ptr)
79 return tcc_relocate_ex(s1, ptr, 0);
81 size = tcc_relocate_ex(s1, NULL, 0);
82 if (size < 0)
83 return -1;
85 #ifdef HAVE_SELINUX
87 /* Using mmap instead of malloc */
88 void *prx;
89 char tmpfname[] = "/tmp/.tccrunXXXXXX";
90 int fd = mkstemp(tmpfname);
91 unlink(tmpfname);
92 ftruncate(fd, size);
94 ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
95 prx = mmap (NULL, size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
96 if (ptr == MAP_FAILED || prx == MAP_FAILED)
97 tcc_error("tccrun: could not map memory");
98 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
99 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx);
100 ptr_diff = (char*)prx - (char*)ptr;
102 #else
103 ptr = tcc_malloc(size);
104 #endif
105 tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
106 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
107 return 0;
110 ST_FUNC void tcc_run_free(TCCState *s1)
112 int i;
114 for (i = 0; i < s1->nb_runtime_mem; ++i) {
115 #ifdef HAVE_SELINUX
116 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
117 munmap(s1->runtime_mem[i++], size);
118 munmap(s1->runtime_mem[i], size);
119 #else
120 #ifdef _WIN64
121 win64_del_function_table(*(void**)s1->runtime_mem[i]);
122 #endif
123 tcc_free(s1->runtime_mem[i]);
124 #endif
126 tcc_free(s1->runtime_mem);
127 if (get_s1_for_run() == s1)
128 set_s1_for_run(NULL);
131 /* launch the compiled program with the given arguments */
132 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
134 int (*prog_main)(int, char **);
136 s1->runtime_main = s1->nostdlib ? "_start" : "main";
137 if ((s1->dflag & 16) && !find_elf_sym(s1->symtab, s1->runtime_main))
138 return 0;
139 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
140 return -1;
141 prog_main = tcc_get_symbol_err(s1, s1->runtime_main);
143 #ifdef CONFIG_TCC_BACKTRACE
144 if (s1->do_debug) {
145 set_exception_handler();
146 s1->rt_prog_main = prog_main;
147 /* set global state pointer for exception handlers*/
148 set_s1_for_run(s1);
150 #endif
152 errno = 0; /* clean errno value */
154 #ifdef CONFIG_TCC_BCHECK
155 if (s1->do_bounds_check) {
156 void (*bound_init)(void);
157 void (*bound_exit)(void);
158 void (*bounds_add_static_var)(size_t *p);
159 size_t *bounds_start;
160 int ret;
162 /* set error function */
163 s1->rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
164 /* XXX: use .init section so that it also work in binary ? */
165 bound_init = tcc_get_symbol_err(s1, "__bound_init");
166 bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
167 bounds_add_static_var = tcc_get_symbol_err(s1, "__bounds_add_static_var");
168 bounds_start = tcc_get_symbol_err(s1, "__bounds_start");
170 bound_init();
171 bounds_add_static_var (bounds_start);
173 ret = (*prog_main)(argc, argv);
175 bound_exit();
176 return ret;
178 #endif
179 return (*prog_main)(argc, argv);
182 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
183 /* To avoid that x86 processors would reload cached instructions
184 each time when data is written in the near, we need to make
185 sure that code and data do not share the same 64 byte unit */
186 #define RUN_SECTION_ALIGNMENT 63
187 #else
188 #define RUN_SECTION_ALIGNMENT 0
189 #endif
191 /* relocate code. Return -1 on error, required size if ptr is NULL,
192 otherwise copy code into buffer passed by the caller */
193 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
195 Section *s;
196 unsigned offset, length, align, max_align, i, k, f;
197 addr_t mem, addr;
199 if (NULL == ptr) {
200 s1->nb_errors = 0;
201 #ifdef TCC_TARGET_PE
202 pe_output_file(s1, NULL);
203 #else
204 tcc_add_runtime(s1);
205 resolve_common_syms(s1);
206 build_got_entries(s1);
207 #endif
208 if (s1->nb_errors)
209 return -1;
212 offset = max_align = 0, mem = (addr_t)ptr;
213 #ifdef _WIN64
214 offset += sizeof (void*); /* space for function_table pointer */
215 #endif
216 for (k = 0; k < 2; ++k) {
217 f = 0, addr = k ? mem : mem + ptr_diff;
218 for(i = 1; i < s1->nb_sections; i++) {
219 s = s1->sections[i];
220 if (0 == (s->sh_flags & SHF_ALLOC))
221 continue;
222 if (k != !(s->sh_flags & SHF_EXECINSTR))
223 continue;
224 align = s->sh_addralign - 1;
225 if (++f == 1 && align < RUN_SECTION_ALIGNMENT)
226 align = RUN_SECTION_ALIGNMENT;
227 if (max_align < align)
228 max_align = align;
229 offset += -(addr + offset) & align;
230 s->sh_addr = mem ? addr + offset : 0;
231 offset += s->data_offset;
232 #if 0
233 if (mem)
234 printf("%-16s %p len %04x align %2d\n",
235 s->name, (void*)s->sh_addr, (unsigned)s->data_offset, align + 1);
236 #endif
240 /* relocate symbols */
241 relocate_syms(s1, s1->symtab, 1);
242 if (s1->nb_errors)
243 return -1;
245 if (0 == mem)
246 return offset + max_align;
248 #ifdef TCC_TARGET_PE
249 s1->pe_imagebase = mem;
250 #endif
252 /* relocate each section */
253 for(i = 1; i < s1->nb_sections; i++) {
254 s = s1->sections[i];
255 if (s->reloc)
256 relocate_section(s1, s);
258 #ifndef TCC_TARGET_PE
259 relocate_plt(s1);
260 #endif
262 for(i = 1; i < s1->nb_sections; i++) {
263 s = s1->sections[i];
264 if (0 == (s->sh_flags & SHF_ALLOC))
265 continue;
266 length = s->data_offset;
267 ptr = (void*)s->sh_addr;
268 if (s->sh_flags & SHF_EXECINSTR)
269 ptr = (char*)ptr - ptr_diff;
270 if (NULL == s->data || s->sh_type == SHT_NOBITS)
271 memset(ptr, 0, length);
272 else
273 memcpy(ptr, s->data, length);
274 /* mark executable sections as executable in memory */
275 if (s->sh_flags & SHF_EXECINSTR)
276 set_pages_executable(s1, (char*)ptr + ptr_diff, length);
279 #ifdef _WIN64
280 *(void**)mem = win64_add_function_table(s1);
281 #endif
283 return 0;
286 /* ------------------------------------------------------------- */
287 /* allow to run code in memory */
289 static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length)
291 #ifdef _WIN32
292 unsigned long old_protect;
293 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
294 #else
295 void __clear_cache(void *beginning, void *end);
296 # ifndef HAVE_SELINUX
297 addr_t start, end;
298 # ifndef PAGESIZE
299 # define PAGESIZE 4096
300 # endif
301 start = (addr_t)ptr & ~(PAGESIZE - 1);
302 end = (addr_t)ptr + length;
303 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
304 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
305 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
306 # endif
307 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
308 __clear_cache(ptr, (char *)ptr + length);
309 # endif
310 #endif
313 #ifdef _WIN64
314 static void *win64_add_function_table(TCCState *s1)
316 void *p = NULL;
317 if (s1->uw_pdata) {
318 p = (void*)s1->uw_pdata->sh_addr;
319 RtlAddFunctionTable(
320 (RUNTIME_FUNCTION*)p,
321 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
322 s1->pe_imagebase
324 s1->uw_pdata = NULL;
326 return p;
329 static void win64_del_function_table(void *p)
331 if (p) {
332 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
335 #endif
337 /* ------------------------------------------------------------- */
338 #ifdef CONFIG_TCC_BACKTRACE
340 /* print the position in the source file of PC value 'pc' by reading
341 the stabs debug information */
342 static addr_t rt_printline(TCCState *s1, addr_t wanted_pc, const char *msg)
344 char func_name[128], last_func_name[128];
345 addr_t func_addr, last_pc, pc;
346 const char *incl_files[INCLUDE_STACK_SIZE];
347 int incl_index, len, last_line_num, i;
348 const char *str, *p;
350 Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
351 int stab_len = 0;
352 char *stab_str = NULL;
354 if (stab_section) {
355 stab_len = stab_section->data_offset;
356 stab_sym = (Stab_Sym *)stab_section->data;
357 stab_str = (char *) stab_section->link->data;
360 func_name[0] = '\0';
361 func_addr = 0;
362 incl_index = 0;
363 last_func_name[0] = '\0';
364 last_pc = (addr_t)-1;
365 last_line_num = 1;
367 if (!stab_sym)
368 goto no_stabs;
370 stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
371 for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
372 switch(sym->n_type) {
373 /* function start or end */
374 case N_FUN:
375 if (sym->n_strx == 0) {
376 /* we test if between last line and end of function */
377 pc = sym->n_value + func_addr;
378 if (wanted_pc >= last_pc && wanted_pc < pc)
379 goto found;
380 func_name[0] = '\0';
381 func_addr = 0;
382 } else {
383 str = stab_str + sym->n_strx;
384 p = strchr(str, ':');
385 if (!p) {
386 pstrcpy(func_name, sizeof(func_name), str);
387 } else {
388 len = p - str;
389 if (len > sizeof(func_name) - 1)
390 len = sizeof(func_name) - 1;
391 memcpy(func_name, str, len);
392 func_name[len] = '\0';
394 func_addr = sym->n_value;
396 break;
397 /* line number info */
398 case N_SLINE:
399 pc = sym->n_value + func_addr;
400 if (wanted_pc >= last_pc && wanted_pc < pc)
401 goto found;
402 last_pc = pc;
403 last_line_num = sym->n_desc;
404 /* XXX: slow! */
405 strcpy(last_func_name, func_name);
406 break;
407 /* include files */
408 case N_BINCL:
409 str = stab_str + sym->n_strx;
410 add_incl:
411 if (incl_index < INCLUDE_STACK_SIZE) {
412 incl_files[incl_index++] = str;
414 break;
415 case N_EINCL:
416 if (incl_index > 1)
417 incl_index--;
418 break;
419 case N_SO:
420 if (sym->n_strx == 0) {
421 incl_index = 0; /* end of translation unit */
422 } else {
423 str = stab_str + sym->n_strx;
424 /* do not add path */
425 len = strlen(str);
426 if (len > 0 && str[len - 1] != '/')
427 goto add_incl;
429 break;
433 no_stabs:
434 /* second pass: we try symtab symbols (no line number info) */
435 incl_index = 0;
436 if (symtab_section)
438 ElfW(Sym) *sym, *sym_end;
439 int type;
441 sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
442 for(sym = (ElfW(Sym) *)symtab_section->data + 1;
443 sym < sym_end;
444 sym++) {
445 type = ELFW(ST_TYPE)(sym->st_info);
446 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
447 if (wanted_pc >= sym->st_value &&
448 wanted_pc < sym->st_value + sym->st_size) {
449 pstrcpy(last_func_name, sizeof(last_func_name),
450 (char *) symtab_section->link->data + sym->st_name);
451 func_addr = sym->st_value;
452 goto found;
457 /* did not find any info: */
458 fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
459 fflush(stderr);
460 return 0;
461 found:
462 i = incl_index;
463 if (i > 0)
464 fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
465 fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
466 if (last_func_name[0] != '\0')
467 fprintf(stderr, " %s()", last_func_name);
468 if (--i >= 0) {
469 fprintf(stderr, " (included from ");
470 for (;;) {
471 fprintf(stderr, "%s", incl_files[i]);
472 if (--i < 0)
473 break;
474 fprintf(stderr, ", ");
476 fprintf(stderr, ")");
478 fprintf(stderr, "\n");
479 fflush(stderr);
480 return func_addr;
483 /* emit a run time error at position 'pc' */
484 static void rt_error(ucontext_t *uc, const char *fmt, ...)
486 va_list ap;
487 addr_t pc;
488 int i;
489 TCCState *s1 = get_s1_for_run();
491 fprintf(stderr, "Runtime error: ");
492 va_start(ap, fmt);
493 vfprintf(stderr, fmt, ap);
494 va_end(ap);
495 fprintf(stderr, "\n");
497 if (!s1)
498 return;
500 for(i=0;i<s1->rt_num_callers;i++) {
501 if (rt_get_caller_pc(&pc, uc, i) < 0)
502 break;
503 pc = rt_printline(s1, pc, i ? "by" : "at");
504 if (pc == (addr_t)s1->rt_prog_main && pc)
505 break;
509 /* ------------------------------------------------------------- */
510 #ifndef _WIN32
512 /* signal handler for fatal errors */
513 static void sig_error(int signum, siginfo_t *siginf, void *puc)
515 ucontext_t *uc = puc;
516 TCCState *s1;
518 switch(signum) {
519 case SIGFPE:
520 switch(siginf->si_code) {
521 case FPE_INTDIV:
522 case FPE_FLTDIV:
523 rt_error(uc, "division by zero");
524 break;
525 default:
526 rt_error(uc, "floating point exception");
527 break;
529 break;
530 case SIGBUS:
531 case SIGSEGV:
532 s1 = get_s1_for_run();
533 if (s1 && s1->rt_bound_error_msg && *s1->rt_bound_error_msg)
534 rt_error(uc, *s1->rt_bound_error_msg);
535 else
536 rt_error(uc, "dereferencing invalid pointer");
537 break;
538 case SIGILL:
539 rt_error(uc, "illegal instruction");
540 break;
541 case SIGABRT:
542 rt_error(uc, "abort() called");
543 break;
544 default:
545 rt_error(uc, "caught signal %d", signum);
546 break;
548 exit(255);
551 #ifndef SA_SIGINFO
552 # define SA_SIGINFO 0x00000004u
553 #endif
555 /* Generate a stack backtrace when a CPU exception occurs. */
556 static void set_exception_handler(void)
558 struct sigaction sigact;
559 /* install TCC signal handlers to print debug info on fatal
560 runtime errors */
561 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
562 sigact.sa_sigaction = sig_error;
563 sigemptyset(&sigact.sa_mask);
564 sigaction(SIGFPE, &sigact, NULL);
565 sigaction(SIGILL, &sigact, NULL);
566 sigaction(SIGSEGV, &sigact, NULL);
567 sigaction(SIGBUS, &sigact, NULL);
568 sigaction(SIGABRT, &sigact, NULL);
571 /* ------------------------------------------------------------- */
572 #ifdef __i386__
574 /* fix for glibc 2.1 */
575 #ifndef REG_EIP
576 #define REG_EIP EIP
577 #define REG_EBP EBP
578 #endif
580 /* return the PC at frame level 'level'. Return negative if not found */
581 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
583 addr_t fp;
584 int i;
586 if (level == 0) {
587 #if defined(__APPLE__)
588 *paddr = uc->uc_mcontext->__ss.__eip;
589 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
590 *paddr = uc->uc_mcontext.mc_eip;
591 #elif defined(__dietlibc__)
592 *paddr = uc->uc_mcontext.eip;
593 #elif defined(__NetBSD__)
594 *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
595 #elif defined(__OpenBSD__)
596 *paddr = uc->sc_eip;
597 #else
598 *paddr = uc->uc_mcontext.gregs[REG_EIP];
599 #endif
600 return 0;
601 } else {
602 #if defined(__APPLE__)
603 fp = uc->uc_mcontext->__ss.__ebp;
604 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
605 fp = uc->uc_mcontext.mc_ebp;
606 #elif defined(__dietlibc__)
607 fp = uc->uc_mcontext.ebp;
608 #elif defined(__NetBSD__)
609 fp = uc->uc_mcontext.__gregs[_REG_EBP];
610 #elif defined(__OpenBSD__)
611 *paddr = uc->sc_ebp;
612 #else
613 fp = uc->uc_mcontext.gregs[REG_EBP];
614 #endif
615 for(i=1;i<level;i++) {
616 /* XXX: check address validity with program info */
617 if (fp <= 0x1000 || fp >= 0xc0000000)
618 return -1;
619 fp = ((addr_t *)fp)[0];
621 *paddr = ((addr_t *)fp)[1];
622 return 0;
626 /* ------------------------------------------------------------- */
627 #elif defined(__x86_64__)
629 /* return the PC at frame level 'level'. Return negative if not found */
630 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
632 addr_t fp;
633 int i;
635 if (level == 0) {
636 /* XXX: only support linux */
637 #if defined(__APPLE__)
638 *paddr = uc->uc_mcontext->__ss.__rip;
639 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
640 *paddr = uc->uc_mcontext.mc_rip;
641 #elif defined(__NetBSD__)
642 *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
643 #else
644 *paddr = uc->uc_mcontext.gregs[REG_RIP];
645 #endif
646 return 0;
647 } else {
648 #if defined(__APPLE__)
649 fp = uc->uc_mcontext->__ss.__rbp;
650 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
651 fp = uc->uc_mcontext.mc_rbp;
652 #elif defined(__NetBSD__)
653 fp = uc->uc_mcontext.__gregs[_REG_RBP];
654 #else
655 fp = uc->uc_mcontext.gregs[REG_RBP];
656 #endif
657 for(i=1;i<level;i++) {
658 /* XXX: check address validity with program info */
659 if (fp <= 0x1000)
660 return -1;
661 fp = ((addr_t *)fp)[0];
663 *paddr = ((addr_t *)fp)[1];
664 return 0;
668 /* ------------------------------------------------------------- */
669 #elif defined(__arm__)
671 /* return the PC at frame level 'level'. Return negative if not found */
672 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
674 addr_t fp, sp;
675 int i;
677 if (level == 0) {
678 /* XXX: only supports linux */
679 #if defined(__linux__)
680 *paddr = uc->uc_mcontext.arm_pc;
681 #else
682 return -1;
683 #endif
684 return 0;
685 } else {
686 #if defined(__linux__)
687 fp = uc->uc_mcontext.arm_fp;
688 sp = uc->uc_mcontext.arm_sp;
689 if (sp < 0x1000)
690 sp = 0x1000;
691 #else
692 return -1;
693 #endif
694 /* XXX: specific to tinycc stack frames */
695 if (fp < sp + 12 || fp & 3)
696 return -1;
697 for(i = 1; i < level; i++) {
698 sp = ((addr_t *)fp)[-2];
699 if (sp < fp || sp - fp > 16 || sp & 3)
700 return -1;
701 fp = ((addr_t *)fp)[-3];
702 if (fp <= sp || fp - sp < 12 || fp & 3)
703 return -1;
705 /* XXX: check address validity with program info */
706 *paddr = ((addr_t *)fp)[-1];
707 return 0;
711 /* ------------------------------------------------------------- */
712 #elif defined(__aarch64__)
714 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
716 if (level < 0)
717 return -1;
718 else if (level == 0) {
719 *paddr = uc->uc_mcontext.pc;
720 return 0;
722 else {
723 addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
724 int i;
725 for (i = 1; i < level; i++)
726 fp = (addr_t *)fp[0];
727 *paddr = fp[1];
728 return 0;
732 /* ------------------------------------------------------------- */
733 #else
735 #warning add arch specific rt_get_caller_pc()
736 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
738 return -1;
741 #endif /* !__i386__ */
743 /* ------------------------------------------------------------- */
744 #else /* WIN32 */
746 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
748 EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
749 CONTEXT *uc = ex_info->ContextRecord;
750 TCCState *s1;
751 switch (er->ExceptionCode) {
752 case EXCEPTION_ACCESS_VIOLATION:
753 s1 = get_s1_for_run();
754 if (s1 && s1->rt_bound_error_msg && *s1->rt_bound_error_msg)
755 rt_error(uc, *s1->rt_bound_error_msg);
756 else
757 rt_error(uc, "access violation");
758 break;
759 case EXCEPTION_STACK_OVERFLOW:
760 rt_error(uc, "stack overflow");
761 break;
762 case EXCEPTION_INT_DIVIDE_BY_ZERO:
763 rt_error(uc, "division by zero");
764 break;
765 default:
766 rt_error(uc, "exception caught");
767 break;
769 return EXCEPTION_EXECUTE_HANDLER;
772 /* Generate a stack backtrace when a CPU exception occurs. */
773 static void set_exception_handler(void)
775 SetUnhandledExceptionFilter(cpu_exception_handler);
778 /* return the PC at frame level 'level'. Return non zero if not found */
779 static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
781 addr_t fp, pc;
782 int i;
783 #ifdef _WIN64
784 pc = uc->Rip;
785 fp = uc->Rbp;
786 #else
787 pc = uc->Eip;
788 fp = uc->Ebp;
789 #endif
790 if (level > 0) {
791 for(i=1;i<level;i++) {
792 /* XXX: check address validity with program info */
793 if (fp <= 0x1000 || fp >= 0xc0000000)
794 return -1;
795 fp = ((addr_t*)fp)[0];
797 pc = ((addr_t*)fp)[1];
799 *paddr = pc;
800 return 0;
803 #endif /* _WIN32 */
804 #endif /* CONFIG_TCC_BACKTRACE */
805 /* ------------------------------------------------------------- */
806 #ifdef CONFIG_TCC_STATIC
808 /* dummy function for profiling */
809 ST_FUNC void *dlopen(const char *filename, int flag)
811 return NULL;
814 ST_FUNC void dlclose(void *p)
818 ST_FUNC const char *dlerror(void)
820 return "error";
823 typedef struct TCCSyms {
824 char *str;
825 void *ptr;
826 } TCCSyms;
829 /* add the symbol you want here if no dynamic linking is done */
830 static TCCSyms tcc_syms[] = {
831 #if !defined(CONFIG_TCCBOOT)
832 #define TCCSYM(a) { #a, &a, },
833 TCCSYM(printf)
834 TCCSYM(fprintf)
835 TCCSYM(fopen)
836 TCCSYM(fclose)
837 #undef TCCSYM
838 #endif
839 { NULL, NULL },
842 ST_FUNC void *dlsym(void *handle, const char *symbol)
844 TCCSyms *p;
845 p = tcc_syms;
846 while (p->str != NULL) {
847 if (!strcmp(p->str, symbol))
848 return p->ptr;
849 p++;
851 return NULL;
854 #endif /* CONFIG_TCC_STATIC */
855 #endif /* TCC_IS_NATIVE */
856 /* ------------------------------------------------------------- */