tccrun: review last changes
[tinycc.git] / tccrun.c
blobd8ae8fa04ea91b821401997bdf416c128e5acfe6
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 #ifdef __APPLE__
95 # define CONFIG_RUNMEM_ALIGNED 0
96 #else
97 # ifndef CONFIG_RUNMEM_ALIGNED
98 # define CONFIG_RUNMEM_ALIGNED 1
99 # endif
100 # if CONFIG_RUNMEM_ALIGNED
101 # include <malloc.h> /* memalign() */
102 # endif
103 #endif
105 #if !_WIN32 && !__APPLE__
106 //#define HAVE_SELINUX 1
107 #endif
109 static int rt_mem(TCCState *s1, int size)
111 void *ptr;
112 int ptr_diff = 0;
113 #ifdef HAVE_SELINUX
114 /* Using mmap instead of malloc */
115 void *prw;
116 char tmpfname[] = "/tmp/.tccrunXXXXXX";
117 int fd = mkstemp(tmpfname);
118 unlink(tmpfname);
119 ftruncate(fd, size);
121 ptr = mmap(NULL, size * 2, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
122 /* mmap RW memory at fixed distance */
123 prw = mmap((char*)ptr + size, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, 0);
124 close(fd);
125 if (ptr == MAP_FAILED || prw == MAP_FAILED)
126 return tcc_error_noabort("tccrun: could not map memory");
127 ptr_diff = (char*)prw - (char*)ptr; /* = size; */
128 //printf("map %p %p %p\n", ptr, prw, (void*)ptr_diff);
129 #else
130 # if !CONFIG_RUNMEM_ALIGNED
131 ptr = tcc_malloc(size += PAGESIZE);
132 # elif _WIN32
133 ptr = _aligned_malloc(size, PAGESIZE);
134 # else
135 ptr = memalign(PAGESIZE, size);
136 # endif
137 if (NULL == ptr)
138 return tcc_error_noabort("tccrun: could not allocate memory");
139 #endif
140 s1->run_ptr = ptr;
141 s1->run_size = size;
142 return ptr_diff;
145 /* ------------------------------------------------------------- */
146 /* Do all relocations (needed before using tcc_get_symbol())
147 Returns -1 on error. */
149 LIBTCCAPI int tcc_relocate(TCCState *s1)
151 int size, ret, ptr_diff;
153 if (s1->run_ptr)
154 exit(tcc_error_noabort("'tcc_relocate()' twice is no longer supported"));
155 #ifdef CONFIG_TCC_BACKTRACE
156 if (s1->do_backtrace)
157 tcc_add_symbol(s1, "_tcc_backtrace", _tcc_backtrace); /* for bt-log.c */
158 #endif
159 size = tcc_relocate_ex(s1, NULL, 0);
160 if (size < 0)
161 return -1;
162 ptr_diff = rt_mem(s1, size);
163 if (ptr_diff < 0)
164 return -1;
165 ret = tcc_relocate_ex(s1, s1->run_ptr, ptr_diff);
166 if (ret == 0)
167 st_link(s1);
168 return ret;
171 ST_FUNC void tcc_run_free(TCCState *s1)
173 unsigned size;
174 void *ptr;
175 int i;
177 /* free any loaded DLLs */
178 for ( i = 0; i < s1->nb_loaded_dlls; i++) {
179 DLLReference *ref = s1->loaded_dlls[i];
180 if ( ref->handle )
181 #ifdef _WIN32
182 FreeLibrary((HMODULE)ref->handle);
183 #else
184 dlclose(ref->handle);
185 #endif
187 /* free loaded dlls array */
188 dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
189 /* unmap or unprotect and free memory */
190 ptr = s1->run_ptr;
191 if (NULL == ptr)
192 return;
193 st_unlink(s1);
194 size = s1->run_size;
195 #ifdef HAVE_SELINUX
196 munmap(ptr, size * 2);
197 #else
198 /* unprotect memory to make it usable for malloc again */
199 protect_pages(ptr, size, 2 /*rw*/);
200 # ifdef _WIN64
201 win64_del_function_table(s1->run_function_table);
202 # endif
203 # if !CONFIG_RUNMEM_ALIGNED
204 tcc_free(ptr);
205 # elif _WIN32
206 _aligned_free(ptr);
207 # else
208 libc_free(ptr);
209 # endif
210 #endif
213 /* launch the compiled program with the given arguments */
214 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
216 int (*prog_main)(int, char **, char **), ret;
217 const char *top_sym;
218 jmp_buf main_jb;
220 #if defined(__APPLE__) || defined(__FreeBSD__)
221 char **envp = NULL;
222 #elif defined(__OpenBSD__) || defined(__NetBSD__)
223 extern char **environ;
224 char **envp = environ;
225 #else
226 char **envp = environ;
227 #endif
229 /* tcc -dt -run ... nothing to do if no main() */
230 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, "main", 0, 1))
231 return 0;
233 tcc_add_symbol(s1, "__rt_exit", rt_exit);
234 if (s1->nostdlib) {
235 s1->run_main = top_sym = "_start";
236 } else {
237 tcc_add_support(s1, "runmain.o");
238 s1->run_main = "_runmain";
239 top_sym = "main";
241 if (tcc_relocate(s1) < 0)
242 return 1;
244 prog_main = (void*)get_sym_addr(s1, s1->run_main, 1, 1);
245 if ((addr_t)-1 == (addr_t)prog_main)
246 return 1;
247 errno = 0; /* clean errno value */
248 fflush(stdout);
249 fflush(stderr);
251 ret = tcc_setjmp(s1, main_jb, tcc_get_symbol(s1, top_sym));
252 if (0 == ret)
253 ret = prog_main(argc, argv, envp);
254 else if (256 == ret)
255 ret = 0;
257 if (s1->dflag & 16 && ret) /* tcc -dt -run ... */
258 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
259 return ret;
262 /* ------------------------------------------------------------- */
263 /* remove all STB_LOCAL symbols */
264 static void cleanup_symbols(TCCState *s1)
266 Section *s = s1->symtab;
267 int sym_index, end_sym = s->data_offset / sizeof (ElfSym);
268 /* reset symtab */
269 s->data_offset = s->link->data_offset = s->hash->data_offset = 0;
270 init_symtab(s);
271 /* add global symbols again */
272 for (sym_index = 1; sym_index < end_sym; ++sym_index) {
273 ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index];
274 const char *name = (char *)s->link->data + sym->st_name;
275 if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)
276 continue;
277 //printf("sym %s\n", name);
278 put_elf_sym(s, sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx, name);
282 /* free all sections except symbols */
283 static void cleanup_sections(TCCState *s1)
285 struct { Section **secs; int nb_secs; } *p = (void*)&s1->sections;
286 int i, f = 2;
287 do {
288 for (i = --f; i < p->nb_secs; i++) {
289 Section *s = p->secs[i];
290 if (s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash) {
291 s->data = tcc_realloc(s->data, s->data_allocated = s->data_offset);
292 } else {
293 free_section(s), tcc_free(s), p->secs[i] = NULL;
296 } while (++p, f);
299 /* ------------------------------------------------------------- */
300 /* 0 = .text rwx other rw */
301 /* 1 = .text rx .rdata r .data/.bss rw */
302 #ifndef CONFIG_RUNMEM_RO
303 # define CONFIG_RUNMEM_RO 1
304 #endif
306 /* relocate code. Return -1 on error, required size if ptr is NULL,
307 otherwise copy code into buffer passed by the caller */
308 static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff)
310 Section *s;
311 unsigned offset, length, align, i, k, f;
312 unsigned n, copy;
313 addr_t mem, addr;
315 if (NULL == ptr) {
316 #ifdef TCC_TARGET_PE
317 pe_output_file(s1, NULL);
318 #else
319 tcc_add_runtime(s1);
320 resolve_common_syms(s1);
321 build_got_entries(s1, 0);
322 #endif
325 offset = copy = 0;
326 mem = (addr_t)ptr;
327 redo:
328 if (s1->verbose == 2 && copy) {
329 printf(&"-----------------------------------------------------\n"[PTR_SIZE*2 - 8]);
330 if (1 == copy)
331 printf("memory %p len %05x\n", ptr, s1->run_size);
333 if (s1->nb_errors)
334 return -1;
335 if (copy == 2)
336 return 0;
338 for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
339 n = 0; addr = 0;
340 for(i = 1; i < s1->nb_sections; i++) {
341 static const char shf[] = {
342 SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
344 s = s1->sections[i];
345 if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
346 continue;
347 length = s->data_offset;
349 if (copy) { /* final step: copy section data to memory */
350 if (s1->verbose == 2)
351 printf("%d: %-16s %p len %05x align %04x\n",
352 k, s->name, (void*)s->sh_addr, length, s->sh_addralign);
353 if (addr == 0)
354 addr = s->sh_addr;
355 n = (s->sh_addr - addr) + length;
356 ptr = (void*)s->sh_addr;
357 if (k == 0)
358 ptr = (void*)(s->sh_addr + ptr_diff);
359 if (NULL == s->data || s->sh_type == SHT_NOBITS)
360 memset(ptr, 0, length);
361 else
362 memcpy(ptr, s->data, length);
363 #ifdef _WIN64
364 if (s == s1->uw_pdata)
365 s1->run_function_table = win64_add_function_table(s1);
366 #endif
367 continue;
370 align = s->sh_addralign;
371 if (++n == 1) {
372 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
373 /* To avoid that x86 processors would reload cached instructions
374 each time when data is written in the near, we need to make
375 sure that code and data do not share the same 64 byte unit */
376 if (align < 64)
377 align = 64;
378 #endif
379 /* start new page for different permissions */
380 if (CONFIG_RUNMEM_RO || k < 2)
381 align = PAGESIZE;
383 s->sh_addralign = align;
385 addr = k ? mem + ptr_diff : mem;
386 offset += -(addr + offset) & (align - 1);
387 s->sh_addr = mem ? addr + offset : 0;
388 offset += length;
390 if (copy) { /* set permissions */
391 if (n == 0) /* no data */
392 continue;
393 #ifdef HAVE_SELINUX
394 if (k == 0) /* SHF_EXECINSTR has its own mapping */
395 continue;
396 #endif
397 f = k;
398 if (CONFIG_RUNMEM_RO == 0) {
399 if (f != 0)
400 continue;
401 f = 3; /* change only SHF_EXECINSTR to rwx */
403 n = (n + PAGESIZE-1) & ~(PAGESIZE-1);
404 if (s1->verbose == 2) {
405 printf("protect %3s %p len %05x\n",
406 &"rx\0r \0rw\0rwx"[f*3], (void*)addr, (unsigned)n);
408 if (protect_pages((void*)addr, n, f) < 0)
409 return tcc_error_noabort(
410 "mprotect failed (did you mean to configure --with-selinux?)");
414 if (0 == mem)
415 return (offset + (PAGESIZE-1)) & ~(PAGESIZE-1);
417 if (++copy == 2) {
418 /* remove local symbols and free sections except symtab */
419 cleanup_symbols(s1);
420 cleanup_sections(s1);
421 goto redo;
424 /* relocate symbols */
425 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
426 /* relocate sections */
427 #ifdef TCC_TARGET_PE
428 s1->pe_imagebase = mem;
429 #else
430 relocate_plt(s1);
431 #endif
432 relocate_sections(s1);
433 goto redo;
436 /* ------------------------------------------------------------- */
437 /* allow to run code in memory */
439 static int protect_pages(void *ptr, unsigned long length, int mode)
441 #ifdef _WIN32
442 static const unsigned char protect[] = {
443 PAGE_EXECUTE_READ,
444 PAGE_READONLY,
445 PAGE_READWRITE,
446 PAGE_EXECUTE_READWRITE
448 DWORD old;
449 if (!VirtualProtect(ptr, length, protect[mode], &old))
450 return -1;
451 return 0;
452 #else
453 static const unsigned char protect[] = {
454 PROT_READ | PROT_EXEC,
455 PROT_READ,
456 PROT_READ | PROT_WRITE,
457 PROT_READ | PROT_WRITE | PROT_EXEC
459 addr_t start, end;
460 start = (addr_t)ptr & ~(PAGESIZE - 1);
461 end = (addr_t)ptr + length;
462 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
463 if (mprotect((void *)start, end - start, protect[mode]))
464 return -1;
465 /* XXX: BSD sometimes dump core with bad system call */
466 # if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
467 if (mode == 0 || mode == 3) {
468 void __clear_cache(void *beginning, void *end);
469 __clear_cache(ptr, (char *)ptr + length);
471 # endif
472 return 0;
473 #endif
476 #ifdef _WIN64
477 static void *win64_add_function_table(TCCState *s1)
479 void *p = NULL;
480 if (s1->uw_pdata) {
481 p = (void*)s1->uw_pdata->sh_addr;
482 RtlAddFunctionTable(
483 (RUNTIME_FUNCTION*)p,
484 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
485 s1->pe_imagebase
487 s1->uw_pdata = NULL;
489 return p;
492 static void win64_del_function_table(void *p)
494 if (p) {
495 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
498 #endif
500 static void bt_link(TCCState *s1)
502 #ifdef CONFIG_TCC_BACKTRACE
503 rt_context *rc;
504 void *p;
506 if (!s1->do_backtrace)
507 return;
508 rc = tcc_get_symbol(s1, "__rt_info");
509 if (!rc)
510 return;
511 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
512 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
513 rc->elf_str = (char *)symtab_section->link->data;
514 if (PTR_SIZE == 8 && !s1->dwarf)
515 rc->prog_base &= 0xffffffff00000000ULL;
516 #ifdef CONFIG_TCC_BCHECK
517 if (s1->do_bounds_check) {
518 if ((p = tcc_get_symbol(s1, "__bound_init")))
519 ((void(*)(void*,int))p)(rc->bounds_start, 1);
521 #endif
522 rc->next = g_rc, g_rc = rc, s1->rc = rc;
523 if (0 == signal_set)
524 set_exception_handler(), signal_set = 1;
525 #endif
528 static void st_link(TCCState *s1)
530 rt_wait_sem();
531 s1->next = g_s1, g_s1 = s1;
532 bt_link(s1);
533 rt_post_sem();
536 /* remove 'el' from 'list' */
537 static void ptr_unlink(void *list, void *e, unsigned next)
539 void **pp, **nn, *p;
540 for (pp = list; !!(p = *pp); pp = nn) {
541 nn = (void*)((char*)p + next); /* nn = &p->next; */
542 if (p == e) {
543 *pp = *nn;
544 break;
549 static void st_unlink(TCCState *s1)
551 rt_wait_sem();
552 #ifdef CONFIG_TCC_BACKTRACE
553 ptr_unlink(&g_rc, s1->rc, offsetof(rt_context, next));
554 #endif
555 ptr_unlink(&g_s1, s1, offsetof(TCCState, next));
556 rt_post_sem();
559 #ifdef _WIN32
560 # define GETTID() GetCurrentThreadId()
561 #elif defined __linux__
562 # define GETTID() gettid()
563 #elif 0
564 # define GETTID() 1234 // threads not supported
565 #endif
567 LIBTCCAPI void *_tcc_setjmp(TCCState *s1, void *p_jmp_buf, void *func, void *p_longjmp)
569 #ifdef GETTID
570 int tid = GETTID();
571 TCCState *s;
572 rt_wait_sem();
573 for (s = g_s1; s; s = s->next)
574 if (s->run_tid == tid)
575 s->run_tid = -1;
576 s1->run_tid = (int)tid;
577 rt_post_sem();
578 #endif
579 s1->run_lj = p_longjmp;
580 s1->run_jb = p_jmp_buf;
581 #ifdef CONFIG_TCC_BACKTRACE
582 if (s1->rc)
583 s1->rc->top_func = func;
584 #endif
585 return p_jmp_buf;
588 LIBTCCAPI void tcc_set_backtrace_func(TCCState *s1, void *data, TCCBtFunc *func)
590 s1->bt_func = func;
591 s1->bt_data = data;
594 static TCCState *rt_find_state(rt_frame *f)
596 #ifdef GETTID
597 int tid = GETTID();
598 TCCState *s;
599 for (s = g_s1; s; s = s->next)
600 if (s->run_tid == tid)
601 break;
602 return s;
603 #else
604 TCCState *s;
605 int level;
606 addr_t pc;
608 s = g_s1;
609 if (NULL == s || NULL == s->next) {
610 /* play it safe in the simple case when there is only one state */
611 return s;
613 for (level = 0; level < 8; ++level) {
614 if (rt_get_caller_pc(&pc, f, level) < 0)
615 break;
616 for (s = g_s1; s; s = s->next) {
617 if (pc >= (addr_t)s->run_ptr
618 && pc < (addr_t)s->run_ptr + s->run_size)
619 return s;
622 return NULL;
623 #endif
626 static void rt_exit(rt_frame *f, int code)
628 TCCState *s;
629 rt_wait_sem();
630 s = rt_find_state(f);
631 rt_post_sem();
632 if (s && s->run_lj) {
633 if (code == 0)
634 code = 256;
635 ((void(*)(void*,int))s->run_lj)(s->run_jb, code);
637 exit(code);
640 /* ------------------------------------------------------------- */
641 #else // if defined CONFIG_TCC_BACKTRACE_ONLY
642 static void rt_exit(rt_frame *f, int code)
644 exit(code);
646 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
647 /* ------------------------------------------------------------- */
648 #ifdef CONFIG_TCC_BACKTRACE
650 static int rt_vprintf(const char *fmt, va_list ap)
652 int ret = vfprintf(stderr, fmt, ap);
653 fflush(stderr);
654 return ret;
657 static int rt_printf(const char *fmt, ...)
659 va_list ap;
660 int r;
661 va_start(ap, fmt);
662 r = rt_vprintf(fmt, ap);
663 va_end(ap);
664 return r;
667 static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr)
669 ElfW(Sym) *esym;
670 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
671 int type = ELFW(ST_TYPE)(esym->st_info);
672 if ((type == STT_FUNC || type == STT_GNU_IFUNC)
673 && wanted_pc >= esym->st_value
674 && wanted_pc < esym->st_value + esym->st_size) {
675 *func_addr = esym->st_value;
676 return rc->elf_str + esym->st_name;
679 return NULL;
682 typedef struct bt_info
684 char file[100];
685 int line;
686 char func[100];
687 addr_t func_pc;
688 } bt_info;
690 /* print the position in the source file of PC value 'pc' by reading
691 the stabs debug information */
692 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc, bt_info *bi)
694 char func_name[128];
695 addr_t func_addr, last_pc, pc;
696 const char *incl_files[INCLUDE_STACK_SIZE];
697 int incl_index, last_incl_index, len, last_line_num, i;
698 const char *str, *p;
699 Stab_Sym *sym;
701 func_name[0] = '\0';
702 func_addr = 0;
703 incl_index = 0;
704 last_pc = (addr_t)-1;
705 last_line_num = 1;
706 last_incl_index = 0;
708 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
709 str = rc->stab_str + sym->n_strx;
710 pc = sym->n_value;
712 switch(sym->n_type) {
713 case N_SLINE:
714 if (func_addr)
715 goto rel_pc;
716 case N_SO:
717 case N_SOL:
718 goto abs_pc;
719 case N_FUN:
720 if (sym->n_strx == 0) /* end of function */
721 goto rel_pc;
722 abs_pc:
723 #if PTR_SIZE == 8
724 /* Stab_Sym.n_value is only 32bits */
725 pc += rc->prog_base;
726 #endif
727 goto check_pc;
728 rel_pc:
729 pc += func_addr;
730 check_pc:
731 if (pc >= wanted_pc && wanted_pc >= last_pc)
732 goto found;
733 break;
736 switch(sym->n_type) {
737 /* function start or end */
738 case N_FUN:
739 if (sym->n_strx == 0)
740 goto reset_func;
741 p = strchr(str, ':');
742 if (0 == p || (len = p - str + 1, len > sizeof func_name))
743 len = sizeof func_name;
744 pstrcpy(func_name, len, str);
745 func_addr = pc;
746 break;
747 /* line number info */
748 case N_SLINE:
749 last_pc = pc;
750 last_line_num = sym->n_desc;
751 last_incl_index = incl_index;
752 break;
753 /* include files */
754 case N_BINCL:
755 if (incl_index < INCLUDE_STACK_SIZE)
756 incl_files[incl_index++] = str;
757 break;
758 case N_EINCL:
759 if (incl_index > 1)
760 incl_index--;
761 break;
762 /* start/end of translation unit */
763 case N_SO:
764 incl_index = 0;
765 if (sym->n_strx) {
766 /* do not add path */
767 len = strlen(str);
768 if (len > 0 && str[len - 1] != '/')
769 incl_files[incl_index++] = str;
771 reset_func:
772 func_name[0] = '\0';
773 func_addr = 0;
774 last_pc = (addr_t)-1;
775 break;
776 /* alternative file name (from #line or #include directives) */
777 case N_SOL:
778 if (incl_index)
779 incl_files[incl_index-1] = str;
780 break;
783 last_incl_index = 0, func_name[0] = 0, func_addr = 0;
784 found:
785 i = last_incl_index;
786 if (i > 0) {
787 pstrcpy(bi->file, sizeof bi->file, incl_files[--i]);
788 bi->line = last_line_num;
790 pstrcpy(bi->func, sizeof bi->func, func_name);
791 bi->func_pc = func_addr;
792 return func_addr;
795 /* ------------------------------------------------------------- */
796 /* rt_printline - dwarf version */
798 #define MAX_128 ((8 * sizeof (long long) + 6) / 7)
800 #define DIR_TABLE_SIZE (64)
801 #define FILE_TABLE_SIZE (512)
803 #define dwarf_read_1(ln,end) \
804 ((ln) < (end) ? *(ln)++ : 0)
805 #define dwarf_read_2(ln,end) \
806 ((ln) + 2 < (end) ? (ln) += 2, read16le((ln) - 2) : 0)
807 #define dwarf_read_4(ln,end) \
808 ((ln) + 4 < (end) ? (ln) += 4, read32le((ln) - 4) : 0)
809 #define dwarf_read_8(ln,end) \
810 ((ln) + 8 < (end) ? (ln) += 8, read64le((ln) - 8) : 0)
811 #define dwarf_ignore_type(ln, end) /* timestamp/size/md5/... */ \
812 switch (entry_format[j].form) { \
813 case DW_FORM_data1: (ln) += 1; break; \
814 case DW_FORM_data2: (ln) += 2; break; \
815 case DW_FORM_data4: (ln) += 3; break; \
816 case DW_FORM_data8: (ln) += 8; break; \
817 case DW_FORM_data16: (ln) += 16; break; \
818 case DW_FORM_udata: dwarf_read_uleb128(&(ln), (end)); break; \
819 default: goto next_line; \
822 static unsigned long long
823 dwarf_read_uleb128(unsigned char **ln, unsigned char *end)
825 unsigned char *cp = *ln;
826 unsigned long long retval = 0;
827 int i;
829 for (i = 0; i < MAX_128; i++) {
830 unsigned long long byte = dwarf_read_1(cp, end);
832 retval |= (byte & 0x7f) << (i * 7);
833 if ((byte & 0x80) == 0)
834 break;
836 *ln = cp;
837 return retval;
840 static long long
841 dwarf_read_sleb128(unsigned char **ln, unsigned char *end)
843 unsigned char *cp = *ln;
844 long long retval = 0;
845 int i;
847 for (i = 0; i < MAX_128; i++) {
848 unsigned long long byte = dwarf_read_1(cp, end);
850 retval |= (byte & 0x7f) << (i * 7);
851 if ((byte & 0x80) == 0) {
852 if ((byte & 0x40) && (i + 1) * 7 < 64)
853 retval |= -1LL << ((i + 1) * 7);
854 break;
857 *ln = cp;
858 return retval;
861 static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc, bt_info *bi)
863 unsigned char *ln;
864 unsigned char *cp;
865 unsigned char *end;
866 unsigned char *opcode_length;
867 unsigned long long size;
868 unsigned int length;
869 unsigned char version;
870 unsigned int min_insn_length;
871 unsigned int max_ops_per_insn;
872 int line_base;
873 unsigned int line_range;
874 unsigned int opcode_base;
875 unsigned int opindex;
876 unsigned int col;
877 unsigned int i;
878 unsigned int j;
879 unsigned int len;
880 unsigned long long value;
881 struct {
882 unsigned int type;
883 unsigned int form;
884 } entry_format[256];
885 unsigned int dir_size;
886 #if 0
887 char *dirs[DIR_TABLE_SIZE];
888 #endif
889 unsigned int filename_size;
890 struct dwarf_filename_struct {
891 unsigned int dir_entry;
892 char *name;
893 } filename_table[FILE_TABLE_SIZE];
894 addr_t last_pc;
895 addr_t pc;
896 addr_t func_addr;
897 int line;
898 char *filename;
899 char *function;
901 filename = NULL;
902 function = NULL;
903 func_addr = 0;
904 line = 0;
906 ln = rc->dwarf_line;
907 while (ln < rc->dwarf_line_end) {
908 dir_size = 0;
909 filename_size = 0;
910 last_pc = 0;
911 pc = 0;
912 func_addr = 0;
913 line = 1;
914 filename = NULL;
915 function = NULL;
916 length = 4;
917 size = dwarf_read_4(ln, rc->dwarf_line_end);
918 if (size == 0xffffffffu) // dwarf 64
919 length = 8, size = dwarf_read_8(ln, rc->dwarf_line_end);
920 end = ln + size;
921 if (end < ln || end > rc->dwarf_line_end)
922 break;
923 version = dwarf_read_2(ln, end);
924 if (version >= 5)
925 ln += length + 2; // address size, segment selector, prologue Length
926 else
927 ln += length; // prologue Length
928 min_insn_length = dwarf_read_1(ln, end);
929 if (version >= 4)
930 max_ops_per_insn = dwarf_read_1(ln, end);
931 else
932 max_ops_per_insn = 1;
933 ln++; // Initial value of 'is_stmt'
934 line_base = dwarf_read_1(ln, end);
935 line_base |= line_base >= 0x80 ? ~0xff : 0;
936 line_range = dwarf_read_1(ln, end);
937 opcode_base = dwarf_read_1(ln, end);
938 opcode_length = ln;
939 ln += opcode_base - 1;
940 opindex = 0;
941 if (version >= 5) {
942 col = dwarf_read_1(ln, end);
943 for (i = 0; i < col; i++) {
944 entry_format[i].type = dwarf_read_uleb128(&ln, end);
945 entry_format[i].form = dwarf_read_uleb128(&ln, end);
947 dir_size = dwarf_read_uleb128(&ln, end);
948 for (i = 0; i < dir_size; i++) {
949 for (j = 0; j < col; j++) {
950 if (entry_format[j].type == DW_LNCT_path) {
951 if (entry_format[j].form != DW_FORM_line_strp)
952 goto next_line;
953 #if 0
954 value = length == 4 ? dwarf_read_4(ln, end)
955 : dwarf_read_8(ln, end);
956 if (i < DIR_TABLE_SIZE)
957 dirs[i] = (char *)rc->dwarf_line_str + value;
958 #else
959 length == 4 ? dwarf_read_4(ln, end)
960 : dwarf_read_8(ln, end);
961 #endif
963 else
964 dwarf_ignore_type(ln, end);
967 col = dwarf_read_1(ln, end);
968 for (i = 0; i < col; i++) {
969 entry_format[i].type = dwarf_read_uleb128(&ln, end);
970 entry_format[i].form = dwarf_read_uleb128(&ln, end);
972 filename_size = dwarf_read_uleb128(&ln, end);
973 for (i = 0; i < filename_size; i++)
974 for (j = 0; j < col; j++) {
975 if (entry_format[j].type == DW_LNCT_path) {
976 if (entry_format[j].form != DW_FORM_line_strp)
977 goto next_line;
978 value = length == 4 ? dwarf_read_4(ln, end)
979 : dwarf_read_8(ln, end);
980 if (i < FILE_TABLE_SIZE)
981 filename_table[i].name =
982 (char *)rc->dwarf_line_str + value;
984 else if (entry_format[j].type == DW_LNCT_directory_index) {
985 switch (entry_format[j].form) {
986 case DW_FORM_data1: value = dwarf_read_1(ln, end); break;
987 case DW_FORM_data2: value = dwarf_read_2(ln, end); break;
988 case DW_FORM_data4: value = dwarf_read_4(ln, end); break;
989 case DW_FORM_udata: value = dwarf_read_uleb128(&ln, end); break;
990 default: goto next_line;
992 if (i < FILE_TABLE_SIZE)
993 filename_table[i].dir_entry = value;
995 else
996 dwarf_ignore_type(ln, end);
999 else {
1000 while ((dwarf_read_1(ln, end))) {
1001 #if 0
1002 if (++dir_size < DIR_TABLE_SIZE)
1003 dirs[dir_size - 1] = (char *)ln - 1;
1004 #endif
1005 while (dwarf_read_1(ln, end)) {}
1007 while ((dwarf_read_1(ln, end))) {
1008 if (++filename_size < FILE_TABLE_SIZE) {
1009 filename_table[filename_size - 1].name = (char *)ln - 1;
1010 while (dwarf_read_1(ln, end)) {}
1011 filename_table[filename_size - 1].dir_entry =
1012 dwarf_read_uleb128(&ln, end);
1014 else {
1015 while (dwarf_read_1(ln, end)) {}
1016 dwarf_read_uleb128(&ln, end);
1018 dwarf_read_uleb128(&ln, end); // time
1019 dwarf_read_uleb128(&ln, end); // size
1022 if (filename_size >= 1)
1023 filename = filename_table[0].name;
1024 while (ln < end) {
1025 last_pc = pc;
1026 i = dwarf_read_1(ln, end);
1027 if (i >= opcode_base) {
1028 if (max_ops_per_insn == 1)
1029 pc += ((i - opcode_base) / line_range) * min_insn_length;
1030 else {
1031 pc += (opindex + (i - opcode_base) / line_range) /
1032 max_ops_per_insn * min_insn_length;
1033 opindex = (opindex + (i - opcode_base) / line_range) %
1034 max_ops_per_insn;
1036 i = (int)((i - opcode_base) % line_range) + line_base;
1037 check_pc:
1038 if (pc >= wanted_pc && wanted_pc >= last_pc)
1039 goto found;
1040 line += i;
1042 else {
1043 switch (i) {
1044 case 0:
1045 len = dwarf_read_uleb128(&ln, end);
1046 cp = ln;
1047 ln += len;
1048 if (len == 0)
1049 goto next_line;
1050 switch (dwarf_read_1(cp, end)) {
1051 case DW_LNE_end_sequence:
1052 break;
1053 case DW_LNE_set_address:
1054 #if PTR_SIZE == 4
1055 pc = dwarf_read_4(cp, end);
1056 #else
1057 pc = dwarf_read_8(cp, end);
1058 #endif
1059 #if defined TCC_TARGET_MACHO
1060 pc += rc->prog_base;
1061 #endif
1062 opindex = 0;
1063 break;
1064 case DW_LNE_define_file: /* deprecated */
1065 if (++filename_size < FILE_TABLE_SIZE) {
1066 filename_table[filename_size - 1].name = (char *)ln - 1;
1067 while (dwarf_read_1(ln, end)) {}
1068 filename_table[filename_size - 1].dir_entry =
1069 dwarf_read_uleb128(&ln, end);
1071 else {
1072 while (dwarf_read_1(ln, end)) {}
1073 dwarf_read_uleb128(&ln, end);
1075 dwarf_read_uleb128(&ln, end); // time
1076 dwarf_read_uleb128(&ln, end); // size
1077 break;
1078 case DW_LNE_hi_user - 1:
1079 function = (char *)cp;
1080 func_addr = pc;
1081 break;
1082 default:
1083 break;
1085 break;
1086 case DW_LNS_advance_pc:
1087 if (max_ops_per_insn == 1)
1088 pc += dwarf_read_uleb128(&ln, end) * min_insn_length;
1089 else {
1090 unsigned long long off = dwarf_read_uleb128(&ln, end);
1092 pc += (opindex + off) / max_ops_per_insn *
1093 min_insn_length;
1094 opindex = (opindex + off) % max_ops_per_insn;
1096 i = 0;
1097 goto check_pc;
1098 case DW_LNS_advance_line:
1099 line += dwarf_read_sleb128(&ln, end);
1100 break;
1101 case DW_LNS_set_file:
1102 i = dwarf_read_uleb128(&ln, end);
1103 i -= i > 0 && version < 5;
1104 if (i < FILE_TABLE_SIZE && i < filename_size)
1105 filename = filename_table[i].name;
1106 break;
1107 case DW_LNS_const_add_pc:
1108 if (max_ops_per_insn == 1)
1109 pc += ((255 - opcode_base) / line_range) * min_insn_length;
1110 else {
1111 unsigned int off = (255 - opcode_base) / line_range;
1113 pc += ((opindex + off) / max_ops_per_insn) *
1114 min_insn_length;
1115 opindex = (opindex + off) % max_ops_per_insn;
1117 i = 0;
1118 goto check_pc;
1119 case DW_LNS_fixed_advance_pc:
1120 i = dwarf_read_2(ln, end);
1121 pc += i;
1122 opindex = 0;
1123 i = 0;
1124 goto check_pc;
1125 default:
1126 for (j = 0; j < opcode_length[i - 1]; j++)
1127 dwarf_read_uleb128 (&ln, end);
1128 break;
1132 next_line:
1133 ln = end;
1135 filename = function = NULL, func_addr = 0;
1136 found:
1137 if (filename)
1138 pstrcpy(bi->file, sizeof bi->file, filename), bi->line = line;
1139 if (function)
1140 pstrcpy(bi->func, sizeof bi->func, function);
1141 bi->func_pc = func_addr;
1142 return (addr_t)func_addr;
1144 /* ------------------------------------------------------------- */
1145 #ifndef CONFIG_TCC_BACKTRACE_ONLY
1146 static
1147 #endif
1148 int _tcc_backtrace(rt_frame *f, const char *fmt, va_list ap)
1150 rt_context *rc, *rc2;
1151 addr_t pc;
1152 char skip[40], msg[200];
1153 int i, level, ret, n, one;
1154 const char *a, *b;
1155 bt_info bi;
1156 addr_t (*getinfo)(rt_context*, addr_t, bt_info*);
1158 skip[0] = 0;
1159 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
1160 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
1161 memcpy(skip, a, b - a), skip[b - a] = 0;
1162 fmt = b + 1;
1164 one = 0;
1165 /* hack for bcheck.c:dprintf(): one level, no newline */
1166 if (fmt[0] == '\001')
1167 ++fmt, one = 1;
1168 vsnprintf(msg, sizeof msg, fmt, ap);
1170 rt_wait_sem();
1171 rc = g_rc;
1172 getinfo = rt_printline, n = 6;
1173 if (rc) {
1174 if (rc->dwarf)
1175 getinfo = rt_printline_dwarf;
1176 if (rc->num_callers)
1177 n = rc->num_callers;
1180 for (i = level = 0; level < n; i++) {
1181 ret = rt_get_caller_pc(&pc, f, i);
1182 if (ret == -1)
1183 break;
1184 memset(&bi, 0, sizeof bi);
1185 for (rc2 = rc; rc2; rc2 = rc2->next) {
1186 if (getinfo(rc2, pc, &bi))
1187 break;
1188 /* we try symtab symbols (no line number info) */
1189 if (!!(a = rt_elfsym(rc2, pc, &bi.func_pc))) {
1190 pstrcpy(bi.func, sizeof bi.func, a);
1191 break;
1194 //fprintf(stderr, "%d rc %p %p\n", i, (void*)pcfunc, (void*)pc);
1195 if (skip[0] && strstr(bi.file, skip))
1196 continue;
1197 #ifndef CONFIG_TCC_BACKTRACE_ONLY
1199 TCCState *s = rt_find_state(f);
1200 if (s && s->bt_func) {
1201 ret = s->bt_func(
1202 s->bt_data,
1203 (void*)pc,
1204 bi.file[0] ? bi.file : NULL,
1205 bi.line,
1206 bi.func[0] ? bi.func : NULL,
1207 level == 0 ? msg : NULL
1209 if (ret == 0)
1210 break;
1211 goto check_break;
1214 #endif
1215 if (bi.file[0]) {
1216 rt_printf("%s:%d", bi.file, bi.line);
1217 } else {
1218 rt_printf("0x%08llx", (long long)pc);
1220 rt_printf(": %s %s", level ? "by" : "at", bi.func[0] ? bi.func : "???");
1221 if (level == 0) {
1222 rt_printf(": %s", msg);
1223 if (one)
1224 break;
1226 rt_printf("\n");
1228 check_break:
1229 if (rc2
1230 && bi.func_pc
1231 && bi.func_pc == (addr_t)rc2->top_func)
1232 break;
1233 ++level;
1235 rt_post_sem();
1236 return 0;
1239 /* emit a run time error at position 'pc' */
1240 static int rt_error(rt_frame *f, const char *fmt, ...)
1242 va_list ap; char msg[200]; int ret;
1243 va_start(ap, fmt);
1244 snprintf(msg, sizeof msg, "RUNTIME ERROR: %s", fmt);
1245 ret = _tcc_backtrace(f, msg, ap);
1246 va_end(ap);
1247 return ret;
1250 /* ------------------------------------------------------------- */
1252 #ifndef _WIN32
1253 # include <signal.h>
1254 # ifndef __OpenBSD__
1255 # include <sys/ucontext.h>
1256 # endif
1257 #else
1258 # define ucontext_t CONTEXT
1259 #endif
1261 /* translate from ucontext_t* to internal rt_context * */
1262 static void rt_getcontext(ucontext_t *uc, rt_frame *rc)
1264 #if defined _WIN64
1265 rc->ip = uc->Rip;
1266 rc->fp = uc->Rbp;
1267 rc->sp = uc->Rsp;
1268 #elif defined _WIN32
1269 rc->ip = uc->Eip;
1270 rc->fp = uc->Ebp;
1271 rc->sp = uc->Esp;
1272 #elif defined __i386__
1273 # if defined(__APPLE__)
1274 rc->ip = uc->uc_mcontext->__ss.__eip;
1275 rc->fp = uc->uc_mcontext->__ss.__ebp;
1276 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1277 rc->ip = uc->uc_mcontext.mc_eip;
1278 rc->fp = uc->uc_mcontext.mc_ebp;
1279 # elif defined(__dietlibc__)
1280 rc->ip = uc->uc_mcontext.eip;
1281 rc->fp = uc->uc_mcontext.ebp;
1282 # elif defined(__NetBSD__)
1283 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
1284 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
1285 # elif defined(__OpenBSD__)
1286 rc->ip = uc->sc_eip;
1287 rc->fp = uc->sc_ebp;
1288 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
1289 rc->ip = uc->uc_mcontext.gregs[EIP];
1290 rc->fp = uc->uc_mcontext.gregs[EBP];
1291 # else
1292 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
1293 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
1294 # endif
1295 #elif defined(__x86_64__)
1296 # if defined(__APPLE__)
1297 rc->ip = uc->uc_mcontext->__ss.__rip;
1298 rc->fp = uc->uc_mcontext->__ss.__rbp;
1299 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1300 rc->ip = uc->uc_mcontext.mc_rip;
1301 rc->fp = uc->uc_mcontext.mc_rbp;
1302 # elif defined(__NetBSD__)
1303 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
1304 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
1305 # elif defined(__OpenBSD__)
1306 rc->ip = uc->sc_rip;
1307 rc->fp = uc->sc_rbp;
1308 # else
1309 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
1310 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
1311 # endif
1312 #elif defined(__arm__) && defined(__NetBSD__)
1313 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1314 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1315 #elif defined(__arm__) && defined(__OpenBSD__)
1316 rc->ip = uc->sc_pc;
1317 rc->fp = uc->sc_r11;
1318 #elif defined(__arm__) && defined(__FreeBSD__)
1319 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1320 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1321 #elif defined(__arm__)
1322 rc->ip = uc->uc_mcontext.arm_pc;
1323 rc->fp = uc->uc_mcontext.arm_fp;
1324 #elif defined(__aarch64__) && defined(__APPLE__)
1325 // see:
1326 // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h
1327 rc->ip = uc->uc_mcontext->__ss.__pc;
1328 rc->fp = uc->uc_mcontext->__ss.__fp;
1329 #elif defined(__aarch64__) && defined(__FreeBSD__)
1330 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
1331 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
1332 #elif defined(__aarch64__) && defined(__NetBSD__)
1333 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1334 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1335 #elif defined(__aarch64__) && defined(__OpenBSD__)
1336 rc->ip = uc->sc_elr;
1337 rc->fp = uc->sc_x[29];
1338 #elif defined(__aarch64__)
1339 rc->ip = uc->uc_mcontext.pc;
1340 rc->fp = uc->uc_mcontext.regs[29];
1341 #elif defined(__riscv) && defined(__OpenBSD__)
1342 rc->ip = uc->sc_sepc;
1343 rc->fp = uc->sc_s[0];
1344 #elif defined(__riscv)
1345 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
1346 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
1347 #endif
1350 /* ------------------------------------------------------------- */
1351 #ifndef _WIN32
1352 /* signal handler for fatal errors */
1353 static void sig_error(int signum, siginfo_t *siginf, void *puc)
1355 rt_frame f;
1356 rt_getcontext(puc, &f);
1358 switch(signum) {
1359 case SIGFPE:
1360 switch(siginf->si_code) {
1361 case FPE_INTDIV:
1362 case FPE_FLTDIV:
1363 rt_error(&f, "division by zero");
1364 break;
1365 default:
1366 rt_error(&f, "floating point exception");
1367 break;
1369 break;
1370 case SIGBUS:
1371 case SIGSEGV:
1372 rt_error(&f, "invalid memory access");
1373 break;
1374 case SIGILL:
1375 rt_error(&f, "illegal instruction");
1376 break;
1377 case SIGABRT:
1378 rt_error(&f, "abort() called");
1379 break;
1380 default:
1381 rt_error(&f, "caught signal %d", signum);
1382 break;
1385 sigset_t s;
1386 sigemptyset(&s);
1387 sigaddset(&s, signum);
1388 sigprocmask(SIG_UNBLOCK, &s, NULL);
1390 rt_exit(&f, 255);
1393 #ifndef SA_SIGINFO
1394 # define SA_SIGINFO 0x00000004u
1395 #endif
1397 /* Generate a stack backtrace when a CPU exception occurs. */
1398 static void set_exception_handler(void)
1400 struct sigaction sigact;
1401 /* install TCC signal handlers to print debug info on fatal
1402 runtime errors */
1403 sigemptyset (&sigact.sa_mask);
1404 sigact.sa_flags = SA_SIGINFO; //| SA_RESETHAND;
1405 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
1406 sigact.sa_flags |= SA_ONSTACK;
1407 #endif
1408 sigact.sa_sigaction = sig_error;
1409 sigaction(SIGFPE, &sigact, NULL);
1410 sigaction(SIGILL, &sigact, NULL);
1411 sigaction(SIGSEGV, &sigact, NULL);
1412 sigaction(SIGBUS, &sigact, NULL);
1413 sigaction(SIGABRT, &sigact, NULL);
1414 #if 0//def SIGSTKSZ
1415 /* This allows stack overflow to be reported instead of a SEGV */
1417 stack_t ss;
1418 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
1420 ss.ss_sp = stack;
1421 ss.ss_size = SIGSTKSZ;
1422 ss.ss_flags = 0;
1423 sigaltstack(&ss, NULL);
1425 #endif
1428 #else /* WIN32 */
1430 /* signal handler for fatal errors */
1431 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
1433 rt_frame f;
1434 unsigned code;
1435 rt_getcontext(ex_info->ContextRecord, &f);
1437 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
1438 case EXCEPTION_ACCESS_VIOLATION:
1439 rt_error(&f, "invalid memory access");
1440 break;
1441 case EXCEPTION_STACK_OVERFLOW:
1442 rt_error(&f, "stack overflow");
1443 break;
1444 case EXCEPTION_INT_DIVIDE_BY_ZERO:
1445 rt_error(&f, "division by zero");
1446 break;
1447 case EXCEPTION_BREAKPOINT:
1448 case EXCEPTION_SINGLE_STEP:
1449 f.ip = *(addr_t*)f.sp;
1450 rt_error(&f, "breakpoint/single-step exception:");
1451 return EXCEPTION_CONTINUE_SEARCH;
1452 default:
1453 rt_error(&f, "caught exception %08x", code);
1454 break;
1456 rt_exit(&f, 255);
1457 return EXCEPTION_EXECUTE_HANDLER;
1460 /* Generate a stack backtrace when a CPU exception occurs. */
1461 static void set_exception_handler(void)
1463 SetUnhandledExceptionFilter(cpu_exception_handler);
1466 #endif
1468 /* ------------------------------------------------------------- */
1469 /* return the PC at frame level 'level'. Return negative if not found */
1470 #if defined(__i386__) || defined(__x86_64__)
1471 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1473 if (level == 0) {
1474 *paddr = rc->ip;
1475 } else {
1476 addr_t fp = rc->fp;
1477 while (1) {
1478 if (fp < 0x1000)
1479 return -1;
1480 if (0 == --level)
1481 break;
1482 /* XXX: check address validity with program info */
1483 fp = ((addr_t *)fp)[0];
1485 *paddr = ((addr_t *)fp)[1];
1487 return 0;
1490 /* XXX: only supports linux/bsd */
1491 #elif defined(__arm__) && !defined(_WIN32)
1492 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1494 if (level == 0) {
1495 *paddr = rc->ip;
1496 } else {
1497 addr_t fp = rc->fp;
1498 while (1) {
1499 if (fp < 0x1000)
1500 return -1;
1501 if (0 == --level)
1502 break;
1503 fp = ((addr_t *)fp)[0];
1505 *paddr = ((addr_t *)fp)[2];
1507 return 0;
1510 #elif defined(__aarch64__)
1511 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1513 if (level == 0) {
1514 *paddr = rc->ip;
1515 } else {
1516 addr_t fp = rc->fp;
1517 while (1) {
1518 if (fp < 0x1000)
1519 return -1;
1520 if (0 == --level)
1521 break;
1522 fp = ((addr_t *)fp)[0];
1524 *paddr = ((addr_t *)fp)[1];
1526 return 0;
1529 #elif defined(__riscv)
1530 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1532 if (level == 0) {
1533 *paddr = rc->ip;
1534 } else {
1535 addr_t fp = rc->fp;
1536 while (1) {
1537 if (fp < 0x1000)
1538 return -1;
1539 if (0 == --level)
1540 break;
1541 fp = ((addr_t *)fp)[-2];
1543 *paddr = ((addr_t *)fp)[-1];
1545 return 0;
1548 #else
1549 #warning add arch specific rt_get_caller_pc()
1550 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1552 return -1;
1555 #endif
1556 #else // for runmain.c:exit(); when CONFIG_TCC_BACKTRACE == 0 */
1557 static int rt_get_caller_pc(addr_t *paddr, rt_frame *f, int level)
1559 if (level)
1560 return -1;
1561 *paddr = f->ip;
1562 return 0;
1564 #endif /* CONFIG_TCC_BACKTRACE */
1565 /* ------------------------------------------------------------- */
1566 #ifdef CONFIG_TCC_STATIC
1568 /* dummy function for profiling */
1569 ST_FUNC void *dlopen(const char *filename, int flag)
1571 return NULL;
1574 ST_FUNC void dlclose(void *p)
1578 ST_FUNC const char *dlerror(void)
1580 return "error";
1583 typedef struct TCCSyms {
1584 char *str;
1585 void *ptr;
1586 } TCCSyms;
1589 /* add the symbol you want here if no dynamic linking is done */
1590 static TCCSyms tcc_syms[] = {
1591 #if !defined(CONFIG_TCCBOOT)
1592 #define TCCSYM(a) { #a, &a, },
1593 TCCSYM(printf)
1594 TCCSYM(fprintf)
1595 TCCSYM(fopen)
1596 TCCSYM(fclose)
1597 #undef TCCSYM
1598 #endif
1599 { NULL, NULL },
1602 ST_FUNC void *dlsym(void *handle, const char *symbol)
1604 TCCSyms *p;
1605 p = tcc_syms;
1606 while (p->str != NULL) {
1607 if (!strcmp(p->str, symbol))
1608 return p->ptr;
1609 p++;
1611 return NULL;
1614 #endif /* CONFIG_TCC_STATIC */
1615 #endif /* TCC_IS_NATIVE */
1616 /* ------------------------------------------------------------- */