1/*
2 * TCC - Tiny C Compiler - Support for -run switch
3 *
4 * Copyright (c) 2001-2004 Fabrice Bellard
5 *
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.
10 *
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.
15 *
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
19 */
20
21#include "tcc.h"
22
23/* only native compiler supports -run */
24#ifdef TCC_IS_NATIVE
25
26#ifdef CONFIG_TCC_BACKTRACE
27typedef struct rt_context
28{
29 /* --> tccelf.c:tcc_add_btstub wants those below in that order: */
30 Stab_Sym *stab_sym, *stab_sym_end;
31 char *stab_str;
32 ElfW(Sym) *esym_start, *esym_end;
33 char *elf_str;
34 addr_t prog_base;
35 void *bounds_start;
36 struct rt_context *next;
37 /* <-- */
38 int num_callers;
39 addr_t ip, fp, sp;
40 void *top_func;
41 jmp_buf jmp_buf;
42 char do_jmp;
43} rt_context;
44
45static rt_context g_rtctxt;
46static void set_exception_handler(void);
47static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap);
48static void rt_exit(int code);
49#endif /* CONFIG_TCC_BACKTRACE */
50
51/* defined when included from lib/bt-exe.c */
52#ifndef CONFIG_TCC_BACKTRACE_ONLY
53
54#ifndef _WIN32
55# include <sys/mman.h>
56#endif
57
58static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length);
59static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff);
60
61#ifdef _WIN64
62static void *win64_add_function_table(TCCState *s1);
63static void win64_del_function_table(void *);
64#endif
65
66/* ------------------------------------------------------------- */
67/* Do all relocations (needed before using tcc_get_symbol())
68 Returns -1 on error. */
69
70LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
71{
72 int size;
73 addr_t ptr_diff = 0;
74
75 if (TCC_RELOCATE_AUTO != ptr)
76 return tcc_relocate_ex(s1, ptr, 0);
77
78 size = tcc_relocate_ex(s1, NULL, 0);
79 if (size < 0)
80 return -1;
81
82#ifdef HAVE_SELINUX
83{
84 /* Using mmap instead of malloc */
85 void *prx;
86 char tmpfname[] = "/tmp/.tccrunXXXXXX";
87 int fd = mkstemp(tmpfname);
88 unlink(tmpfname);
89 ftruncate(fd, size);
90
91 ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
92 prx = mmap (NULL, size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
93 if (ptr == MAP_FAILED || prx == MAP_FAILED)
94 tcc_error("tccrun: could not map memory");
95 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
96 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx);
97 ptr_diff = (char*)prx - (char*)ptr;
98 close(fd);
99}
100#else
101 ptr = tcc_malloc(size);
102#endif
103 tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
104 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
105 return 0;
106}
107
108ST_FUNC void tcc_run_free(TCCState *s1)
109{
110 int i;
111
112 for (i = 0; i < s1->nb_runtime_mem; ++i) {
113#ifdef HAVE_SELINUX
114 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
115 munmap(s1->runtime_mem[i++], size);
116 munmap(s1->runtime_mem[i], size);
117#else
118#ifdef _WIN64
119 win64_del_function_table(*(void**)s1->runtime_mem[i]);
120#endif
121 tcc_free(s1->runtime_mem[i]);
122#endif
123 }
124 tcc_free(s1->runtime_mem);
125}
126
127static void run_cdtors(TCCState *s1, const char *start, const char *end,
128 int argc, char **argv, char **envp)
129{
130 void **a = (void **)get_sym_addr(s1, start, 0, 0);
131 void **b = (void **)get_sym_addr(s1, end, 0, 0);
132 while (a != b)
133 ((void(*)(int, char **, char **))*a++)(argc, argv, envp);
134}
135
136/* launch the compiled program with the given arguments */
137LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
138{
139 int (*prog_main)(int, char **, char **), ret;
140#ifdef CONFIG_TCC_BACKTRACE
141 rt_context *rc = &g_rtctxt;
142#endif
143# if defined(__APPLE__)
144 char **envp = NULL;
145#else
146 char **envp = environ;
147#endif
148
149 s1->runtime_main = s1->nostdlib ? "_start" : "main";
150 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1))
151 return 0;
152#ifdef CONFIG_TCC_BACKTRACE
153 if (s1->do_debug)
154 tcc_add_symbol(s1, "exit", rt_exit);
155#endif
156 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
157 return -1;
158 prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1);
159
160#ifdef CONFIG_TCC_BACKTRACE
161 memset(rc, 0, sizeof *rc);
162 if (s1->do_debug) {
163 void *p;
164 rc->stab_sym = (Stab_Sym *)stab_section->data;
165 rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
166 rc->stab_str = (char *)stab_section->link->data;
167 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
168 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
169 rc->elf_str = (char *)symtab_section->link->data;
170#if PTR_SIZE == 8
171 rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL;
172#endif
173 rc->top_func = tcc_get_symbol(s1, "main");
174 rc->num_callers = s1->rt_num_callers;
175 rc->do_jmp = 1;
176 if ((p = tcc_get_symbol(s1, "__rt_error")))
177 *(void**)p = _rt_error;
178#ifdef CONFIG_TCC_BCHECK
179 if (s1->do_bounds_check) {
180 if ((p = tcc_get_symbol(s1, "__bound_init")))
181 ((void(*)(void*, int))p)(bounds_section->data, 1);
182 }
183#endif
184 set_exception_handler();
185 }
186#endif
187
188 errno = 0; /* clean errno value */
189 fflush(stdout);
190 fflush(stderr);
191 /* These aren't C symbols, so don't need leading underscore handling. */
192 run_cdtors(s1, "__init_array_start", "__init_array_end", argc, argv, envp);
193#ifdef CONFIG_TCC_BACKTRACE
194 if (!rc->do_jmp || !(ret = setjmp(rc->jmp_buf)))
195#endif
196 {
197 ret = prog_main(argc, argv, envp);
198 }
199 run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL);
200 if ((s1->dflag & 16) && ret)
201 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
202 return ret;
203}
204
205#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
206/* To avoid that x86 processors would reload cached instructions
207 each time when data is written in the near, we need to make
208 sure that code and data do not share the same 64 byte unit */
209 #define RUN_SECTION_ALIGNMENT 63
210#else
211 #define RUN_SECTION_ALIGNMENT 0
212#endif
213
214/* relocate code. Return -1 on error, required size if ptr is NULL,
215 otherwise copy code into buffer passed by the caller */
216static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
217{
218 Section *s;
219 unsigned offset, length, align, max_align, i, k, f;
220 addr_t mem, addr;
221
222 if (NULL == ptr) {
223 s1->nb_errors = 0;
224#ifdef TCC_TARGET_PE
225 pe_output_file(s1, NULL);
226#else
227 tcc_add_runtime(s1);
228 resolve_common_syms(s1);
229 build_got_entries(s1);
230#endif
231 if (s1->nb_errors)
232 return -1;
233 }
234
235 offset = max_align = 0, mem = (addr_t)ptr;
236#ifdef _WIN64
237 offset += sizeof (void*); /* space for function_table pointer */
238#endif
239 for (k = 0; k < 2; ++k) {
240 f = 0, addr = k ? mem : mem + ptr_diff;
241 for(i = 1; i < s1->nb_sections; i++) {
242 s = s1->sections[i];
243 if (0 == (s->sh_flags & SHF_ALLOC))
244 continue;
245 if (k != !(s->sh_flags & SHF_EXECINSTR))
246 continue;
247 align = s->sh_addralign - 1;
248 if (++f == 1 && align < RUN_SECTION_ALIGNMENT)
249 align = RUN_SECTION_ALIGNMENT;
250 if (max_align < align)
251 max_align = align;
252 offset += -(addr + offset) & align;
253 s->sh_addr = mem ? addr + offset : 0;
254 offset += s->data_offset;
255#if 0
256 if (mem)
257 printf("%-16s %p len %04x align %2d\n",
258 s->name, (void*)s->sh_addr, (unsigned)s->data_offset, align + 1);
259#endif
260 }
261 }
262
263 /* relocate symbols */
264 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
265 if (s1->nb_errors)
266 return -1;
267
268 if (0 == mem)
269 return offset + max_align;
270
271#ifdef TCC_TARGET_PE
272 s1->pe_imagebase = mem;
273#endif
274
275 /* relocate each section */
276 for(i = 1; i < s1->nb_sections; i++) {
277 s = s1->sections[i];
278 if (s->reloc)
279 relocate_section(s1, s);
280 }
281#if !defined(TCC_TARGET_PE) || defined(TCC_TARGET_MACHO)
282 relocate_plt(s1);
283#endif
284
285 for(i = 1; i < s1->nb_sections; i++) {
286 s = s1->sections[i];
287 if (0 == (s->sh_flags & SHF_ALLOC))
288 continue;
289 length = s->data_offset;
290 ptr = (void*)s->sh_addr;
291 if (s->sh_flags & SHF_EXECINSTR)
292 ptr = (char*)((addr_t)ptr - ptr_diff);
293 if (NULL == s->data || s->sh_type == SHT_NOBITS)
294 memset(ptr, 0, length);
295 else
296 memcpy(ptr, s->data, length);
297 /* mark executable sections as executable in memory */
298 if (s->sh_flags & SHF_EXECINSTR)
299 set_pages_executable(s1, (char*)((addr_t)ptr + ptr_diff), length);
300 }
301
302#ifdef _WIN64
303 *(void**)mem = win64_add_function_table(s1);
304#endif
305
306 return 0;
307}
308
309/* ------------------------------------------------------------- */
310/* allow to run code in memory */
311
312static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length)
313{
314#ifdef _WIN32
315 unsigned long old_protect;
316 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
317#else
318 void __clear_cache(void *beginning, void *end);
319# ifndef HAVE_SELINUX
320 addr_t start, end;
321# ifndef PAGESIZE
322# define PAGESIZE 4096
323# endif
324 start = (addr_t)ptr & ~(PAGESIZE - 1);
325 end = (addr_t)ptr + length;
326 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
327 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
328 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
329# endif
330# if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
331 __clear_cache(ptr, (char *)ptr + length);
332# endif
333#endif
334}
335
336#ifdef _WIN64
337static void *win64_add_function_table(TCCState *s1)
338{
339 void *p = NULL;
340 if (s1->uw_pdata) {
341 p = (void*)s1->uw_pdata->sh_addr;
342 RtlAddFunctionTable(
343 (RUNTIME_FUNCTION*)p,
344 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
345 s1->pe_imagebase
346 );
347 s1->uw_pdata = NULL;
348 }
349 return p;
350}
351
352static void win64_del_function_table(void *p)
353{
354 if (p) {
355 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
356 }
357}
358#endif
359#endif //ndef CONFIG_TCC_BACKTRACE_ONLY
360/* ------------------------------------------------------------- */
361#ifdef CONFIG_TCC_BACKTRACE
362
363static int rt_vprintf(const char *fmt, va_list ap)
364{
365 int ret = vfprintf(stderr, fmt, ap);
366 fflush(stderr);
367 return ret;
368}
369
370static int rt_printf(const char *fmt, ...)
371{
372 va_list ap;
373 int r;
374 va_start(ap, fmt);
375 r = rt_vprintf(fmt, ap);
376 va_end(ap);
377 return r;
378}
379
380#define INCLUDE_STACK_SIZE 32
381
382/* print the position in the source file of PC value 'pc' by reading
383 the stabs debug information */
384static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
385 const char *msg, const char *skip)
386{
387 char func_name[128];
388 addr_t func_addr, last_pc, pc;
389 const char *incl_files[INCLUDE_STACK_SIZE];
390 int incl_index, last_incl_index, len, last_line_num, i;
391 const char *str, *p;
392 ElfW(Sym) *esym;
393 Stab_Sym *sym;
394
395next:
396 func_name[0] = '\0';
397 func_addr = 0;
398 incl_index = 0;
399 last_pc = (addr_t)-1;
400 last_line_num = 1;
401 last_incl_index = 0;
402
403 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
404 str = rc->stab_str + sym->n_strx;
405 pc = sym->n_value;
406
407 switch(sym->n_type) {
408 case N_SLINE:
409 if (func_addr)
410 goto rel_pc;
411 case N_SO:
412 case N_SOL:
413 goto abs_pc;
414 case N_FUN:
415 if (sym->n_strx == 0) /* end of function */
416 goto rel_pc;
417 abs_pc:
418#if PTR_SIZE == 8
419 /* Stab_Sym.n_value is only 32bits */
420 pc += rc->prog_base;
421#endif
422 goto check_pc;
423 rel_pc:
424 pc += func_addr;
425 check_pc:
426 if (pc >= wanted_pc && wanted_pc >= last_pc)
427 goto found;
428 break;
429 }
430
431 switch(sym->n_type) {
432 /* function start or end */
433 case N_FUN:
434 if (sym->n_strx == 0)
435 goto reset_func;
436 p = strchr(str, ':');
437 if (0 == p || (len = p - str + 1, len > sizeof func_name))
438 len = sizeof func_name;
439 pstrcpy(func_name, len, str);
440 func_addr = pc;
441 break;
442 /* line number info */
443 case N_SLINE:
444 last_pc = pc;
445 last_line_num = sym->n_desc;
446 last_incl_index = incl_index;
447 break;
448 /* include files */
449 case N_BINCL:
450 if (incl_index < INCLUDE_STACK_SIZE)
451 incl_files[incl_index++] = str;
452 break;
453 case N_EINCL:
454 if (incl_index > 1)
455 incl_index--;
456 break;
457 /* start/end of translation unit */
458 case N_SO:
459 incl_index = 0;
460 if (sym->n_strx) {
461 /* do not add path */
462 len = strlen(str);
463 if (len > 0 && str[len - 1] != '/')
464 incl_files[incl_index++] = str;
465 }
466 reset_func:
467 func_name[0] = '\0';
468 func_addr = 0;
469 last_pc = (addr_t)-1;
470 break;
471 /* alternative file name (from #line or #include directives) */
472 case N_SOL:
473 if (incl_index)
474 incl_files[incl_index-1] = str;
475 break;
476 }
477 }
478
479 func_name[0] = '\0';
480 func_addr = 0;
481 last_incl_index = 0;
482
483 /* we try symtab symbols (no line number info) */
484 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
485 int type = ELFW(ST_TYPE)(esym->st_info);
486 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
487 if (wanted_pc >= esym->st_value &&
488 wanted_pc < esym->st_value + esym->st_size) {
489 pstrcpy(func_name, sizeof(func_name),
490 rc->elf_str + esym->st_name);
491 func_addr = esym->st_value;
492 goto found;
493 }
494 }
495 }
496
497 if ((rc = rc->next))
498 goto next;
499
500found:
501 i = last_incl_index;
502 if (i > 0) {
503 str = incl_files[--i];
504 if (skip[0] && strstr(str, skip))
505 return (addr_t)-1;
506 rt_printf("%s:%d: ", str, last_line_num);
507 } else
508 rt_printf("%08llx : ", (long long)wanted_pc);
509 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
510#if 0
511 if (--i >= 0) {
512 rt_printf(" (included from ");
513 for (;;) {
514 rt_printf("%s", incl_files[i]);
515 if (--i < 0)
516 break;
517 rt_printf(", ");
518 }
519 rt_printf(")");
520 }
521#endif
522 return func_addr;
523}
524
525static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level);
526
527static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap)
528{
529 rt_context *rc = &g_rtctxt;
530 addr_t pc = 0;
531 char skip[100];
532 int i, level, ret, n;
533 const char *a, *b, *msg;
534
535 if (fp) {
536 /* we're called from tcc_backtrace. */
537 rc->fp = (addr_t)fp;
538 rc->ip = (addr_t)ip;
539 msg = "";
540 } else {
541 /* we're called from signal/exception handler */
542 msg = "RUNTIME ERROR: ";
543 }
544
545 skip[0] = 0;
546 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
547 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
548 memcpy(skip, a, b - a), skip[b - a] = 0;
549 fmt = b + 1;
550 }
551
552 n = rc->num_callers ? rc->num_callers : 6;
553 for (i = level = 0; level < n; i++) {
554 ret = rt_get_caller_pc(&pc, rc, i);
555 a = "%s";
556 if (ret != -1) {
557 pc = rt_printline(rc, pc, level ? "by" : "at", skip);
558 if (pc == (addr_t)-1)
559 continue;
560 a = ": %s";
561 }
562 if (level == 0) {
563 rt_printf(a, msg);
564 rt_vprintf(fmt, ap);
565 } else if (ret == -1)
566 break;
567 rt_printf("\n");
568 if (ret == -1 || (pc == (addr_t)rc->top_func && pc))
569 break;
570 ++level;
571 }
572
573 rc->ip = rc->fp = 0;
574 return 0;
575}
576
577/* emit a run time error at position 'pc' */
578static int rt_error(const char *fmt, ...)
579{
580 va_list ap;
581 int ret;
582 va_start(ap, fmt);
583 ret = _rt_error(0, 0, fmt, ap);
584 va_end(ap);
585 return ret;
586}
587
588static void rt_exit(int code)
589{
590 rt_context *rc = &g_rtctxt;
591 if (rc->do_jmp)
592 longjmp(rc->jmp_buf, code ? code : 256);
593 exit(code);
594}
595
596/* ------------------------------------------------------------- */
597
598#ifndef _WIN32
599# include <signal.h>
600# ifndef __OpenBSD__
601# include <sys/ucontext.h>
602# endif
603#else
604# define ucontext_t CONTEXT
605#endif
606
607/* translate from ucontext_t* to internal rt_context * */
608static void rt_getcontext(ucontext_t *uc, rt_context *rc)
609{
610#if defined _WIN64
611 rc->ip = uc->Rip;
612 rc->fp = uc->Rbp;
613 rc->sp = uc->Rsp;
614#elif defined _WIN32
615 rc->ip = uc->Eip;
616 rc->fp = uc->Ebp;
617 rc->sp = uc->Esp;
618#elif defined __i386__
619# if defined(__APPLE__)
620 rc->ip = uc->uc_mcontext->__ss.__eip;
621 rc->fp = uc->uc_mcontext->__ss.__ebp;
622# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
623 rc->ip = uc->uc_mcontext.mc_eip;
624 rc->fp = uc->uc_mcontext.mc_ebp;
625# elif defined(__dietlibc__)
626 rc->ip = uc->uc_mcontext.eip;
627 rc->fp = uc->uc_mcontext.ebp;
628# elif defined(__NetBSD__)
629 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
630 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
631# elif defined(__OpenBSD__)
632 rc->ip = uc->sc_eip;
633 rc->fp = uc->sc_ebp;
634# elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
635 rc->ip = uc->uc_mcontext.gregs[EIP];
636 rc->fp = uc->uc_mcontext.gregs[EBP];
637# else
638 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
639 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
640# endif
641#elif defined(__x86_64__)
642# if defined(__APPLE__)
643 rc->ip = uc->uc_mcontext->__ss.__rip;
644 rc->fp = uc->uc_mcontext->__ss.__rbp;
645# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
646 rc->ip = uc->uc_mcontext.mc_rip;
647 rc->fp = uc->uc_mcontext.mc_rbp;
648# elif defined(__NetBSD__)
649 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
650 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
651# else
652 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
653 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
654# endif
655#elif defined(__arm__)
656 rc->ip = uc->uc_mcontext.arm_pc;
657 rc->fp = uc->uc_mcontext.arm_fp;
658#elif defined(__aarch64__)
659 rc->ip = uc->uc_mcontext.pc;
660 rc->fp = uc->uc_mcontext.regs[29];
661#elif defined(__riscv)
662 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
663 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
664#endif
665}
666
667/* ------------------------------------------------------------- */
668#ifndef _WIN32
669/* signal handler for fatal errors */
670static void sig_error(int signum, siginfo_t *siginf, void *puc)
671{
672 rt_context *rc = &g_rtctxt;
673 rt_getcontext(puc, rc);
674
675 switch(signum) {
676 case SIGFPE:
677 switch(siginf->si_code) {
678 case FPE_INTDIV:
679 case FPE_FLTDIV:
680 rt_error("division by zero");
681 break;
682 default:
683 rt_error("floating point exception");
684 break;
685 }
686 break;
687 case SIGBUS:
688 case SIGSEGV:
689 rt_error("invalid memory access");
690 break;
691 case SIGILL:
692 rt_error("illegal instruction");
693 break;
694 case SIGABRT:
695 rt_error("abort() called");
696 break;
697 default:
698 rt_error("caught signal %d", signum);
699 break;
700 }
701 rt_exit(255);
702}
703
704#ifndef SA_SIGINFO
705# define SA_SIGINFO 0x00000004u
706#endif
707
708/* Generate a stack backtrace when a CPU exception occurs. */
709static void set_exception_handler(void)
710{
711 struct sigaction sigact;
712 /* install TCC signal handlers to print debug info on fatal
713 runtime errors */
714 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
715#if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
716 sigact.sa_flags |= SA_ONSTACK;
717#endif
718 sigact.sa_sigaction = sig_error;
719 sigemptyset(&sigact.sa_mask);
720 sigaction(SIGFPE, &sigact, NULL);
721 sigaction(SIGILL, &sigact, NULL);
722 sigaction(SIGSEGV, &sigact, NULL);
723 sigaction(SIGBUS, &sigact, NULL);
724 sigaction(SIGABRT, &sigact, NULL);
725#if 0//def SIGSTKSZ
726 /* This allows stack overflow to be reported instead of a SEGV */
727 {
728 stack_t ss;
729 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
730
731 ss.ss_sp = stack;
732 ss.ss_size = SIGSTKSZ;
733 ss.ss_flags = 0;
734 sigaltstack(&ss, NULL);
735 }
736#endif
737}
738
739#else /* WIN32 */
740
741/* signal handler for fatal errors */
742static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
743{
744 rt_context *rc = &g_rtctxt;
745 unsigned code;
746 rt_getcontext(ex_info->ContextRecord, rc);
747
748 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
749 case EXCEPTION_ACCESS_VIOLATION:
750 rt_error("invalid memory access");
751 break;
752 case EXCEPTION_STACK_OVERFLOW:
753 rt_error("stack overflow");
754 break;
755 case EXCEPTION_INT_DIVIDE_BY_ZERO:
756 rt_error("division by zero");
757 break;
758 case EXCEPTION_BREAKPOINT:
759 case EXCEPTION_SINGLE_STEP:
760 rc->ip = *(addr_t*)rc->sp;
761 rt_error("breakpoint/single-step exception:");
762 return EXCEPTION_CONTINUE_SEARCH;
763 default:
764 rt_error("caught exception %08x", code);
765 break;
766 }
767 if (rc->do_jmp)
768 rt_exit(255);
769 return EXCEPTION_EXECUTE_HANDLER;
770}
771
772/* Generate a stack backtrace when a CPU exception occurs. */
773static void set_exception_handler(void)
774{
775 SetUnhandledExceptionFilter(cpu_exception_handler);
776}
777
778#endif
779
780/* ------------------------------------------------------------- */
781/* return the PC at frame level 'level'. Return negative if not found */
782#if defined(__i386__) || defined(__x86_64__)
783static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
784{
785 addr_t ip, fp;
786 if (level == 0) {
787 ip = rc->ip;
788 } else {
789 ip = 0;
790 fp = rc->fp;
791 while (--level) {
792 /* XXX: check address validity with program info */
793 if (fp <= 0x1000)
794 break;
795 fp = ((addr_t *)fp)[0];
796 }
797 if (fp > 0x1000)
798 ip = ((addr_t *)fp)[1];
799 }
800 if (ip <= 0x1000)
801 return -1;
802 *paddr = ip;
803 return 0;
804}
805
806#elif defined(__arm__)
807static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
808{
809 /* XXX: only supports linux */
810#if !defined(__linux__)
811 return -1;
812#else
813 if (level == 0) {
814 *paddr = rc->ip;
815 } else {
816 addr_t fp = rc->fp;
817 while (--level)
818 fp = ((addr_t *)fp)[0];
819 *paddr = ((addr_t *)fp)[2];
820 }
821 return 0;
822#endif
823}
824
825#elif defined(__aarch64__)
826static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
827{
828 if (level == 0) {
829 *paddr = rc->ip;
830 } else {
831 addr_t *fp = (addr_t*)rc->fp;
832 while (--level)
833 fp = (addr_t *)fp[0];
834 *paddr = fp[1];
835 }
836 return 0;
837}
838
839#elif defined(__riscv)
840static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
841{
842 if (level == 0) {
843 *paddr = rc->ip;
844 } else {
845 addr_t *fp = (addr_t*)rc->fp;
846 while (--level && fp >= (addr_t*)0x1000)
847 fp = (addr_t *)fp[-2];
848 if (fp < (addr_t*)0x1000)
849 return -1;
850 *paddr = fp[-1];
851 }
852 return 0;
853}
854
855#else
856#warning add arch specific rt_get_caller_pc()
857static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
858{
859 return -1;
860}
861
862#endif
863#endif /* CONFIG_TCC_BACKTRACE */
864/* ------------------------------------------------------------- */
865#ifdef CONFIG_TCC_STATIC
866
867/* dummy function for profiling */
868ST_FUNC void *dlopen(const char *filename, int flag)
869{
870 return NULL;
871}
872
873ST_FUNC void dlclose(void *p)
874{
875}
876
877ST_FUNC const char *dlerror(void)
878{
879 return "error";
880}
881
882typedef struct TCCSyms {
883 char *str;
884 void *ptr;
885} TCCSyms;
886
887
888/* add the symbol you want here if no dynamic linking is done */
889static TCCSyms tcc_syms[] = {
890#if !defined(CONFIG_TCCBOOT)
891#define TCCSYM(a) { #a, &a, },
892 TCCSYM(printf)
893 TCCSYM(fprintf)
894 TCCSYM(fopen)
895 TCCSYM(fclose)
896#undef TCCSYM
897#endif
898 { NULL, NULL },
899};
900
901ST_FUNC void *dlsym(void *handle, const char *symbol)
902{
903 TCCSyms *p;
904 p = tcc_syms;
905 while (p->str != NULL) {
906 if (!strcmp(p->str, symbol))
907 return p->ptr;
908 p++;
909 }
910 return NULL;
911}
912
913#endif /* CONFIG_TCC_STATIC */
914#endif /* TCC_IS_NATIVE */
915/* ------------------------------------------------------------- */
916