macOS configure correctly reports clang and version
[tinycc.git] / tccrun.c
blobd0379c9b85394c9084c9717dba4285d787f6c99d
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 #ifdef CONFIG_TCC_BACKTRACE
27 typedef struct rt_context
29 /* --> tccelf.c:tcc_add_btstub wants those below in that order: */
30 Stab_Sym *stab_sym, *stab_sym_end;
31 char *stab_str;
32 ElfW(Sym) *esym_start, *esym_end;
33 char *elf_str;
34 addr_t prog_base;
35 void *bounds_start;
36 struct rt_context *next;
37 /* <-- */
38 int num_callers;
39 addr_t ip, fp, sp;
40 void *top_func;
41 jmp_buf jmp_buf;
42 char do_jmp;
43 } rt_context;
45 static rt_context g_rtctxt;
46 static void set_exception_handler(void);
47 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap);
48 static void rt_exit(int code);
49 #endif /* CONFIG_TCC_BACKTRACE */
51 /* defined when included from lib/bt-exe.c */
52 #ifndef CONFIG_TCC_BACKTRACE_ONLY
54 #ifndef _WIN32
55 # include <sys/mman.h>
56 #endif
58 static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length);
59 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff);
61 #ifdef _WIN64
62 static void *win64_add_function_table(TCCState *s1);
63 static void win64_del_function_table(void *);
64 #endif
66 /* ------------------------------------------------------------- */
67 /* Do all relocations (needed before using tcc_get_symbol())
68 Returns -1 on error. */
70 LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
72 int size;
73 addr_t ptr_diff = 0;
75 if (TCC_RELOCATE_AUTO != ptr)
76 return tcc_relocate_ex(s1, ptr, 0);
78 size = tcc_relocate_ex(s1, NULL, 0);
79 if (size < 0)
80 return -1;
82 #ifdef HAVE_SELINUX
84 /* Using mmap instead of malloc */
85 void *prx;
86 char tmpfname[] = "/tmp/.tccrunXXXXXX";
87 int fd = mkstemp(tmpfname);
88 unlink(tmpfname);
89 ftruncate(fd, size);
91 ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
92 prx = mmap (NULL, size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
93 if (ptr == MAP_FAILED || prx == MAP_FAILED)
94 tcc_error("tccrun: could not map memory");
95 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
96 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx);
97 ptr_diff = (char*)prx - (char*)ptr;
98 close(fd);
100 #else
101 ptr = tcc_malloc(size);
102 #endif
103 tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
104 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
105 return 0;
108 ST_FUNC void tcc_run_free(TCCState *s1)
110 int i;
112 for (i = 0; i < s1->nb_runtime_mem; ++i) {
113 #ifdef HAVE_SELINUX
114 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
115 munmap(s1->runtime_mem[i++], size);
116 munmap(s1->runtime_mem[i], size);
117 #else
118 #ifdef _WIN64
119 win64_del_function_table(*(void**)s1->runtime_mem[i]);
120 #endif
121 tcc_free(s1->runtime_mem[i]);
122 #endif
124 tcc_free(s1->runtime_mem);
127 static void run_cdtors(TCCState *s1, const char *start, const char *end)
129 void **a = (void **)get_sym_addr(s1, start, 0, 0);
130 void **b = (void **)get_sym_addr(s1, end, 0, 0);
131 while (a != b)
132 ((void(*)(void))*a++)();
135 /* launch the compiled program with the given arguments */
136 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
138 int (*prog_main)(int, char **), ret;
139 #ifdef CONFIG_TCC_BACKTRACE
140 rt_context *rc = &g_rtctxt;
141 #endif
143 s1->runtime_main = s1->nostdlib ? "_start" : "main";
144 if ((s1->dflag & 16) && !find_c_sym(s1, s1->runtime_main))
145 return 0;
146 #ifdef CONFIG_TCC_BACKTRACE
147 if (s1->do_debug)
148 tcc_add_symbol(s1, "_exit" + !s1->leading_underscore, rt_exit);
149 #endif
150 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
151 return -1;
152 prog_main = tcc_get_symbol_err(s1, s1->runtime_main);
154 #ifdef CONFIG_TCC_BACKTRACE
155 memset(rc, 0, sizeof *rc);
156 if (s1->do_debug) {
157 void *p;
158 rc->stab_sym = (Stab_Sym *)stab_section->data;
159 rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
160 rc->stab_str = (char *)stab_section->link->data;
161 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
162 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
163 rc->elf_str = (char *)symtab_section->link->data;
164 #if PTR_SIZE == 8
165 rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL;
166 #endif
167 rc->top_func = tcc_get_symbol(s1, "main");
168 rc->num_callers = s1->rt_num_callers;
169 rc->do_jmp = 1;
170 if ((p = tcc_get_symbol(s1, "__rt_error")))
171 *(void**)p = _rt_error;
172 #ifdef CONFIG_TCC_BCHECK
173 if (s1->do_bounds_check) {
174 if ((p = tcc_get_symbol(s1, "__bound_init")))
175 ((void(*)(void*, int))p)(bounds_section->data, 1);
177 #endif
178 set_exception_handler();
180 #endif
182 errno = 0; /* clean errno value */
183 fflush(stdout);
184 fflush(stderr);
185 /* These aren't C symbols, so don't need leading underscore handling. */
186 run_cdtors(s1, "__init_array_start", "__init_array_end");
187 #ifdef CONFIG_TCC_BACKTRACE
188 if (!rc->do_jmp || !(ret = setjmp(rc->jmp_buf)))
189 #endif
191 ret = prog_main(argc, argv);
193 run_cdtors(s1, "__fini_array_start", "__fini_array_end");
194 if ((s1->dflag & 16) && ret)
195 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
196 return ret;
199 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
200 /* To avoid that x86 processors would reload cached instructions
201 each time when data is written in the near, we need to make
202 sure that code and data do not share the same 64 byte unit */
203 #define RUN_SECTION_ALIGNMENT 63
204 #else
205 #define RUN_SECTION_ALIGNMENT 0
206 #endif
208 /* relocate code. Return -1 on error, required size if ptr is NULL,
209 otherwise copy code into buffer passed by the caller */
210 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
212 Section *s;
213 unsigned offset, length, align, max_align, i, k, f;
214 addr_t mem, addr;
216 if (NULL == ptr) {
217 s1->nb_errors = 0;
218 #ifdef TCC_TARGET_PE
219 pe_output_file(s1, NULL);
220 #else
221 tcc_add_runtime(s1);
222 resolve_common_syms(s1);
223 build_got_entries(s1);
224 #endif
225 if (s1->nb_errors)
226 return -1;
229 offset = max_align = 0, mem = (addr_t)ptr;
230 #ifdef _WIN64
231 offset += sizeof (void*); /* space for function_table pointer */
232 #endif
233 for (k = 0; k < 2; ++k) {
234 f = 0, addr = k ? mem : mem + ptr_diff;
235 for(i = 1; i < s1->nb_sections; i++) {
236 s = s1->sections[i];
237 if (0 == (s->sh_flags & SHF_ALLOC))
238 continue;
239 if (k != !(s->sh_flags & SHF_EXECINSTR))
240 continue;
241 align = s->sh_addralign - 1;
242 if (++f == 1 && align < RUN_SECTION_ALIGNMENT)
243 align = RUN_SECTION_ALIGNMENT;
244 if (max_align < align)
245 max_align = align;
246 offset += -(addr + offset) & align;
247 s->sh_addr = mem ? addr + offset : 0;
248 offset += s->data_offset;
249 #if 0
250 if (mem)
251 printf("%-16s %p len %04x align %2d\n",
252 s->name, (void*)s->sh_addr, (unsigned)s->data_offset, align + 1);
253 #endif
257 /* relocate symbols */
258 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
259 if (s1->nb_errors)
260 return -1;
262 if (0 == mem)
263 return offset + max_align;
265 #ifdef TCC_TARGET_PE
266 s1->pe_imagebase = mem;
267 #endif
269 /* relocate each section */
270 for(i = 1; i < s1->nb_sections; i++) {
271 s = s1->sections[i];
272 if (s->reloc)
273 relocate_section(s1, s);
275 #if !defined(TCC_TARGET_PE) || defined(TCC_TARGET_MACHO)
276 relocate_plt(s1);
277 #endif
279 for(i = 1; i < s1->nb_sections; i++) {
280 s = s1->sections[i];
281 if (0 == (s->sh_flags & SHF_ALLOC))
282 continue;
283 length = s->data_offset;
284 ptr = (void*)s->sh_addr;
285 if (s->sh_flags & SHF_EXECINSTR)
286 ptr = (char*)((addr_t)ptr - ptr_diff);
287 if (NULL == s->data || s->sh_type == SHT_NOBITS)
288 memset(ptr, 0, length);
289 else
290 memcpy(ptr, s->data, length);
291 /* mark executable sections as executable in memory */
292 if (s->sh_flags & SHF_EXECINSTR)
293 set_pages_executable(s1, (char*)((addr_t)ptr + ptr_diff), length);
296 #ifdef _WIN64
297 *(void**)mem = win64_add_function_table(s1);
298 #endif
300 return 0;
303 /* ------------------------------------------------------------- */
304 /* allow to run code in memory */
306 static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length)
308 #ifdef _WIN32
309 unsigned long old_protect;
310 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
311 #else
312 void __clear_cache(void *beginning, void *end);
313 # ifndef HAVE_SELINUX
314 addr_t start, end;
315 # ifndef PAGESIZE
316 # define PAGESIZE 4096
317 # endif
318 start = (addr_t)ptr & ~(PAGESIZE - 1);
319 end = (addr_t)ptr + length;
320 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
321 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
322 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
323 # endif
324 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
325 __clear_cache(ptr, (char *)ptr + length);
326 # endif
327 #endif
330 #ifdef _WIN64
331 static void *win64_add_function_table(TCCState *s1)
333 void *p = NULL;
334 if (s1->uw_pdata) {
335 p = (void*)s1->uw_pdata->sh_addr;
336 RtlAddFunctionTable(
337 (RUNTIME_FUNCTION*)p,
338 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
339 s1->pe_imagebase
341 s1->uw_pdata = NULL;
343 return p;
346 static void win64_del_function_table(void *p)
348 if (p) {
349 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
352 #endif
353 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
354 /* ------------------------------------------------------------- */
355 #ifdef CONFIG_TCC_BACKTRACE
357 static int rt_vprintf(const char *fmt, va_list ap)
359 int ret = vfprintf(stderr, fmt, ap);
360 fflush(stderr);
361 return ret;
364 static int rt_printf(const char *fmt, ...)
366 va_list ap;
367 int r;
368 va_start(ap, fmt);
369 r = rt_vprintf(fmt, ap);
370 va_end(ap);
371 return r;
374 #define INCLUDE_STACK_SIZE 32
376 /* print the position in the source file of PC value 'pc' by reading
377 the stabs debug information */
378 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
379 const char *msg, const char *skip)
381 char func_name[128];
382 addr_t func_addr, last_pc, pc;
383 const char *incl_files[INCLUDE_STACK_SIZE];
384 int incl_index, last_incl_index, len, last_line_num, i;
385 const char *str, *p;
386 ElfW(Sym) *esym;
387 Stab_Sym *sym;
389 next:
390 func_name[0] = '\0';
391 func_addr = 0;
392 incl_index = 0;
393 last_pc = (addr_t)-1;
394 last_line_num = 1;
395 last_incl_index = 0;
397 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
398 str = rc->stab_str + sym->n_strx;
399 pc = sym->n_value;
401 switch(sym->n_type) {
402 case N_SLINE:
403 if (func_addr)
404 goto rel_pc;
405 case N_SO:
406 case N_SOL:
407 goto abs_pc;
408 case N_FUN:
409 if (sym->n_strx == 0) /* end of function */
410 goto rel_pc;
411 abs_pc:
412 #if PTR_SIZE == 8
413 /* Stab_Sym.n_value is only 32bits */
414 pc += rc->prog_base;
415 #endif
416 goto check_pc;
417 rel_pc:
418 pc += func_addr;
419 check_pc:
420 if (pc >= wanted_pc && wanted_pc >= last_pc)
421 goto found;
422 break;
425 switch(sym->n_type) {
426 /* function start or end */
427 case N_FUN:
428 if (sym->n_strx == 0)
429 goto reset_func;
430 p = strchr(str, ':');
431 if (0 == p || (len = p - str + 1, len > sizeof func_name))
432 len = sizeof func_name;
433 pstrcpy(func_name, len, str);
434 func_addr = pc;
435 break;
436 /* line number info */
437 case N_SLINE:
438 last_pc = pc;
439 last_line_num = sym->n_desc;
440 last_incl_index = incl_index;
441 break;
442 /* include files */
443 case N_BINCL:
444 if (incl_index < INCLUDE_STACK_SIZE)
445 incl_files[incl_index++] = str;
446 break;
447 case N_EINCL:
448 if (incl_index > 1)
449 incl_index--;
450 break;
451 /* start/end of translation unit */
452 case N_SO:
453 incl_index = 0;
454 if (sym->n_strx) {
455 /* do not add path */
456 len = strlen(str);
457 if (len > 0 && str[len - 1] != '/')
458 incl_files[incl_index++] = str;
460 reset_func:
461 func_name[0] = '\0';
462 func_addr = 0;
463 last_pc = (addr_t)-1;
464 break;
465 /* alternative file name (from #line or #include directives) */
466 case N_SOL:
467 if (incl_index)
468 incl_files[incl_index-1] = str;
469 break;
473 func_name[0] = '\0';
474 func_addr = 0;
475 last_incl_index = 0;
477 /* we try symtab symbols (no line number info) */
478 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
479 int type = ELFW(ST_TYPE)(esym->st_info);
480 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
481 if (wanted_pc >= esym->st_value &&
482 wanted_pc < esym->st_value + esym->st_size) {
483 pstrcpy(func_name, sizeof(func_name),
484 rc->elf_str + esym->st_name);
485 func_addr = esym->st_value;
486 goto found;
491 if ((rc = rc->next))
492 goto next;
494 found:
495 i = last_incl_index;
496 if (i > 0) {
497 str = incl_files[--i];
498 if (skip[0] && strstr(str, skip))
499 return (addr_t)-1;
500 rt_printf("%s:%d: ", str, last_line_num);
501 } else
502 rt_printf("%08llx : ", (long long)wanted_pc);
503 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
504 #if 0
505 if (--i >= 0) {
506 rt_printf(" (included from ");
507 for (;;) {
508 rt_printf("%s", incl_files[i]);
509 if (--i < 0)
510 break;
511 rt_printf(", ");
513 rt_printf(")");
515 #endif
516 return func_addr;
519 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level);
521 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap)
523 rt_context *rc = &g_rtctxt;
524 addr_t pc = 0;
525 char skip[100];
526 int i, level, ret, n;
527 const char *a, *b, *msg;
529 if (fp) {
530 /* we're called from tcc_backtrace. */
531 rc->fp = (addr_t)fp;
532 rc->ip = (addr_t)ip;
533 msg = "";
534 } else {
535 /* we're called from signal/exception handler */
536 msg = "RUNTIME ERROR: ";
539 skip[0] = 0;
540 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
541 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
542 memcpy(skip, a, b - a), skip[b - a] = 0;
543 fmt = b + 1;
546 n = rc->num_callers ? rc->num_callers : 6;
547 for (i = level = 0; level < n; i++) {
548 ret = rt_get_caller_pc(&pc, rc, i);
549 a = "%s";
550 if (ret != -1) {
551 pc = rt_printline(rc, pc, level ? "by" : "at", skip);
552 if (pc == (addr_t)-1)
553 continue;
554 a = ": %s";
556 if (level == 0) {
557 rt_printf(a, msg);
558 rt_vprintf(fmt, ap);
559 } else if (ret == -1)
560 break;
561 rt_printf("\n");
562 if (ret == -1 || (pc == (addr_t)rc->top_func && pc))
563 break;
564 ++level;
567 rc->ip = rc->fp = 0;
568 return 0;
571 /* emit a run time error at position 'pc' */
572 static int rt_error(const char *fmt, ...)
574 va_list ap;
575 int ret;
576 va_start(ap, fmt);
577 ret = _rt_error(0, 0, fmt, ap);
578 va_end(ap);
579 return ret;
582 static void rt_exit(int code)
584 rt_context *rc = &g_rtctxt;
585 if (rc->do_jmp)
586 longjmp(rc->jmp_buf, code ? code : 256);
587 exit(code);
590 /* ------------------------------------------------------------- */
592 #ifndef _WIN32
593 # include <signal.h>
594 # ifndef __OpenBSD__
595 # include <sys/ucontext.h>
596 # endif
597 #else
598 # define ucontext_t CONTEXT
599 #endif
601 /* translate from ucontext_t* to internal rt_context * */
602 static void rt_getcontext(ucontext_t *uc, rt_context *rc)
604 #if defined _WIN64
605 rc->ip = uc->Rip;
606 rc->fp = uc->Rbp;
607 rc->sp = uc->Rsp;
608 #elif defined _WIN32
609 rc->ip = uc->Eip;
610 rc->fp = uc->Ebp;
611 rc->sp = uc->Esp;
612 #elif defined __i386__
613 # if defined(__APPLE__)
614 rc->ip = uc->uc_mcontext->__ss.__eip;
615 rc->fp = uc->uc_mcontext->__ss.__ebp;
616 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
617 rc->ip = uc->uc_mcontext.mc_eip;
618 rc->fp = uc->uc_mcontext.mc_ebp;
619 # elif defined(__dietlibc__)
620 rc->ip = uc->uc_mcontext.eip;
621 rc->fp = uc->uc_mcontext.ebp;
622 # elif defined(__NetBSD__)
623 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
624 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
625 # elif defined(__OpenBSD__)
626 rc->ip = uc->sc_eip;
627 rc->fp = uc->sc_ebp;
628 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
629 rc->ip = uc->uc_mcontext.gregs[EIP];
630 rc->fp = uc->uc_mcontext.gregs[EBP];
631 # else
632 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
633 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
634 # endif
635 #elif defined(__x86_64__)
636 # if defined(__APPLE__)
637 rc->ip = uc->uc_mcontext->__ss.__rip;
638 rc->fp = uc->uc_mcontext->__ss.__rbp;
639 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
640 rc->ip = uc->uc_mcontext.mc_rip;
641 rc->fp = uc->uc_mcontext.mc_rbp;
642 # elif defined(__NetBSD__)
643 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
644 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
645 # else
646 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
647 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
648 # endif
649 #elif defined(__arm__)
650 rc->ip = uc->uc_mcontext.arm_pc;
651 rc->fp = uc->uc_mcontext.arm_fp;
652 #elif defined(__aarch64__)
653 rc->ip = uc->uc_mcontext.pc;
654 rc->fp = uc->uc_mcontext.regs[29];
655 #elif defined(__riscv)
656 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
657 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
658 #endif
661 /* ------------------------------------------------------------- */
662 #ifndef _WIN32
663 /* signal handler for fatal errors */
664 static void sig_error(int signum, siginfo_t *siginf, void *puc)
666 rt_context *rc = &g_rtctxt;
667 rt_getcontext(puc, rc);
669 switch(signum) {
670 case SIGFPE:
671 switch(siginf->si_code) {
672 case FPE_INTDIV:
673 case FPE_FLTDIV:
674 rt_error("division by zero");
675 break;
676 default:
677 rt_error("floating point exception");
678 break;
680 break;
681 case SIGBUS:
682 case SIGSEGV:
683 rt_error("invalid memory access");
684 break;
685 case SIGILL:
686 rt_error("illegal instruction");
687 break;
688 case SIGABRT:
689 rt_error("abort() called");
690 break;
691 default:
692 rt_error("caught signal %d", signum);
693 break;
695 rt_exit(255);
698 #ifndef SA_SIGINFO
699 # define SA_SIGINFO 0x00000004u
700 #endif
702 /* Generate a stack backtrace when a CPU exception occurs. */
703 static void set_exception_handler(void)
705 struct sigaction sigact;
706 /* install TCC signal handlers to print debug info on fatal
707 runtime errors */
708 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
709 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
710 sigact.sa_flags |= SA_ONSTACK;
711 #endif
712 sigact.sa_sigaction = sig_error;
713 sigemptyset(&sigact.sa_mask);
714 sigaction(SIGFPE, &sigact, NULL);
715 sigaction(SIGILL, &sigact, NULL);
716 sigaction(SIGSEGV, &sigact, NULL);
717 sigaction(SIGBUS, &sigact, NULL);
718 sigaction(SIGABRT, &sigact, NULL);
719 #if 0//def SIGSTKSZ
720 /* This allows stack overflow to be reported instead of a SEGV */
722 stack_t ss;
723 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
725 ss.ss_sp = stack;
726 ss.ss_size = SIGSTKSZ;
727 ss.ss_flags = 0;
728 sigaltstack(&ss, NULL);
730 #endif
733 #else /* WIN32 */
735 /* signal handler for fatal errors */
736 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
738 rt_context *rc = &g_rtctxt;
739 unsigned code;
740 rt_getcontext(ex_info->ContextRecord, rc);
742 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
743 case EXCEPTION_ACCESS_VIOLATION:
744 rt_error("invalid memory access");
745 break;
746 case EXCEPTION_STACK_OVERFLOW:
747 rt_error("stack overflow");
748 break;
749 case EXCEPTION_INT_DIVIDE_BY_ZERO:
750 rt_error("division by zero");
751 break;
752 case EXCEPTION_BREAKPOINT:
753 case EXCEPTION_SINGLE_STEP:
754 rc->ip = *(addr_t*)rc->sp;
755 rt_error("breakpoint/single-step exception:");
756 return EXCEPTION_CONTINUE_SEARCH;
757 default:
758 rt_error("caught exception %08x", code);
759 break;
761 if (rc->do_jmp)
762 rt_exit(255);
763 return EXCEPTION_EXECUTE_HANDLER;
766 /* Generate a stack backtrace when a CPU exception occurs. */
767 static void set_exception_handler(void)
769 SetUnhandledExceptionFilter(cpu_exception_handler);
772 #endif
774 /* ------------------------------------------------------------- */
775 /* return the PC at frame level 'level'. Return negative if not found */
776 #if defined(__i386__) || defined(__x86_64__)
777 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
779 addr_t ip, fp;
780 if (level == 0) {
781 ip = rc->ip;
782 } else {
783 ip = 0;
784 fp = rc->fp;
785 while (--level) {
786 /* XXX: check address validity with program info */
787 if (fp <= 0x1000)
788 break;
789 fp = ((addr_t *)fp)[0];
791 if (fp > 0x1000)
792 ip = ((addr_t *)fp)[1];
794 if (ip <= 0x1000)
795 return -1;
796 *paddr = ip;
797 return 0;
800 #elif defined(__arm__)
801 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
803 /* XXX: only supports linux */
804 #if !defined(__linux__)
805 return -1;
806 #else
807 if (level == 0) {
808 *paddr = rc->ip;
809 } else {
810 addr_t fp = rc->fp;
811 while (--level)
812 fp = ((addr_t *)fp)[0];
813 *paddr = ((addr_t *)fp)[2];
815 return 0;
816 #endif
819 #elif defined(__aarch64__)
820 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
822 if (level == 0) {
823 *paddr = rc->ip;
824 } else {
825 addr_t *fp = (addr_t*)rc->fp;
826 while (--level)
827 fp = (addr_t *)fp[0];
828 *paddr = fp[1];
830 return 0;
833 #elif defined(__riscv)
834 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
836 if (level == 0) {
837 *paddr = rc->ip;
838 } else {
839 addr_t *fp = (addr_t*)rc->fp;
840 while (--level)
841 fp = (addr_t *)fp[-2];
842 *paddr = fp[-1];
844 return 0;
847 #else
848 #warning add arch specific rt_get_caller_pc()
849 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
851 return -1;
854 #endif
855 #endif /* CONFIG_TCC_BACKTRACE */
856 /* ------------------------------------------------------------- */
857 #ifdef CONFIG_TCC_STATIC
859 /* dummy function for profiling */
860 ST_FUNC void *dlopen(const char *filename, int flag)
862 return NULL;
865 ST_FUNC void dlclose(void *p)
869 ST_FUNC const char *dlerror(void)
871 return "error";
874 typedef struct TCCSyms {
875 char *str;
876 void *ptr;
877 } TCCSyms;
880 /* add the symbol you want here if no dynamic linking is done */
881 static TCCSyms tcc_syms[] = {
882 #if !defined(CONFIG_TCCBOOT)
883 #define TCCSYM(a) { #a, &a, },
884 TCCSYM(printf)
885 TCCSYM(fprintf)
886 TCCSYM(fopen)
887 TCCSYM(fclose)
888 #undef TCCSYM
889 #endif
890 { NULL, NULL },
893 ST_FUNC void *dlsym(void *handle, const char *symbol)
895 TCCSyms *p;
896 p = tcc_syms;
897 while (p->str != NULL) {
898 if (!strcmp(p->str, symbol))
899 return p->ptr;
900 p++;
902 return NULL;
905 #endif /* CONFIG_TCC_STATIC */
906 #endif /* TCC_IS_NATIVE */
907 /* ------------------------------------------------------------- */