Long double Constant problem
[tinycc.git] / tccrun.c
blob1d753b0e175bd563003d3a3875ca6b6c85faf100
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 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 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)(size*2));
98 ptr_diff = (char*)prx - (char*)ptr;
99 close(fd);
100 //printf("map %p %p %p\n", ptr, prx, (void*)ptr_diff);
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 #else
119 #ifdef _WIN64
120 win64_del_function_table(*(void**)s1->runtime_mem[i]);
121 #endif
122 tcc_free(s1->runtime_mem[i]);
123 #endif
125 tcc_free(s1->runtime_mem);
128 static void run_cdtors(TCCState *s1, const char *start, const char *end,
129 int argc, char **argv, char **envp)
131 void **a = (void **)get_sym_addr(s1, start, 0, 0);
132 void **b = (void **)get_sym_addr(s1, end, 0, 0);
133 while (a != b)
134 ((void(*)(int, char **, char **))*a++)(argc, argv, envp);
137 /* launch the compiled program with the given arguments */
138 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
140 int (*prog_main)(int, char **, char **), ret;
141 #ifdef CONFIG_TCC_BACKTRACE
142 rt_context *rc = &g_rtctxt;
143 #endif
145 #if defined(__APPLE__) || defined(__FreeBSD__)
146 char **envp = NULL;
147 #elif defined(__OpenBSD__) || defined(__NetBSD__)
148 extern char **environ;
149 char **envp = environ;
150 #else
151 char **envp = environ;
152 #endif
154 s1->runtime_main = s1->nostdlib ? "_start" : "main";
155 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1))
156 return 0;
157 #ifdef CONFIG_TCC_BACKTRACE
158 if (s1->do_debug)
159 tcc_add_symbol(s1, "exit", rt_exit);
160 #endif
161 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
162 return -1;
163 prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1);
165 #ifdef CONFIG_TCC_BACKTRACE
166 memset(rc, 0, sizeof *rc);
167 if (s1->do_debug) {
168 void *p;
169 rc->stab_sym = (Stab_Sym *)stab_section->data;
170 rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
171 rc->stab_str = (char *)stab_section->link->data;
172 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
173 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
174 rc->elf_str = (char *)symtab_section->link->data;
175 #if PTR_SIZE == 8
176 rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL;
177 #endif
178 rc->top_func = tcc_get_symbol(s1, "main");
179 rc->num_callers = s1->rt_num_callers;
180 rc->do_jmp = 1;
181 if ((p = tcc_get_symbol(s1, "__rt_error")))
182 *(void**)p = _rt_error;
183 #ifdef CONFIG_TCC_BCHECK
184 if (s1->do_bounds_check) {
185 if ((p = tcc_get_symbol(s1, "__bound_init")))
186 ((void(*)(addr_t, int))p)(bounds_section->sh_addr, 1);
188 #endif
189 set_exception_handler();
191 #endif
193 errno = 0; /* clean errno value */
194 fflush(stdout);
195 fflush(stderr);
196 /* These aren't C symbols, so don't need leading underscore handling. */
197 run_cdtors(s1, "__init_array_start", "__init_array_end", argc, argv, envp);
198 #ifdef CONFIG_TCC_BACKTRACE
199 if (!rc->do_jmp || !(ret = setjmp(rc->jmp_buf)))
200 #endif
202 ret = prog_main(argc, argv, envp);
204 run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL);
205 if ((s1->dflag & 16) && ret)
206 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
207 return ret;
210 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
211 /* To avoid that x86 processors would reload cached instructions
212 each time when data is written in the near, we need to make
213 sure that code and data do not share the same 64 byte unit */
214 #define RUN_SECTION_ALIGNMENT 63
215 #else
216 #define RUN_SECTION_ALIGNMENT 0
217 #endif
219 /* relocate code. Return -1 on error, required size if ptr is NULL,
220 otherwise copy code into buffer passed by the caller */
221 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
223 Section *s;
224 unsigned offset, length, align, max_align, i, k, f;
225 addr_t mem, addr;
227 if (NULL == ptr) {
228 s1->nb_errors = 0;
229 #ifdef TCC_TARGET_PE
230 pe_output_file(s1, NULL);
231 #else
232 tcc_add_runtime(s1);
233 resolve_common_syms(s1);
234 build_got_entries(s1);
235 #endif
236 if (s1->nb_errors)
237 return -1;
240 offset = max_align = 0, mem = (addr_t)ptr;
241 #ifdef _WIN64
242 offset += sizeof (void*); /* space for function_table pointer */
243 #endif
244 for (k = 0; k < 2; ++k) {
245 f = 0, addr = k ? mem : mem + ptr_diff;
246 for(i = 1; i < s1->nb_sections; i++) {
247 s = s1->sections[i];
248 if (0 == (s->sh_flags & SHF_ALLOC))
249 continue;
250 if (k != !(s->sh_flags & SHF_EXECINSTR))
251 continue;
252 align = s->sh_addralign - 1;
253 if (++f == 1 && align < RUN_SECTION_ALIGNMENT)
254 align = RUN_SECTION_ALIGNMENT;
255 if (max_align < align)
256 max_align = align;
257 offset += -(addr + offset) & align;
258 s->sh_addr = mem ? addr + offset : 0;
259 offset += s->data_offset;
260 #if 0
261 if (mem)
262 printf("%-16s %p len %04x align %2d\n",
263 s->name, (void*)s->sh_addr, (unsigned)s->data_offset, align + 1);
264 #endif
268 /* relocate symbols */
269 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
270 if (s1->nb_errors)
271 return -1;
273 if (0 == mem)
274 return offset + max_align;
276 #ifdef TCC_TARGET_PE
277 s1->pe_imagebase = mem;
278 #endif
280 /* relocate each section */
281 for(i = 1; i < s1->nb_sections; i++) {
282 s = s1->sections[i];
283 if (s->reloc)
284 relocate_section(s1, s);
286 #if !defined(TCC_TARGET_PE) || defined(TCC_TARGET_MACHO)
287 relocate_plt(s1);
288 #endif
290 for(i = 1; i < s1->nb_sections; i++) {
291 s = s1->sections[i];
292 if (0 == (s->sh_flags & SHF_ALLOC))
293 continue;
294 length = s->data_offset;
295 ptr = (void*)s->sh_addr;
296 if (s->sh_flags & SHF_EXECINSTR)
297 ptr = (char*)((addr_t)ptr - ptr_diff);
298 if (NULL == s->data || s->sh_type == SHT_NOBITS)
299 memset(ptr, 0, length);
300 else
301 memcpy(ptr, s->data, length);
302 /* mark executable sections as executable in memory */
303 if (s->sh_flags & SHF_EXECINSTR)
304 set_pages_executable(s1, (char*)((addr_t)ptr + ptr_diff), length);
307 #ifdef _WIN64
308 *(void**)mem = win64_add_function_table(s1);
309 #endif
311 return 0;
314 /* ------------------------------------------------------------- */
315 /* allow to run code in memory */
317 static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length)
319 #ifdef _WIN32
320 unsigned long old_protect;
321 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
322 #else
323 void __clear_cache(void *beginning, void *end);
324 # ifndef HAVE_SELINUX
325 addr_t start, end;
326 start = (addr_t)ptr & ~(PAGESIZE - 1);
327 end = (addr_t)ptr + length;
328 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
329 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
330 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
331 # endif
332 /* XXX: BSD sometimes dump core with bad system call */
333 # if (defined(TCC_TARGET_ARM) && \
334 !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)) || \
335 defined(TCC_TARGET_ARM64)
336 __clear_cache(ptr, (char *)ptr + length);
337 # endif
338 #endif
341 #ifdef _WIN64
342 static void *win64_add_function_table(TCCState *s1)
344 void *p = NULL;
345 if (s1->uw_pdata) {
346 p = (void*)s1->uw_pdata->sh_addr;
347 RtlAddFunctionTable(
348 (RUNTIME_FUNCTION*)p,
349 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
350 s1->pe_imagebase
352 s1->uw_pdata = NULL;
354 return p;
357 static void win64_del_function_table(void *p)
359 if (p) {
360 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
363 #endif
364 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
365 /* ------------------------------------------------------------- */
366 #ifdef CONFIG_TCC_BACKTRACE
368 static int rt_vprintf(const char *fmt, va_list ap)
370 int ret = vfprintf(stderr, fmt, ap);
371 fflush(stderr);
372 return ret;
375 static int rt_printf(const char *fmt, ...)
377 va_list ap;
378 int r;
379 va_start(ap, fmt);
380 r = rt_vprintf(fmt, ap);
381 va_end(ap);
382 return r;
385 #define INCLUDE_STACK_SIZE 32
387 /* print the position in the source file of PC value 'pc' by reading
388 the stabs debug information */
389 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
390 const char *msg, const char *skip)
392 char func_name[128];
393 addr_t func_addr, last_pc, pc;
394 const char *incl_files[INCLUDE_STACK_SIZE];
395 int incl_index, last_incl_index, len, last_line_num, i;
396 const char *str, *p;
397 ElfW(Sym) *esym;
398 Stab_Sym *sym;
400 next:
401 func_name[0] = '\0';
402 func_addr = 0;
403 incl_index = 0;
404 last_pc = (addr_t)-1;
405 last_line_num = 1;
406 last_incl_index = 0;
408 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
409 str = rc->stab_str + sym->n_strx;
410 pc = sym->n_value;
412 switch(sym->n_type) {
413 case N_SLINE:
414 if (func_addr)
415 goto rel_pc;
416 case N_SO:
417 case N_SOL:
418 goto abs_pc;
419 case N_FUN:
420 if (sym->n_strx == 0) /* end of function */
421 goto rel_pc;
422 abs_pc:
423 #if PTR_SIZE == 8
424 /* Stab_Sym.n_value is only 32bits */
425 pc += rc->prog_base;
426 #endif
427 goto check_pc;
428 rel_pc:
429 pc += func_addr;
430 check_pc:
431 if (pc >= wanted_pc && wanted_pc >= last_pc)
432 goto found;
433 break;
436 switch(sym->n_type) {
437 /* function start or end */
438 case N_FUN:
439 if (sym->n_strx == 0)
440 goto reset_func;
441 p = strchr(str, ':');
442 if (0 == p || (len = p - str + 1, len > sizeof func_name))
443 len = sizeof func_name;
444 pstrcpy(func_name, len, str);
445 func_addr = pc;
446 break;
447 /* line number info */
448 case N_SLINE:
449 last_pc = pc;
450 last_line_num = sym->n_desc;
451 last_incl_index = incl_index;
452 break;
453 /* include files */
454 case N_BINCL:
455 if (incl_index < INCLUDE_STACK_SIZE)
456 incl_files[incl_index++] = str;
457 break;
458 case N_EINCL:
459 if (incl_index > 1)
460 incl_index--;
461 break;
462 /* start/end of translation unit */
463 case N_SO:
464 incl_index = 0;
465 if (sym->n_strx) {
466 /* do not add path */
467 len = strlen(str);
468 if (len > 0 && str[len - 1] != '/')
469 incl_files[incl_index++] = str;
471 reset_func:
472 func_name[0] = '\0';
473 func_addr = 0;
474 last_pc = (addr_t)-1;
475 break;
476 /* alternative file name (from #line or #include directives) */
477 case N_SOL:
478 if (incl_index)
479 incl_files[incl_index-1] = str;
480 break;
484 func_name[0] = '\0';
485 func_addr = 0;
486 last_incl_index = 0;
488 /* we try symtab symbols (no line number info) */
489 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
490 int type = ELFW(ST_TYPE)(esym->st_info);
491 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
492 if (wanted_pc >= esym->st_value &&
493 wanted_pc < esym->st_value + esym->st_size) {
494 pstrcpy(func_name, sizeof(func_name),
495 rc->elf_str + esym->st_name);
496 func_addr = esym->st_value;
497 goto found;
502 if ((rc = rc->next))
503 goto next;
505 found:
506 i = last_incl_index;
507 if (i > 0) {
508 str = incl_files[--i];
509 if (skip[0] && strstr(str, skip))
510 return (addr_t)-1;
511 rt_printf("%s:%d: ", str, last_line_num);
512 } else
513 rt_printf("%08llx : ", (long long)wanted_pc);
514 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
515 #if 0
516 if (--i >= 0) {
517 rt_printf(" (included from ");
518 for (;;) {
519 rt_printf("%s", incl_files[i]);
520 if (--i < 0)
521 break;
522 rt_printf(", ");
524 rt_printf(")");
526 #endif
527 return func_addr;
530 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level);
532 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap)
534 rt_context *rc = &g_rtctxt;
535 addr_t pc = 0;
536 char skip[100];
537 int i, level, ret, n;
538 const char *a, *b, *msg;
540 if (fp) {
541 /* we're called from tcc_backtrace. */
542 rc->fp = (addr_t)fp;
543 rc->ip = (addr_t)ip;
544 msg = "";
545 } else {
546 /* we're called from signal/exception handler */
547 msg = "RUNTIME ERROR: ";
550 skip[0] = 0;
551 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
552 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
553 memcpy(skip, a, b - a), skip[b - a] = 0;
554 fmt = b + 1;
557 n = rc->num_callers ? rc->num_callers : 6;
558 for (i = level = 0; level < n; i++) {
559 ret = rt_get_caller_pc(&pc, rc, i);
560 a = "%s";
561 if (ret != -1) {
562 pc = rt_printline(rc, pc, level ? "by" : "at", skip);
563 if (pc == (addr_t)-1)
564 continue;
565 a = ": %s";
567 if (level == 0) {
568 rt_printf(a, msg);
569 rt_vprintf(fmt, ap);
570 } else if (ret == -1)
571 break;
572 rt_printf("\n");
573 if (ret == -1 || (pc == (addr_t)rc->top_func && pc))
574 break;
575 ++level;
578 rc->ip = rc->fp = 0;
579 return 0;
582 /* emit a run time error at position 'pc' */
583 static int rt_error(const char *fmt, ...)
585 va_list ap;
586 int ret;
587 va_start(ap, fmt);
588 ret = _rt_error(0, 0, fmt, ap);
589 va_end(ap);
590 return ret;
593 static void rt_exit(int code)
595 rt_context *rc = &g_rtctxt;
596 if (rc->do_jmp)
597 longjmp(rc->jmp_buf, code ? code : 256);
598 exit(code);
601 /* ------------------------------------------------------------- */
603 #ifndef _WIN32
604 # include <signal.h>
605 # ifndef __OpenBSD__
606 # include <sys/ucontext.h>
607 # endif
608 #else
609 # define ucontext_t CONTEXT
610 #endif
612 /* translate from ucontext_t* to internal rt_context * */
613 static void rt_getcontext(ucontext_t *uc, rt_context *rc)
615 #if defined _WIN64
616 rc->ip = uc->Rip;
617 rc->fp = uc->Rbp;
618 rc->sp = uc->Rsp;
619 #elif defined _WIN32
620 rc->ip = uc->Eip;
621 rc->fp = uc->Ebp;
622 rc->sp = uc->Esp;
623 #elif defined __i386__
624 # if defined(__APPLE__)
625 rc->ip = uc->uc_mcontext->__ss.__eip;
626 rc->fp = uc->uc_mcontext->__ss.__ebp;
627 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
628 rc->ip = uc->uc_mcontext.mc_eip;
629 rc->fp = uc->uc_mcontext.mc_ebp;
630 # elif defined(__dietlibc__)
631 rc->ip = uc->uc_mcontext.eip;
632 rc->fp = uc->uc_mcontext.ebp;
633 # elif defined(__NetBSD__)
634 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
635 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
636 # elif defined(__OpenBSD__)
637 rc->ip = uc->sc_eip;
638 rc->fp = uc->sc_ebp;
639 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
640 rc->ip = uc->uc_mcontext.gregs[EIP];
641 rc->fp = uc->uc_mcontext.gregs[EBP];
642 # else
643 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
644 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
645 # endif
646 #elif defined(__x86_64__)
647 # if defined(__APPLE__)
648 rc->ip = uc->uc_mcontext->__ss.__rip;
649 rc->fp = uc->uc_mcontext->__ss.__rbp;
650 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
651 rc->ip = uc->uc_mcontext.mc_rip;
652 rc->fp = uc->uc_mcontext.mc_rbp;
653 # elif defined(__NetBSD__)
654 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
655 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
656 # elif defined(__OpenBSD__)
657 rc->ip = uc->sc_rip;
658 rc->fp = uc->sc_rbp;
659 # else
660 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
661 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
662 # endif
663 #elif defined(__arm__) && defined(__NetBSD__)
664 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
665 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
666 #elif defined(__arm__) && defined(__OpenBSD__)
667 rc->ip = uc->sc_pc;
668 rc->fp = uc->sc_r11;
669 #elif defined(__arm__) && defined(__FreeBSD__)
670 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
671 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
672 #elif defined(__arm__)
673 rc->ip = uc->uc_mcontext.arm_pc;
674 rc->fp = uc->uc_mcontext.arm_fp;
675 #elif defined(__aarch64__) && defined(__FreeBSD__)
676 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
677 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
678 #elif defined(__aarch64__) && defined(__NetBSD__)
679 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
680 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
681 #elif defined(__aarch64__) && defined(__OpenBSD__)
682 rc->ip = uc->sc_elr;
683 rc->fp = uc->sc_x[29];
684 #elif defined(__aarch64__)
685 rc->ip = uc->uc_mcontext.pc;
686 rc->fp = uc->uc_mcontext.regs[29];
687 #elif defined(__riscv)
688 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
689 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
690 #endif
693 /* ------------------------------------------------------------- */
694 #ifndef _WIN32
695 /* signal handler for fatal errors */
696 static void sig_error(int signum, siginfo_t *siginf, void *puc)
698 rt_context *rc = &g_rtctxt;
699 rt_getcontext(puc, rc);
701 switch(signum) {
702 case SIGFPE:
703 switch(siginf->si_code) {
704 case FPE_INTDIV:
705 case FPE_FLTDIV:
706 rt_error("division by zero");
707 break;
708 default:
709 rt_error("floating point exception");
710 break;
712 break;
713 case SIGBUS:
714 case SIGSEGV:
715 rt_error("invalid memory access");
716 break;
717 case SIGILL:
718 rt_error("illegal instruction");
719 break;
720 case SIGABRT:
721 rt_error("abort() called");
722 break;
723 default:
724 rt_error("caught signal %d", signum);
725 break;
727 rt_exit(255);
730 #ifndef SA_SIGINFO
731 # define SA_SIGINFO 0x00000004u
732 #endif
734 /* Generate a stack backtrace when a CPU exception occurs. */
735 static void set_exception_handler(void)
737 struct sigaction sigact;
738 /* install TCC signal handlers to print debug info on fatal
739 runtime errors */
740 sigemptyset (&sigact.sa_mask);
741 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
742 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
743 sigact.sa_flags |= SA_ONSTACK;
744 #endif
745 sigact.sa_sigaction = sig_error;
746 sigemptyset(&sigact.sa_mask);
747 sigaction(SIGFPE, &sigact, NULL);
748 sigaction(SIGILL, &sigact, NULL);
749 sigaction(SIGSEGV, &sigact, NULL);
750 sigaction(SIGBUS, &sigact, NULL);
751 sigaction(SIGABRT, &sigact, NULL);
752 #if 0//def SIGSTKSZ
753 /* This allows stack overflow to be reported instead of a SEGV */
755 stack_t ss;
756 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
758 ss.ss_sp = stack;
759 ss.ss_size = SIGSTKSZ;
760 ss.ss_flags = 0;
761 sigaltstack(&ss, NULL);
763 #endif
766 #else /* WIN32 */
768 /* signal handler for fatal errors */
769 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
771 rt_context *rc = &g_rtctxt;
772 unsigned code;
773 rt_getcontext(ex_info->ContextRecord, rc);
775 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
776 case EXCEPTION_ACCESS_VIOLATION:
777 rt_error("invalid memory access");
778 break;
779 case EXCEPTION_STACK_OVERFLOW:
780 rt_error("stack overflow");
781 break;
782 case EXCEPTION_INT_DIVIDE_BY_ZERO:
783 rt_error("division by zero");
784 break;
785 case EXCEPTION_BREAKPOINT:
786 case EXCEPTION_SINGLE_STEP:
787 rc->ip = *(addr_t*)rc->sp;
788 rt_error("breakpoint/single-step exception:");
789 return EXCEPTION_CONTINUE_SEARCH;
790 default:
791 rt_error("caught exception %08x", code);
792 break;
794 if (rc->do_jmp)
795 rt_exit(255);
796 return EXCEPTION_EXECUTE_HANDLER;
799 /* Generate a stack backtrace when a CPU exception occurs. */
800 static void set_exception_handler(void)
802 SetUnhandledExceptionFilter(cpu_exception_handler);
805 #endif
807 /* ------------------------------------------------------------- */
808 /* return the PC at frame level 'level'. Return negative if not found */
809 #if defined(__i386__) || defined(__x86_64__)
810 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
812 addr_t ip, fp;
813 if (level == 0) {
814 ip = rc->ip;
815 } else {
816 ip = 0;
817 fp = rc->fp;
818 while (--level) {
819 /* XXX: check address validity with program info */
820 if (fp <= 0x1000)
821 break;
822 fp = ((addr_t *)fp)[0];
824 if (fp > 0x1000)
825 ip = ((addr_t *)fp)[1];
827 if (ip <= 0x1000)
828 return -1;
829 *paddr = ip;
830 return 0;
833 #elif defined(__arm__)
834 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
836 /* XXX: only supports linux/bsd */
837 #if !defined(__linux__) && \
838 !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
839 return -1;
840 #else
841 if (level == 0) {
842 *paddr = rc->ip;
843 } else {
844 addr_t fp = rc->fp;
845 while (--level)
846 fp = ((addr_t *)fp)[0];
847 *paddr = ((addr_t *)fp)[2];
849 return 0;
850 #endif
853 #elif defined(__aarch64__)
854 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
856 if (level == 0) {
857 *paddr = rc->ip;
858 } else {
859 addr_t *fp = (addr_t*)rc->fp;
860 while (--level)
861 fp = (addr_t *)fp[0];
862 *paddr = fp[1];
864 return 0;
867 #elif defined(__riscv)
868 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
870 if (level == 0) {
871 *paddr = rc->ip;
872 } else {
873 addr_t *fp = (addr_t*)rc->fp;
874 while (--level && fp >= (addr_t*)0x1000)
875 fp = (addr_t *)fp[-2];
876 if (fp < (addr_t*)0x1000)
877 return -1;
878 *paddr = fp[-1];
880 return 0;
883 #else
884 #warning add arch specific rt_get_caller_pc()
885 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
887 return -1;
890 #endif
891 #endif /* CONFIG_TCC_BACKTRACE */
892 /* ------------------------------------------------------------- */
893 #ifdef CONFIG_TCC_STATIC
895 /* dummy function for profiling */
896 ST_FUNC void *dlopen(const char *filename, int flag)
898 return NULL;
901 ST_FUNC void dlclose(void *p)
905 ST_FUNC const char *dlerror(void)
907 return "error";
910 typedef struct TCCSyms {
911 char *str;
912 void *ptr;
913 } TCCSyms;
916 /* add the symbol you want here if no dynamic linking is done */
917 static TCCSyms tcc_syms[] = {
918 #if !defined(CONFIG_TCCBOOT)
919 #define TCCSYM(a) { #a, &a, },
920 TCCSYM(printf)
921 TCCSYM(fprintf)
922 TCCSYM(fopen)
923 TCCSYM(fclose)
924 #undef TCCSYM
925 #endif
926 { NULL, NULL },
929 ST_FUNC void *dlsym(void *handle, const char *symbol)
931 TCCSyms *p;
932 p = tcc_syms;
933 while (p->str != NULL) {
934 if (!strcmp(p->str, symbol))
935 return p->ptr;
936 p++;
938 return NULL;
941 #endif /* CONFIG_TCC_STATIC */
942 #endif /* TCC_IS_NATIVE */
943 /* ------------------------------------------------------------- */