fix UB in constant folding of double -> signed integer conversion
[tinycc.git] / tccrun.c
blobb0cb4dba9bdce4688100726af61c58133f30e85b
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 /* runtime debug info block */
28 typedef struct rt_context
30 /* tccelf.c:tcc_add_btstub() wants these in that order: */
31 union {
32 struct {
33 Stab_Sym *stab_sym;
34 Stab_Sym *stab_sym_end;
35 char *stab_str;
37 struct {
38 unsigned char *dwarf_line;
39 unsigned char *dwarf_line_end;
40 unsigned char *dwarf_line_str;
43 ElfW(Sym) *esym_start;
44 ElfW(Sym) *esym_end;
45 char *elf_str;
46 // 6 * PTR_SIZE
47 addr_t prog_base;
48 void *bounds_start;
49 void *top_func;
50 struct rt_context *next;
51 // 10 * PTR_SIZE
52 int num_callers;
53 int dwarf;
54 } rt_context;
56 /* linked list of rt_contexts */
57 static rt_context *g_rc;
58 static int signal_set;
59 static void set_exception_handler(void);
60 #endif /* def CONFIG_TCC_BACKTRACE */
62 typedef struct rt_frame {
63 addr_t ip, fp, sp;
64 } rt_frame;
66 static TCCState *g_s1;
67 /* semaphore to protect it */
68 TCC_SEM(static rt_sem);
69 static void rt_wait_sem(void) { WAIT_SEM(&rt_sem); }
70 static void rt_post_sem(void) { POST_SEM(&rt_sem); }
71 static int rt_get_caller_pc(addr_t *paddr, rt_frame *f, int level);
72 static void rt_exit(rt_frame *f, int code);
74 /* ------------------------------------------------------------- */
75 /* defined when included from lib/bt-exe.c */
76 #ifndef CONFIG_TCC_BACKTRACE_ONLY
78 #ifndef _WIN32
79 # include <sys/mman.h>
80 #endif
82 static int protect_pages(void *ptr, unsigned long length, int mode);
83 static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff);
84 static void st_link(TCCState *s1);
85 static void st_unlink(TCCState *s1);
86 #ifdef CONFIG_TCC_BACKTRACE
87 static int _tcc_backtrace(rt_frame *f, const char *fmt, va_list ap);
88 #endif
89 #ifdef _WIN64
90 static void *win64_add_function_table(TCCState *s1);
91 static void win64_del_function_table(void *);
92 #endif
94 #if !defined PAGESIZE
95 # if defined _SC_PAGESIZE
96 # define PAGESIZE sysconf(_SC_PAGESIZE)
97 # elif defined __APPLE__
98 # include <libkern/OSCacheControl.h>
99 # define PAGESIZE getpagesize()
100 # else
101 # define PAGESIZE 4096
102 # endif
103 #endif
105 #define PAGEALIGN(n) ((addr_t)n + (-(addr_t)n & (PAGESIZE-1)))
107 #if !_WIN32 && !__APPLE__
108 //#define HAVE_SELINUX 1
109 #endif
111 static int rt_mem(TCCState *s1, int size)
113 void *ptr;
114 int ptr_diff = 0;
115 #ifdef HAVE_SELINUX
116 /* Using mmap instead of malloc */
117 void *prw;
118 char tmpfname[] = "/tmp/.tccrunXXXXXX";
119 int fd = mkstemp(tmpfname);
120 unlink(tmpfname);
121 ftruncate(fd, size);
123 ptr = mmap(NULL, size * 2, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
124 /* mmap RW memory at fixed distance */
125 prw = mmap((char*)ptr + size, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, 0);
126 close(fd);
127 if (ptr == MAP_FAILED || prw == MAP_FAILED)
128 return tcc_error_noabort("tccrun: could not map memory");
129 ptr_diff = (char*)prw - (char*)ptr; /* = size; */
130 //printf("map %p %p %p\n", ptr, prw, (void*)ptr_diff);
131 size *= 2;
132 #else
133 ptr = tcc_malloc(size += PAGESIZE); /* one extra page to align malloc memory */
134 #endif
135 s1->run_ptr = ptr;
136 s1->run_size = size;
137 return ptr_diff;
140 /* ------------------------------------------------------------- */
141 /* Do all relocations (needed before using tcc_get_symbol())
142 Returns -1 on error. */
144 LIBTCCAPI int tcc_relocate(TCCState *s1)
146 int size, ret, ptr_diff;
148 if (s1->run_ptr)
149 exit(tcc_error_noabort("'tcc_relocate()' twice is no longer supported"));
150 #ifdef CONFIG_TCC_BACKTRACE
151 if (s1->do_backtrace)
152 tcc_add_symbol(s1, "_tcc_backtrace", _tcc_backtrace); /* for bt-log.c */
153 #endif
154 size = tcc_relocate_ex(s1, NULL, 0);
155 if (size < 0)
156 return -1;
157 ptr_diff = rt_mem(s1, size);
158 if (ptr_diff < 0)
159 return -1;
160 ret = tcc_relocate_ex(s1, s1->run_ptr, ptr_diff);
161 if (ret == 0)
162 st_link(s1);
163 return ret;
166 ST_FUNC void tcc_run_free(TCCState *s1)
168 unsigned size;
169 void *ptr;
170 int i;
172 /* free any loaded DLLs */
173 for ( i = 0; i < s1->nb_loaded_dlls; i++) {
174 DLLReference *ref = s1->loaded_dlls[i];
175 if ( ref->handle )
176 #ifdef _WIN32
177 FreeLibrary((HMODULE)ref->handle);
178 #else
179 dlclose(ref->handle);
180 #endif
182 /* unmap or unprotect and free memory */
183 ptr = s1->run_ptr;
184 if (NULL == ptr)
185 return;
186 st_unlink(s1);
187 size = s1->run_size;
188 #ifdef HAVE_SELINUX
189 munmap(ptr, size);
190 #else
191 /* unprotect memory to make it usable for malloc again */
192 protect_pages((void*)PAGEALIGN(ptr), size - PAGESIZE, 2 /*rw*/);
193 # ifdef _WIN64
194 win64_del_function_table(s1->run_function_table);
195 # endif
196 tcc_free(ptr);
197 #endif
200 /* launch the compiled program with the given arguments */
201 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
203 int (*prog_main)(int, char **, char **), ret;
204 const char *top_sym;
205 jmp_buf main_jb;
207 #if defined(__APPLE__) || defined(__FreeBSD__)
208 char **envp = NULL;
209 #elif defined(__OpenBSD__) || defined(__NetBSD__)
210 extern char **environ;
211 char **envp = environ;
212 #else
213 char **envp = environ;
214 #endif
216 /* tcc -dt -run ... nothing to do if no main() */
217 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, "main", 0, 1))
218 return 0;
220 tcc_add_symbol(s1, "__rt_exit", rt_exit);
221 if (s1->nostdlib) {
222 s1->run_main = top_sym = "_start";
223 } else {
224 tcc_add_support(s1, "runmain.o");
225 s1->run_main = "_runmain";
226 top_sym = "main";
228 if (tcc_relocate(s1) < 0)
229 return -1;
231 prog_main = (void*)get_sym_addr(s1, s1->run_main, 1, 1);
232 if ((addr_t)-1 == (addr_t)prog_main)
233 return -1;
234 errno = 0; /* clean errno value */
235 fflush(stdout);
236 fflush(stderr);
238 ret = tcc_setjmp(s1, main_jb, tcc_get_symbol(s1, top_sym));
239 if (0 == ret)
240 ret = prog_main(argc, argv, envp);
241 else if (256 == ret)
242 ret = 0;
244 if (s1->dflag & 16 && ret) /* tcc -dt -run ... */
245 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
246 return ret;
249 /* ------------------------------------------------------------- */
250 /* remove all STB_LOCAL symbols */
251 static void cleanup_symbols(TCCState *s1)
253 Section *s = s1->symtab;
254 int sym_index, end_sym = s->data_offset / sizeof (ElfSym);
255 /* reset symtab */
256 s->data_offset = s->link->data_offset = s->hash->data_offset = 0;
257 init_symtab(s);
258 /* add global symbols again */
259 for (sym_index = 1; sym_index < end_sym; ++sym_index) {
260 ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index];
261 const char *name = (char *)s->link->data + sym->st_name;
262 if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)
263 continue;
264 //printf("sym %s\n", name);
265 put_elf_sym(s, sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx, name);
269 /* free all sections except symbols */
270 static void cleanup_sections(TCCState *s1)
272 struct { Section **secs; int nb_secs; } *p = (void*)&s1->sections;
273 int i, f = 2;
274 do {
275 for (i = --f; i < p->nb_secs; i++) {
276 Section *s = p->secs[i];
277 if (s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash) {
278 s->data = tcc_realloc(s->data, s->data_allocated = s->data_offset);
279 } else {
280 free_section(s), tcc_free(s), p->secs[i] = NULL;
283 } while (++p, f);
286 /* ------------------------------------------------------------- */
287 /* 0 = .text rwx other rw (memory >= 2 pages a 4096 bytes) */
288 /* 1 = .text rx other rw (memory >= 3 pages) */
289 /* 2 = .text rx .rdata ro .data/.bss rw (memory >= 4 pages) */
291 /* Some targets implement secutiry options that do not allow write in
292 executable code. These targets need CONFIG_RUNMEM_RO=1.
293 The disadvantage of this is that it requires a little bit more memory. */
295 #ifndef CONFIG_RUNMEM_RO
296 # ifdef __APPLE__
297 # define CONFIG_RUNMEM_RO 1
298 # else
299 # define CONFIG_RUNMEM_RO 0
300 # endif
301 #endif
303 /* relocate code. Return -1 on error, required size if ptr is NULL,
304 otherwise copy code into buffer passed by the caller */
305 static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff)
307 Section *s;
308 unsigned offset, length, align, i, k, f;
309 unsigned n, copy;
310 addr_t mem, addr;
312 if (NULL == ptr) {
313 #ifdef TCC_TARGET_PE
314 pe_output_file(s1, NULL);
315 #else
316 tcc_add_runtime(s1);
317 resolve_common_syms(s1);
318 build_got_entries(s1, 0);
319 #endif
322 offset = copy = 0;
323 mem = (addr_t)ptr;
324 redo:
325 if (s1->verbose == 2 && copy)
326 printf(&"-----------------------------------------------------\n"[PTR_SIZE*2 - 8]);
327 if (s1->nb_errors)
328 return -1;
329 if (copy == 3)
330 return 0;
332 for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
333 n = 0; addr = 0;
334 for(i = 1; i < s1->nb_sections; i++) {
335 static const char shf[] = {
336 SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
338 s = s1->sections[i];
339 if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
340 continue;
341 length = s->data_offset;
342 if (copy == 2) {
343 if (addr == 0)
344 addr = s->sh_addr;
345 n = (s->sh_addr - addr) + length;
346 continue;
348 if (copy) { /* final step: copy section data to memory */
349 if (s1->verbose == 2)
350 printf("%d: %-16s %p len %05x align %04x\n",
351 k, s->name, (void*)s->sh_addr, length, s->sh_addralign);
352 ptr = (void*)s->sh_addr;
353 if (k == 0)
354 ptr = (void*)(s->sh_addr + ptr_diff);
355 if (NULL == s->data || s->sh_type == SHT_NOBITS)
356 memset(ptr, 0, length);
357 else
358 memcpy(ptr, s->data, length);
359 continue;
362 align = s->sh_addralign;
363 if (++n == 1) {
364 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
365 /* To avoid that x86 processors would reload cached instructions
366 each time when data is written in the near, we need to make
367 sure that code and data do not share the same 64 byte unit */
368 if (align < 64)
369 align = 64;
370 #endif
371 /* start new page for different permissions */
372 if (k <= CONFIG_RUNMEM_RO)
373 align = PAGESIZE;
375 s->sh_addralign = align;
376 addr = k ? mem + ptr_diff : mem;
377 offset += -(addr + offset) & (align - 1);
378 s->sh_addr = mem ? addr + offset : 0;
379 offset += length;
381 if (copy == 2) { /* set permissions */
382 if (n == 0) /* no data */
383 continue;
384 #ifdef HAVE_SELINUX
385 if (k == 0) /* SHF_EXECINSTR has its own mapping */
386 continue;
387 #endif
388 f = k;
389 if (f >= CONFIG_RUNMEM_RO) {
390 if (f != 0)
391 continue;
392 f = 3; /* change only SHF_EXECINSTR to rwx */
394 n = PAGEALIGN(n);
395 if (s1->verbose == 2) {
396 printf("protect %3s %p len %05x\n",
397 &"rx\0ro\0rw\0rwx"[f*3], (void*)addr, (unsigned)n);
399 if (protect_pages((void*)addr, n, f) < 0)
400 return tcc_error_noabort(
401 "mprotect failed (did you mean to configure --with-selinux?)");
405 if (0 == mem)
406 return PAGEALIGN(offset);
408 if (++copy == 2) {
409 goto redo;
411 if (copy == 3) {
412 #ifdef _WIN64
413 s1->run_function_table = win64_add_function_table(s1);
414 #endif
415 /* remove local symbols and free sections except symtab */
416 cleanup_symbols(s1);
417 cleanup_sections(s1);
418 goto redo;
421 /* relocate symbols */
422 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
423 /* relocate sections */
424 #ifdef TCC_TARGET_PE
425 s1->pe_imagebase = mem;
426 #else
427 relocate_plt(s1);
428 #endif
429 relocate_sections(s1);
430 goto redo;
433 /* ------------------------------------------------------------- */
434 /* allow to run code in memory */
436 static int protect_pages(void *ptr, unsigned long length, int mode)
438 #ifdef _WIN32
439 static const unsigned char protect[] = {
440 PAGE_EXECUTE_READ,
441 PAGE_READONLY,
442 PAGE_READWRITE,
443 PAGE_EXECUTE_READWRITE
445 DWORD old;
446 if (!VirtualProtect(ptr, length, protect[mode], &old))
447 return -1;
448 #else
449 static const unsigned char protect[] = {
450 PROT_READ | PROT_EXEC,
451 PROT_READ,
452 PROT_READ | PROT_WRITE,
453 PROT_READ | PROT_WRITE | PROT_EXEC
455 if (mprotect(ptr, length, protect[mode]))
456 return -1;
457 /* XXX: BSD sometimes dump core with bad system call */
458 # if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
459 if (mode == 0 || mode == 3) {
460 void __clear_cache(void *beginning, void *end);
461 __clear_cache(ptr, (char *)ptr + length);
463 # endif
464 #endif
465 return 0;
468 #ifdef _WIN64
469 static void *win64_add_function_table(TCCState *s1)
471 void *p = NULL;
472 if (s1->uw_pdata) {
473 p = (void*)s1->uw_pdata->sh_addr;
474 RtlAddFunctionTable(
475 (RUNTIME_FUNCTION*)p,
476 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
477 s1->pe_imagebase
479 s1->uw_pdata = NULL;
481 return p;
484 static void win64_del_function_table(void *p)
486 if (p) {
487 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
490 #endif
492 static void bt_link(TCCState *s1)
494 #ifdef CONFIG_TCC_BACKTRACE
495 rt_context *rc;
496 void *p;
498 if (!s1->do_backtrace)
499 return;
500 rc = tcc_get_symbol(s1, "__rt_info");
501 if (!rc)
502 return;
503 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
504 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
505 rc->elf_str = (char *)symtab_section->link->data;
506 if (PTR_SIZE == 8 && !s1->dwarf)
507 rc->prog_base &= 0xffffffff00000000ULL;
508 #ifdef CONFIG_TCC_BCHECK
509 if (s1->do_bounds_check) {
510 if ((p = tcc_get_symbol(s1, "__bound_init")))
511 ((void(*)(void*,int))p)(rc->bounds_start, 1);
513 #endif
514 rc->next = g_rc, g_rc = rc, s1->rc = rc;
515 if (0 == signal_set)
516 set_exception_handler(), signal_set = 1;
517 #endif
520 static void st_link(TCCState *s1)
522 rt_wait_sem();
523 s1->next = g_s1, g_s1 = s1;
524 bt_link(s1);
525 rt_post_sem();
528 /* remove 'el' from 'list' */
529 static void ptr_unlink(void *list, void *e, unsigned next)
531 void **pp, **nn, *p;
532 for (pp = list; !!(p = *pp); pp = nn) {
533 nn = (void*)((char*)p + next); /* nn = &p->next; */
534 if (p == e) {
535 *pp = *nn;
536 break;
541 static void st_unlink(TCCState *s1)
543 rt_wait_sem();
544 #ifdef CONFIG_TCC_BACKTRACE
545 ptr_unlink(&g_rc, s1->rc, offsetof(rt_context, next));
546 #endif
547 ptr_unlink(&g_s1, s1, offsetof(TCCState, next));
548 rt_post_sem();
551 LIBTCCAPI void *_tcc_setjmp(TCCState *s1, void *p_jmp_buf, void *func, void *p_longjmp)
553 s1->run_lj = p_longjmp;
554 s1->run_jb = p_jmp_buf;
555 #ifdef CONFIG_TCC_BACKTRACE
556 if (s1->rc)
557 s1->rc->top_func = func;
558 #endif
559 return p_jmp_buf;
562 LIBTCCAPI void tcc_set_backtrace_func(TCCState *s1, void *data, TCCBtFunc *func)
564 s1->bt_func = func;
565 s1->bt_data = data;
568 static TCCState *rt_find_state(rt_frame *f)
570 TCCState *s;
571 int level;
572 addr_t pc;
574 s = g_s1;
575 if (NULL == s || NULL == s->next) {
576 /* play it safe in the simple case when there is only one state */
577 return s;
579 for (level = 0; level < 8; ++level) {
580 if (rt_get_caller_pc(&pc, f, level) < 0)
581 break;
582 for (s = g_s1; s; s = s->next) {
583 if (pc >= (addr_t)s->run_ptr
584 && pc < (addr_t)s->run_ptr + s->run_size)
585 return s;
588 return NULL;
591 static void rt_exit(rt_frame *f, int code)
593 TCCState *s;
594 rt_wait_sem();
595 s = rt_find_state(f);
596 rt_post_sem();
597 if (s && s->run_lj) {
598 if (code == 0)
599 code = 256;
600 ((void(*)(void*,int))s->run_lj)(s->run_jb, code);
602 exit(code);
605 /* ------------------------------------------------------------- */
606 #else // if defined CONFIG_TCC_BACKTRACE_ONLY
607 static void rt_exit(rt_frame *f, int code)
609 exit(code);
611 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
612 /* ------------------------------------------------------------- */
613 #ifdef CONFIG_TCC_BACKTRACE
615 static int rt_vprintf(const char *fmt, va_list ap)
617 int ret = vfprintf(stderr, fmt, ap);
618 fflush(stderr);
619 return ret;
622 static int rt_printf(const char *fmt, ...)
624 va_list ap;
625 int r;
626 va_start(ap, fmt);
627 r = rt_vprintf(fmt, ap);
628 va_end(ap);
629 return r;
632 static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr)
634 ElfW(Sym) *esym;
635 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
636 int type = ELFW(ST_TYPE)(esym->st_info);
637 if ((type == STT_FUNC || type == STT_GNU_IFUNC)
638 && wanted_pc >= esym->st_value
639 && wanted_pc < esym->st_value + esym->st_size) {
640 *func_addr = esym->st_value;
641 return rc->elf_str + esym->st_name;
644 return NULL;
647 typedef struct bt_info
649 char file[100];
650 int line;
651 char func[100];
652 addr_t func_pc;
653 } bt_info;
655 /* print the position in the source file of PC value 'pc' by reading
656 the stabs debug information */
657 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc, bt_info *bi)
659 char func_name[128];
660 addr_t func_addr, last_pc, pc;
661 const char *incl_files[INCLUDE_STACK_SIZE];
662 int incl_index, last_incl_index, len, last_line_num, i;
663 const char *str, *p;
664 Stab_Sym *sym;
666 func_name[0] = '\0';
667 func_addr = 0;
668 incl_index = 0;
669 last_pc = (addr_t)-1;
670 last_line_num = 1;
671 last_incl_index = 0;
673 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
674 str = rc->stab_str + sym->n_strx;
675 pc = sym->n_value;
677 switch(sym->n_type) {
678 case N_SLINE:
679 if (func_addr)
680 goto rel_pc;
681 case N_SO:
682 case N_SOL:
683 goto abs_pc;
684 case N_FUN:
685 if (sym->n_strx == 0) /* end of function */
686 goto rel_pc;
687 abs_pc:
688 #if PTR_SIZE == 8
689 /* Stab_Sym.n_value is only 32bits */
690 pc += rc->prog_base;
691 #endif
692 goto check_pc;
693 rel_pc:
694 pc += func_addr;
695 check_pc:
696 if (pc >= wanted_pc && wanted_pc >= last_pc)
697 goto found;
698 break;
701 switch(sym->n_type) {
702 /* function start or end */
703 case N_FUN:
704 if (sym->n_strx == 0)
705 goto reset_func;
706 p = strchr(str, ':');
707 if (0 == p || (len = p - str + 1, len > sizeof func_name))
708 len = sizeof func_name;
709 pstrcpy(func_name, len, str);
710 func_addr = pc;
711 break;
712 /* line number info */
713 case N_SLINE:
714 last_pc = pc;
715 last_line_num = sym->n_desc;
716 last_incl_index = incl_index;
717 break;
718 /* include files */
719 case N_BINCL:
720 if (incl_index < INCLUDE_STACK_SIZE)
721 incl_files[incl_index++] = str;
722 break;
723 case N_EINCL:
724 if (incl_index > 1)
725 incl_index--;
726 break;
727 /* start/end of translation unit */
728 case N_SO:
729 incl_index = 0;
730 if (sym->n_strx) {
731 /* do not add path */
732 len = strlen(str);
733 if (len > 0 && str[len - 1] != '/')
734 incl_files[incl_index++] = str;
736 reset_func:
737 func_name[0] = '\0';
738 func_addr = 0;
739 last_pc = (addr_t)-1;
740 break;
741 /* alternative file name (from #line or #include directives) */
742 case N_SOL:
743 if (incl_index)
744 incl_files[incl_index-1] = str;
745 break;
748 last_incl_index = 0, func_name[0] = 0, func_addr = 0;
749 found:
750 i = last_incl_index;
751 if (i > 0) {
752 pstrcpy(bi->file, sizeof bi->file, incl_files[--i]);
753 bi->line = last_line_num;
755 pstrcpy(bi->func, sizeof bi->func, func_name);
756 bi->func_pc = func_addr;
757 return func_addr;
760 /* ------------------------------------------------------------- */
761 /* rt_printline - dwarf version */
763 #define MAX_128 ((8 * sizeof (long long) + 6) / 7)
765 #define DIR_TABLE_SIZE (64)
766 #define FILE_TABLE_SIZE (512)
768 #define dwarf_read_1(ln,end) \
769 ((ln) < (end) ? *(ln)++ : 0)
770 #define dwarf_read_2(ln,end) \
771 ((ln) + 2 < (end) ? (ln) += 2, read16le((ln) - 2) : 0)
772 #define dwarf_read_4(ln,end) \
773 ((ln) + 4 < (end) ? (ln) += 4, read32le((ln) - 4) : 0)
774 #define dwarf_read_8(ln,end) \
775 ((ln) + 8 < (end) ? (ln) += 8, read64le((ln) - 8) : 0)
776 #define dwarf_ignore_type(ln, end) /* timestamp/size/md5/... */ \
777 switch (entry_format[j].form) { \
778 case DW_FORM_data1: (ln) += 1; break; \
779 case DW_FORM_data2: (ln) += 2; break; \
780 case DW_FORM_data4: (ln) += 3; break; \
781 case DW_FORM_data8: (ln) += 8; break; \
782 case DW_FORM_data16: (ln) += 16; break; \
783 case DW_FORM_udata: dwarf_read_uleb128(&(ln), (end)); break; \
784 default: goto next_line; \
787 static unsigned long long
788 dwarf_read_uleb128(unsigned char **ln, unsigned char *end)
790 unsigned char *cp = *ln;
791 unsigned long long retval = 0;
792 int i;
794 for (i = 0; i < MAX_128; i++) {
795 unsigned long long byte = dwarf_read_1(cp, end);
797 retval |= (byte & 0x7f) << (i * 7);
798 if ((byte & 0x80) == 0)
799 break;
801 *ln = cp;
802 return retval;
805 static long long
806 dwarf_read_sleb128(unsigned char **ln, unsigned char *end)
808 unsigned char *cp = *ln;
809 long long retval = 0;
810 int i;
812 for (i = 0; i < MAX_128; i++) {
813 unsigned long long byte = dwarf_read_1(cp, end);
815 retval |= (byte & 0x7f) << (i * 7);
816 if ((byte & 0x80) == 0) {
817 if ((byte & 0x40) && (i + 1) * 7 < 64)
818 retval |= -1LL << ((i + 1) * 7);
819 break;
822 *ln = cp;
823 return retval;
826 static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc, bt_info *bi)
828 unsigned char *ln;
829 unsigned char *cp;
830 unsigned char *end;
831 unsigned char *opcode_length;
832 unsigned long long size;
833 unsigned int length;
834 unsigned char version;
835 unsigned int min_insn_length;
836 unsigned int max_ops_per_insn;
837 int line_base;
838 unsigned int line_range;
839 unsigned int opcode_base;
840 unsigned int opindex;
841 unsigned int col;
842 unsigned int i;
843 unsigned int j;
844 unsigned int len;
845 unsigned long long value;
846 struct {
847 unsigned int type;
848 unsigned int form;
849 } entry_format[256];
850 unsigned int dir_size;
851 #if 0
852 char *dirs[DIR_TABLE_SIZE];
853 #endif
854 unsigned int filename_size;
855 struct /*dwarf_filename_struct*/ {
856 unsigned int dir_entry;
857 char *name;
858 } filename_table[FILE_TABLE_SIZE];
859 addr_t last_pc;
860 addr_t pc;
861 addr_t func_addr;
862 int line;
863 char *filename;
864 char *function;
866 filename = NULL;
867 function = NULL;
868 func_addr = 0;
869 line = 0;
871 ln = rc->dwarf_line;
872 while (ln < rc->dwarf_line_end) {
873 dir_size = 0;
874 filename_size = 0;
875 last_pc = 0;
876 pc = 0;
877 func_addr = 0;
878 line = 1;
879 filename = NULL;
880 function = NULL;
881 length = 4;
882 size = dwarf_read_4(ln, rc->dwarf_line_end);
883 if (size == 0xffffffffu) // dwarf 64
884 length = 8, size = dwarf_read_8(ln, rc->dwarf_line_end);
885 end = ln + size;
886 if (end < ln || end > rc->dwarf_line_end)
887 break;
888 version = dwarf_read_2(ln, end);
889 if (version >= 5)
890 ln += length + 2; // address size, segment selector, prologue Length
891 else
892 ln += length; // prologue Length
893 min_insn_length = dwarf_read_1(ln, end);
894 if (version >= 4)
895 max_ops_per_insn = dwarf_read_1(ln, end);
896 else
897 max_ops_per_insn = 1;
898 ln++; // Initial value of 'is_stmt'
899 line_base = dwarf_read_1(ln, end);
900 line_base |= line_base >= 0x80 ? ~0xff : 0;
901 line_range = dwarf_read_1(ln, end);
902 opcode_base = dwarf_read_1(ln, end);
903 opcode_length = ln;
904 ln += opcode_base - 1;
905 opindex = 0;
906 if (version >= 5) {
907 col = dwarf_read_1(ln, end);
908 for (i = 0; i < col; i++) {
909 entry_format[i].type = dwarf_read_uleb128(&ln, end);
910 entry_format[i].form = dwarf_read_uleb128(&ln, end);
912 dir_size = dwarf_read_uleb128(&ln, end);
913 for (i = 0; i < dir_size; i++) {
914 for (j = 0; j < col; j++) {
915 if (entry_format[j].type == DW_LNCT_path) {
916 if (entry_format[j].form != DW_FORM_line_strp)
917 goto next_line;
918 #if 0
919 value = length == 4 ? dwarf_read_4(ln, end)
920 : dwarf_read_8(ln, end);
921 if (i < DIR_TABLE_SIZE)
922 dirs[i] = (char *)rc->dwarf_line_str + value;
923 #else
924 length == 4 ? dwarf_read_4(ln, end)
925 : dwarf_read_8(ln, end);
926 #endif
928 else
929 dwarf_ignore_type(ln, end);
932 col = dwarf_read_1(ln, end);
933 for (i = 0; i < col; i++) {
934 entry_format[i].type = dwarf_read_uleb128(&ln, end);
935 entry_format[i].form = dwarf_read_uleb128(&ln, end);
937 filename_size = dwarf_read_uleb128(&ln, end);
938 for (i = 0; i < filename_size; i++)
939 for (j = 0; j < col; j++) {
940 if (entry_format[j].type == DW_LNCT_path) {
941 if (entry_format[j].form != DW_FORM_line_strp)
942 goto next_line;
943 value = length == 4 ? dwarf_read_4(ln, end)
944 : dwarf_read_8(ln, end);
945 if (i < FILE_TABLE_SIZE)
946 filename_table[i].name =
947 (char *)rc->dwarf_line_str + value;
949 else if (entry_format[j].type == DW_LNCT_directory_index) {
950 switch (entry_format[j].form) {
951 case DW_FORM_data1: value = dwarf_read_1(ln, end); break;
952 case DW_FORM_data2: value = dwarf_read_2(ln, end); break;
953 case DW_FORM_data4: value = dwarf_read_4(ln, end); break;
954 case DW_FORM_udata: value = dwarf_read_uleb128(&ln, end); break;
955 default: goto next_line;
957 if (i < FILE_TABLE_SIZE)
958 filename_table[i].dir_entry = value;
960 else
961 dwarf_ignore_type(ln, end);
964 else {
965 while ((dwarf_read_1(ln, end))) {
966 #if 0
967 if (++dir_size < DIR_TABLE_SIZE)
968 dirs[dir_size - 1] = (char *)ln - 1;
969 #endif
970 while (dwarf_read_1(ln, end)) {}
972 while ((dwarf_read_1(ln, end))) {
973 if (++filename_size < FILE_TABLE_SIZE) {
974 filename_table[filename_size - 1].name = (char *)ln - 1;
975 while (dwarf_read_1(ln, end)) {}
976 filename_table[filename_size - 1].dir_entry =
977 dwarf_read_uleb128(&ln, end);
979 else {
980 while (dwarf_read_1(ln, end)) {}
981 dwarf_read_uleb128(&ln, end);
983 dwarf_read_uleb128(&ln, end); // time
984 dwarf_read_uleb128(&ln, end); // size
987 if (filename_size >= 1)
988 filename = filename_table[0].name;
989 while (ln < end) {
990 last_pc = pc;
991 i = dwarf_read_1(ln, end);
992 if (i >= opcode_base) {
993 if (max_ops_per_insn == 1)
994 pc += ((i - opcode_base) / line_range) * min_insn_length;
995 else {
996 pc += (opindex + (i - opcode_base) / line_range) /
997 max_ops_per_insn * min_insn_length;
998 opindex = (opindex + (i - opcode_base) / line_range) %
999 max_ops_per_insn;
1001 i = (int)((i - opcode_base) % line_range) + line_base;
1002 check_pc:
1003 if (pc >= wanted_pc && wanted_pc >= last_pc)
1004 goto found;
1005 line += i;
1007 else {
1008 switch (i) {
1009 case 0:
1010 len = dwarf_read_uleb128(&ln, end);
1011 cp = ln;
1012 ln += len;
1013 if (len == 0)
1014 goto next_line;
1015 switch (dwarf_read_1(cp, end)) {
1016 case DW_LNE_end_sequence:
1017 break;
1018 case DW_LNE_set_address:
1019 #if PTR_SIZE == 4
1020 pc = dwarf_read_4(cp, end);
1021 #else
1022 pc = dwarf_read_8(cp, end);
1023 #endif
1024 #if defined TCC_TARGET_MACHO
1025 pc += rc->prog_base;
1026 #endif
1027 opindex = 0;
1028 break;
1029 case DW_LNE_define_file: /* deprecated */
1030 if (++filename_size < FILE_TABLE_SIZE) {
1031 filename_table[filename_size - 1].name = (char *)ln - 1;
1032 while (dwarf_read_1(ln, end)) {}
1033 filename_table[filename_size - 1].dir_entry =
1034 dwarf_read_uleb128(&ln, end);
1036 else {
1037 while (dwarf_read_1(ln, end)) {}
1038 dwarf_read_uleb128(&ln, end);
1040 dwarf_read_uleb128(&ln, end); // time
1041 dwarf_read_uleb128(&ln, end); // size
1042 break;
1043 case DW_LNE_hi_user - 1:
1044 function = (char *)cp;
1045 func_addr = pc;
1046 break;
1047 default:
1048 break;
1050 break;
1051 case DW_LNS_advance_pc:
1052 if (max_ops_per_insn == 1)
1053 pc += dwarf_read_uleb128(&ln, end) * min_insn_length;
1054 else {
1055 unsigned long long off = dwarf_read_uleb128(&ln, end);
1057 pc += (opindex + off) / max_ops_per_insn *
1058 min_insn_length;
1059 opindex = (opindex + off) % max_ops_per_insn;
1061 i = 0;
1062 goto check_pc;
1063 case DW_LNS_advance_line:
1064 line += dwarf_read_sleb128(&ln, end);
1065 break;
1066 case DW_LNS_set_file:
1067 i = dwarf_read_uleb128(&ln, end);
1068 i -= i > 0 && version < 5;
1069 if (i < FILE_TABLE_SIZE && i < filename_size)
1070 filename = filename_table[i].name;
1071 break;
1072 case DW_LNS_const_add_pc:
1073 if (max_ops_per_insn == 1)
1074 pc += ((255 - opcode_base) / line_range) * min_insn_length;
1075 else {
1076 unsigned int off = (255 - opcode_base) / line_range;
1078 pc += ((opindex + off) / max_ops_per_insn) *
1079 min_insn_length;
1080 opindex = (opindex + off) % max_ops_per_insn;
1082 i = 0;
1083 goto check_pc;
1084 case DW_LNS_fixed_advance_pc:
1085 i = dwarf_read_2(ln, end);
1086 pc += i;
1087 opindex = 0;
1088 i = 0;
1089 goto check_pc;
1090 default:
1091 for (j = 0; j < opcode_length[i - 1]; j++)
1092 dwarf_read_uleb128 (&ln, end);
1093 break;
1097 next_line:
1098 ln = end;
1100 filename = function = NULL, func_addr = 0;
1101 found:
1102 if (filename)
1103 pstrcpy(bi->file, sizeof bi->file, filename), bi->line = line;
1104 if (function)
1105 pstrcpy(bi->func, sizeof bi->func, function);
1106 bi->func_pc = func_addr;
1107 return (addr_t)func_addr;
1109 /* ------------------------------------------------------------- */
1110 #ifndef CONFIG_TCC_BACKTRACE_ONLY
1111 static
1112 #endif
1113 int _tcc_backtrace(rt_frame *f, const char *fmt, va_list ap)
1115 rt_context *rc, *rc2;
1116 addr_t pc;
1117 char skip[40], msg[200];
1118 int i, level, ret, n, one;
1119 const char *a, *b;
1120 bt_info bi;
1121 addr_t (*getinfo)(rt_context*, addr_t, bt_info*);
1123 skip[0] = 0;
1124 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
1125 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
1126 memcpy(skip, a, b - a), skip[b - a] = 0;
1127 fmt = b + 1;
1129 one = 0;
1130 /* hack for bcheck.c:dprintf(): one level, no newline */
1131 if (fmt[0] == '\001')
1132 ++fmt, one = 1;
1133 vsnprintf(msg, sizeof msg, fmt, ap);
1135 rt_wait_sem();
1136 rc = g_rc;
1137 getinfo = rt_printline, n = 6;
1138 if (rc) {
1139 if (rc->dwarf)
1140 getinfo = rt_printline_dwarf;
1141 if (rc->num_callers)
1142 n = rc->num_callers;
1145 for (i = level = 0; level < n; i++) {
1146 ret = rt_get_caller_pc(&pc, f, i);
1147 if (ret == -1)
1148 break;
1149 memset(&bi, 0, sizeof bi);
1150 for (rc2 = rc; rc2; rc2 = rc2->next) {
1151 if (getinfo(rc2, pc, &bi))
1152 break;
1153 /* we try symtab symbols (no line number info) */
1154 if (!!(a = rt_elfsym(rc2, pc, &bi.func_pc))) {
1155 pstrcpy(bi.func, sizeof bi.func, a);
1156 break;
1159 //fprintf(stderr, "%d rc %p %p\n", i, (void*)pcfunc, (void*)pc);
1160 if (skip[0] && strstr(bi.file, skip))
1161 continue;
1162 #ifndef CONFIG_TCC_BACKTRACE_ONLY
1164 TCCState *s = rt_find_state(f);
1165 if (s && s->bt_func) {
1166 ret = s->bt_func(
1167 s->bt_data,
1168 (void*)pc,
1169 bi.file[0] ? bi.file : NULL,
1170 bi.line,
1171 bi.func[0] ? bi.func : NULL,
1172 level == 0 ? msg : NULL
1174 if (ret == 0)
1175 break;
1176 goto check_break;
1179 #endif
1180 if (bi.file[0]) {
1181 rt_printf("%s:%d", bi.file, bi.line);
1182 } else {
1183 rt_printf("0x%08llx", (long long)pc);
1185 rt_printf(": %s %s", level ? "by" : "at", bi.func[0] ? bi.func : "???");
1186 if (level == 0) {
1187 rt_printf(": %s", msg);
1188 if (one)
1189 break;
1191 rt_printf("\n");
1193 #ifndef CONFIG_TCC_BACKTRACE_ONLY
1194 check_break:
1195 #endif
1196 if (rc2
1197 && bi.func_pc
1198 && bi.func_pc == (addr_t)rc2->top_func)
1199 break;
1200 ++level;
1202 rt_post_sem();
1203 return 0;
1206 /* emit a run time error at position 'pc' */
1207 static int rt_error(rt_frame *f, const char *fmt, ...)
1209 va_list ap; char msg[200]; int ret;
1210 va_start(ap, fmt);
1211 snprintf(msg, sizeof msg, "RUNTIME ERROR: %s", fmt);
1212 ret = _tcc_backtrace(f, msg, ap);
1213 va_end(ap);
1214 return ret;
1217 /* ------------------------------------------------------------- */
1219 #ifndef _WIN32
1220 # include <signal.h>
1221 # ifndef __OpenBSD__
1222 # include <sys/ucontext.h>
1223 # endif
1224 #else
1225 # define ucontext_t CONTEXT
1226 #endif
1228 /* translate from ucontext_t* to internal rt_context * */
1229 static void rt_getcontext(ucontext_t *uc, rt_frame *rc)
1231 #if defined _WIN64
1232 rc->ip = uc->Rip;
1233 rc->fp = uc->Rbp;
1234 rc->sp = uc->Rsp;
1235 #elif defined _WIN32
1236 rc->ip = uc->Eip;
1237 rc->fp = uc->Ebp;
1238 rc->sp = uc->Esp;
1239 #elif defined __i386__
1240 # if defined(__APPLE__)
1241 rc->ip = uc->uc_mcontext->__ss.__eip;
1242 rc->fp = uc->uc_mcontext->__ss.__ebp;
1243 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1244 rc->ip = uc->uc_mcontext.mc_eip;
1245 rc->fp = uc->uc_mcontext.mc_ebp;
1246 # elif defined(__dietlibc__)
1247 rc->ip = uc->uc_mcontext.eip;
1248 rc->fp = uc->uc_mcontext.ebp;
1249 # elif defined(__NetBSD__)
1250 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
1251 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
1252 # elif defined(__OpenBSD__)
1253 rc->ip = uc->sc_eip;
1254 rc->fp = uc->sc_ebp;
1255 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
1256 rc->ip = uc->uc_mcontext.gregs[EIP];
1257 rc->fp = uc->uc_mcontext.gregs[EBP];
1258 # else
1259 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
1260 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
1261 # endif
1262 #elif defined(__x86_64__)
1263 # if defined(__APPLE__)
1264 rc->ip = uc->uc_mcontext->__ss.__rip;
1265 rc->fp = uc->uc_mcontext->__ss.__rbp;
1266 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1267 rc->ip = uc->uc_mcontext.mc_rip;
1268 rc->fp = uc->uc_mcontext.mc_rbp;
1269 # elif defined(__NetBSD__)
1270 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
1271 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
1272 # elif defined(__OpenBSD__)
1273 rc->ip = uc->sc_rip;
1274 rc->fp = uc->sc_rbp;
1275 # else
1276 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
1277 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
1278 # endif
1279 #elif defined(__arm__) && defined(__NetBSD__)
1280 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1281 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1282 #elif defined(__arm__) && defined(__OpenBSD__)
1283 rc->ip = uc->sc_pc;
1284 rc->fp = uc->sc_r11;
1285 #elif defined(__arm__) && defined(__FreeBSD__)
1286 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1287 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1288 #elif defined(__arm__)
1289 rc->ip = uc->uc_mcontext.arm_pc;
1290 rc->fp = uc->uc_mcontext.arm_fp;
1291 #elif defined(__aarch64__) && defined(__APPLE__)
1292 // see:
1293 // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h
1294 rc->ip = uc->uc_mcontext->__ss.__pc;
1295 rc->fp = uc->uc_mcontext->__ss.__fp;
1296 #elif defined(__aarch64__) && defined(__FreeBSD__)
1297 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
1298 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
1299 #elif defined(__aarch64__) && defined(__NetBSD__)
1300 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1301 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1302 #elif defined(__aarch64__) && defined(__OpenBSD__)
1303 rc->ip = uc->sc_elr;
1304 rc->fp = uc->sc_x[29];
1305 #elif defined(__aarch64__)
1306 rc->ip = uc->uc_mcontext.pc;
1307 rc->fp = uc->uc_mcontext.regs[29];
1308 #elif defined(__riscv) && defined(__OpenBSD__)
1309 rc->ip = uc->sc_sepc;
1310 rc->fp = uc->sc_s[0];
1311 #elif defined(__riscv)
1312 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
1313 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
1314 #endif
1317 /* ------------------------------------------------------------- */
1318 #ifndef _WIN32
1319 /* signal handler for fatal errors */
1320 static void sig_error(int signum, siginfo_t *siginf, void *puc)
1322 rt_frame f;
1323 rt_getcontext(puc, &f);
1325 switch(signum) {
1326 case SIGFPE:
1327 switch(siginf->si_code) {
1328 case FPE_INTDIV:
1329 case FPE_FLTDIV:
1330 rt_error(&f, "division by zero");
1331 break;
1332 default:
1333 rt_error(&f, "floating point exception");
1334 break;
1336 break;
1337 case SIGBUS:
1338 case SIGSEGV:
1339 rt_error(&f, "invalid memory access");
1340 break;
1341 case SIGILL:
1342 rt_error(&f, "illegal instruction");
1343 break;
1344 case SIGABRT:
1345 rt_error(&f, "abort() called");
1346 break;
1347 default:
1348 rt_error(&f, "caught signal %d", signum);
1349 break;
1352 sigset_t s;
1353 sigemptyset(&s);
1354 sigaddset(&s, signum);
1355 sigprocmask(SIG_UNBLOCK, &s, NULL);
1357 rt_exit(&f, 255);
1360 #ifndef SA_SIGINFO
1361 # define SA_SIGINFO 0x00000004u
1362 #endif
1364 /* Generate a stack backtrace when a CPU exception occurs. */
1365 static void set_exception_handler(void)
1367 struct sigaction sigact;
1368 /* install TCC signal handlers to print debug info on fatal
1369 runtime errors */
1370 sigemptyset (&sigact.sa_mask);
1371 sigact.sa_flags = SA_SIGINFO; //| SA_RESETHAND;
1372 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
1373 sigact.sa_flags |= SA_ONSTACK;
1374 #endif
1375 sigact.sa_sigaction = sig_error;
1376 sigaction(SIGFPE, &sigact, NULL);
1377 sigaction(SIGILL, &sigact, NULL);
1378 sigaction(SIGSEGV, &sigact, NULL);
1379 sigaction(SIGBUS, &sigact, NULL);
1380 sigaction(SIGABRT, &sigact, NULL);
1381 #if 0//def SIGSTKSZ
1382 /* This allows stack overflow to be reported instead of a SEGV */
1384 stack_t ss;
1385 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
1387 ss.ss_sp = stack;
1388 ss.ss_size = SIGSTKSZ;
1389 ss.ss_flags = 0;
1390 sigaltstack(&ss, NULL);
1392 #endif
1395 #else /* WIN32 */
1397 /* signal handler for fatal errors */
1398 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
1400 rt_frame f;
1401 unsigned code;
1402 rt_getcontext(ex_info->ContextRecord, &f);
1404 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
1405 case EXCEPTION_ACCESS_VIOLATION:
1406 rt_error(&f, "invalid memory access");
1407 break;
1408 case EXCEPTION_STACK_OVERFLOW:
1409 rt_error(&f, "stack overflow");
1410 break;
1411 case EXCEPTION_INT_DIVIDE_BY_ZERO:
1412 rt_error(&f, "division by zero");
1413 break;
1414 case EXCEPTION_BREAKPOINT:
1415 case EXCEPTION_SINGLE_STEP:
1416 f.ip = *(addr_t*)f.sp;
1417 rt_error(&f, "breakpoint/single-step exception:");
1418 return EXCEPTION_CONTINUE_SEARCH;
1419 default:
1420 rt_error(&f, "caught exception %08x", code);
1421 break;
1423 rt_exit(&f, 255);
1424 return EXCEPTION_EXECUTE_HANDLER;
1427 /* Generate a stack backtrace when a CPU exception occurs. */
1428 static void set_exception_handler(void)
1430 SetUnhandledExceptionFilter(cpu_exception_handler);
1433 #endif
1435 /* ------------------------------------------------------------- */
1436 /* return the PC at frame level 'level'. Return negative if not found */
1437 #if defined(__i386__) || defined(__x86_64__)
1438 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1440 if (level == 0) {
1441 *paddr = rc->ip;
1442 } else {
1443 addr_t fp = rc->fp;
1444 while (1) {
1445 if (fp < 0x1000)
1446 return -1;
1447 if (0 == --level)
1448 break;
1449 /* XXX: check address validity with program info */
1450 fp = ((addr_t *)fp)[0];
1452 *paddr = ((addr_t *)fp)[1];
1454 return 0;
1457 /* XXX: only supports linux/bsd */
1458 #elif defined(__arm__) && !defined(_WIN32)
1459 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1461 if (level == 0) {
1462 *paddr = rc->ip;
1463 } else {
1464 addr_t fp = rc->fp;
1465 while (1) {
1466 if (fp < 0x1000)
1467 return -1;
1468 if (0 == --level)
1469 break;
1470 fp = ((addr_t *)fp)[0];
1472 *paddr = ((addr_t *)fp)[2];
1474 return 0;
1477 #elif defined(__aarch64__)
1478 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1480 if (level == 0) {
1481 *paddr = rc->ip;
1482 } else {
1483 addr_t fp = rc->fp;
1484 while (1) {
1485 if (fp < 0x1000)
1486 return -1;
1487 if (0 == --level)
1488 break;
1489 fp = ((addr_t *)fp)[0];
1491 *paddr = ((addr_t *)fp)[1];
1493 return 0;
1496 #elif defined(__riscv)
1497 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1499 if (level == 0) {
1500 *paddr = rc->ip;
1501 } else {
1502 addr_t fp = rc->fp;
1503 while (1) {
1504 if (fp < 0x1000)
1505 return -1;
1506 if (0 == --level)
1507 break;
1508 fp = ((addr_t *)fp)[-2];
1510 *paddr = ((addr_t *)fp)[-1];
1512 return 0;
1515 #else
1516 #warning add arch specific rt_get_caller_pc()
1517 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1519 return -1;
1522 #endif
1523 #else // for runmain.c:exit(); when CONFIG_TCC_BACKTRACE == 0 */
1524 static int rt_get_caller_pc(addr_t *paddr, rt_frame *f, int level)
1526 if (level)
1527 return -1;
1528 *paddr = f->ip;
1529 return 0;
1531 #endif /* CONFIG_TCC_BACKTRACE */
1532 /* ------------------------------------------------------------- */
1533 #ifdef CONFIG_TCC_STATIC
1535 /* dummy function for profiling */
1536 ST_FUNC void *dlopen(const char *filename, int flag)
1538 return NULL;
1541 ST_FUNC void dlclose(void *p)
1545 ST_FUNC const char *dlerror(void)
1547 return "error";
1550 typedef struct TCCSyms {
1551 char *str;
1552 void *ptr;
1553 } TCCSyms;
1556 /* add the symbol you want here if no dynamic linking is done */
1557 static TCCSyms tcc_syms[] = {
1558 #if !defined(CONFIG_TCCBOOT)
1559 #define TCCSYM(a) { #a, &a, },
1560 TCCSYM(printf)
1561 TCCSYM(fprintf)
1562 TCCSYM(fopen)
1563 TCCSYM(fclose)
1564 #undef TCCSYM
1565 #endif
1566 { NULL, NULL },
1569 ST_FUNC void *dlsym(void *handle, const char *symbol)
1571 TCCSyms *p;
1572 p = tcc_syms;
1573 while (p->str != NULL) {
1574 if (!strcmp(p->str, symbol))
1575 return p->ptr;
1576 p++;
1578 return NULL;
1581 #endif /* CONFIG_TCC_STATIC */
1582 #endif /* TCC_IS_NATIVE */
1583 /* ------------------------------------------------------------- */