tccrun: add option CONFIG_RUNMEM_RO=2
[tinycc.git] / tccrun.c
blob56ddcc20291a017102e734c29bf3f3a0cea6f3e9
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 /* free loaded dlls array */
183 dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
184 /* unmap or unprotect and free memory */
185 ptr = s1->run_ptr;
186 if (NULL == ptr)
187 return;
188 st_unlink(s1);
189 size = s1->run_size;
190 #ifdef HAVE_SELINUX
191 munmap(ptr, size);
192 #else
193 /* unprotect memory to make it usable for malloc again */
194 protect_pages((void*)PAGEALIGN(ptr), size - PAGESIZE, 2 /*rw*/);
195 # ifdef _WIN64
196 win64_del_function_table(s1->run_function_table);
197 # endif
198 tcc_free(ptr);
199 #endif
202 /* launch the compiled program with the given arguments */
203 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
205 int (*prog_main)(int, char **, char **), ret;
206 const char *top_sym;
207 jmp_buf main_jb;
209 #if defined(__APPLE__) || defined(__FreeBSD__)
210 char **envp = NULL;
211 #elif defined(__OpenBSD__) || defined(__NetBSD__)
212 extern char **environ;
213 char **envp = environ;
214 #else
215 char **envp = environ;
216 #endif
218 /* tcc -dt -run ... nothing to do if no main() */
219 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, "main", 0, 1))
220 return 0;
222 tcc_add_symbol(s1, "__rt_exit", rt_exit);
223 if (s1->nostdlib) {
224 s1->run_main = top_sym = "_start";
225 } else {
226 tcc_add_support(s1, "runmain.o");
227 s1->run_main = "_runmain";
228 top_sym = "main";
230 if (tcc_relocate(s1) < 0)
231 return -1;
233 prog_main = (void*)get_sym_addr(s1, s1->run_main, 1, 1);
234 if ((addr_t)-1 == (addr_t)prog_main)
235 return -1;
236 errno = 0; /* clean errno value */
237 fflush(stdout);
238 fflush(stderr);
240 ret = tcc_setjmp(s1, main_jb, tcc_get_symbol(s1, top_sym));
241 if (0 == ret)
242 ret = prog_main(argc, argv, envp);
243 else if (256 == ret)
244 ret = 0;
246 if (s1->dflag & 16 && ret) /* tcc -dt -run ... */
247 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
248 return ret;
251 /* ------------------------------------------------------------- */
252 /* remove all STB_LOCAL symbols */
253 static void cleanup_symbols(TCCState *s1)
255 Section *s = s1->symtab;
256 int sym_index, end_sym = s->data_offset / sizeof (ElfSym);
257 /* reset symtab */
258 s->data_offset = s->link->data_offset = s->hash->data_offset = 0;
259 init_symtab(s);
260 /* add global symbols again */
261 for (sym_index = 1; sym_index < end_sym; ++sym_index) {
262 ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index];
263 const char *name = (char *)s->link->data + sym->st_name;
264 if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)
265 continue;
266 //printf("sym %s\n", name);
267 put_elf_sym(s, sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx, name);
271 /* free all sections except symbols */
272 static void cleanup_sections(TCCState *s1)
274 struct { Section **secs; int nb_secs; } *p = (void*)&s1->sections;
275 int i, f = 2;
276 do {
277 for (i = --f; i < p->nb_secs; i++) {
278 Section *s = p->secs[i];
279 if (s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash) {
280 s->data = tcc_realloc(s->data, s->data_allocated = s->data_offset);
281 } else {
282 free_section(s), tcc_free(s), p->secs[i] = NULL;
285 } while (++p, f);
288 /* ------------------------------------------------------------- */
289 /* 0 = .text rwx other rw (memory >= 2 pages a 4096 bytes) */
290 /* 1 = .text rx other rw (memory >= 3 pages) */
291 /* 2 = .text rx .rdata ro .data/.bss rw (memory >= 4 pages) */
293 /* Some targets implement secutiry options that do not allow write in
294 executable code. These targets need CONFIG_RUNMEM_RO=1.
295 The disadvantage of this is that it requires a little bit more memory. */
297 #ifndef CONFIG_RUNMEM_RO
298 # ifdef __APPLE__
299 # define CONFIG_RUNMEM_RO 1
300 # else
301 # define CONFIG_RUNMEM_RO 0
302 # endif
303 #endif
305 /* relocate code. Return -1 on error, required size if ptr is NULL,
306 otherwise copy code into buffer passed by the caller */
307 static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff)
309 Section *s;
310 unsigned offset, length, align, i, k, f;
311 unsigned n, copy;
312 addr_t mem, addr;
314 if (NULL == ptr) {
315 #ifdef TCC_TARGET_PE
316 pe_output_file(s1, NULL);
317 #else
318 tcc_add_runtime(s1);
319 resolve_common_syms(s1);
320 build_got_entries(s1, 0);
321 #endif
324 offset = copy = 0;
325 mem = (addr_t)ptr;
326 redo:
327 if (s1->verbose == 2 && copy)
328 printf(&"-----------------------------------------------------\n"[PTR_SIZE*2 - 8]);
329 if (s1->nb_errors)
330 return -1;
331 if (copy == 3)
332 return 0;
334 for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
335 n = 0; addr = 0;
336 for(i = 1; i < s1->nb_sections; i++) {
337 static const char shf[] = {
338 SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
340 s = s1->sections[i];
341 if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
342 continue;
343 length = s->data_offset;
344 if (copy == 2) {
345 if (addr == 0)
346 addr = s->sh_addr;
347 n = (s->sh_addr - addr) + length;
348 continue;
350 if (copy) { /* final step: copy section data to memory */
351 if (s1->verbose == 2)
352 printf("%d: %-16s %p len %05x align %04x\n",
353 k, s->name, (void*)s->sh_addr, length, s->sh_addralign);
354 ptr = (void*)s->sh_addr;
355 if (k == 0)
356 ptr = (void*)(s->sh_addr + ptr_diff);
357 if (NULL == s->data || s->sh_type == SHT_NOBITS)
358 memset(ptr, 0, length);
359 else
360 memcpy(ptr, s->data, length);
361 continue;
364 align = s->sh_addralign;
365 if (++n == 1) {
366 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
367 /* To avoid that x86 processors would reload cached instructions
368 each time when data is written in the near, we need to make
369 sure that code and data do not share the same 64 byte unit */
370 if (align < 64)
371 align = 64;
372 #endif
373 /* start new page for different permissions */
374 if (k <= CONFIG_RUNMEM_RO)
375 align = PAGESIZE;
377 s->sh_addralign = align;
378 addr = k ? mem + ptr_diff : mem;
379 offset += -(addr + offset) & (align - 1);
380 s->sh_addr = mem ? addr + offset : 0;
381 offset += length;
383 if (copy == 2) { /* set permissions */
384 if (n == 0) /* no data */
385 continue;
386 #ifdef HAVE_SELINUX
387 if (k == 0) /* SHF_EXECINSTR has its own mapping */
388 continue;
389 #endif
390 f = k;
391 if (f >= CONFIG_RUNMEM_RO) {
392 if (f != 0)
393 continue;
394 f = 3; /* change only SHF_EXECINSTR to rwx */
396 n = PAGEALIGN(n);
397 if (s1->verbose == 2) {
398 printf("protect %3s %p len %05x\n",
399 &"rx\0ro\0rw\0rwx"[f*3], (void*)addr, (unsigned)n);
401 if (protect_pages((void*)addr, n, f) < 0)
402 return tcc_error_noabort(
403 "mprotect failed (did you mean to configure --with-selinux?)");
407 if (0 == mem)
408 return PAGEALIGN(offset);
410 if (++copy == 2) {
411 goto redo;
413 if (copy == 3) {
414 #ifdef _WIN64
415 s1->run_function_table = win64_add_function_table(s1);
416 #endif
417 /* remove local symbols and free sections except symtab */
418 cleanup_symbols(s1);
419 cleanup_sections(s1);
420 goto redo;
423 /* relocate symbols */
424 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
425 /* relocate sections */
426 #ifdef TCC_TARGET_PE
427 s1->pe_imagebase = mem;
428 #else
429 relocate_plt(s1);
430 #endif
431 relocate_sections(s1);
432 goto redo;
435 /* ------------------------------------------------------------- */
436 /* allow to run code in memory */
438 static int protect_pages(void *ptr, unsigned long length, int mode)
440 #ifdef _WIN32
441 static const unsigned char protect[] = {
442 PAGE_EXECUTE_READ,
443 PAGE_READONLY,
444 PAGE_READWRITE,
445 PAGE_EXECUTE_READWRITE
447 DWORD old;
448 if (!VirtualProtect(ptr, length, protect[mode], &old))
449 return -1;
450 #else
451 static const unsigned char protect[] = {
452 PROT_READ | PROT_EXEC,
453 PROT_READ,
454 PROT_READ | PROT_WRITE,
455 PROT_READ | PROT_WRITE | PROT_EXEC
457 if (mprotect(ptr, length, protect[mode]))
458 return -1;
459 /* XXX: BSD sometimes dump core with bad system call */
460 # if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
461 if (mode == 0 || mode == 3) {
462 void __clear_cache(void *beginning, void *end);
463 __clear_cache(ptr, (char *)ptr + length);
465 # endif
466 #endif
467 return 0;
470 #ifdef _WIN64
471 static void *win64_add_function_table(TCCState *s1)
473 void *p = NULL;
474 if (s1->uw_pdata) {
475 p = (void*)s1->uw_pdata->sh_addr;
476 RtlAddFunctionTable(
477 (RUNTIME_FUNCTION*)p,
478 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
479 s1->pe_imagebase
481 s1->uw_pdata = NULL;
483 return p;
486 static void win64_del_function_table(void *p)
488 if (p) {
489 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
492 #endif
494 static void bt_link(TCCState *s1)
496 #ifdef CONFIG_TCC_BACKTRACE
497 rt_context *rc;
498 void *p;
500 if (!s1->do_backtrace)
501 return;
502 rc = tcc_get_symbol(s1, "__rt_info");
503 if (!rc)
504 return;
505 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
506 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
507 rc->elf_str = (char *)symtab_section->link->data;
508 if (PTR_SIZE == 8 && !s1->dwarf)
509 rc->prog_base &= 0xffffffff00000000ULL;
510 #ifdef CONFIG_TCC_BCHECK
511 if (s1->do_bounds_check) {
512 if ((p = tcc_get_symbol(s1, "__bound_init")))
513 ((void(*)(void*,int))p)(rc->bounds_start, 1);
515 #endif
516 rc->next = g_rc, g_rc = rc, s1->rc = rc;
517 if (0 == signal_set)
518 set_exception_handler(), signal_set = 1;
519 #endif
522 static void st_link(TCCState *s1)
524 rt_wait_sem();
525 s1->next = g_s1, g_s1 = s1;
526 bt_link(s1);
527 rt_post_sem();
530 /* remove 'el' from 'list' */
531 static void ptr_unlink(void *list, void *e, unsigned next)
533 void **pp, **nn, *p;
534 for (pp = list; !!(p = *pp); pp = nn) {
535 nn = (void*)((char*)p + next); /* nn = &p->next; */
536 if (p == e) {
537 *pp = *nn;
538 break;
543 static void st_unlink(TCCState *s1)
545 rt_wait_sem();
546 #ifdef CONFIG_TCC_BACKTRACE
547 ptr_unlink(&g_rc, s1->rc, offsetof(rt_context, next));
548 #endif
549 ptr_unlink(&g_s1, s1, offsetof(TCCState, next));
550 rt_post_sem();
553 LIBTCCAPI void *_tcc_setjmp(TCCState *s1, void *p_jmp_buf, void *func, void *p_longjmp)
555 s1->run_lj = p_longjmp;
556 s1->run_jb = p_jmp_buf;
557 #ifdef CONFIG_TCC_BACKTRACE
558 if (s1->rc)
559 s1->rc->top_func = func;
560 #endif
561 return p_jmp_buf;
564 LIBTCCAPI void tcc_set_backtrace_func(TCCState *s1, void *data, TCCBtFunc *func)
566 s1->bt_func = func;
567 s1->bt_data = data;
570 static TCCState *rt_find_state(rt_frame *f)
572 TCCState *s;
573 int level;
574 addr_t pc;
576 s = g_s1;
577 if (NULL == s || NULL == s->next) {
578 /* play it safe in the simple case when there is only one state */
579 return s;
581 for (level = 0; level < 8; ++level) {
582 if (rt_get_caller_pc(&pc, f, level) < 0)
583 break;
584 for (s = g_s1; s; s = s->next) {
585 if (pc >= (addr_t)s->run_ptr
586 && pc < (addr_t)s->run_ptr + s->run_size)
587 return s;
590 return NULL;
593 static void rt_exit(rt_frame *f, int code)
595 TCCState *s;
596 rt_wait_sem();
597 s = rt_find_state(f);
598 rt_post_sem();
599 if (s && s->run_lj) {
600 if (code == 0)
601 code = 256;
602 ((void(*)(void*,int))s->run_lj)(s->run_jb, code);
604 exit(code);
607 /* ------------------------------------------------------------- */
608 #else // if defined CONFIG_TCC_BACKTRACE_ONLY
609 static void rt_exit(rt_frame *f, int code)
611 exit(code);
613 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
614 /* ------------------------------------------------------------- */
615 #ifdef CONFIG_TCC_BACKTRACE
617 static int rt_vprintf(const char *fmt, va_list ap)
619 int ret = vfprintf(stderr, fmt, ap);
620 fflush(stderr);
621 return ret;
624 static int rt_printf(const char *fmt, ...)
626 va_list ap;
627 int r;
628 va_start(ap, fmt);
629 r = rt_vprintf(fmt, ap);
630 va_end(ap);
631 return r;
634 static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr)
636 ElfW(Sym) *esym;
637 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
638 int type = ELFW(ST_TYPE)(esym->st_info);
639 if ((type == STT_FUNC || type == STT_GNU_IFUNC)
640 && wanted_pc >= esym->st_value
641 && wanted_pc < esym->st_value + esym->st_size) {
642 *func_addr = esym->st_value;
643 return rc->elf_str + esym->st_name;
646 return NULL;
649 typedef struct bt_info
651 char file[100];
652 int line;
653 char func[100];
654 addr_t func_pc;
655 } bt_info;
657 /* print the position in the source file of PC value 'pc' by reading
658 the stabs debug information */
659 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc, bt_info *bi)
661 char func_name[128];
662 addr_t func_addr, last_pc, pc;
663 const char *incl_files[INCLUDE_STACK_SIZE];
664 int incl_index, last_incl_index, len, last_line_num, i;
665 const char *str, *p;
666 Stab_Sym *sym;
668 func_name[0] = '\0';
669 func_addr = 0;
670 incl_index = 0;
671 last_pc = (addr_t)-1;
672 last_line_num = 1;
673 last_incl_index = 0;
675 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
676 str = rc->stab_str + sym->n_strx;
677 pc = sym->n_value;
679 switch(sym->n_type) {
680 case N_SLINE:
681 if (func_addr)
682 goto rel_pc;
683 case N_SO:
684 case N_SOL:
685 goto abs_pc;
686 case N_FUN:
687 if (sym->n_strx == 0) /* end of function */
688 goto rel_pc;
689 abs_pc:
690 #if PTR_SIZE == 8
691 /* Stab_Sym.n_value is only 32bits */
692 pc += rc->prog_base;
693 #endif
694 goto check_pc;
695 rel_pc:
696 pc += func_addr;
697 check_pc:
698 if (pc >= wanted_pc && wanted_pc >= last_pc)
699 goto found;
700 break;
703 switch(sym->n_type) {
704 /* function start or end */
705 case N_FUN:
706 if (sym->n_strx == 0)
707 goto reset_func;
708 p = strchr(str, ':');
709 if (0 == p || (len = p - str + 1, len > sizeof func_name))
710 len = sizeof func_name;
711 pstrcpy(func_name, len, str);
712 func_addr = pc;
713 break;
714 /* line number info */
715 case N_SLINE:
716 last_pc = pc;
717 last_line_num = sym->n_desc;
718 last_incl_index = incl_index;
719 break;
720 /* include files */
721 case N_BINCL:
722 if (incl_index < INCLUDE_STACK_SIZE)
723 incl_files[incl_index++] = str;
724 break;
725 case N_EINCL:
726 if (incl_index > 1)
727 incl_index--;
728 break;
729 /* start/end of translation unit */
730 case N_SO:
731 incl_index = 0;
732 if (sym->n_strx) {
733 /* do not add path */
734 len = strlen(str);
735 if (len > 0 && str[len - 1] != '/')
736 incl_files[incl_index++] = str;
738 reset_func:
739 func_name[0] = '\0';
740 func_addr = 0;
741 last_pc = (addr_t)-1;
742 break;
743 /* alternative file name (from #line or #include directives) */
744 case N_SOL:
745 if (incl_index)
746 incl_files[incl_index-1] = str;
747 break;
750 last_incl_index = 0, func_name[0] = 0, func_addr = 0;
751 found:
752 i = last_incl_index;
753 if (i > 0) {
754 pstrcpy(bi->file, sizeof bi->file, incl_files[--i]);
755 bi->line = last_line_num;
757 pstrcpy(bi->func, sizeof bi->func, func_name);
758 bi->func_pc = func_addr;
759 return func_addr;
762 /* ------------------------------------------------------------- */
763 /* rt_printline - dwarf version */
765 #define MAX_128 ((8 * sizeof (long long) + 6) / 7)
767 #define DIR_TABLE_SIZE (64)
768 #define FILE_TABLE_SIZE (512)
770 #define dwarf_read_1(ln,end) \
771 ((ln) < (end) ? *(ln)++ : 0)
772 #define dwarf_read_2(ln,end) \
773 ((ln) + 2 < (end) ? (ln) += 2, read16le((ln) - 2) : 0)
774 #define dwarf_read_4(ln,end) \
775 ((ln) + 4 < (end) ? (ln) += 4, read32le((ln) - 4) : 0)
776 #define dwarf_read_8(ln,end) \
777 ((ln) + 8 < (end) ? (ln) += 8, read64le((ln) - 8) : 0)
778 #define dwarf_ignore_type(ln, end) /* timestamp/size/md5/... */ \
779 switch (entry_format[j].form) { \
780 case DW_FORM_data1: (ln) += 1; break; \
781 case DW_FORM_data2: (ln) += 2; break; \
782 case DW_FORM_data4: (ln) += 3; break; \
783 case DW_FORM_data8: (ln) += 8; break; \
784 case DW_FORM_data16: (ln) += 16; break; \
785 case DW_FORM_udata: dwarf_read_uleb128(&(ln), (end)); break; \
786 default: goto next_line; \
789 static unsigned long long
790 dwarf_read_uleb128(unsigned char **ln, unsigned char *end)
792 unsigned char *cp = *ln;
793 unsigned long long retval = 0;
794 int i;
796 for (i = 0; i < MAX_128; i++) {
797 unsigned long long byte = dwarf_read_1(cp, end);
799 retval |= (byte & 0x7f) << (i * 7);
800 if ((byte & 0x80) == 0)
801 break;
803 *ln = cp;
804 return retval;
807 static long long
808 dwarf_read_sleb128(unsigned char **ln, unsigned char *end)
810 unsigned char *cp = *ln;
811 long long retval = 0;
812 int i;
814 for (i = 0; i < MAX_128; i++) {
815 unsigned long long byte = dwarf_read_1(cp, end);
817 retval |= (byte & 0x7f) << (i * 7);
818 if ((byte & 0x80) == 0) {
819 if ((byte & 0x40) && (i + 1) * 7 < 64)
820 retval |= -1LL << ((i + 1) * 7);
821 break;
824 *ln = cp;
825 return retval;
828 static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc, bt_info *bi)
830 unsigned char *ln;
831 unsigned char *cp;
832 unsigned char *end;
833 unsigned char *opcode_length;
834 unsigned long long size;
835 unsigned int length;
836 unsigned char version;
837 unsigned int min_insn_length;
838 unsigned int max_ops_per_insn;
839 int line_base;
840 unsigned int line_range;
841 unsigned int opcode_base;
842 unsigned int opindex;
843 unsigned int col;
844 unsigned int i;
845 unsigned int j;
846 unsigned int len;
847 unsigned long long value;
848 struct {
849 unsigned int type;
850 unsigned int form;
851 } entry_format[256];
852 unsigned int dir_size;
853 #if 0
854 char *dirs[DIR_TABLE_SIZE];
855 #endif
856 unsigned int filename_size;
857 struct dwarf_filename_struct {
858 unsigned int dir_entry;
859 char *name;
860 } filename_table[FILE_TABLE_SIZE];
861 addr_t last_pc;
862 addr_t pc;
863 addr_t func_addr;
864 int line;
865 char *filename;
866 char *function;
868 filename = NULL;
869 function = NULL;
870 func_addr = 0;
871 line = 0;
873 ln = rc->dwarf_line;
874 while (ln < rc->dwarf_line_end) {
875 dir_size = 0;
876 filename_size = 0;
877 last_pc = 0;
878 pc = 0;
879 func_addr = 0;
880 line = 1;
881 filename = NULL;
882 function = NULL;
883 length = 4;
884 size = dwarf_read_4(ln, rc->dwarf_line_end);
885 if (size == 0xffffffffu) // dwarf 64
886 length = 8, size = dwarf_read_8(ln, rc->dwarf_line_end);
887 end = ln + size;
888 if (end < ln || end > rc->dwarf_line_end)
889 break;
890 version = dwarf_read_2(ln, end);
891 if (version >= 5)
892 ln += length + 2; // address size, segment selector, prologue Length
893 else
894 ln += length; // prologue Length
895 min_insn_length = dwarf_read_1(ln, end);
896 if (version >= 4)
897 max_ops_per_insn = dwarf_read_1(ln, end);
898 else
899 max_ops_per_insn = 1;
900 ln++; // Initial value of 'is_stmt'
901 line_base = dwarf_read_1(ln, end);
902 line_base |= line_base >= 0x80 ? ~0xff : 0;
903 line_range = dwarf_read_1(ln, end);
904 opcode_base = dwarf_read_1(ln, end);
905 opcode_length = ln;
906 ln += opcode_base - 1;
907 opindex = 0;
908 if (version >= 5) {
909 col = dwarf_read_1(ln, end);
910 for (i = 0; i < col; i++) {
911 entry_format[i].type = dwarf_read_uleb128(&ln, end);
912 entry_format[i].form = dwarf_read_uleb128(&ln, end);
914 dir_size = dwarf_read_uleb128(&ln, end);
915 for (i = 0; i < dir_size; i++) {
916 for (j = 0; j < col; j++) {
917 if (entry_format[j].type == DW_LNCT_path) {
918 if (entry_format[j].form != DW_FORM_line_strp)
919 goto next_line;
920 #if 0
921 value = length == 4 ? dwarf_read_4(ln, end)
922 : dwarf_read_8(ln, end);
923 if (i < DIR_TABLE_SIZE)
924 dirs[i] = (char *)rc->dwarf_line_str + value;
925 #else
926 length == 4 ? dwarf_read_4(ln, end)
927 : dwarf_read_8(ln, end);
928 #endif
930 else
931 dwarf_ignore_type(ln, end);
934 col = dwarf_read_1(ln, end);
935 for (i = 0; i < col; i++) {
936 entry_format[i].type = dwarf_read_uleb128(&ln, end);
937 entry_format[i].form = dwarf_read_uleb128(&ln, end);
939 filename_size = dwarf_read_uleb128(&ln, end);
940 for (i = 0; i < filename_size; i++)
941 for (j = 0; j < col; j++) {
942 if (entry_format[j].type == DW_LNCT_path) {
943 if (entry_format[j].form != DW_FORM_line_strp)
944 goto next_line;
945 value = length == 4 ? dwarf_read_4(ln, end)
946 : dwarf_read_8(ln, end);
947 if (i < FILE_TABLE_SIZE)
948 filename_table[i].name =
949 (char *)rc->dwarf_line_str + value;
951 else if (entry_format[j].type == DW_LNCT_directory_index) {
952 switch (entry_format[j].form) {
953 case DW_FORM_data1: value = dwarf_read_1(ln, end); break;
954 case DW_FORM_data2: value = dwarf_read_2(ln, end); break;
955 case DW_FORM_data4: value = dwarf_read_4(ln, end); break;
956 case DW_FORM_udata: value = dwarf_read_uleb128(&ln, end); break;
957 default: goto next_line;
959 if (i < FILE_TABLE_SIZE)
960 filename_table[i].dir_entry = value;
962 else
963 dwarf_ignore_type(ln, end);
966 else {
967 while ((dwarf_read_1(ln, end))) {
968 #if 0
969 if (++dir_size < DIR_TABLE_SIZE)
970 dirs[dir_size - 1] = (char *)ln - 1;
971 #endif
972 while (dwarf_read_1(ln, end)) {}
974 while ((dwarf_read_1(ln, end))) {
975 if (++filename_size < FILE_TABLE_SIZE) {
976 filename_table[filename_size - 1].name = (char *)ln - 1;
977 while (dwarf_read_1(ln, end)) {}
978 filename_table[filename_size - 1].dir_entry =
979 dwarf_read_uleb128(&ln, end);
981 else {
982 while (dwarf_read_1(ln, end)) {}
983 dwarf_read_uleb128(&ln, end);
985 dwarf_read_uleb128(&ln, end); // time
986 dwarf_read_uleb128(&ln, end); // size
989 if (filename_size >= 1)
990 filename = filename_table[0].name;
991 while (ln < end) {
992 last_pc = pc;
993 i = dwarf_read_1(ln, end);
994 if (i >= opcode_base) {
995 if (max_ops_per_insn == 1)
996 pc += ((i - opcode_base) / line_range) * min_insn_length;
997 else {
998 pc += (opindex + (i - opcode_base) / line_range) /
999 max_ops_per_insn * min_insn_length;
1000 opindex = (opindex + (i - opcode_base) / line_range) %
1001 max_ops_per_insn;
1003 i = (int)((i - opcode_base) % line_range) + line_base;
1004 check_pc:
1005 if (pc >= wanted_pc && wanted_pc >= last_pc)
1006 goto found;
1007 line += i;
1009 else {
1010 switch (i) {
1011 case 0:
1012 len = dwarf_read_uleb128(&ln, end);
1013 cp = ln;
1014 ln += len;
1015 if (len == 0)
1016 goto next_line;
1017 switch (dwarf_read_1(cp, end)) {
1018 case DW_LNE_end_sequence:
1019 break;
1020 case DW_LNE_set_address:
1021 #if PTR_SIZE == 4
1022 pc = dwarf_read_4(cp, end);
1023 #else
1024 pc = dwarf_read_8(cp, end);
1025 #endif
1026 #if defined TCC_TARGET_MACHO
1027 pc += rc->prog_base;
1028 #endif
1029 opindex = 0;
1030 break;
1031 case DW_LNE_define_file: /* deprecated */
1032 if (++filename_size < FILE_TABLE_SIZE) {
1033 filename_table[filename_size - 1].name = (char *)ln - 1;
1034 while (dwarf_read_1(ln, end)) {}
1035 filename_table[filename_size - 1].dir_entry =
1036 dwarf_read_uleb128(&ln, end);
1038 else {
1039 while (dwarf_read_1(ln, end)) {}
1040 dwarf_read_uleb128(&ln, end);
1042 dwarf_read_uleb128(&ln, end); // time
1043 dwarf_read_uleb128(&ln, end); // size
1044 break;
1045 case DW_LNE_hi_user - 1:
1046 function = (char *)cp;
1047 func_addr = pc;
1048 break;
1049 default:
1050 break;
1052 break;
1053 case DW_LNS_advance_pc:
1054 if (max_ops_per_insn == 1)
1055 pc += dwarf_read_uleb128(&ln, end) * min_insn_length;
1056 else {
1057 unsigned long long off = dwarf_read_uleb128(&ln, end);
1059 pc += (opindex + off) / max_ops_per_insn *
1060 min_insn_length;
1061 opindex = (opindex + off) % max_ops_per_insn;
1063 i = 0;
1064 goto check_pc;
1065 case DW_LNS_advance_line:
1066 line += dwarf_read_sleb128(&ln, end);
1067 break;
1068 case DW_LNS_set_file:
1069 i = dwarf_read_uleb128(&ln, end);
1070 i -= i > 0 && version < 5;
1071 if (i < FILE_TABLE_SIZE && i < filename_size)
1072 filename = filename_table[i].name;
1073 break;
1074 case DW_LNS_const_add_pc:
1075 if (max_ops_per_insn == 1)
1076 pc += ((255 - opcode_base) / line_range) * min_insn_length;
1077 else {
1078 unsigned int off = (255 - opcode_base) / line_range;
1080 pc += ((opindex + off) / max_ops_per_insn) *
1081 min_insn_length;
1082 opindex = (opindex + off) % max_ops_per_insn;
1084 i = 0;
1085 goto check_pc;
1086 case DW_LNS_fixed_advance_pc:
1087 i = dwarf_read_2(ln, end);
1088 pc += i;
1089 opindex = 0;
1090 i = 0;
1091 goto check_pc;
1092 default:
1093 for (j = 0; j < opcode_length[i - 1]; j++)
1094 dwarf_read_uleb128 (&ln, end);
1095 break;
1099 next_line:
1100 ln = end;
1102 filename = function = NULL, func_addr = 0;
1103 found:
1104 if (filename)
1105 pstrcpy(bi->file, sizeof bi->file, filename), bi->line = line;
1106 if (function)
1107 pstrcpy(bi->func, sizeof bi->func, function);
1108 bi->func_pc = func_addr;
1109 return (addr_t)func_addr;
1111 /* ------------------------------------------------------------- */
1112 #ifndef CONFIG_TCC_BACKTRACE_ONLY
1113 static
1114 #endif
1115 int _tcc_backtrace(rt_frame *f, const char *fmt, va_list ap)
1117 rt_context *rc, *rc2;
1118 addr_t pc;
1119 char skip[40], msg[200];
1120 int i, level, ret, n, one;
1121 const char *a, *b;
1122 bt_info bi;
1123 addr_t (*getinfo)(rt_context*, addr_t, bt_info*);
1125 skip[0] = 0;
1126 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
1127 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
1128 memcpy(skip, a, b - a), skip[b - a] = 0;
1129 fmt = b + 1;
1131 one = 0;
1132 /* hack for bcheck.c:dprintf(): one level, no newline */
1133 if (fmt[0] == '\001')
1134 ++fmt, one = 1;
1135 vsnprintf(msg, sizeof msg, fmt, ap);
1137 rt_wait_sem();
1138 rc = g_rc;
1139 getinfo = rt_printline, n = 6;
1140 if (rc) {
1141 if (rc->dwarf)
1142 getinfo = rt_printline_dwarf;
1143 if (rc->num_callers)
1144 n = rc->num_callers;
1147 for (i = level = 0; level < n; i++) {
1148 ret = rt_get_caller_pc(&pc, f, i);
1149 if (ret == -1)
1150 break;
1151 memset(&bi, 0, sizeof bi);
1152 for (rc2 = rc; rc2; rc2 = rc2->next) {
1153 if (getinfo(rc2, pc, &bi))
1154 break;
1155 /* we try symtab symbols (no line number info) */
1156 if (!!(a = rt_elfsym(rc2, pc, &bi.func_pc))) {
1157 pstrcpy(bi.func, sizeof bi.func, a);
1158 break;
1161 //fprintf(stderr, "%d rc %p %p\n", i, (void*)pcfunc, (void*)pc);
1162 if (skip[0] && strstr(bi.file, skip))
1163 continue;
1164 #ifndef CONFIG_TCC_BACKTRACE_ONLY
1166 TCCState *s = rt_find_state(f);
1167 if (s && s->bt_func) {
1168 ret = s->bt_func(
1169 s->bt_data,
1170 (void*)pc,
1171 bi.file[0] ? bi.file : NULL,
1172 bi.line,
1173 bi.func[0] ? bi.func : NULL,
1174 level == 0 ? msg : NULL
1176 if (ret == 0)
1177 break;
1178 goto check_break;
1181 #endif
1182 if (bi.file[0]) {
1183 rt_printf("%s:%d", bi.file, bi.line);
1184 } else {
1185 rt_printf("0x%08llx", (long long)pc);
1187 rt_printf(": %s %s", level ? "by" : "at", bi.func[0] ? bi.func : "???");
1188 if (level == 0) {
1189 rt_printf(": %s", msg);
1190 if (one)
1191 break;
1193 rt_printf("\n");
1195 #ifndef CONFIG_TCC_BACKTRACE_ONLY
1196 check_break:
1197 #endif
1198 if (rc2
1199 && bi.func_pc
1200 && bi.func_pc == (addr_t)rc2->top_func)
1201 break;
1202 ++level;
1204 rt_post_sem();
1205 return 0;
1208 /* emit a run time error at position 'pc' */
1209 static int rt_error(rt_frame *f, const char *fmt, ...)
1211 va_list ap; char msg[200]; int ret;
1212 va_start(ap, fmt);
1213 snprintf(msg, sizeof msg, "RUNTIME ERROR: %s", fmt);
1214 ret = _tcc_backtrace(f, msg, ap);
1215 va_end(ap);
1216 return ret;
1219 /* ------------------------------------------------------------- */
1221 #ifndef _WIN32
1222 # include <signal.h>
1223 # ifndef __OpenBSD__
1224 # include <sys/ucontext.h>
1225 # endif
1226 #else
1227 # define ucontext_t CONTEXT
1228 #endif
1230 /* translate from ucontext_t* to internal rt_context * */
1231 static void rt_getcontext(ucontext_t *uc, rt_frame *rc)
1233 #if defined _WIN64
1234 rc->ip = uc->Rip;
1235 rc->fp = uc->Rbp;
1236 rc->sp = uc->Rsp;
1237 #elif defined _WIN32
1238 rc->ip = uc->Eip;
1239 rc->fp = uc->Ebp;
1240 rc->sp = uc->Esp;
1241 #elif defined __i386__
1242 # if defined(__APPLE__)
1243 rc->ip = uc->uc_mcontext->__ss.__eip;
1244 rc->fp = uc->uc_mcontext->__ss.__ebp;
1245 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1246 rc->ip = uc->uc_mcontext.mc_eip;
1247 rc->fp = uc->uc_mcontext.mc_ebp;
1248 # elif defined(__dietlibc__)
1249 rc->ip = uc->uc_mcontext.eip;
1250 rc->fp = uc->uc_mcontext.ebp;
1251 # elif defined(__NetBSD__)
1252 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
1253 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
1254 # elif defined(__OpenBSD__)
1255 rc->ip = uc->sc_eip;
1256 rc->fp = uc->sc_ebp;
1257 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
1258 rc->ip = uc->uc_mcontext.gregs[EIP];
1259 rc->fp = uc->uc_mcontext.gregs[EBP];
1260 # else
1261 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
1262 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
1263 # endif
1264 #elif defined(__x86_64__)
1265 # if defined(__APPLE__)
1266 rc->ip = uc->uc_mcontext->__ss.__rip;
1267 rc->fp = uc->uc_mcontext->__ss.__rbp;
1268 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1269 rc->ip = uc->uc_mcontext.mc_rip;
1270 rc->fp = uc->uc_mcontext.mc_rbp;
1271 # elif defined(__NetBSD__)
1272 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
1273 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
1274 # elif defined(__OpenBSD__)
1275 rc->ip = uc->sc_rip;
1276 rc->fp = uc->sc_rbp;
1277 # else
1278 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
1279 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
1280 # endif
1281 #elif defined(__arm__) && defined(__NetBSD__)
1282 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1283 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1284 #elif defined(__arm__) && defined(__OpenBSD__)
1285 rc->ip = uc->sc_pc;
1286 rc->fp = uc->sc_r11;
1287 #elif defined(__arm__) && defined(__FreeBSD__)
1288 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1289 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1290 #elif defined(__arm__)
1291 rc->ip = uc->uc_mcontext.arm_pc;
1292 rc->fp = uc->uc_mcontext.arm_fp;
1293 #elif defined(__aarch64__) && defined(__APPLE__)
1294 // see:
1295 // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h
1296 rc->ip = uc->uc_mcontext->__ss.__pc;
1297 rc->fp = uc->uc_mcontext->__ss.__fp;
1298 #elif defined(__aarch64__) && defined(__FreeBSD__)
1299 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
1300 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
1301 #elif defined(__aarch64__) && defined(__NetBSD__)
1302 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1303 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1304 #elif defined(__aarch64__) && defined(__OpenBSD__)
1305 rc->ip = uc->sc_elr;
1306 rc->fp = uc->sc_x[29];
1307 #elif defined(__aarch64__)
1308 rc->ip = uc->uc_mcontext.pc;
1309 rc->fp = uc->uc_mcontext.regs[29];
1310 #elif defined(__riscv) && defined(__OpenBSD__)
1311 rc->ip = uc->sc_sepc;
1312 rc->fp = uc->sc_s[0];
1313 #elif defined(__riscv)
1314 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
1315 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
1316 #endif
1319 /* ------------------------------------------------------------- */
1320 #ifndef _WIN32
1321 /* signal handler for fatal errors */
1322 static void sig_error(int signum, siginfo_t *siginf, void *puc)
1324 rt_frame f;
1325 rt_getcontext(puc, &f);
1327 switch(signum) {
1328 case SIGFPE:
1329 switch(siginf->si_code) {
1330 case FPE_INTDIV:
1331 case FPE_FLTDIV:
1332 rt_error(&f, "division by zero");
1333 break;
1334 default:
1335 rt_error(&f, "floating point exception");
1336 break;
1338 break;
1339 case SIGBUS:
1340 case SIGSEGV:
1341 rt_error(&f, "invalid memory access");
1342 break;
1343 case SIGILL:
1344 rt_error(&f, "illegal instruction");
1345 break;
1346 case SIGABRT:
1347 rt_error(&f, "abort() called");
1348 break;
1349 default:
1350 rt_error(&f, "caught signal %d", signum);
1351 break;
1354 sigset_t s;
1355 sigemptyset(&s);
1356 sigaddset(&s, signum);
1357 sigprocmask(SIG_UNBLOCK, &s, NULL);
1359 rt_exit(&f, 255);
1362 #ifndef SA_SIGINFO
1363 # define SA_SIGINFO 0x00000004u
1364 #endif
1366 /* Generate a stack backtrace when a CPU exception occurs. */
1367 static void set_exception_handler(void)
1369 struct sigaction sigact;
1370 /* install TCC signal handlers to print debug info on fatal
1371 runtime errors */
1372 sigemptyset (&sigact.sa_mask);
1373 sigact.sa_flags = SA_SIGINFO; //| SA_RESETHAND;
1374 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
1375 sigact.sa_flags |= SA_ONSTACK;
1376 #endif
1377 sigact.sa_sigaction = sig_error;
1378 sigaction(SIGFPE, &sigact, NULL);
1379 sigaction(SIGILL, &sigact, NULL);
1380 sigaction(SIGSEGV, &sigact, NULL);
1381 sigaction(SIGBUS, &sigact, NULL);
1382 sigaction(SIGABRT, &sigact, NULL);
1383 #if 0//def SIGSTKSZ
1384 /* This allows stack overflow to be reported instead of a SEGV */
1386 stack_t ss;
1387 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
1389 ss.ss_sp = stack;
1390 ss.ss_size = SIGSTKSZ;
1391 ss.ss_flags = 0;
1392 sigaltstack(&ss, NULL);
1394 #endif
1397 #else /* WIN32 */
1399 /* signal handler for fatal errors */
1400 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
1402 rt_frame f;
1403 unsigned code;
1404 rt_getcontext(ex_info->ContextRecord, &f);
1406 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
1407 case EXCEPTION_ACCESS_VIOLATION:
1408 rt_error(&f, "invalid memory access");
1409 break;
1410 case EXCEPTION_STACK_OVERFLOW:
1411 rt_error(&f, "stack overflow");
1412 break;
1413 case EXCEPTION_INT_DIVIDE_BY_ZERO:
1414 rt_error(&f, "division by zero");
1415 break;
1416 case EXCEPTION_BREAKPOINT:
1417 case EXCEPTION_SINGLE_STEP:
1418 f.ip = *(addr_t*)f.sp;
1419 rt_error(&f, "breakpoint/single-step exception:");
1420 return EXCEPTION_CONTINUE_SEARCH;
1421 default:
1422 rt_error(&f, "caught exception %08x", code);
1423 break;
1425 rt_exit(&f, 255);
1426 return EXCEPTION_EXECUTE_HANDLER;
1429 /* Generate a stack backtrace when a CPU exception occurs. */
1430 static void set_exception_handler(void)
1432 SetUnhandledExceptionFilter(cpu_exception_handler);
1435 #endif
1437 /* ------------------------------------------------------------- */
1438 /* return the PC at frame level 'level'. Return negative if not found */
1439 #if defined(__i386__) || defined(__x86_64__)
1440 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1442 if (level == 0) {
1443 *paddr = rc->ip;
1444 } else {
1445 addr_t fp = rc->fp;
1446 while (1) {
1447 if (fp < 0x1000)
1448 return -1;
1449 if (0 == --level)
1450 break;
1451 /* XXX: check address validity with program info */
1452 fp = ((addr_t *)fp)[0];
1454 *paddr = ((addr_t *)fp)[1];
1456 return 0;
1459 /* XXX: only supports linux/bsd */
1460 #elif defined(__arm__) && !defined(_WIN32)
1461 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1463 if (level == 0) {
1464 *paddr = rc->ip;
1465 } else {
1466 addr_t fp = rc->fp;
1467 while (1) {
1468 if (fp < 0x1000)
1469 return -1;
1470 if (0 == --level)
1471 break;
1472 fp = ((addr_t *)fp)[0];
1474 *paddr = ((addr_t *)fp)[2];
1476 return 0;
1479 #elif defined(__aarch64__)
1480 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1482 if (level == 0) {
1483 *paddr = rc->ip;
1484 } else {
1485 addr_t fp = rc->fp;
1486 while (1) {
1487 if (fp < 0x1000)
1488 return -1;
1489 if (0 == --level)
1490 break;
1491 fp = ((addr_t *)fp)[0];
1493 *paddr = ((addr_t *)fp)[1];
1495 return 0;
1498 #elif defined(__riscv)
1499 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1501 if (level == 0) {
1502 *paddr = rc->ip;
1503 } else {
1504 addr_t fp = rc->fp;
1505 while (1) {
1506 if (fp < 0x1000)
1507 return -1;
1508 if (0 == --level)
1509 break;
1510 fp = ((addr_t *)fp)[-2];
1512 *paddr = ((addr_t *)fp)[-1];
1514 return 0;
1517 #else
1518 #warning add arch specific rt_get_caller_pc()
1519 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1521 return -1;
1524 #endif
1525 #else // for runmain.c:exit(); when CONFIG_TCC_BACKTRACE == 0 */
1526 static int rt_get_caller_pc(addr_t *paddr, rt_frame *f, int level)
1528 if (level)
1529 return -1;
1530 *paddr = f->ip;
1531 return 0;
1533 #endif /* CONFIG_TCC_BACKTRACE */
1534 /* ------------------------------------------------------------- */
1535 #ifdef CONFIG_TCC_STATIC
1537 /* dummy function for profiling */
1538 ST_FUNC void *dlopen(const char *filename, int flag)
1540 return NULL;
1543 ST_FUNC void dlclose(void *p)
1547 ST_FUNC const char *dlerror(void)
1549 return "error";
1552 typedef struct TCCSyms {
1553 char *str;
1554 void *ptr;
1555 } TCCSyms;
1558 /* add the symbol you want here if no dynamic linking is done */
1559 static TCCSyms tcc_syms[] = {
1560 #if !defined(CONFIG_TCCBOOT)
1561 #define TCCSYM(a) { #a, &a, },
1562 TCCSYM(printf)
1563 TCCSYM(fprintf)
1564 TCCSYM(fopen)
1565 TCCSYM(fclose)
1566 #undef TCCSYM
1567 #endif
1568 { NULL, NULL },
1571 ST_FUNC void *dlsym(void *handle, const char *symbol)
1573 TCCSyms *p;
1574 p = tcc_syms;
1575 while (p->str != NULL) {
1576 if (!strcmp(p->str, symbol))
1577 return p->ptr;
1578 p++;
1580 return NULL;
1583 #endif /* CONFIG_TCC_STATIC */
1584 #endif /* TCC_IS_NATIVE */
1585 /* ------------------------------------------------------------- */