Allow atexit to be used with -run
[tinycc.git] / tccrun.c
blob064af3000e591dc3d2f53bd61bfda35a2e0a8e47
1 /*
2 * TCC - Tiny C Compiler - Support for -run switch
4 * Copyright (c) 2001-2004 Fabrice Bellard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "tcc.h"
23 /* only native compiler supports -run */
24 #ifdef TCC_IS_NATIVE
26 #ifdef CONFIG_TCC_BACKTRACE
27 typedef struct rt_context
29 /* --> tccelf.c:tcc_add_btstub wants those below in that order: */
30 union {
31 struct {
32 Stab_Sym *stab_sym, *stab_sym_end;
33 char *stab_str;
35 struct {
36 unsigned char *dwarf_line, *dwarf_line_end, *dwarf_line_str;
39 addr_t dwarf;
40 ElfW(Sym) *esym_start, *esym_end;
41 char *elf_str;
42 addr_t prog_base;
43 void *bounds_start;
44 struct rt_context *next;
45 /* <-- */
46 int num_callers;
47 addr_t ip, fp, sp;
48 void *top_func;
49 jmp_buf jmp_buf;
50 char do_jmp;
51 } rt_context;
53 static rt_context g_rtctxt;
54 static void set_exception_handler(void);
55 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap);
56 static void rt_exit(int code);
57 static void init_atexit(void);
58 static void run_atexit(void);
59 static int rt_atexit(void (*function)(void));
60 #endif /* CONFIG_TCC_BACKTRACE */
62 /* defined when included from lib/bt-exe.c */
63 #ifndef CONFIG_TCC_BACKTRACE_ONLY
65 #ifndef _WIN32
66 # include <sys/mman.h>
67 #endif
69 static void set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length);
70 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff);
72 #ifdef _WIN64
73 static void *win64_add_function_table(TCCState *s1);
74 static void win64_del_function_table(void *);
75 #endif
77 /* ------------------------------------------------------------- */
78 /* Do all relocations (needed before using tcc_get_symbol())
79 Returns -1 on error. */
81 LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
83 int size;
84 addr_t ptr_diff = 0;
86 if (TCC_RELOCATE_AUTO != ptr)
87 return tcc_relocate_ex(s1, ptr, 0);
89 size = tcc_relocate_ex(s1, NULL, 0);
90 if (size < 0)
91 return -1;
93 #ifdef HAVE_SELINUX
95 /* Using mmap instead of malloc */
96 void *prx;
97 char tmpfname[] = "/tmp/.tccrunXXXXXX";
98 int fd = mkstemp(tmpfname);
99 unlink(tmpfname);
100 ftruncate(fd, size);
102 size = (size + (PAGESIZE-1)) & ~(PAGESIZE-1);
103 ptr = mmap(NULL, size * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
104 /* mmap RX memory at a fixed distance */
105 prx = mmap((char*)ptr + size, size, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_FIXED, fd, 0);
106 if (ptr == MAP_FAILED || prx == MAP_FAILED)
107 tcc_error("tccrun: could not map memory");
108 ptr_diff = (char*)prx - (char*)ptr;
109 close(fd);
110 //printf("map %p %p %p\n", ptr, prx, (void*)ptr_diff);
112 #else
113 ptr = tcc_malloc(size);
114 #endif
115 tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
116 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
117 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
118 return 0;
121 ST_FUNC void tcc_run_free(TCCState *s1)
123 int i;
125 for (i = 0; i < s1->nb_runtime_mem; i += 2) {
126 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i];
127 void *ptr = s1->runtime_mem[i+1];
128 #ifdef HAVE_SELINUX
129 munmap(ptr, size * 2);
130 #else
131 /* unprotect memory to make it usable for malloc again */
132 set_pages_executable(s1, 2, ptr, size);
133 #ifdef _WIN64
134 win64_del_function_table(*(void**)ptr);
135 #endif
136 tcc_free(ptr);
137 #endif
139 tcc_free(s1->runtime_mem);
142 static void run_cdtors(TCCState *s1, const char *start, const char *end,
143 int argc, char **argv, char **envp)
145 void **a = (void **)get_sym_addr(s1, start, 0, 0);
146 void **b = (void **)get_sym_addr(s1, end, 0, 0);
147 while (a != b)
148 ((void(*)(int, char **, char **))*a++)(argc, argv, envp);
151 /* launch the compiled program with the given arguments */
152 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
154 int (*prog_main)(int, char **, char **), ret;
155 #ifdef CONFIG_TCC_BACKTRACE
156 rt_context *rc = &g_rtctxt;
157 #endif
159 #if defined(__APPLE__) || defined(__FreeBSD__)
160 char **envp = NULL;
161 #elif defined(__OpenBSD__) || defined(__NetBSD__)
162 extern char **environ;
163 char **envp = environ;
164 #else
165 char **envp = environ;
166 #endif
168 s1->runtime_main = s1->nostdlib ? "_start" : "main";
169 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1))
170 return 0;
171 #ifdef CONFIG_TCC_BACKTRACE
172 if (s1->do_debug)
173 tcc_add_symbol(s1, "exit", rt_exit);
174 #endif
175 tcc_add_symbol(s1, "atexit", rt_atexit);
176 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
177 return -1;
178 prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1);
180 #ifdef CONFIG_TCC_BACKTRACE
181 memset(rc, 0, sizeof *rc);
182 if (s1->do_debug) {
183 void *p;
184 if (s1->dwarf) {
185 rc->dwarf_line = dwarf_line_section->data;
186 rc->dwarf_line_end = dwarf_line_section->data + dwarf_line_section->data_offset;
187 if (dwarf_line_str_section)
188 rc->dwarf_line_str = dwarf_line_str_section->data;
190 else
192 rc->stab_sym = (Stab_Sym *)stab_section->data;
193 rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
194 rc->stab_str = (char *)stab_section->link->data;
196 rc->dwarf = s1->dwarf;
197 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
198 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
199 rc->elf_str = (char *)symtab_section->link->data;
200 #if PTR_SIZE == 8
201 rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL;
202 #endif
203 rc->top_func = tcc_get_symbol(s1, "main");
204 rc->num_callers = s1->rt_num_callers;
205 rc->do_jmp = 1;
206 if ((p = tcc_get_symbol(s1, "__rt_error")))
207 *(void**)p = _rt_error;
208 #ifdef CONFIG_TCC_BCHECK
209 if (s1->do_bounds_check) {
210 rc->bounds_start = (void*)bounds_section->sh_addr;
211 if ((p = tcc_get_symbol(s1, "__bound_init")))
212 ((void(*)(void*,int))p)(rc->bounds_start, 1);
214 #endif
215 set_exception_handler();
217 #endif
219 errno = 0; /* clean errno value */
220 fflush(stdout);
221 fflush(stderr);
222 init_atexit();
223 /* These aren't C symbols, so don't need leading underscore handling. */
224 run_cdtors(s1, "__init_array_start", "__init_array_end", argc, argv, envp);
225 #ifdef CONFIG_TCC_BACKTRACE
226 if (!rc->do_jmp || !(ret = setjmp(rc->jmp_buf)))
227 #endif
229 ret = prog_main(argc, argv, envp);
231 run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL);
232 run_atexit();
233 if ((s1->dflag & 16) && ret)
234 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
235 return ret;
238 #define DEBUG_RUNMEN 0
240 /* enable rx/ro/rw permissions */
241 #define CONFIG_RUNMEM_RO 1
243 #if CONFIG_RUNMEM_RO
244 # define PAGE_ALIGN PAGESIZE
245 #elif defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
246 /* To avoid that x86 processors would reload cached instructions
247 each time when data is written in the near, we need to make
248 sure that code and data do not share the same 64 byte unit */
249 # define PAGE_ALIGN 64
250 #else
251 # define PAGE_ALIGN 1
252 #endif
254 /* relocate code. Return -1 on error, required size if ptr is NULL,
255 otherwise copy code into buffer passed by the caller */
256 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
258 Section *s;
259 unsigned offset, length, align, max_align, i, k, f;
260 unsigned n, copy;
261 addr_t mem, addr;
263 if (NULL == ptr) {
264 s1->nb_errors = 0;
265 #ifdef TCC_TARGET_PE
266 pe_output_file(s1, NULL);
267 #else
268 tcc_add_runtime(s1);
269 resolve_common_syms(s1);
270 build_got_entries(s1, 0);
271 #endif
272 if (s1->nb_errors)
273 return -1;
276 offset = max_align = 0, mem = (addr_t)ptr;
277 #ifdef _WIN64
278 offset += sizeof (void*); /* space for function_table pointer */
279 #endif
280 copy = 0;
281 redo:
282 for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
283 n = 0; addr = 0;
284 for(i = 1; i < s1->nb_sections; i++) {
285 static const char shf[] = {
286 SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
288 s = s1->sections[i];
289 if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
290 continue;
291 length = s->data_offset;
292 if (copy) {
293 if (addr == 0)
294 addr = s->sh_addr;
295 n = (s->sh_addr - addr) + length;
296 ptr = (void*)s->sh_addr;
297 if (k == 0)
298 ptr = (void*)(s->sh_addr - ptr_diff);
299 if (NULL == s->data || s->sh_type == SHT_NOBITS)
300 memset(ptr, 0, length);
301 else
302 memcpy(ptr, s->data, length);
303 #ifdef _WIN64
304 if (s == s1->uw_pdata)
305 *(void**)mem = win64_add_function_table(s1);
306 #endif
307 if (s->data) {
308 tcc_free(s->data);
309 s->data = NULL;
310 s->data_allocated = 0;
312 s->data_offset = 0;
313 continue;
315 align = s->sh_addralign - 1;
316 if (++n == 1 && align < (PAGE_ALIGN - 1))
317 align = (PAGE_ALIGN - 1);
318 if (max_align < align)
319 max_align = align;
320 addr = k ? mem : mem + ptr_diff;
321 offset += -(addr + offset) & align;
322 s->sh_addr = mem ? addr + offset : 0;
323 offset += length;
324 #if DEBUG_RUNMEN
325 if (mem)
326 printf("%d: %-16s %p len %04x align %04x\n",
327 k, s->name, (void*)s->sh_addr, length, align + 1);
328 #endif
330 if (copy) { /* set permissions */
331 if (k == 0 && ptr_diff)
332 continue; /* not with HAVE_SELINUX */
333 f = k;
334 #if !CONFIG_RUNMEM_RO
335 if (f != 0)
336 continue;
337 f = 3; /* change only SHF_EXECINSTR to rwx */
338 #endif
339 #if DEBUG_RUNMEN
340 printf("protect %d %p %04x\n", f, (void*)addr, n);
341 #endif
342 if (n)
343 set_pages_executable(s1, f, (void*)addr, n);
347 if (copy)
348 return 0;
350 /* relocate symbols */
351 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
352 if (s1->nb_errors)
353 return -1;
354 if (0 == mem)
355 return offset + max_align;
357 #ifdef TCC_TARGET_PE
358 s1->pe_imagebase = mem;
359 #endif
361 /* relocate sections */
362 #ifndef TCC_TARGET_PE
363 relocate_plt(s1);
364 #endif
365 relocate_sections(s1);
366 copy = 1;
367 goto redo;
370 /* ------------------------------------------------------------- */
371 /* allow to run code in memory */
373 static void set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length)
375 #ifdef _WIN32
376 static const unsigned char protect[] = {
377 PAGE_EXECUTE_READ,
378 PAGE_READONLY,
379 PAGE_READWRITE,
380 PAGE_EXECUTE_READWRITE
382 DWORD old;
383 VirtualProtect(ptr, length, protect[mode], &old);
384 #else
385 static const unsigned char protect[] = {
386 PROT_READ | PROT_EXEC,
387 PROT_READ,
388 PROT_READ | PROT_WRITE,
389 PROT_READ | PROT_WRITE | PROT_EXEC
391 addr_t start, end;
392 start = (addr_t)ptr & ~(PAGESIZE - 1);
393 end = (addr_t)ptr + length;
394 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
395 if (mprotect((void *)start, end - start, protect[mode]))
396 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
398 /* XXX: BSD sometimes dump core with bad system call */
399 # if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
400 if (mode == 0 || mode == 3) {
401 void __clear_cache(void *beginning, void *end);
402 __clear_cache(ptr, (char *)ptr + length);
404 # endif
406 #endif
409 #ifdef _WIN64
410 static void *win64_add_function_table(TCCState *s1)
412 void *p = NULL;
413 if (s1->uw_pdata) {
414 p = (void*)s1->uw_pdata->sh_addr;
415 RtlAddFunctionTable(
416 (RUNTIME_FUNCTION*)p,
417 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
418 s1->pe_imagebase
420 s1->uw_pdata = NULL;
422 return p;
425 static void win64_del_function_table(void *p)
427 if (p) {
428 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
431 #endif
432 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
433 /* ------------------------------------------------------------- */
434 #ifdef CONFIG_TCC_BACKTRACE
436 static int rt_vprintf(const char *fmt, va_list ap)
438 int ret = vfprintf(stderr, fmt, ap);
439 fflush(stderr);
440 return ret;
443 static int rt_printf(const char *fmt, ...)
445 va_list ap;
446 int r;
447 va_start(ap, fmt);
448 r = rt_vprintf(fmt, ap);
449 va_end(ap);
450 return r;
453 static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr)
455 ElfW(Sym) *esym;
456 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
457 int type = ELFW(ST_TYPE)(esym->st_info);
458 if ((type == STT_FUNC || type == STT_GNU_IFUNC)
459 && wanted_pc >= esym->st_value
460 && wanted_pc < esym->st_value + esym->st_size) {
461 *func_addr = esym->st_value;
462 return rc->elf_str + esym->st_name;
465 return NULL;
468 #define INCLUDE_STACK_SIZE 32
470 /* print the position in the source file of PC value 'pc' by reading
471 the stabs debug information */
472 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
473 const char *msg, const char *skip)
475 char func_name[128];
476 addr_t func_addr, last_pc, pc;
477 const char *incl_files[INCLUDE_STACK_SIZE];
478 int incl_index, last_incl_index, len, last_line_num, i;
479 const char *str, *p;
480 Stab_Sym *sym;
482 next:
483 func_name[0] = '\0';
484 func_addr = 0;
485 incl_index = 0;
486 last_pc = (addr_t)-1;
487 last_line_num = 1;
488 last_incl_index = 0;
490 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
491 str = rc->stab_str + sym->n_strx;
492 pc = sym->n_value;
494 switch(sym->n_type) {
495 case N_SLINE:
496 if (func_addr)
497 goto rel_pc;
498 case N_SO:
499 case N_SOL:
500 goto abs_pc;
501 case N_FUN:
502 if (sym->n_strx == 0) /* end of function */
503 goto rel_pc;
504 abs_pc:
505 #if PTR_SIZE == 8
506 /* Stab_Sym.n_value is only 32bits */
507 pc += rc->prog_base;
508 #endif
509 goto check_pc;
510 rel_pc:
511 pc += func_addr;
512 check_pc:
513 if (pc >= wanted_pc && wanted_pc >= last_pc)
514 goto found;
515 break;
518 switch(sym->n_type) {
519 /* function start or end */
520 case N_FUN:
521 if (sym->n_strx == 0)
522 goto reset_func;
523 p = strchr(str, ':');
524 if (0 == p || (len = p - str + 1, len > sizeof func_name))
525 len = sizeof func_name;
526 pstrcpy(func_name, len, str);
527 func_addr = pc;
528 break;
529 /* line number info */
530 case N_SLINE:
531 last_pc = pc;
532 last_line_num = sym->n_desc;
533 last_incl_index = incl_index;
534 break;
535 /* include files */
536 case N_BINCL:
537 if (incl_index < INCLUDE_STACK_SIZE)
538 incl_files[incl_index++] = str;
539 break;
540 case N_EINCL:
541 if (incl_index > 1)
542 incl_index--;
543 break;
544 /* start/end of translation unit */
545 case N_SO:
546 incl_index = 0;
547 if (sym->n_strx) {
548 /* do not add path */
549 len = strlen(str);
550 if (len > 0 && str[len - 1] != '/')
551 incl_files[incl_index++] = str;
553 reset_func:
554 func_name[0] = '\0';
555 func_addr = 0;
556 last_pc = (addr_t)-1;
557 break;
558 /* alternative file name (from #line or #include directives) */
559 case N_SOL:
560 if (incl_index)
561 incl_files[incl_index-1] = str;
562 break;
566 func_name[0] = '\0';
567 func_addr = 0;
568 last_incl_index = 0;
569 /* we try symtab symbols (no line number info) */
570 p = rt_elfsym(rc, wanted_pc, &func_addr);
571 if (p) {
572 pstrcpy(func_name, sizeof func_name, p);
573 goto found;
575 if ((rc = rc->next))
576 goto next;
577 found:
578 i = last_incl_index;
579 if (i > 0) {
580 str = incl_files[--i];
581 if (skip[0] && strstr(str, skip))
582 return (addr_t)-1;
583 rt_printf("%s:%d: ", str, last_line_num);
584 } else
585 rt_printf("%08llx : ", (long long)wanted_pc);
586 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
587 #if 0
588 if (--i >= 0) {
589 rt_printf(" (included from ");
590 for (;;) {
591 rt_printf("%s", incl_files[i]);
592 if (--i < 0)
593 break;
594 rt_printf(", ");
596 rt_printf(")");
598 #endif
599 return func_addr;
602 /* ------------------------------------------------------------- */
603 /* rt_printline - dwarf version */
605 #define MAX_128 ((8 * sizeof (long long) + 6) / 7)
607 #define DIR_TABLE_SIZE (64)
608 #define FILE_TABLE_SIZE (512)
610 #define dwarf_read_1(ln,end) \
611 ((ln) < (end) ? *(ln)++ : 0)
612 #define dwarf_read_2(ln,end) \
613 ((ln) + 2 < (end) ? (ln) += 2, read16le((ln) - 2) : 0)
614 #define dwarf_read_4(ln,end) \
615 ((ln) + 4 < (end) ? (ln) += 4, read32le((ln) - 4) : 0)
616 #define dwarf_read_8(ln,end) \
617 ((ln) + 8 < (end) ? (ln) += 8, read64le((ln) - 8) : 0)
618 #define dwarf_ignore_type(ln, end) /* timestamp/size/md5/... */ \
619 switch (entry_format[j].form) { \
620 case DW_FORM_data1: (ln) += 1; break; \
621 case DW_FORM_data2: (ln) += 2; break; \
622 case DW_FORM_data4: (ln) += 3; break; \
623 case DW_FORM_data8: (ln) += 8; break; \
624 case DW_FORM_data16: (ln) += 16; break; \
625 case DW_FORM_udata: dwarf_read_uleb128(&(ln), (end)); break; \
626 default: goto next_line; \
629 static unsigned long long
630 dwarf_read_uleb128(unsigned char **ln, unsigned char *end)
632 unsigned char *cp = *ln;
633 unsigned long long retval = 0;
634 int i;
636 for (i = 0; i < MAX_128; i++) {
637 unsigned long long byte = dwarf_read_1(cp, end);
639 retval |= (byte & 0x7f) << (i * 7);
640 if ((byte & 0x80) == 0)
641 break;
643 *ln = cp;
644 return retval;
647 static long long
648 dwarf_read_sleb128(unsigned char **ln, unsigned char *end)
650 unsigned char *cp = *ln;
651 long long retval = 0;
652 int i;
654 for (i = 0; i < MAX_128; i++) {
655 unsigned long long byte = dwarf_read_1(cp, end);
657 retval |= (byte & 0x7f) << (i * 7);
658 if ((byte & 0x80) == 0) {
659 if ((byte & 0x40) && (i + 1) * 7 < 64)
660 retval |= -1LL << ((i + 1) * 7);
661 break;
664 *ln = cp;
665 return retval;
668 static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc,
669 const char *msg, const char *skip)
671 unsigned char *ln;
672 unsigned char *cp;
673 unsigned char *end;
674 unsigned char *opcode_length;
675 unsigned long long size;
676 unsigned int length;
677 unsigned char version;
678 unsigned int min_insn_length;
679 unsigned int max_ops_per_insn;
680 int line_base;
681 unsigned int line_range;
682 unsigned int opcode_base;
683 unsigned int opindex;
684 unsigned int col;
685 unsigned int i;
686 unsigned int j;
687 unsigned int len;
688 unsigned long long value;
689 struct {
690 unsigned int type;
691 unsigned int form;
692 } entry_format[256];
693 unsigned int dir_size;
694 #if 0
695 char *dirs[DIR_TABLE_SIZE];
696 #endif
697 unsigned int filename_size;
698 struct dwarf_filename_struct {
699 unsigned int dir_entry;
700 char *name;
701 } filename_table[FILE_TABLE_SIZE];
702 addr_t last_pc;
703 addr_t pc;
704 addr_t func_addr;
705 int line;
706 char *filename;
707 char *function;
709 next:
710 ln = rc->dwarf_line;
711 while (ln < rc->dwarf_line_end) {
712 dir_size = 0;
713 filename_size = 0;
714 last_pc = 0;
715 pc = 0;
716 func_addr = 0;
717 line = 1;
718 filename = NULL;
719 function = NULL;
720 length = 4;
721 size = dwarf_read_4(ln, rc->dwarf_line_end);
722 if (size == 0xffffffffu) // dwarf 64
723 length = 8, size = dwarf_read_8(ln, rc->dwarf_line_end);
724 end = ln + size;
725 if (end < ln || end > rc->dwarf_line_end)
726 break;
727 version = dwarf_read_2(ln, end);
728 if (version >= 5)
729 ln += length + 2; // address size, segment selector, prologue Length
730 else
731 ln += length; // prologue Length
732 min_insn_length = dwarf_read_1(ln, end);
733 if (version >= 4)
734 max_ops_per_insn = dwarf_read_1(ln, end);
735 else
736 max_ops_per_insn = 1;
737 ln++; // Initial value of 'is_stmt'
738 line_base = dwarf_read_1(ln, end);
739 line_base |= line_base >= 0x80 ? ~0xff : 0;
740 line_range = dwarf_read_1(ln, end);
741 opcode_base = dwarf_read_1(ln, end);
742 opcode_length = ln;
743 ln += opcode_base - 1;
744 opindex = 0;
745 if (version >= 5) {
746 col = dwarf_read_1(ln, end);
747 for (i = 0; i < col; i++) {
748 entry_format[i].type = dwarf_read_uleb128(&ln, end);
749 entry_format[i].form = dwarf_read_uleb128(&ln, end);
751 dir_size = dwarf_read_uleb128(&ln, end);
752 for (i = 0; i < dir_size; i++) {
753 for (j = 0; j < col; j++) {
754 if (entry_format[j].type == DW_LNCT_path) {
755 if (entry_format[j].form != DW_FORM_line_strp)
756 goto next_line;
757 #if 0
758 value = length == 4 ? dwarf_read_4(ln, end)
759 : dwarf_read_8(ln, end);
760 if (i < DIR_TABLE_SIZE)
761 dirs[i] = (char *)rc->dwarf_line_str + value;
762 #else
763 length == 4 ? dwarf_read_4(ln, end)
764 : dwarf_read_8(ln, end);
765 #endif
767 else
768 dwarf_ignore_type(ln, end);
771 col = dwarf_read_1(ln, end);
772 for (i = 0; i < col; i++) {
773 entry_format[i].type = dwarf_read_uleb128(&ln, end);
774 entry_format[i].form = dwarf_read_uleb128(&ln, end);
776 filename_size = dwarf_read_uleb128(&ln, end);
777 for (i = 0; i < filename_size; i++)
778 for (j = 0; j < col; j++) {
779 if (entry_format[j].type == DW_LNCT_path) {
780 if (entry_format[j].form != DW_FORM_line_strp)
781 goto next_line;
782 value = length == 4 ? dwarf_read_4(ln, end)
783 : dwarf_read_8(ln, end);
784 if (i < FILE_TABLE_SIZE)
785 filename_table[i].name =
786 (char *)rc->dwarf_line_str + value;
788 else if (entry_format[j].type == DW_LNCT_directory_index) {
789 switch (entry_format[j].form) {
790 case DW_FORM_data1: value = dwarf_read_1(ln, end); break;
791 case DW_FORM_data2: value = dwarf_read_2(ln, end); break;
792 case DW_FORM_data4: value = dwarf_read_4(ln, end); break;
793 case DW_FORM_udata: value = dwarf_read_uleb128(&ln, end); break;
794 default: goto next_line;
796 if (i < FILE_TABLE_SIZE)
797 filename_table[i].dir_entry = value;
799 else
800 dwarf_ignore_type(ln, end);
803 else {
804 while ((dwarf_read_1(ln, end))) {
805 #if 0
806 if (++dir_size < DIR_TABLE_SIZE)
807 dirs[dir_size - 1] = (char *)ln - 1;
808 #endif
809 while (dwarf_read_1(ln, end)) {}
811 while ((dwarf_read_1(ln, end))) {
812 if (++filename_size < FILE_TABLE_SIZE) {
813 filename_table[filename_size - 1].name = (char *)ln - 1;
814 while (dwarf_read_1(ln, end)) {}
815 filename_table[filename_size - 1].dir_entry =
816 dwarf_read_uleb128(&ln, end);
818 else {
819 while (dwarf_read_1(ln, end)) {}
820 dwarf_read_uleb128(&ln, end);
822 dwarf_read_uleb128(&ln, end); // time
823 dwarf_read_uleb128(&ln, end); // size
826 if (filename_size >= 1)
827 filename = filename_table[0].name;
828 while (ln < end) {
829 last_pc = pc;
830 i = dwarf_read_1(ln, end);
831 if (i >= opcode_base) {
832 if (max_ops_per_insn == 1)
833 pc += ((i - opcode_base) / line_range) * min_insn_length;
834 else {
835 pc += (opindex + (i - opcode_base) / line_range) /
836 max_ops_per_insn * min_insn_length;
837 opindex = (opindex + (i - opcode_base) / line_range) %
838 max_ops_per_insn;
840 i = (int)((i - opcode_base) % line_range) + line_base;
841 check_pc:
842 if (pc >= wanted_pc && wanted_pc >= last_pc)
843 goto found;
844 line += i;
846 else {
847 switch (i) {
848 case 0:
849 len = dwarf_read_uleb128(&ln, end);
850 cp = ln;
851 ln += len;
852 if (len == 0)
853 goto next_line;
854 switch (dwarf_read_1(cp, end)) {
855 case DW_LNE_end_sequence:
856 break;
857 case DW_LNE_set_address:
858 #if PTR_SIZE == 4
859 pc = dwarf_read_4(cp, end);
860 #else
861 pc = dwarf_read_8(cp, end);
862 #endif
863 opindex = 0;
864 break;
865 case DW_LNE_define_file: /* deprecated */
866 if (++filename_size < FILE_TABLE_SIZE) {
867 filename_table[filename_size - 1].name = (char *)ln - 1;
868 while (dwarf_read_1(ln, end)) {}
869 filename_table[filename_size - 1].dir_entry =
870 dwarf_read_uleb128(&ln, end);
872 else {
873 while (dwarf_read_1(ln, end)) {}
874 dwarf_read_uleb128(&ln, end);
876 dwarf_read_uleb128(&ln, end); // time
877 dwarf_read_uleb128(&ln, end); // size
878 break;
879 case DW_LNE_hi_user - 1:
880 function = (char *)cp;
881 func_addr = pc;
882 break;
883 default:
884 break;
886 break;
887 case DW_LNS_advance_pc:
888 if (max_ops_per_insn == 1)
889 pc += dwarf_read_uleb128(&ln, end) * min_insn_length;
890 else {
891 unsigned long long off = dwarf_read_uleb128(&ln, end);
893 pc += (opindex + off) / max_ops_per_insn *
894 min_insn_length;
895 opindex = (opindex + off) % max_ops_per_insn;
897 i = 0;
898 goto check_pc;
899 case DW_LNS_advance_line:
900 line += dwarf_read_sleb128(&ln, end);
901 break;
902 case DW_LNS_set_file:
903 i = dwarf_read_uleb128(&ln, end);
904 if (i < FILE_TABLE_SIZE && i < filename_size)
905 filename = filename_table[i].name;
906 break;
907 case DW_LNS_const_add_pc:
908 if (max_ops_per_insn == 1)
909 pc += ((255 - opcode_base) / line_range) * min_insn_length;
910 else {
911 unsigned int off = (255 - opcode_base) / line_range;
913 pc += ((opindex + off) / max_ops_per_insn) *
914 min_insn_length;
915 opindex = (opindex + off) % max_ops_per_insn;
917 i = 0;
918 goto check_pc;
919 case DW_LNS_fixed_advance_pc:
920 i = dwarf_read_2(ln, end);
921 pc += i;
922 opindex = 0;
923 i = 0;
924 goto check_pc;
925 default:
926 for (j = 0; j < opcode_length[i - 1]; j++)
927 dwarf_read_uleb128 (&ln, end);
928 break;
932 next_line:
933 ln = end;
936 filename = NULL;
937 func_addr = 0;
938 /* we try symtab symbols (no line number info) */
939 function = rt_elfsym(rc, wanted_pc, &func_addr);
940 if (function)
941 goto found;
942 if ((rc = rc->next))
943 goto next;
944 found:
945 if (filename) {
946 if (skip[0] && strstr(filename, skip))
947 return (addr_t)-1;
948 rt_printf("%s:%d: ", filename, line);
950 else
951 rt_printf("0x%08llx : ", (long long)wanted_pc);
952 rt_printf("%s %s", msg, function ? function : "???");
953 return (addr_t)func_addr;
955 /* ------------------------------------------------------------- */
957 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level);
959 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap)
961 rt_context *rc = &g_rtctxt;
962 addr_t pc = 0;
963 char skip[100];
964 int i, level, ret, n;
965 const char *a, *b, *msg;
967 if (fp) {
968 /* we're called from tcc_backtrace. */
969 rc->fp = (addr_t)fp;
970 rc->ip = (addr_t)ip;
971 msg = "";
972 } else {
973 /* we're called from signal/exception handler */
974 msg = "RUNTIME ERROR: ";
977 skip[0] = 0;
978 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
979 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
980 memcpy(skip, a, b - a), skip[b - a] = 0;
981 fmt = b + 1;
984 n = rc->num_callers ? rc->num_callers : 6;
985 for (i = level = 0; level < n; i++) {
986 ret = rt_get_caller_pc(&pc, rc, i);
987 a = "%s";
988 if (ret != -1) {
989 if (rc->dwarf)
990 pc = rt_printline_dwarf(rc, pc, level ? "by" : "at", skip);
991 else
992 pc = rt_printline(rc, pc, level ? "by" : "at", skip);
993 if (pc == (addr_t)-1)
994 continue;
995 a = ": %s";
997 if (level == 0) {
998 rt_printf(a, msg);
999 rt_vprintf(fmt, ap);
1000 } else if (ret == -1)
1001 break;
1002 rt_printf("\n");
1003 if (ret == -1 || (pc == (addr_t)rc->top_func && pc))
1004 break;
1005 ++level;
1008 rc->ip = rc->fp = 0;
1009 return 0;
1012 /* emit a run time error at position 'pc' */
1013 static int rt_error(const char *fmt, ...)
1015 va_list ap;
1016 int ret;
1017 va_start(ap, fmt);
1018 ret = _rt_error(0, 0, fmt, ap);
1019 va_end(ap);
1020 return ret;
1023 static void rt_exit(int code)
1025 rt_context *rc = &g_rtctxt;
1026 if (rc->do_jmp)
1027 longjmp(rc->jmp_buf, code ? code : 256);
1028 exit(code);
1031 #define NR_AT_EXIT 32
1033 static int nr_atexit = 0;
1034 static void (*at_exitfunc[NR_AT_EXIT])(void);
1036 static void init_atexit(void)
1038 nr_atexit = 0;
1041 static void run_atexit(void)
1043 while (nr_atexit)
1044 at_exitfunc[--nr_atexit]();
1047 static int rt_atexit(void (*function)(void))
1049 if (nr_atexit < NR_AT_EXIT)
1050 at_exitfunc[nr_atexit++] = function;
1051 return 1;
1054 /* ------------------------------------------------------------- */
1056 #ifndef _WIN32
1057 # include <signal.h>
1058 # ifndef __OpenBSD__
1059 # include <sys/ucontext.h>
1060 # endif
1061 #else
1062 # define ucontext_t CONTEXT
1063 #endif
1065 /* translate from ucontext_t* to internal rt_context * */
1066 static void rt_getcontext(ucontext_t *uc, rt_context *rc)
1068 #if defined _WIN64
1069 rc->ip = uc->Rip;
1070 rc->fp = uc->Rbp;
1071 rc->sp = uc->Rsp;
1072 #elif defined _WIN32
1073 rc->ip = uc->Eip;
1074 rc->fp = uc->Ebp;
1075 rc->sp = uc->Esp;
1076 #elif defined __i386__
1077 # if defined(__APPLE__)
1078 rc->ip = uc->uc_mcontext->__ss.__eip;
1079 rc->fp = uc->uc_mcontext->__ss.__ebp;
1080 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1081 rc->ip = uc->uc_mcontext.mc_eip;
1082 rc->fp = uc->uc_mcontext.mc_ebp;
1083 # elif defined(__dietlibc__)
1084 rc->ip = uc->uc_mcontext.eip;
1085 rc->fp = uc->uc_mcontext.ebp;
1086 # elif defined(__NetBSD__)
1087 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
1088 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
1089 # elif defined(__OpenBSD__)
1090 rc->ip = uc->sc_eip;
1091 rc->fp = uc->sc_ebp;
1092 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
1093 rc->ip = uc->uc_mcontext.gregs[EIP];
1094 rc->fp = uc->uc_mcontext.gregs[EBP];
1095 # else
1096 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
1097 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
1098 # endif
1099 #elif defined(__x86_64__)
1100 # if defined(__APPLE__)
1101 rc->ip = uc->uc_mcontext->__ss.__rip;
1102 rc->fp = uc->uc_mcontext->__ss.__rbp;
1103 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1104 rc->ip = uc->uc_mcontext.mc_rip;
1105 rc->fp = uc->uc_mcontext.mc_rbp;
1106 # elif defined(__NetBSD__)
1107 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
1108 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
1109 # elif defined(__OpenBSD__)
1110 rc->ip = uc->sc_rip;
1111 rc->fp = uc->sc_rbp;
1112 # else
1113 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
1114 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
1115 # endif
1116 #elif defined(__arm__) && defined(__NetBSD__)
1117 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1118 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1119 #elif defined(__arm__) && defined(__OpenBSD__)
1120 rc->ip = uc->sc_pc;
1121 rc->fp = uc->sc_r11;
1122 #elif defined(__arm__) && defined(__FreeBSD__)
1123 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1124 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1125 #elif defined(__arm__)
1126 rc->ip = uc->uc_mcontext.arm_pc;
1127 rc->fp = uc->uc_mcontext.arm_fp;
1128 #elif defined(__aarch64__) && defined(__APPLE__)
1129 // see:
1130 // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h
1131 rc->ip = uc->uc_mcontext->__ss.__pc;
1132 rc->fp = uc->uc_mcontext->__ss.__fp;
1133 #elif defined(__aarch64__) && defined(__FreeBSD__)
1134 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
1135 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
1136 #elif defined(__aarch64__) && defined(__NetBSD__)
1137 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1138 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1139 #elif defined(__aarch64__) && defined(__OpenBSD__)
1140 rc->ip = uc->sc_elr;
1141 rc->fp = uc->sc_x[29];
1142 #elif defined(__aarch64__)
1143 rc->ip = uc->uc_mcontext.pc;
1144 rc->fp = uc->uc_mcontext.regs[29];
1145 #elif defined(__riscv) && defined(__OpenBSD__)
1146 rc->ip = uc->sc_sepc;
1147 rc->fp = uc->sc_s[0];
1148 #elif defined(__riscv)
1149 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
1150 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
1151 #endif
1154 /* ------------------------------------------------------------- */
1155 #ifndef _WIN32
1156 /* signal handler for fatal errors */
1157 static void sig_error(int signum, siginfo_t *siginf, void *puc)
1159 rt_context *rc = &g_rtctxt;
1160 rt_getcontext(puc, rc);
1162 switch(signum) {
1163 case SIGFPE:
1164 switch(siginf->si_code) {
1165 case FPE_INTDIV:
1166 case FPE_FLTDIV:
1167 rt_error("division by zero");
1168 break;
1169 default:
1170 rt_error("floating point exception");
1171 break;
1173 break;
1174 case SIGBUS:
1175 case SIGSEGV:
1176 rt_error("invalid memory access");
1177 break;
1178 case SIGILL:
1179 rt_error("illegal instruction");
1180 break;
1181 case SIGABRT:
1182 rt_error("abort() called");
1183 break;
1184 default:
1185 rt_error("caught signal %d", signum);
1186 break;
1188 rt_exit(255);
1191 #ifndef SA_SIGINFO
1192 # define SA_SIGINFO 0x00000004u
1193 #endif
1195 /* Generate a stack backtrace when a CPU exception occurs. */
1196 static void set_exception_handler(void)
1198 struct sigaction sigact;
1199 /* install TCC signal handlers to print debug info on fatal
1200 runtime errors */
1201 sigemptyset (&sigact.sa_mask);
1202 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
1203 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
1204 sigact.sa_flags |= SA_ONSTACK;
1205 #endif
1206 sigact.sa_sigaction = sig_error;
1207 sigemptyset(&sigact.sa_mask);
1208 sigaction(SIGFPE, &sigact, NULL);
1209 sigaction(SIGILL, &sigact, NULL);
1210 sigaction(SIGSEGV, &sigact, NULL);
1211 sigaction(SIGBUS, &sigact, NULL);
1212 sigaction(SIGABRT, &sigact, NULL);
1213 #if 0//def SIGSTKSZ
1214 /* This allows stack overflow to be reported instead of a SEGV */
1216 stack_t ss;
1217 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
1219 ss.ss_sp = stack;
1220 ss.ss_size = SIGSTKSZ;
1221 ss.ss_flags = 0;
1222 sigaltstack(&ss, NULL);
1224 #endif
1227 #else /* WIN32 */
1229 /* signal handler for fatal errors */
1230 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
1232 rt_context *rc = &g_rtctxt;
1233 unsigned code;
1234 rt_getcontext(ex_info->ContextRecord, rc);
1236 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
1237 case EXCEPTION_ACCESS_VIOLATION:
1238 rt_error("invalid memory access");
1239 break;
1240 case EXCEPTION_STACK_OVERFLOW:
1241 rt_error("stack overflow");
1242 break;
1243 case EXCEPTION_INT_DIVIDE_BY_ZERO:
1244 rt_error("division by zero");
1245 break;
1246 case EXCEPTION_BREAKPOINT:
1247 case EXCEPTION_SINGLE_STEP:
1248 rc->ip = *(addr_t*)rc->sp;
1249 rt_error("breakpoint/single-step exception:");
1250 return EXCEPTION_CONTINUE_SEARCH;
1251 default:
1252 rt_error("caught exception %08x", code);
1253 break;
1255 if (rc->do_jmp)
1256 rt_exit(255);
1257 return EXCEPTION_EXECUTE_HANDLER;
1260 /* Generate a stack backtrace when a CPU exception occurs. */
1261 static void set_exception_handler(void)
1263 SetUnhandledExceptionFilter(cpu_exception_handler);
1266 #endif
1268 /* ------------------------------------------------------------- */
1269 /* return the PC at frame level 'level'. Return negative if not found */
1270 #if defined(__i386__) || defined(__x86_64__)
1271 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1273 addr_t ip, fp;
1274 if (level == 0) {
1275 ip = rc->ip;
1276 } else {
1277 ip = 0;
1278 fp = rc->fp;
1279 while (--level) {
1280 /* XXX: check address validity with program info */
1281 if (fp <= 0x1000)
1282 break;
1283 fp = ((addr_t *)fp)[0];
1285 if (fp > 0x1000)
1286 ip = ((addr_t *)fp)[1];
1288 if (ip <= 0x1000)
1289 return -1;
1290 *paddr = ip;
1291 return 0;
1294 #elif defined(__arm__)
1295 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1297 /* XXX: only supports linux/bsd */
1298 #if !defined(__linux__) && \
1299 !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
1300 return -1;
1301 #else
1302 if (level == 0) {
1303 *paddr = rc->ip;
1304 } else {
1305 addr_t fp = rc->fp;
1306 while (--level)
1307 fp = ((addr_t *)fp)[0];
1308 *paddr = ((addr_t *)fp)[2];
1310 return 0;
1311 #endif
1314 #elif defined(__aarch64__)
1315 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1317 if (level == 0) {
1318 *paddr = rc->ip;
1319 } else {
1320 addr_t *fp = (addr_t*)rc->fp;
1321 while (--level)
1322 fp = (addr_t *)fp[0];
1323 *paddr = fp[1];
1325 return 0;
1328 #elif defined(__riscv)
1329 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1331 if (level == 0) {
1332 *paddr = rc->ip;
1333 } else {
1334 addr_t *fp = (addr_t*)rc->fp;
1335 while (--level && fp >= (addr_t*)0x1000)
1336 fp = (addr_t *)fp[-2];
1337 if (fp < (addr_t*)0x1000)
1338 return -1;
1339 *paddr = fp[-1];
1341 return 0;
1344 #else
1345 #warning add arch specific rt_get_caller_pc()
1346 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1348 return -1;
1351 #endif
1352 #endif /* CONFIG_TCC_BACKTRACE */
1353 /* ------------------------------------------------------------- */
1354 #ifdef CONFIG_TCC_STATIC
1356 /* dummy function for profiling */
1357 ST_FUNC void *dlopen(const char *filename, int flag)
1359 return NULL;
1362 ST_FUNC void dlclose(void *p)
1366 ST_FUNC const char *dlerror(void)
1368 return "error";
1371 typedef struct TCCSyms {
1372 char *str;
1373 void *ptr;
1374 } TCCSyms;
1377 /* add the symbol you want here if no dynamic linking is done */
1378 static TCCSyms tcc_syms[] = {
1379 #if !defined(CONFIG_TCCBOOT)
1380 #define TCCSYM(a) { #a, &a, },
1381 TCCSYM(printf)
1382 TCCSYM(fprintf)
1383 TCCSYM(fopen)
1384 TCCSYM(fclose)
1385 #undef TCCSYM
1386 #endif
1387 { NULL, NULL },
1390 ST_FUNC void *dlsym(void *handle, const char *symbol)
1392 TCCSyms *p;
1393 p = tcc_syms;
1394 while (p->str != NULL) {
1395 if (!strcmp(p->str, symbol))
1396 return p->ptr;
1397 p++;
1399 return NULL;
1402 #endif /* CONFIG_TCC_STATIC */
1403 #endif /* TCC_IS_NATIVE */
1404 /* ------------------------------------------------------------- */