tccgen: Allow struct init from struct
[tinycc.git] / tccrun.c
blob9c9f4e079359d3a98b9317542f46d2e83d62afe0
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, int mode, 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 size = (size + (PAGESIZE-1)) & ~(PAGESIZE-1);
92 ptr = mmap(NULL, size * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
93 /* mmap RX memory at a fixed distance */
94 prx = mmap((char*)ptr + size, size, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_FIXED, fd, 0);
95 if (ptr == MAP_FAILED || prx == MAP_FAILED)
96 tcc_error("tccrun: could not map memory");
97 ptr_diff = (char*)prx - (char*)ptr;
98 close(fd);
99 //printf("map %p %p %p\n", ptr, prx, (void*)ptr_diff);
101 #else
102 ptr = tcc_malloc(size);
103 #endif
104 tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
105 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
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 += 2) {
115 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i];
116 void *ptr = s1->runtime_mem[i+1];
117 #ifdef HAVE_SELINUX
118 munmap(ptr, size * 2);
119 #else
120 /* unprotect memory to make it usable for malloc again */
121 set_pages_executable(s1, 2, ptr, size);
122 #ifdef _WIN64
123 win64_del_function_table(*(void**)ptr);
124 #endif
125 tcc_free(ptr);
126 #endif
128 tcc_free(s1->runtime_mem);
131 static void run_cdtors(TCCState *s1, const char *start, const char *end,
132 int argc, char **argv, char **envp)
134 void **a = (void **)get_sym_addr(s1, start, 0, 0);
135 void **b = (void **)get_sym_addr(s1, end, 0, 0);
136 while (a != b)
137 ((void(*)(int, char **, char **))*a++)(argc, argv, envp);
140 /* launch the compiled program with the given arguments */
141 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
143 int (*prog_main)(int, char **, char **), ret;
144 #ifdef CONFIG_TCC_BACKTRACE
145 rt_context *rc = &g_rtctxt;
146 #endif
148 #if defined(__APPLE__) || defined(__FreeBSD__)
149 char **envp = NULL;
150 #elif defined(__OpenBSD__) || defined(__NetBSD__)
151 extern char **environ;
152 char **envp = environ;
153 #else
154 char **envp = environ;
155 #endif
157 s1->runtime_main = s1->nostdlib ? "_start" : "main";
158 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1))
159 return 0;
160 #ifdef CONFIG_TCC_BACKTRACE
161 if (s1->do_debug)
162 tcc_add_symbol(s1, "exit", rt_exit);
163 #endif
164 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
165 return -1;
166 prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1);
168 #ifdef CONFIG_TCC_BACKTRACE
169 memset(rc, 0, sizeof *rc);
170 if (s1->do_debug) {
171 void *p;
172 rc->stab_sym = (Stab_Sym *)stab_section->data;
173 rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
174 rc->stab_str = (char *)stab_section->link->data;
175 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
176 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
177 rc->elf_str = (char *)symtab_section->link->data;
178 #if PTR_SIZE == 8
179 rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL;
180 #endif
181 rc->top_func = tcc_get_symbol(s1, "main");
182 rc->num_callers = s1->rt_num_callers;
183 rc->do_jmp = 1;
184 if ((p = tcc_get_symbol(s1, "__rt_error")))
185 *(void**)p = _rt_error;
186 #ifdef CONFIG_TCC_BCHECK
187 if (s1->do_bounds_check) {
188 rc->bounds_start = (void*)bounds_section->sh_addr;
189 if ((p = tcc_get_symbol(s1, "__bound_init")))
190 ((void(*)(void*,int))p)(rc->bounds_start, 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 #define DEBUG_RUNMEN 0
216 /* enable rx/ro/rw permissions */
217 #define CONFIG_RUNMEM_RO 1
219 #if CONFIG_RUNMEM_RO
220 # define PAGE_ALIGN PAGESIZE
221 #elif defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
222 /* To avoid that x86 processors would reload cached instructions
223 each time when data is written in the near, we need to make
224 sure that code and data do not share the same 64 byte unit */
225 # define PAGE_ALIGN 64
226 #else
227 # define PAGE_ALIGN 1
228 #endif
230 /* relocate code. Return -1 on error, required size if ptr is NULL,
231 otherwise copy code into buffer passed by the caller */
232 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
234 Section *s;
235 unsigned offset, length, align, max_align, i, k, f;
236 unsigned n, copy;
237 addr_t mem, addr;
239 if (NULL == ptr) {
240 s1->nb_errors = 0;
241 #ifdef TCC_TARGET_PE
242 pe_output_file(s1, NULL);
243 #else
244 tcc_add_runtime(s1);
245 resolve_common_syms(s1);
246 build_got_entries(s1);
247 #endif
248 if (s1->nb_errors)
249 return -1;
252 offset = max_align = 0, mem = (addr_t)ptr;
253 #ifdef _WIN64
254 offset += sizeof (void*); /* space for function_table pointer */
255 #endif
256 copy = 0;
257 redo:
258 for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
259 n = 0; addr = 0;
260 for(i = 1; i < s1->nb_sections; i++) {
261 static const char shf[] = {
262 SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
264 s = s1->sections[i];
265 if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
266 continue;
267 length = s->data_offset;
268 if (copy) {
269 if (addr == 0)
270 addr = s->sh_addr;
271 n = (s->sh_addr - addr) + length;
272 ptr = (void*)s->sh_addr;
273 if (k == 0)
274 ptr = (void*)(s->sh_addr - ptr_diff);
275 if (NULL == s->data || s->sh_type == SHT_NOBITS)
276 memset(ptr, 0, length);
277 else
278 memcpy(ptr, s->data, length);
279 #ifdef _WIN64
280 if (s == s1->uw_pdata)
281 *(void**)mem = win64_add_function_table(s1);
282 #endif
283 if (s->data) {
284 tcc_free(s->data);
285 s->data = NULL;
286 s->data_allocated = 0;
288 s->data_offset = 0;
289 continue;
291 align = s->sh_addralign - 1;
292 if (++n == 1 && align < (PAGE_ALIGN - 1))
293 align = (PAGE_ALIGN - 1);
294 if (max_align < align)
295 max_align = align;
296 addr = k ? mem : mem + ptr_diff;
297 offset += -(addr + offset) & align;
298 s->sh_addr = mem ? addr + offset : 0;
299 offset += length;
300 #if DEBUG_RUNMEN
301 if (mem)
302 printf("%d: %-16s %p len %04x align %04x\n",
303 k, s->name, (void*)s->sh_addr, length, align + 1);
304 #endif
306 if (copy) { /* set permissions */
307 if (k == 0 && ptr_diff)
308 continue; /* not with HAVE_SELINUX */
309 f = k;
310 #if !CONFIG_RUNMEM_RO
311 if (f != 0)
312 continue;
313 f = 3; /* change only SHF_EXECINSTR to rwx */
314 #endif
315 #if DEBUG_RUNMEN
316 printf("protect %d %p %04x\n", f, (void*)addr, n);
317 #endif
318 if (n)
319 set_pages_executable(s1, f, (void*)addr, n);
323 if (copy)
324 return 0;
326 /* relocate symbols */
327 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
328 if (s1->nb_errors)
329 return -1;
330 if (0 == mem)
331 return offset + max_align;
333 #ifdef TCC_TARGET_PE
334 s1->pe_imagebase = mem;
335 #endif
337 /* relocate sections */
338 #ifndef TCC_TARGET_PE
339 relocate_plt(s1);
340 #endif
341 relocate_sections(s1);
342 copy = 1;
343 goto redo;
346 /* ------------------------------------------------------------- */
347 /* allow to run code in memory */
349 static void set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length)
351 #ifdef _WIN32
352 static const unsigned char protect[] = {
353 PAGE_EXECUTE_READ,
354 PAGE_READONLY,
355 PAGE_READWRITE,
356 PAGE_EXECUTE_READWRITE
358 DWORD old;
359 VirtualProtect(ptr, length, protect[mode], &old);
360 #else
361 static const unsigned char protect[] = {
362 PROT_READ | PROT_EXEC,
363 PROT_READ,
364 PROT_READ | PROT_WRITE,
365 PROT_READ | PROT_WRITE | PROT_EXEC
367 addr_t start, end;
368 start = (addr_t)ptr & ~(PAGESIZE - 1);
369 end = (addr_t)ptr + length;
370 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
371 if (mprotect((void *)start, end - start, protect[mode]))
372 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
374 /* XXX: BSD sometimes dump core with bad system call */
375 # if (TCC_TARGET_ARM && !TARGETOS_BSD) || TCC_TARGET_ARM64
376 if (mode == 0 || mode == 3) {
377 void __clear_cache(void *beginning, void *end);
378 __clear_cache(ptr, (char *)ptr + length);
380 # endif
382 #endif
385 #ifdef _WIN64
386 static void *win64_add_function_table(TCCState *s1)
388 void *p = NULL;
389 if (s1->uw_pdata) {
390 p = (void*)s1->uw_pdata->sh_addr;
391 RtlAddFunctionTable(
392 (RUNTIME_FUNCTION*)p,
393 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
394 s1->pe_imagebase
396 s1->uw_pdata = NULL;
398 return p;
401 static void win64_del_function_table(void *p)
403 if (p) {
404 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
407 #endif
408 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
409 /* ------------------------------------------------------------- */
410 #ifdef CONFIG_TCC_BACKTRACE
412 static int rt_vprintf(const char *fmt, va_list ap)
414 int ret = vfprintf(stderr, fmt, ap);
415 fflush(stderr);
416 return ret;
419 static int rt_printf(const char *fmt, ...)
421 va_list ap;
422 int r;
423 va_start(ap, fmt);
424 r = rt_vprintf(fmt, ap);
425 va_end(ap);
426 return r;
429 #define INCLUDE_STACK_SIZE 32
431 /* print the position in the source file of PC value 'pc' by reading
432 the stabs debug information */
433 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
434 const char *msg, const char *skip)
436 char func_name[128];
437 addr_t func_addr, last_pc, pc;
438 const char *incl_files[INCLUDE_STACK_SIZE];
439 int incl_index, last_incl_index, len, last_line_num, i;
440 const char *str, *p;
441 ElfW(Sym) *esym;
442 Stab_Sym *sym;
444 next:
445 func_name[0] = '\0';
446 func_addr = 0;
447 incl_index = 0;
448 last_pc = (addr_t)-1;
449 last_line_num = 1;
450 last_incl_index = 0;
452 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
453 str = rc->stab_str + sym->n_strx;
454 pc = sym->n_value;
456 switch(sym->n_type) {
457 case N_SLINE:
458 if (func_addr)
459 goto rel_pc;
460 case N_SO:
461 case N_SOL:
462 goto abs_pc;
463 case N_FUN:
464 if (sym->n_strx == 0) /* end of function */
465 goto rel_pc;
466 abs_pc:
467 #if PTR_SIZE == 8
468 /* Stab_Sym.n_value is only 32bits */
469 pc += rc->prog_base;
470 #endif
471 goto check_pc;
472 rel_pc:
473 pc += func_addr;
474 check_pc:
475 if (pc >= wanted_pc && wanted_pc >= last_pc)
476 goto found;
477 break;
480 switch(sym->n_type) {
481 /* function start or end */
482 case N_FUN:
483 if (sym->n_strx == 0)
484 goto reset_func;
485 p = strchr(str, ':');
486 if (0 == p || (len = p - str + 1, len > sizeof func_name))
487 len = sizeof func_name;
488 pstrcpy(func_name, len, str);
489 func_addr = pc;
490 break;
491 /* line number info */
492 case N_SLINE:
493 last_pc = pc;
494 last_line_num = sym->n_desc;
495 last_incl_index = incl_index;
496 break;
497 /* include files */
498 case N_BINCL:
499 if (incl_index < INCLUDE_STACK_SIZE)
500 incl_files[incl_index++] = str;
501 break;
502 case N_EINCL:
503 if (incl_index > 1)
504 incl_index--;
505 break;
506 /* start/end of translation unit */
507 case N_SO:
508 incl_index = 0;
509 if (sym->n_strx) {
510 /* do not add path */
511 len = strlen(str);
512 if (len > 0 && str[len - 1] != '/')
513 incl_files[incl_index++] = str;
515 reset_func:
516 func_name[0] = '\0';
517 func_addr = 0;
518 last_pc = (addr_t)-1;
519 break;
520 /* alternative file name (from #line or #include directives) */
521 case N_SOL:
522 if (incl_index)
523 incl_files[incl_index-1] = str;
524 break;
528 func_name[0] = '\0';
529 func_addr = 0;
530 last_incl_index = 0;
532 /* we try symtab symbols (no line number info) */
533 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
534 int type = ELFW(ST_TYPE)(esym->st_info);
535 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
536 if (wanted_pc >= esym->st_value &&
537 wanted_pc < esym->st_value + esym->st_size) {
538 pstrcpy(func_name, sizeof(func_name),
539 rc->elf_str + esym->st_name);
540 func_addr = esym->st_value;
541 goto found;
546 if ((rc = rc->next))
547 goto next;
549 found:
550 i = last_incl_index;
551 if (i > 0) {
552 str = incl_files[--i];
553 if (skip[0] && strstr(str, skip))
554 return (addr_t)-1;
555 rt_printf("%s:%d: ", str, last_line_num);
556 } else
557 rt_printf("%08llx : ", (long long)wanted_pc);
558 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
559 #if 0
560 if (--i >= 0) {
561 rt_printf(" (included from ");
562 for (;;) {
563 rt_printf("%s", incl_files[i]);
564 if (--i < 0)
565 break;
566 rt_printf(", ");
568 rt_printf(")");
570 #endif
571 return func_addr;
574 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level);
576 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap)
578 rt_context *rc = &g_rtctxt;
579 addr_t pc = 0;
580 char skip[100];
581 int i, level, ret, n;
582 const char *a, *b, *msg;
584 if (fp) {
585 /* we're called from tcc_backtrace. */
586 rc->fp = (addr_t)fp;
587 rc->ip = (addr_t)ip;
588 msg = "";
589 } else {
590 /* we're called from signal/exception handler */
591 msg = "RUNTIME ERROR: ";
594 skip[0] = 0;
595 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
596 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
597 memcpy(skip, a, b - a), skip[b - a] = 0;
598 fmt = b + 1;
601 n = rc->num_callers ? rc->num_callers : 6;
602 for (i = level = 0; level < n; i++) {
603 ret = rt_get_caller_pc(&pc, rc, i);
604 a = "%s";
605 if (ret != -1) {
606 pc = rt_printline(rc, pc, level ? "by" : "at", skip);
607 if (pc == (addr_t)-1)
608 continue;
609 a = ": %s";
611 if (level == 0) {
612 rt_printf(a, msg);
613 rt_vprintf(fmt, ap);
614 } else if (ret == -1)
615 break;
616 rt_printf("\n");
617 if (ret == -1 || (pc == (addr_t)rc->top_func && pc))
618 break;
619 ++level;
622 rc->ip = rc->fp = 0;
623 return 0;
626 /* emit a run time error at position 'pc' */
627 static int rt_error(const char *fmt, ...)
629 va_list ap;
630 int ret;
631 va_start(ap, fmt);
632 ret = _rt_error(0, 0, fmt, ap);
633 va_end(ap);
634 return ret;
637 static void rt_exit(int code)
639 rt_context *rc = &g_rtctxt;
640 if (rc->do_jmp)
641 longjmp(rc->jmp_buf, code ? code : 256);
642 exit(code);
645 /* ------------------------------------------------------------- */
647 #ifndef _WIN32
648 # include <signal.h>
649 # ifndef __OpenBSD__
650 # include <sys/ucontext.h>
651 # endif
652 #else
653 # define ucontext_t CONTEXT
654 #endif
656 /* translate from ucontext_t* to internal rt_context * */
657 static void rt_getcontext(ucontext_t *uc, rt_context *rc)
659 #if defined _WIN64
660 rc->ip = uc->Rip;
661 rc->fp = uc->Rbp;
662 rc->sp = uc->Rsp;
663 #elif defined _WIN32
664 rc->ip = uc->Eip;
665 rc->fp = uc->Ebp;
666 rc->sp = uc->Esp;
667 #elif defined __i386__
668 # if defined(__APPLE__)
669 rc->ip = uc->uc_mcontext->__ss.__eip;
670 rc->fp = uc->uc_mcontext->__ss.__ebp;
671 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
672 rc->ip = uc->uc_mcontext.mc_eip;
673 rc->fp = uc->uc_mcontext.mc_ebp;
674 # elif defined(__dietlibc__)
675 rc->ip = uc->uc_mcontext.eip;
676 rc->fp = uc->uc_mcontext.ebp;
677 # elif defined(__NetBSD__)
678 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
679 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
680 # elif defined(__OpenBSD__)
681 rc->ip = uc->sc_eip;
682 rc->fp = uc->sc_ebp;
683 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
684 rc->ip = uc->uc_mcontext.gregs[EIP];
685 rc->fp = uc->uc_mcontext.gregs[EBP];
686 # else
687 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
688 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
689 # endif
690 #elif defined(__x86_64__)
691 # if defined(__APPLE__)
692 rc->ip = uc->uc_mcontext->__ss.__rip;
693 rc->fp = uc->uc_mcontext->__ss.__rbp;
694 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
695 rc->ip = uc->uc_mcontext.mc_rip;
696 rc->fp = uc->uc_mcontext.mc_rbp;
697 # elif defined(__NetBSD__)
698 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
699 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
700 # elif defined(__OpenBSD__)
701 rc->ip = uc->sc_rip;
702 rc->fp = uc->sc_rbp;
703 # else
704 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
705 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
706 # endif
707 #elif defined(__arm__) && defined(__NetBSD__)
708 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
709 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
710 #elif defined(__arm__) && defined(__OpenBSD__)
711 rc->ip = uc->sc_pc;
712 rc->fp = uc->sc_r11;
713 #elif defined(__arm__) && defined(__FreeBSD__)
714 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
715 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
716 #elif defined(__arm__)
717 rc->ip = uc->uc_mcontext.arm_pc;
718 rc->fp = uc->uc_mcontext.arm_fp;
719 #elif defined(__aarch64__) && defined(__APPLE__)
720 // see:
721 // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h
722 rc->ip = uc->uc_mcontext->__ss.__pc;
723 rc->fp = uc->uc_mcontext->__ss.__fp;
724 #elif defined(__aarch64__) && defined(__FreeBSD__)
725 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
726 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
727 #elif defined(__aarch64__) && defined(__NetBSD__)
728 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
729 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
730 #elif defined(__aarch64__) && defined(__OpenBSD__)
731 rc->ip = uc->sc_elr;
732 rc->fp = uc->sc_x[29];
733 #elif defined(__aarch64__)
734 rc->ip = uc->uc_mcontext.pc;
735 rc->fp = uc->uc_mcontext.regs[29];
736 #elif defined(__riscv) && defined(__OpenBSD__)
737 rc->ip = uc->sc_sepc;
738 rc->fp = uc->sc_s[0];
739 #elif defined(__riscv)
740 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
741 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
742 #endif
745 /* ------------------------------------------------------------- */
746 #ifndef _WIN32
747 /* signal handler for fatal errors */
748 static void sig_error(int signum, siginfo_t *siginf, void *puc)
750 rt_context *rc = &g_rtctxt;
751 rt_getcontext(puc, rc);
753 switch(signum) {
754 case SIGFPE:
755 switch(siginf->si_code) {
756 case FPE_INTDIV:
757 case FPE_FLTDIV:
758 rt_error("division by zero");
759 break;
760 default:
761 rt_error("floating point exception");
762 break;
764 break;
765 case SIGBUS:
766 case SIGSEGV:
767 rt_error("invalid memory access");
768 break;
769 case SIGILL:
770 rt_error("illegal instruction");
771 break;
772 case SIGABRT:
773 rt_error("abort() called");
774 break;
775 default:
776 rt_error("caught signal %d", signum);
777 break;
779 rt_exit(255);
782 #ifndef SA_SIGINFO
783 # define SA_SIGINFO 0x00000004u
784 #endif
786 /* Generate a stack backtrace when a CPU exception occurs. */
787 static void set_exception_handler(void)
789 struct sigaction sigact;
790 /* install TCC signal handlers to print debug info on fatal
791 runtime errors */
792 sigemptyset (&sigact.sa_mask);
793 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
794 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
795 sigact.sa_flags |= SA_ONSTACK;
796 #endif
797 sigact.sa_sigaction = sig_error;
798 sigemptyset(&sigact.sa_mask);
799 sigaction(SIGFPE, &sigact, NULL);
800 sigaction(SIGILL, &sigact, NULL);
801 sigaction(SIGSEGV, &sigact, NULL);
802 sigaction(SIGBUS, &sigact, NULL);
803 sigaction(SIGABRT, &sigact, NULL);
804 #if 0//def SIGSTKSZ
805 /* This allows stack overflow to be reported instead of a SEGV */
807 stack_t ss;
808 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
810 ss.ss_sp = stack;
811 ss.ss_size = SIGSTKSZ;
812 ss.ss_flags = 0;
813 sigaltstack(&ss, NULL);
815 #endif
818 #else /* WIN32 */
820 /* signal handler for fatal errors */
821 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
823 rt_context *rc = &g_rtctxt;
824 unsigned code;
825 rt_getcontext(ex_info->ContextRecord, rc);
827 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
828 case EXCEPTION_ACCESS_VIOLATION:
829 rt_error("invalid memory access");
830 break;
831 case EXCEPTION_STACK_OVERFLOW:
832 rt_error("stack overflow");
833 break;
834 case EXCEPTION_INT_DIVIDE_BY_ZERO:
835 rt_error("division by zero");
836 break;
837 case EXCEPTION_BREAKPOINT:
838 case EXCEPTION_SINGLE_STEP:
839 rc->ip = *(addr_t*)rc->sp;
840 rt_error("breakpoint/single-step exception:");
841 return EXCEPTION_CONTINUE_SEARCH;
842 default:
843 rt_error("caught exception %08x", code);
844 break;
846 if (rc->do_jmp)
847 rt_exit(255);
848 return EXCEPTION_EXECUTE_HANDLER;
851 /* Generate a stack backtrace when a CPU exception occurs. */
852 static void set_exception_handler(void)
854 SetUnhandledExceptionFilter(cpu_exception_handler);
857 #endif
859 /* ------------------------------------------------------------- */
860 /* return the PC at frame level 'level'. Return negative if not found */
861 #if defined(__i386__) || defined(__x86_64__)
862 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
864 addr_t ip, fp;
865 if (level == 0) {
866 ip = rc->ip;
867 } else {
868 ip = 0;
869 fp = rc->fp;
870 while (--level) {
871 /* XXX: check address validity with program info */
872 if (fp <= 0x1000)
873 break;
874 fp = ((addr_t *)fp)[0];
876 if (fp > 0x1000)
877 ip = ((addr_t *)fp)[1];
879 if (ip <= 0x1000)
880 return -1;
881 *paddr = ip;
882 return 0;
885 #elif defined(__arm__)
886 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
888 /* XXX: only supports linux/bsd */
889 #if !defined(__linux__) && \
890 !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
891 return -1;
892 #else
893 if (level == 0) {
894 *paddr = rc->ip;
895 } else {
896 addr_t fp = rc->fp;
897 while (--level)
898 fp = ((addr_t *)fp)[0];
899 *paddr = ((addr_t *)fp)[2];
901 return 0;
902 #endif
905 #elif defined(__aarch64__)
906 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
908 if (level == 0) {
909 *paddr = rc->ip;
910 } else {
911 addr_t *fp = (addr_t*)rc->fp;
912 while (--level)
913 fp = (addr_t *)fp[0];
914 *paddr = fp[1];
916 return 0;
919 #elif defined(__riscv)
920 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
922 if (level == 0) {
923 *paddr = rc->ip;
924 } else {
925 addr_t *fp = (addr_t*)rc->fp;
926 while (--level && fp >= (addr_t*)0x1000)
927 fp = (addr_t *)fp[-2];
928 if (fp < (addr_t*)0x1000)
929 return -1;
930 *paddr = fp[-1];
932 return 0;
935 #else
936 #warning add arch specific rt_get_caller_pc()
937 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
939 return -1;
942 #endif
943 #endif /* CONFIG_TCC_BACKTRACE */
944 /* ------------------------------------------------------------- */
945 #ifdef CONFIG_TCC_STATIC
947 /* dummy function for profiling */
948 ST_FUNC void *dlopen(const char *filename, int flag)
950 return NULL;
953 ST_FUNC void dlclose(void *p)
957 ST_FUNC const char *dlerror(void)
959 return "error";
962 typedef struct TCCSyms {
963 char *str;
964 void *ptr;
965 } TCCSyms;
968 /* add the symbol you want here if no dynamic linking is done */
969 static TCCSyms tcc_syms[] = {
970 #if !defined(CONFIG_TCCBOOT)
971 #define TCCSYM(a) { #a, &a, },
972 TCCSYM(printf)
973 TCCSYM(fprintf)
974 TCCSYM(fopen)
975 TCCSYM(fclose)
976 #undef TCCSYM
977 #endif
978 { NULL, NULL },
981 ST_FUNC void *dlsym(void *handle, const char *symbol)
983 TCCSyms *p;
984 p = tcc_syms;
985 while (p->str != NULL) {
986 if (!strcmp(p->str, symbol))
987 return p->ptr;
988 p++;
990 return NULL;
993 #endif /* CONFIG_TCC_STATIC */
994 #endif /* TCC_IS_NATIVE */
995 /* ------------------------------------------------------------- */