Fix include SHT_NOTE sections everywhere
[tinycc/self_contained.git] / tccrun.c
blob198dd0d5ec8d7b708257bcd5cb8c8de0c1bef372
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 #ifndef PAGESIZE
67 # define PAGESIZE 4096
68 #endif
70 /* ------------------------------------------------------------- */
71 /* Do all relocations (needed before using tcc_get_symbol())
72 Returns -1 on error. */
74 LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
76 int size;
77 addr_t ptr_diff = 0;
79 if (TCC_RELOCATE_AUTO != ptr)
80 return tcc_relocate_ex(s1, ptr, 0);
82 size = tcc_relocate_ex(s1, NULL, 0);
83 if (size < 0)
84 return -1;
86 #ifdef HAVE_SELINUX
88 /* Using mmap instead of malloc */
89 void *prx;
90 char tmpfname[] = "/tmp/.tccrunXXXXXX";
91 int fd = mkstemp(tmpfname);
92 unlink(tmpfname);
93 ftruncate(fd, size);
95 size = (size + (PAGESIZE-1)) & ~(PAGESIZE-1);
96 ptr = mmap(NULL, size * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
97 /* mmap RX memory at a fixed distance */
98 prx = mmap((char*)ptr + size, size, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_FIXED, fd, 0);
99 if (ptr == MAP_FAILED || prx == MAP_FAILED)
100 tcc_error("tccrun: could not map memory");
101 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)(size*2));
102 ptr_diff = (char*)prx - (char*)ptr;
103 close(fd);
104 //printf("map %p %p %p\n", ptr, prx, (void*)ptr_diff);
106 #else
107 ptr = tcc_malloc(size);
108 #endif
109 tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
110 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
111 return 0;
114 ST_FUNC void tcc_run_free(TCCState *s1)
116 int i;
118 for (i = 0; i < s1->nb_runtime_mem; ++i) {
119 #ifdef HAVE_SELINUX
120 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
121 munmap(s1->runtime_mem[i], size);
122 #else
123 #ifdef _WIN64
124 win64_del_function_table(*(void**)s1->runtime_mem[i]);
125 #endif
126 tcc_free(s1->runtime_mem[i]);
127 #endif
129 tcc_free(s1->runtime_mem);
132 static void run_cdtors(TCCState *s1, const char *start, const char *end,
133 int argc, char **argv, char **envp)
135 void **a = (void **)get_sym_addr(s1, start, 0, 0);
136 void **b = (void **)get_sym_addr(s1, end, 0, 0);
137 while (a != b)
138 ((void(*)(int, char **, char **))*a++)(argc, argv, envp);
141 /* launch the compiled program with the given arguments */
142 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
144 int (*prog_main)(int, char **, char **), ret;
145 #ifdef CONFIG_TCC_BACKTRACE
146 rt_context *rc = &g_rtctxt;
147 #endif
149 #if defined(__APPLE__) || defined(__FreeBSD__)
150 char **envp = NULL;
151 #elif defined(__OpenBSD__) || defined(__NetBSD__)
152 extern char **environ;
153 char **envp = environ;
154 #else
155 char **envp = environ;
156 #endif
158 s1->runtime_main = s1->nostdlib ? "_start" : "main";
159 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1))
160 return 0;
161 #ifdef CONFIG_TCC_BACKTRACE
162 if (s1->do_debug)
163 tcc_add_symbol(s1, "exit", rt_exit);
164 #endif
165 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
166 return -1;
167 prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1);
169 #ifdef CONFIG_TCC_BACKTRACE
170 memset(rc, 0, sizeof *rc);
171 if (s1->do_debug) {
172 void *p;
173 rc->stab_sym = (Stab_Sym *)stab_section->data;
174 rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
175 rc->stab_str = (char *)stab_section->link->data;
176 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
177 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
178 rc->elf_str = (char *)symtab_section->link->data;
179 #if PTR_SIZE == 8
180 rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL;
181 #endif
182 rc->top_func = tcc_get_symbol(s1, "main");
183 rc->num_callers = s1->rt_num_callers;
184 rc->do_jmp = 1;
185 if ((p = tcc_get_symbol(s1, "__rt_error")))
186 *(void**)p = _rt_error;
187 #ifdef CONFIG_TCC_BCHECK
188 if (s1->do_bounds_check) {
189 if ((p = tcc_get_symbol(s1, "__bound_init")))
190 ((void(*)(void*, int))p)(bounds_section->data, 1);
192 #endif
193 set_exception_handler();
195 #endif
197 errno = 0; /* clean errno value */
198 fflush(stdout);
199 fflush(stderr);
200 /* These aren't C symbols, so don't need leading underscore handling. */
201 run_cdtors(s1, "__init_array_start", "__init_array_end", argc, argv, envp);
202 #ifdef CONFIG_TCC_BACKTRACE
203 if (!rc->do_jmp || !(ret = setjmp(rc->jmp_buf)))
204 #endif
206 ret = prog_main(argc, argv, envp);
208 run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL);
209 if ((s1->dflag & 16) && ret)
210 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
211 return ret;
214 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
215 /* To avoid that x86 processors would reload cached instructions
216 each time when data is written in the near, we need to make
217 sure that code and data do not share the same 64 byte unit */
218 #define RUN_SECTION_ALIGNMENT 63
219 #else
220 #define RUN_SECTION_ALIGNMENT 0
221 #endif
223 /* relocate code. Return -1 on error, required size if ptr is NULL,
224 otherwise copy code into buffer passed by the caller */
225 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
227 Section *s;
228 unsigned offset, length, align, max_align, i, k, f;
229 addr_t mem, addr;
231 if (NULL == ptr) {
232 s1->nb_errors = 0;
233 #ifdef TCC_TARGET_PE
234 pe_output_file(s1, NULL);
235 #else
236 tcc_add_runtime(s1);
237 resolve_common_syms(s1);
238 build_got_entries(s1);
239 #endif
240 if (s1->nb_errors)
241 return -1;
244 offset = max_align = 0, mem = (addr_t)ptr;
245 #ifdef _WIN64
246 offset += sizeof (void*); /* space for function_table pointer */
247 #endif
248 for (k = 0; k < 2; ++k) {
249 f = 0, addr = k ? mem : mem + ptr_diff;
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 if (k != !(s->sh_flags & SHF_EXECINSTR))
255 continue;
256 align = s->sh_addralign - 1;
257 if (++f == 1 && align < RUN_SECTION_ALIGNMENT)
258 align = RUN_SECTION_ALIGNMENT;
259 if (max_align < align)
260 max_align = align;
261 offset += -(addr + offset) & align;
262 s->sh_addr = mem ? addr + offset : 0;
263 offset += s->data_offset;
264 #if 0
265 if (mem)
266 printf("%-16s %p len %04x align %2d\n",
267 s->name, (void*)s->sh_addr, (unsigned)s->data_offset, align + 1);
268 #endif
272 /* relocate symbols */
273 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
274 if (s1->nb_errors)
275 return -1;
277 if (0 == mem)
278 return offset + max_align;
280 #ifdef TCC_TARGET_PE
281 s1->pe_imagebase = mem;
282 #endif
284 /* relocate each section */
285 for(i = 1; i < s1->nb_sections; i++) {
286 s = s1->sections[i];
287 if (s->reloc)
288 relocate_section(s1, s);
290 #if !defined(TCC_TARGET_PE) || defined(TCC_TARGET_MACHO)
291 relocate_plt(s1);
292 #endif
294 for(i = 1; i < s1->nb_sections; i++) {
295 s = s1->sections[i];
296 if (0 == (s->sh_flags & SHF_ALLOC))
297 continue;
298 length = s->data_offset;
299 ptr = (void*)s->sh_addr;
300 if (s->sh_flags & SHF_EXECINSTR)
301 ptr = (char*)((addr_t)ptr - ptr_diff);
302 if (NULL == s->data || s->sh_type == SHT_NOBITS)
303 memset(ptr, 0, length);
304 else
305 memcpy(ptr, s->data, length);
306 /* mark executable sections as executable in memory */
307 if (s->sh_flags & SHF_EXECINSTR)
308 set_pages_executable(s1, (char*)((addr_t)ptr + ptr_diff), length);
311 #ifdef _WIN64
312 *(void**)mem = win64_add_function_table(s1);
313 #endif
315 return 0;
318 /* ------------------------------------------------------------- */
319 /* allow to run code in memory */
321 static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length)
323 #ifdef _WIN32
324 unsigned long old_protect;
325 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
326 #else
327 void __clear_cache(void *beginning, void *end);
328 # ifndef HAVE_SELINUX
329 addr_t start, end;
330 start = (addr_t)ptr & ~(PAGESIZE - 1);
331 end = (addr_t)ptr + length;
332 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
333 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
334 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
335 # endif
336 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
337 __clear_cache(ptr, (char *)ptr + length);
338 # endif
339 #endif
342 #ifdef _WIN64
343 static void *win64_add_function_table(TCCState *s1)
345 void *p = NULL;
346 if (s1->uw_pdata) {
347 p = (void*)s1->uw_pdata->sh_addr;
348 RtlAddFunctionTable(
349 (RUNTIME_FUNCTION*)p,
350 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
351 s1->pe_imagebase
353 s1->uw_pdata = NULL;
355 return p;
358 static void win64_del_function_table(void *p)
360 if (p) {
361 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
364 #endif
365 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
366 /* ------------------------------------------------------------- */
367 #ifdef CONFIG_TCC_BACKTRACE
369 static int rt_vprintf(const char *fmt, va_list ap)
371 int ret = vfprintf(stderr, fmt, ap);
372 fflush(stderr);
373 return ret;
376 static int rt_printf(const char *fmt, ...)
378 va_list ap;
379 int r;
380 va_start(ap, fmt);
381 r = rt_vprintf(fmt, ap);
382 va_end(ap);
383 return r;
386 #define INCLUDE_STACK_SIZE 32
388 /* print the position in the source file of PC value 'pc' by reading
389 the stabs debug information */
390 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
391 const char *msg, const char *skip)
393 char func_name[128];
394 addr_t func_addr, last_pc, pc;
395 const char *incl_files[INCLUDE_STACK_SIZE];
396 int incl_index, last_incl_index, len, last_line_num, i;
397 const char *str, *p;
398 ElfW(Sym) *esym;
399 Stab_Sym *sym;
401 next:
402 func_name[0] = '\0';
403 func_addr = 0;
404 incl_index = 0;
405 last_pc = (addr_t)-1;
406 last_line_num = 1;
407 last_incl_index = 0;
409 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
410 str = rc->stab_str + sym->n_strx;
411 pc = sym->n_value;
413 switch(sym->n_type) {
414 case N_SLINE:
415 if (func_addr)
416 goto rel_pc;
417 case N_SO:
418 case N_SOL:
419 goto abs_pc;
420 case N_FUN:
421 if (sym->n_strx == 0) /* end of function */
422 goto rel_pc;
423 abs_pc:
424 #if PTR_SIZE == 8
425 /* Stab_Sym.n_value is only 32bits */
426 pc += rc->prog_base;
427 #endif
428 goto check_pc;
429 rel_pc:
430 pc += func_addr;
431 check_pc:
432 if (pc >= wanted_pc && wanted_pc >= last_pc)
433 goto found;
434 break;
437 switch(sym->n_type) {
438 /* function start or end */
439 case N_FUN:
440 if (sym->n_strx == 0)
441 goto reset_func;
442 p = strchr(str, ':');
443 if (0 == p || (len = p - str + 1, len > sizeof func_name))
444 len = sizeof func_name;
445 pstrcpy(func_name, len, str);
446 func_addr = pc;
447 break;
448 /* line number info */
449 case N_SLINE:
450 last_pc = pc;
451 last_line_num = sym->n_desc;
452 last_incl_index = incl_index;
453 break;
454 /* include files */
455 case N_BINCL:
456 if (incl_index < INCLUDE_STACK_SIZE)
457 incl_files[incl_index++] = str;
458 break;
459 case N_EINCL:
460 if (incl_index > 1)
461 incl_index--;
462 break;
463 /* start/end of translation unit */
464 case N_SO:
465 incl_index = 0;
466 if (sym->n_strx) {
467 /* do not add path */
468 len = strlen(str);
469 if (len > 0 && str[len - 1] != '/')
470 incl_files[incl_index++] = str;
472 reset_func:
473 func_name[0] = '\0';
474 func_addr = 0;
475 last_pc = (addr_t)-1;
476 break;
477 /* alternative file name (from #line or #include directives) */
478 case N_SOL:
479 if (incl_index)
480 incl_files[incl_index-1] = str;
481 break;
485 func_name[0] = '\0';
486 func_addr = 0;
487 last_incl_index = 0;
489 /* we try symtab symbols (no line number info) */
490 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
491 int type = ELFW(ST_TYPE)(esym->st_info);
492 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
493 if (wanted_pc >= esym->st_value &&
494 wanted_pc < esym->st_value + esym->st_size) {
495 pstrcpy(func_name, sizeof(func_name),
496 rc->elf_str + esym->st_name);
497 func_addr = esym->st_value;
498 goto found;
503 if ((rc = rc->next))
504 goto next;
506 found:
507 i = last_incl_index;
508 if (i > 0) {
509 str = incl_files[--i];
510 if (skip[0] && strstr(str, skip))
511 return (addr_t)-1;
512 rt_printf("%s:%d: ", str, last_line_num);
513 } else
514 rt_printf("%08llx : ", (long long)wanted_pc);
515 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
516 #if 0
517 if (--i >= 0) {
518 rt_printf(" (included from ");
519 for (;;) {
520 rt_printf("%s", incl_files[i]);
521 if (--i < 0)
522 break;
523 rt_printf(", ");
525 rt_printf(")");
527 #endif
528 return func_addr;
531 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level);
533 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap)
535 rt_context *rc = &g_rtctxt;
536 addr_t pc = 0;
537 char skip[100];
538 int i, level, ret, n;
539 const char *a, *b, *msg;
541 if (fp) {
542 /* we're called from tcc_backtrace. */
543 rc->fp = (addr_t)fp;
544 rc->ip = (addr_t)ip;
545 msg = "";
546 } else {
547 /* we're called from signal/exception handler */
548 msg = "RUNTIME ERROR: ";
551 skip[0] = 0;
552 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
553 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
554 memcpy(skip, a, b - a), skip[b - a] = 0;
555 fmt = b + 1;
558 n = rc->num_callers ? rc->num_callers : 6;
559 for (i = level = 0; level < n; i++) {
560 ret = rt_get_caller_pc(&pc, rc, i);
561 a = "%s";
562 if (ret != -1) {
563 pc = rt_printline(rc, pc, level ? "by" : "at", skip);
564 if (pc == (addr_t)-1)
565 continue;
566 a = ": %s";
568 if (level == 0) {
569 rt_printf(a, msg);
570 rt_vprintf(fmt, ap);
571 } else if (ret == -1)
572 break;
573 rt_printf("\n");
574 if (ret == -1 || (pc == (addr_t)rc->top_func && pc))
575 break;
576 ++level;
579 rc->ip = rc->fp = 0;
580 return 0;
583 /* emit a run time error at position 'pc' */
584 static int rt_error(const char *fmt, ...)
586 va_list ap;
587 int ret;
588 va_start(ap, fmt);
589 ret = _rt_error(0, 0, fmt, ap);
590 va_end(ap);
591 return ret;
594 static void rt_exit(int code)
596 rt_context *rc = &g_rtctxt;
597 if (rc->do_jmp)
598 longjmp(rc->jmp_buf, code ? code : 256);
599 exit(code);
602 /* ------------------------------------------------------------- */
604 #ifndef _WIN32
605 # include <signal.h>
606 # ifndef __OpenBSD__
607 # include <sys/ucontext.h>
608 # endif
609 #else
610 # define ucontext_t CONTEXT
611 #endif
613 /* translate from ucontext_t* to internal rt_context * */
614 static void rt_getcontext(ucontext_t *uc, rt_context *rc)
616 #if defined _WIN64
617 rc->ip = uc->Rip;
618 rc->fp = uc->Rbp;
619 rc->sp = uc->Rsp;
620 #elif defined _WIN32
621 rc->ip = uc->Eip;
622 rc->fp = uc->Ebp;
623 rc->sp = uc->Esp;
624 #elif defined __i386__
625 # if defined(__APPLE__)
626 rc->ip = uc->uc_mcontext->__ss.__eip;
627 rc->fp = uc->uc_mcontext->__ss.__ebp;
628 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
629 rc->ip = uc->uc_mcontext.mc_eip;
630 rc->fp = uc->uc_mcontext.mc_ebp;
631 # elif defined(__dietlibc__)
632 rc->ip = uc->uc_mcontext.eip;
633 rc->fp = uc->uc_mcontext.ebp;
634 # elif defined(__NetBSD__)
635 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
636 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
637 # elif defined(__OpenBSD__)
638 rc->ip = uc->sc_eip;
639 rc->fp = uc->sc_ebp;
640 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
641 rc->ip = uc->uc_mcontext.gregs[EIP];
642 rc->fp = uc->uc_mcontext.gregs[EBP];
643 # else
644 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
645 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
646 # endif
647 #elif defined(__x86_64__)
648 # if defined(__APPLE__)
649 rc->ip = uc->uc_mcontext->__ss.__rip;
650 rc->fp = uc->uc_mcontext->__ss.__rbp;
651 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
652 rc->ip = uc->uc_mcontext.mc_rip;
653 rc->fp = uc->uc_mcontext.mc_rbp;
654 # elif defined(__NetBSD__)
655 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
656 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
657 # elif defined(__OpenBSD__)
658 rc->ip = uc->sc_rip;
659 rc->fp = uc->sc_rbp;
660 # else
661 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
662 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
663 # endif
664 #elif defined(__arm__)
665 rc->ip = uc->uc_mcontext.arm_pc;
666 rc->fp = uc->uc_mcontext.arm_fp;
667 #elif defined(__aarch64__) && defined(__FreeBSD__)
668 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
669 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
670 #elif defined(__aarch64__) && defined(__NetBSD__)
671 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
672 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
673 #elif defined(__aarch64__)
674 rc->ip = uc->uc_mcontext.pc;
675 rc->fp = uc->uc_mcontext.regs[29];
676 #elif defined(__riscv)
677 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
678 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
679 #endif
682 /* ------------------------------------------------------------- */
683 #ifndef _WIN32
684 /* signal handler for fatal errors */
685 static void sig_error(int signum, siginfo_t *siginf, void *puc)
687 rt_context *rc = &g_rtctxt;
688 rt_getcontext(puc, rc);
690 switch(signum) {
691 case SIGFPE:
692 switch(siginf->si_code) {
693 case FPE_INTDIV:
694 case FPE_FLTDIV:
695 rt_error("division by zero");
696 break;
697 default:
698 rt_error("floating point exception");
699 break;
701 break;
702 case SIGBUS:
703 case SIGSEGV:
704 rt_error("invalid memory access");
705 break;
706 case SIGILL:
707 rt_error("illegal instruction");
708 break;
709 case SIGABRT:
710 rt_error("abort() called");
711 break;
712 default:
713 rt_error("caught signal %d", signum);
714 break;
716 rt_exit(255);
719 #ifndef SA_SIGINFO
720 # define SA_SIGINFO 0x00000004u
721 #endif
723 /* Generate a stack backtrace when a CPU exception occurs. */
724 static void set_exception_handler(void)
726 struct sigaction sigact;
727 /* install TCC signal handlers to print debug info on fatal
728 runtime errors */
729 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
730 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
731 sigact.sa_flags |= SA_ONSTACK;
732 #endif
733 sigact.sa_sigaction = sig_error;
734 sigemptyset(&sigact.sa_mask);
735 sigaction(SIGFPE, &sigact, NULL);
736 sigaction(SIGILL, &sigact, NULL);
737 sigaction(SIGSEGV, &sigact, NULL);
738 sigaction(SIGBUS, &sigact, NULL);
739 sigaction(SIGABRT, &sigact, NULL);
740 #if 0//def SIGSTKSZ
741 /* This allows stack overflow to be reported instead of a SEGV */
743 stack_t ss;
744 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
746 ss.ss_sp = stack;
747 ss.ss_size = SIGSTKSZ;
748 ss.ss_flags = 0;
749 sigaltstack(&ss, NULL);
751 #endif
754 #else /* WIN32 */
756 /* signal handler for fatal errors */
757 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
759 rt_context *rc = &g_rtctxt;
760 unsigned code;
761 rt_getcontext(ex_info->ContextRecord, rc);
763 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
764 case EXCEPTION_ACCESS_VIOLATION:
765 rt_error("invalid memory access");
766 break;
767 case EXCEPTION_STACK_OVERFLOW:
768 rt_error("stack overflow");
769 break;
770 case EXCEPTION_INT_DIVIDE_BY_ZERO:
771 rt_error("division by zero");
772 break;
773 case EXCEPTION_BREAKPOINT:
774 case EXCEPTION_SINGLE_STEP:
775 rc->ip = *(addr_t*)rc->sp;
776 rt_error("breakpoint/single-step exception:");
777 return EXCEPTION_CONTINUE_SEARCH;
778 default:
779 rt_error("caught exception %08x", code);
780 break;
782 if (rc->do_jmp)
783 rt_exit(255);
784 return EXCEPTION_EXECUTE_HANDLER;
787 /* Generate a stack backtrace when a CPU exception occurs. */
788 static void set_exception_handler(void)
790 SetUnhandledExceptionFilter(cpu_exception_handler);
793 #endif
795 /* ------------------------------------------------------------- */
796 /* return the PC at frame level 'level'. Return negative if not found */
797 #if defined(__i386__) || defined(__x86_64__)
798 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
800 addr_t ip, fp;
801 if (level == 0) {
802 ip = rc->ip;
803 } else {
804 ip = 0;
805 fp = rc->fp;
806 while (--level) {
807 /* XXX: check address validity with program info */
808 if (fp <= 0x1000)
809 break;
810 fp = ((addr_t *)fp)[0];
812 if (fp > 0x1000)
813 ip = ((addr_t *)fp)[1];
815 if (ip <= 0x1000)
816 return -1;
817 *paddr = ip;
818 return 0;
821 #elif defined(__arm__)
822 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
824 /* XXX: only supports linux */
825 #if !defined(__linux__)
826 return -1;
827 #else
828 if (level == 0) {
829 *paddr = rc->ip;
830 } else {
831 addr_t fp = rc->fp;
832 while (--level)
833 fp = ((addr_t *)fp)[0];
834 *paddr = ((addr_t *)fp)[2];
836 return 0;
837 #endif
840 #elif defined(__aarch64__)
841 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
843 if (level == 0) {
844 *paddr = rc->ip;
845 } else {
846 addr_t *fp = (addr_t*)rc->fp;
847 while (--level)
848 fp = (addr_t *)fp[0];
849 *paddr = fp[1];
851 return 0;
854 #elif defined(__riscv)
855 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
857 if (level == 0) {
858 *paddr = rc->ip;
859 } else {
860 addr_t *fp = (addr_t*)rc->fp;
861 while (--level && fp >= (addr_t*)0x1000)
862 fp = (addr_t *)fp[-2];
863 if (fp < (addr_t*)0x1000)
864 return -1;
865 *paddr = fp[-1];
867 return 0;
870 #else
871 #warning add arch specific rt_get_caller_pc()
872 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
874 return -1;
877 #endif
878 #endif /* CONFIG_TCC_BACKTRACE */
879 /* ------------------------------------------------------------- */
880 #ifdef CONFIG_TCC_STATIC
882 /* dummy function for profiling */
883 ST_FUNC void *dlopen(const char *filename, int flag)
885 return NULL;
888 ST_FUNC void dlclose(void *p)
892 ST_FUNC const char *dlerror(void)
894 return "error";
897 typedef struct TCCSyms {
898 char *str;
899 void *ptr;
900 } TCCSyms;
903 /* add the symbol you want here if no dynamic linking is done */
904 static TCCSyms tcc_syms[] = {
905 #if !defined(CONFIG_TCCBOOT)
906 #define TCCSYM(a) { #a, &a, },
907 TCCSYM(printf)
908 TCCSYM(fprintf)
909 TCCSYM(fopen)
910 TCCSYM(fclose)
911 #undef TCCSYM
912 #endif
913 { NULL, NULL },
916 ST_FUNC void *dlsym(void *handle, const char *symbol)
918 TCCSyms *p;
919 p = tcc_syms;
920 while (p->str != NULL) {
921 if (!strcmp(p->str, symbol))
922 return p->ptr;
923 p++;
925 return NULL;
928 #endif /* CONFIG_TCC_STATIC */
929 #endif /* TCC_IS_NATIVE */
930 /* ------------------------------------------------------------- */