tccrun: 'tcc_relocate()' twice no longer supported
[tinycc.git] / tccrun.c
blobff8849ee358eb3f2b9fc0f21995c644b5ed45af0
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;
33 Stab_Sym *stab_sym_end;
34 char *stab_str;
36 struct {
37 unsigned char *dwarf_line;
38 unsigned char *dwarf_line_end;
39 unsigned char *dwarf_line_str;
42 ElfW(Sym) *esym_start;
43 ElfW(Sym) *esym_end;
44 char *elf_str;
46 addr_t prog_base;
47 void *bounds_start;
48 void *top_func;
49 TCCState *s1;
50 struct rt_context *next;
52 int num_callers;
53 int dwarf;
54 /* <-- */
55 } rt_context; /* size = 11 * PTR_SIZE + 2 * sizeof (int) */
57 typedef struct rt_frame
59 addr_t ip, fp, sp;
60 } rt_frame;
62 /* linked list of rt_contexts */
63 static rt_context *g_rc;
64 /* semaphore to protect it */
65 TCC_SEM(static rt_sem);
66 static int signal_set;
67 static void set_exception_handler(void);
69 int _rt_error(rt_frame *f, const char *msg, const char *fmt, va_list ap);
70 void rt_wait_sem(void) { WAIT_SEM(&rt_sem); }
71 void rt_post_sem(void) { POST_SEM(&rt_sem); }
72 #endif /* def CONFIG_TCC_BACKTRACE */
74 /* handle exit/atexit for tcc_run() -- thread-unsafe */
75 static jmp_buf rt_jb;
76 static int rt_do_jmp;
77 static int rt_nr_exit;
78 static void *rt_exitfunc[32];
79 static void *rt_exitarg[32];
81 static void rt_exit(int code)
83 if (rt_do_jmp)
84 longjmp(rt_jb, code ? code : 256);
85 exit(code);
88 /* ------------------------------------------------------------- */
89 /* defined when included from lib/bt-exe.c */
90 #ifndef CONFIG_TCC_BACKTRACE_ONLY
92 #ifndef _WIN32
93 # include <sys/mman.h>
94 #endif
96 static int protect_pages(void *ptr, unsigned long length, int mode);
97 static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff);
99 #ifdef _WIN64
100 static void *win64_add_function_table(TCCState *s1);
101 static void win64_del_function_table(void *);
102 #endif
104 static void bt_link(TCCState *s1)
106 #ifdef CONFIG_TCC_BACKTRACE
107 rt_context *rc;
108 void *p;
110 if (!s1->do_backtrace)
111 return;
112 rc = tcc_get_symbol(s1, "__rt_info");
113 if (!rc)
114 return;
115 rt_wait_sem();
116 rc->next = g_rc, g_rc = rc, rc->s1 = s1;
117 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
118 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
119 rc->elf_str = (char *)symtab_section->link->data;
120 rc->top_func = tcc_get_symbol(s1, "main");
121 if (PTR_SIZE == 8 && !s1->dwarf)
122 rc->prog_base &= 0xffffffff00000000ULL;
123 #ifdef CONFIG_TCC_BCHECK
124 if (s1->do_bounds_check) {
125 if ((p = tcc_get_symbol(s1, "__bound_init")))
126 ((void(*)(void*,int))p)(rc->bounds_start, 1);
128 #endif
129 rt_post_sem();
130 if (0 == signal_set)
131 set_exception_handler(), signal_set = 1;
132 #endif
135 static void bt_unlink(TCCState *s1)
137 #ifdef CONFIG_TCC_BACKTRACE
138 rt_context *rc, **pp;
139 rt_wait_sem();
140 for (pp = &g_rc; rc = *pp, rc; pp = &rc->next)
141 if (rc->s1 == s1) {
142 *pp = rc->next;
143 break;
145 rt_post_sem();
146 #endif
149 #if !_WIN32 && !__APPLE__
150 //#define HAVE_SELINUX 1
151 #endif
153 static int rt_mem(TCCState *s1, int size)
155 void *ptr;
156 int ptr_diff = 0;
157 #ifdef HAVE_SELINUX
158 /* Using mmap instead of malloc */
159 void *prw;
160 char tmpfname[] = "/tmp/.tccrunXXXXXX";
161 int fd = mkstemp(tmpfname);
162 unlink(tmpfname);
163 ftruncate(fd, size);
165 ptr = mmap(NULL, size * 2, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
166 /* mmap RW memory at fixed distance */
167 prw = mmap((char*)ptr + size, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, 0);
168 close(fd);
169 if (ptr == MAP_FAILED || prw == MAP_FAILED)
170 return tcc_error_noabort("tccrun: could not map memory");
171 ptr_diff = (char*)prw - (char*)ptr; /* = size; */
172 //printf("map %p %p %p\n", ptr, prw, (void*)ptr_diff);
173 #else
174 ptr = tcc_malloc(size);
175 #endif
176 s1->run_ptr = ptr;
177 s1->run_size = size;
178 return ptr_diff;
181 /* ------------------------------------------------------------- */
182 /* Do all relocations (needed before using tcc_get_symbol())
183 Returns -1 on error. */
185 LIBTCCAPI int tcc_relocate(TCCState *s1)
187 int size, ret, ptr_diff;
189 if (s1->run_ptr)
190 exit(tcc_error_noabort("'tcc_relocate()' twice is no longer supported"));
192 #ifdef CONFIG_TCC_BACKTRACE
193 /* for bt-log.c (but not when 'tcc -bt -run tcc.c') */
194 if (s1->do_backtrace && !tcc_get_symbol(s1, "_rt_error"))
195 tcc_add_symbol(s1, "_rt_error", _rt_error);
196 #endif
198 size = tcc_relocate_ex(s1, NULL, 0);
199 if (size < 0)
200 return -1;
201 ptr_diff = rt_mem(s1, size);
202 if (ptr_diff < 0)
203 return -1;
204 ret = tcc_relocate_ex(s1, s1->run_ptr, ptr_diff);
205 if (ret == 0)
206 bt_link(s1);
207 return ret;
210 ST_FUNC void tcc_run_free(TCCState *s1)
212 unsigned size;
213 void *ptr;
214 int i;
216 /* free any loaded DLLs */
217 for ( i = 0; i < s1->nb_loaded_dlls; i++) {
218 DLLReference *ref = s1->loaded_dlls[i];
219 if ( ref->handle )
220 #ifdef _WIN32
221 FreeLibrary((HMODULE)ref->handle);
222 #else
223 dlclose(ref->handle);
224 #endif
226 /* free loaded dlls array */
227 dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
229 /* unmap or unprotect and free memory */
230 ptr = s1->run_ptr;
231 if (NULL == ptr)
232 return;
233 bt_unlink(s1);
234 size = s1->run_size;
235 #ifdef HAVE_SELINUX
236 munmap(ptr, size * 2);
237 #else
238 /* unprotect memory to make it usable for malloc again */
239 protect_pages(ptr, size, 2 /*rw*/);
240 #ifdef _WIN64
241 win64_del_function_table(s1->run_function_table);
242 #endif
243 tcc_free(ptr);
244 #endif
247 static void run_cdtors(TCCState *s1, const char *start, const char *end,
248 int argc, char **argv, char **envp)
250 void **a = (void **)get_sym_addr(s1, start, 0, 0);
251 void **b = (void **)get_sym_addr(s1, end, 0, 0);
252 while (a != b)
253 ((void(*)(int, char **, char **))*a++)(argc, argv, envp);
256 static void run_on_exit(int ret)
258 int n = rt_nr_exit;
259 while (n)
260 --n, ((void(*)(int,void*))rt_exitfunc[n])(ret, rt_exitarg[n]);
263 static int rt_on_exit(void *function, void *arg)
265 if (rt_nr_exit < countof(rt_exitfunc)) {
266 rt_exitfunc[rt_nr_exit] = function;
267 rt_exitarg[rt_nr_exit++] = arg;
268 return 0;
270 return 1;
273 static int rt_atexit(void *function)
275 return rt_on_exit(function, NULL);
278 /* launch the compiled program with the given arguments */
279 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
281 int (*prog_main)(int, char **, char **), ret;
283 #if defined(__APPLE__) || defined(__FreeBSD__)
284 char **envp = NULL;
285 #elif defined(__OpenBSD__) || defined(__NetBSD__)
286 extern char **environ;
287 char **envp = environ;
288 #else
289 char **envp = environ;
290 #endif
292 s1->run_main = s1->nostdlib ? "_start" : "main";
293 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->run_main, 0, 1))
294 return 0;
296 tcc_add_symbol(s1, "exit", rt_exit);
297 tcc_add_symbol(s1, "atexit", rt_atexit);
298 tcc_add_symbol(s1, "on_exit", rt_on_exit);
299 if (tcc_relocate(s1) < 0)
300 return -1;
302 prog_main = (void*)get_sym_addr(s1, s1->run_main, 1, 1);
303 if ((addr_t)-1 == (addr_t)prog_main)
304 return -1;
306 errno = 0; /* clean errno value */
307 fflush(stdout);
308 fflush(stderr);
309 rt_do_jmp = 1;
310 rt_nr_exit = 0;
312 /* These aren't C symbols, so don't need leading underscore handling. */
313 run_cdtors(s1, "__init_array_start", "__init_array_end", argc, argv, envp);
314 ret = setjmp(rt_jb);
315 if (0 == ret)
316 ret = prog_main(argc, argv, envp);
317 else if (256 == ret)
318 ret = 0;
319 run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL);
320 run_on_exit(ret);
321 if (s1->dflag & 16 && ret) /* tcc -dt -run ... */
322 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
323 return ret;
326 /* ------------------------------------------------------------- */
327 /* remove all STB_LOCAL symbols */
328 static void cleanup_symbols(TCCState *s1)
330 Section *s = s1->symtab;
331 int sym_index, end_sym = s->data_offset / sizeof (ElfSym);
332 /* reset symtab */
333 s->data_offset = s->link->data_offset = s->hash->data_offset = 0;
334 init_symtab(s);
335 /* add global symbols again */
336 for (sym_index = 1; sym_index < end_sym; ++sym_index) {
337 ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index];
338 const char *name = (char *)s->link->data + sym->st_name;
339 if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)
340 continue;
341 //printf("sym %s\n", name);
342 put_elf_sym(s, sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx, name);
346 /* free all sections except symbols */
347 static void cleanup_sections(TCCState *s1)
349 struct { Section **secs; int nb_secs; } *p = (void*)&s1->sections;
350 int i, f = 2;
351 do {
352 for (i = --f; i < p->nb_secs; i++) {
353 Section *s = p->secs[i];
354 if (s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash) {
355 s->data = tcc_realloc(s->data, s->data_allocated = s->data_offset);
356 } else {
357 free_section(s), tcc_free(s), p->secs[i] = NULL;
360 } while (++p, f);
363 /* ------------------------------------------------------------- */
364 /* 0 = .text rwx other rw */
365 /* 1 = .text rx .rdata r .data/.bss rw */
366 #ifndef CONFIG_RUNMEM_RO
367 # define CONFIG_RUNMEM_RO 1
368 #endif
370 #define DEBUG_RUNMEN 0
372 /* relocate code. Return -1 on error, required size if ptr is NULL,
373 otherwise copy code into buffer passed by the caller */
374 static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff)
376 Section *s;
377 unsigned offset, length, align, i, k, f;
378 unsigned n, copy;
379 addr_t mem, addr;
381 if (NULL == ptr) {
382 s1->nb_errors = 0;
383 #ifdef TCC_TARGET_PE
384 pe_output_file(s1, NULL);
385 #else
386 tcc_add_runtime(s1);
387 resolve_common_syms(s1);
388 build_got_entries(s1, 0);
389 #endif
392 offset = copy = 0;
393 mem = (addr_t)ptr;
395 #if DEBUG_RUNMEN
396 if (mem)
397 fprintf(stderr, "X: <base> %p len %5x\n",
398 ptr, s1->run_size);
399 #endif
401 redo:
402 if (s1->nb_errors)
403 return -1;
404 for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
405 n = 0; addr = 0;
406 for(i = 1; i < s1->nb_sections; i++) {
407 static const char shf[] = {
408 SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
410 s = s1->sections[i];
411 if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
412 continue;
413 length = s->data_offset;
415 if (copy) { /* final step: copy section data to memory */
416 void *ptr;
417 if (addr == 0)
418 addr = s->sh_addr;
419 n = (s->sh_addr - addr) + length;
420 ptr = (void*)s->sh_addr;
421 if (k == 0)
422 ptr = (void*)(s->sh_addr + ptr_diff);
423 if (NULL == s->data || s->sh_type == SHT_NOBITS)
424 memset(ptr, 0, length);
425 else
426 memcpy(ptr, s->data, length);
427 #ifdef _WIN64
428 if (s == s1->uw_pdata)
429 s1->run_function_table = win64_add_function_table(s1);
430 #endif
431 continue;
434 align = s->sh_addralign - 1;
435 if (++n == 1) {
436 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
437 /* To avoid that x86 processors would reload cached instructions
438 each time when data is written in the near, we need to make
439 sure that code and data do not share the same 64 byte unit */
440 if (align < 63)
441 align = 63;
442 #endif
443 /* start new page for different permissions */
444 if (CONFIG_RUNMEM_RO || k < 2)
445 align = PAGESIZE - 1;
447 addr = k ? mem + ptr_diff : mem;
448 offset += -(addr + offset) & align;
449 s->sh_addr = mem ? addr + offset : 0;
450 offset += length;
451 #if DEBUG_RUNMEN
452 if (mem)
453 fprintf(stderr, "%d: %-16s %p len %5x align %04x\n",
454 k, s->name, (void*)s->sh_addr, length, align + 1);
455 #endif
457 if (copy) { /* set permissions */
458 if (n == 0) /* no data */
459 continue;
460 #ifdef HAVE_SELINUX
461 if (k == 0) /* SHF_EXECINSTR has its own mapping */
462 continue;
463 #endif
464 f = k;
465 if (CONFIG_RUNMEM_RO == 0) {
466 if (f != 0)
467 continue;
468 f = 3; /* change only SHF_EXECINSTR to rwx */
470 #if DEBUG_RUNMEN
471 fprintf(stderr, "protect %3s %p len %5x\n",
472 &"rx\0r \0rw\0rwx"[f*3],
473 (void*)addr, (unsigned)((n + PAGESIZE-1) & ~(PAGESIZE-1)));
474 #endif
475 if (protect_pages((void*)addr, n, f) < 0)
476 return tcc_error_noabort(
477 "mprotect failed (did you mean to configure --with-selinux?)");
481 if (0 == mem) {
482 offset = (offset + (PAGESIZE-1)) & ~(PAGESIZE-1);
483 #ifndef HAVE_SELINUX
484 offset += PAGESIZE; /* extra space to align malloc memory start */
485 #endif
486 return offset;
489 if (copy) {
490 /* remove local symbols and free sections except symtab */
491 cleanup_symbols(s1);
492 cleanup_sections(s1);
493 return 0;
496 /* relocate symbols */
497 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
498 /* relocate sections */
499 #ifdef TCC_TARGET_PE
500 s1->pe_imagebase = mem;
501 #else
502 relocate_plt(s1);
503 #endif
504 relocate_sections(s1);
505 copy = 1;
506 goto redo;
509 /* ------------------------------------------------------------- */
510 /* allow to run code in memory */
512 static int protect_pages(void *ptr, unsigned long length, int mode)
514 #ifdef _WIN32
515 static const unsigned char protect[] = {
516 PAGE_EXECUTE_READ,
517 PAGE_READONLY,
518 PAGE_READWRITE,
519 PAGE_EXECUTE_READWRITE
521 DWORD old;
522 if (!VirtualProtect(ptr, length, protect[mode], &old))
523 return -1;
524 return 0;
525 #else
526 static const unsigned char protect[] = {
527 PROT_READ | PROT_EXEC,
528 PROT_READ,
529 PROT_READ | PROT_WRITE,
530 PROT_READ | PROT_WRITE | PROT_EXEC
532 addr_t start, end;
533 start = (addr_t)ptr & ~(PAGESIZE - 1);
534 end = (addr_t)ptr + length;
535 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
536 if (mprotect((void *)start, end - start, protect[mode]))
537 return -1;
538 /* XXX: BSD sometimes dump core with bad system call */
539 # if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
540 if (mode == 0 || mode == 3) {
541 void __clear_cache(void *beginning, void *end);
542 __clear_cache(ptr, (char *)ptr + length);
544 # endif
545 return 0;
546 #endif
549 #ifdef _WIN64
550 static void *win64_add_function_table(TCCState *s1)
552 void *p = NULL;
553 if (s1->uw_pdata) {
554 p = (void*)s1->uw_pdata->sh_addr;
555 RtlAddFunctionTable(
556 (RUNTIME_FUNCTION*)p,
557 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
558 s1->pe_imagebase
560 s1->uw_pdata = NULL;
562 return p;
565 static void win64_del_function_table(void *p)
567 if (p) {
568 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
571 #endif
573 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
574 /* ------------------------------------------------------------- */
575 #ifdef CONFIG_TCC_BACKTRACE
577 static int rt_vprintf(const char *fmt, va_list ap)
579 int ret = vfprintf(stderr, fmt, ap);
580 fflush(stderr);
581 return ret;
584 static int rt_printf(const char *fmt, ...)
586 va_list ap;
587 int r;
588 va_start(ap, fmt);
589 r = rt_vprintf(fmt, ap);
590 va_end(ap);
591 return r;
594 static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr)
596 ElfW(Sym) *esym;
597 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
598 int type = ELFW(ST_TYPE)(esym->st_info);
599 if ((type == STT_FUNC || type == STT_GNU_IFUNC)
600 && wanted_pc >= esym->st_value
601 && wanted_pc < esym->st_value + esym->st_size) {
602 *func_addr = esym->st_value;
603 return rc->elf_str + esym->st_name;
606 return NULL;
610 /* print the position in the source file of PC value 'pc' by reading
611 the stabs debug information */
612 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
613 const char *msg, const char *skip)
615 char func_name[128];
616 addr_t func_addr, last_pc, pc;
617 const char *incl_files[INCLUDE_STACK_SIZE];
618 int incl_index, last_incl_index, len, last_line_num, i;
619 const char *str, *p;
620 Stab_Sym *sym;
622 next:
623 func_name[0] = '\0';
624 func_addr = 0;
625 incl_index = 0;
626 last_pc = (addr_t)-1;
627 last_line_num = 1;
628 last_incl_index = 0;
629 if (NULL == rc)
630 goto found;
632 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
633 str = rc->stab_str + sym->n_strx;
634 pc = sym->n_value;
636 switch(sym->n_type) {
637 case N_SLINE:
638 if (func_addr)
639 goto rel_pc;
640 case N_SO:
641 case N_SOL:
642 goto abs_pc;
643 case N_FUN:
644 if (sym->n_strx == 0) /* end of function */
645 goto rel_pc;
646 abs_pc:
647 #if PTR_SIZE == 8
648 /* Stab_Sym.n_value is only 32bits */
649 pc += rc->prog_base;
650 #endif
651 goto check_pc;
652 rel_pc:
653 pc += func_addr;
654 check_pc:
655 if (pc >= wanted_pc && wanted_pc >= last_pc)
656 goto found;
657 break;
660 switch(sym->n_type) {
661 /* function start or end */
662 case N_FUN:
663 if (sym->n_strx == 0)
664 goto reset_func;
665 p = strchr(str, ':');
666 if (0 == p || (len = p - str + 1, len > sizeof func_name))
667 len = sizeof func_name;
668 pstrcpy(func_name, len, str);
669 func_addr = pc;
670 break;
671 /* line number info */
672 case N_SLINE:
673 last_pc = pc;
674 last_line_num = sym->n_desc;
675 last_incl_index = incl_index;
676 break;
677 /* include files */
678 case N_BINCL:
679 if (incl_index < INCLUDE_STACK_SIZE)
680 incl_files[incl_index++] = str;
681 break;
682 case N_EINCL:
683 if (incl_index > 1)
684 incl_index--;
685 break;
686 /* start/end of translation unit */
687 case N_SO:
688 incl_index = 0;
689 if (sym->n_strx) {
690 /* do not add path */
691 len = strlen(str);
692 if (len > 0 && str[len - 1] != '/')
693 incl_files[incl_index++] = str;
695 reset_func:
696 func_name[0] = '\0';
697 func_addr = 0;
698 last_pc = (addr_t)-1;
699 break;
700 /* alternative file name (from #line or #include directives) */
701 case N_SOL:
702 if (incl_index)
703 incl_files[incl_index-1] = str;
704 break;
708 func_name[0] = '\0';
709 func_addr = 0;
710 last_incl_index = 0;
712 /* we try symtab symbols (no line number info) */
713 p = rt_elfsym(rc, wanted_pc, &func_addr);
714 if (p) {
715 pstrcpy(func_name, sizeof func_name, p);
716 goto found;
718 rc = rc->next;
719 goto next;
721 found:
722 i = last_incl_index;
723 if (i > 0) {
724 str = incl_files[--i];
725 if (skip[0] && strstr(str, skip))
726 return (addr_t)-1;
727 rt_printf("%s:%d: ", str, last_line_num);
728 } else
729 rt_printf("%08llx : ", (long long)wanted_pc);
730 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
731 #if 0
732 if (--i >= 0) {
733 rt_printf(" (included from ");
734 for (;;) {
735 rt_printf("%s", incl_files[i]);
736 if (--i < 0)
737 break;
738 rt_printf(", ");
740 rt_printf(")");
742 #endif
743 return func_addr;
746 /* ------------------------------------------------------------- */
747 /* rt_printline - dwarf version */
749 #define MAX_128 ((8 * sizeof (long long) + 6) / 7)
751 #define DIR_TABLE_SIZE (64)
752 #define FILE_TABLE_SIZE (512)
754 #define dwarf_read_1(ln,end) \
755 ((ln) < (end) ? *(ln)++ : 0)
756 #define dwarf_read_2(ln,end) \
757 ((ln) + 2 < (end) ? (ln) += 2, read16le((ln) - 2) : 0)
758 #define dwarf_read_4(ln,end) \
759 ((ln) + 4 < (end) ? (ln) += 4, read32le((ln) - 4) : 0)
760 #define dwarf_read_8(ln,end) \
761 ((ln) + 8 < (end) ? (ln) += 8, read64le((ln) - 8) : 0)
762 #define dwarf_ignore_type(ln, end) /* timestamp/size/md5/... */ \
763 switch (entry_format[j].form) { \
764 case DW_FORM_data1: (ln) += 1; break; \
765 case DW_FORM_data2: (ln) += 2; break; \
766 case DW_FORM_data4: (ln) += 3; break; \
767 case DW_FORM_data8: (ln) += 8; break; \
768 case DW_FORM_data16: (ln) += 16; break; \
769 case DW_FORM_udata: dwarf_read_uleb128(&(ln), (end)); break; \
770 default: goto next_line; \
773 static unsigned long long
774 dwarf_read_uleb128(unsigned char **ln, unsigned char *end)
776 unsigned char *cp = *ln;
777 unsigned long long retval = 0;
778 int i;
780 for (i = 0; i < MAX_128; i++) {
781 unsigned long long byte = dwarf_read_1(cp, end);
783 retval |= (byte & 0x7f) << (i * 7);
784 if ((byte & 0x80) == 0)
785 break;
787 *ln = cp;
788 return retval;
791 static long long
792 dwarf_read_sleb128(unsigned char **ln, unsigned char *end)
794 unsigned char *cp = *ln;
795 long long retval = 0;
796 int i;
798 for (i = 0; i < MAX_128; i++) {
799 unsigned long long byte = dwarf_read_1(cp, end);
801 retval |= (byte & 0x7f) << (i * 7);
802 if ((byte & 0x80) == 0) {
803 if ((byte & 0x40) && (i + 1) * 7 < 64)
804 retval |= -1LL << ((i + 1) * 7);
805 break;
808 *ln = cp;
809 return retval;
812 static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc,
813 const char *msg, const char *skip)
815 unsigned char *ln;
816 unsigned char *cp;
817 unsigned char *end;
818 unsigned char *opcode_length;
819 unsigned long long size;
820 unsigned int length;
821 unsigned char version;
822 unsigned int min_insn_length;
823 unsigned int max_ops_per_insn;
824 int line_base;
825 unsigned int line_range;
826 unsigned int opcode_base;
827 unsigned int opindex;
828 unsigned int col;
829 unsigned int i;
830 unsigned int j;
831 unsigned int len;
832 unsigned long long value;
833 struct {
834 unsigned int type;
835 unsigned int form;
836 } entry_format[256];
837 unsigned int dir_size;
838 #if 0
839 char *dirs[DIR_TABLE_SIZE];
840 #endif
841 unsigned int filename_size;
842 struct dwarf_filename_struct {
843 unsigned int dir_entry;
844 char *name;
845 } filename_table[FILE_TABLE_SIZE];
846 addr_t last_pc;
847 addr_t pc;
848 addr_t func_addr;
849 int line;
850 char *filename;
851 char *function;
853 next:
854 filename = NULL;
855 func_addr = 0;
856 if (NULL == rc)
857 goto found;
859 ln = rc->dwarf_line;
860 while (ln < rc->dwarf_line_end) {
861 dir_size = 0;
862 filename_size = 0;
863 last_pc = 0;
864 pc = 0;
865 func_addr = 0;
866 line = 1;
867 filename = NULL;
868 function = NULL;
869 length = 4;
870 size = dwarf_read_4(ln, rc->dwarf_line_end);
871 if (size == 0xffffffffu) // dwarf 64
872 length = 8, size = dwarf_read_8(ln, rc->dwarf_line_end);
873 end = ln + size;
874 if (end < ln || end > rc->dwarf_line_end)
875 break;
876 version = dwarf_read_2(ln, end);
877 if (version >= 5)
878 ln += length + 2; // address size, segment selector, prologue Length
879 else
880 ln += length; // prologue Length
881 min_insn_length = dwarf_read_1(ln, end);
882 if (version >= 4)
883 max_ops_per_insn = dwarf_read_1(ln, end);
884 else
885 max_ops_per_insn = 1;
886 ln++; // Initial value of 'is_stmt'
887 line_base = dwarf_read_1(ln, end);
888 line_base |= line_base >= 0x80 ? ~0xff : 0;
889 line_range = dwarf_read_1(ln, end);
890 opcode_base = dwarf_read_1(ln, end);
891 opcode_length = ln;
892 ln += opcode_base - 1;
893 opindex = 0;
894 if (version >= 5) {
895 col = dwarf_read_1(ln, end);
896 for (i = 0; i < col; i++) {
897 entry_format[i].type = dwarf_read_uleb128(&ln, end);
898 entry_format[i].form = dwarf_read_uleb128(&ln, end);
900 dir_size = dwarf_read_uleb128(&ln, end);
901 for (i = 0; i < dir_size; i++) {
902 for (j = 0; j < col; j++) {
903 if (entry_format[j].type == DW_LNCT_path) {
904 if (entry_format[j].form != DW_FORM_line_strp)
905 goto next_line;
906 #if 0
907 value = length == 4 ? dwarf_read_4(ln, end)
908 : dwarf_read_8(ln, end);
909 if (i < DIR_TABLE_SIZE)
910 dirs[i] = (char *)rc->dwarf_line_str + value;
911 #else
912 length == 4 ? dwarf_read_4(ln, end)
913 : dwarf_read_8(ln, end);
914 #endif
916 else
917 dwarf_ignore_type(ln, end);
920 col = dwarf_read_1(ln, end);
921 for (i = 0; i < col; i++) {
922 entry_format[i].type = dwarf_read_uleb128(&ln, end);
923 entry_format[i].form = dwarf_read_uleb128(&ln, end);
925 filename_size = dwarf_read_uleb128(&ln, end);
926 for (i = 0; i < filename_size; i++)
927 for (j = 0; j < col; j++) {
928 if (entry_format[j].type == DW_LNCT_path) {
929 if (entry_format[j].form != DW_FORM_line_strp)
930 goto next_line;
931 value = length == 4 ? dwarf_read_4(ln, end)
932 : dwarf_read_8(ln, end);
933 if (i < FILE_TABLE_SIZE)
934 filename_table[i].name =
935 (char *)rc->dwarf_line_str + value;
937 else if (entry_format[j].type == DW_LNCT_directory_index) {
938 switch (entry_format[j].form) {
939 case DW_FORM_data1: value = dwarf_read_1(ln, end); break;
940 case DW_FORM_data2: value = dwarf_read_2(ln, end); break;
941 case DW_FORM_data4: value = dwarf_read_4(ln, end); break;
942 case DW_FORM_udata: value = dwarf_read_uleb128(&ln, end); break;
943 default: goto next_line;
945 if (i < FILE_TABLE_SIZE)
946 filename_table[i].dir_entry = value;
948 else
949 dwarf_ignore_type(ln, end);
952 else {
953 while ((dwarf_read_1(ln, end))) {
954 #if 0
955 if (++dir_size < DIR_TABLE_SIZE)
956 dirs[dir_size - 1] = (char *)ln - 1;
957 #endif
958 while (dwarf_read_1(ln, end)) {}
960 while ((dwarf_read_1(ln, end))) {
961 if (++filename_size < FILE_TABLE_SIZE) {
962 filename_table[filename_size - 1].name = (char *)ln - 1;
963 while (dwarf_read_1(ln, end)) {}
964 filename_table[filename_size - 1].dir_entry =
965 dwarf_read_uleb128(&ln, end);
967 else {
968 while (dwarf_read_1(ln, end)) {}
969 dwarf_read_uleb128(&ln, end);
971 dwarf_read_uleb128(&ln, end); // time
972 dwarf_read_uleb128(&ln, end); // size
975 if (filename_size >= 1)
976 filename = filename_table[0].name;
977 while (ln < end) {
978 last_pc = pc;
979 i = dwarf_read_1(ln, end);
980 if (i >= opcode_base) {
981 if (max_ops_per_insn == 1)
982 pc += ((i - opcode_base) / line_range) * min_insn_length;
983 else {
984 pc += (opindex + (i - opcode_base) / line_range) /
985 max_ops_per_insn * min_insn_length;
986 opindex = (opindex + (i - opcode_base) / line_range) %
987 max_ops_per_insn;
989 i = (int)((i - opcode_base) % line_range) + line_base;
990 check_pc:
991 if (pc >= wanted_pc && wanted_pc >= last_pc)
992 goto found;
993 line += i;
995 else {
996 switch (i) {
997 case 0:
998 len = dwarf_read_uleb128(&ln, end);
999 cp = ln;
1000 ln += len;
1001 if (len == 0)
1002 goto next_line;
1003 switch (dwarf_read_1(cp, end)) {
1004 case DW_LNE_end_sequence:
1005 break;
1006 case DW_LNE_set_address:
1007 #if PTR_SIZE == 4
1008 pc = dwarf_read_4(cp, end);
1009 #else
1010 pc = dwarf_read_8(cp, end);
1011 #endif
1012 #if defined TCC_TARGET_MACHO
1013 pc += rc->prog_base;
1014 #endif
1015 opindex = 0;
1016 break;
1017 case DW_LNE_define_file: /* deprecated */
1018 if (++filename_size < FILE_TABLE_SIZE) {
1019 filename_table[filename_size - 1].name = (char *)ln - 1;
1020 while (dwarf_read_1(ln, end)) {}
1021 filename_table[filename_size - 1].dir_entry =
1022 dwarf_read_uleb128(&ln, end);
1024 else {
1025 while (dwarf_read_1(ln, end)) {}
1026 dwarf_read_uleb128(&ln, end);
1028 dwarf_read_uleb128(&ln, end); // time
1029 dwarf_read_uleb128(&ln, end); // size
1030 break;
1031 case DW_LNE_hi_user - 1:
1032 function = (char *)cp;
1033 func_addr = pc;
1034 break;
1035 default:
1036 break;
1038 break;
1039 case DW_LNS_advance_pc:
1040 if (max_ops_per_insn == 1)
1041 pc += dwarf_read_uleb128(&ln, end) * min_insn_length;
1042 else {
1043 unsigned long long off = dwarf_read_uleb128(&ln, end);
1045 pc += (opindex + off) / max_ops_per_insn *
1046 min_insn_length;
1047 opindex = (opindex + off) % max_ops_per_insn;
1049 i = 0;
1050 goto check_pc;
1051 case DW_LNS_advance_line:
1052 line += dwarf_read_sleb128(&ln, end);
1053 break;
1054 case DW_LNS_set_file:
1055 i = dwarf_read_uleb128(&ln, end);
1056 i -= i > 0 && version < 5;
1057 if (i < FILE_TABLE_SIZE && i < filename_size)
1058 filename = filename_table[i].name;
1059 break;
1060 case DW_LNS_const_add_pc:
1061 if (max_ops_per_insn == 1)
1062 pc += ((255 - opcode_base) / line_range) * min_insn_length;
1063 else {
1064 unsigned int off = (255 - opcode_base) / line_range;
1066 pc += ((opindex + off) / max_ops_per_insn) *
1067 min_insn_length;
1068 opindex = (opindex + off) % max_ops_per_insn;
1070 i = 0;
1071 goto check_pc;
1072 case DW_LNS_fixed_advance_pc:
1073 i = dwarf_read_2(ln, end);
1074 pc += i;
1075 opindex = 0;
1076 i = 0;
1077 goto check_pc;
1078 default:
1079 for (j = 0; j < opcode_length[i - 1]; j++)
1080 dwarf_read_uleb128 (&ln, end);
1081 break;
1085 next_line:
1086 ln = end;
1089 filename = NULL;
1090 func_addr = 0;
1091 /* we try symtab symbols (no line number info) */
1092 function = rt_elfsym(rc, wanted_pc, &func_addr);
1093 if (function)
1094 goto found;
1095 rc = rc->next;
1096 goto next;
1098 found:
1099 if (filename) {
1100 if (skip[0] && strstr(filename, skip))
1101 return (addr_t)-1;
1102 rt_printf("%s:%d: ", filename, line);
1104 else
1105 rt_printf("0x%08llx : ", (long long)wanted_pc);
1106 rt_printf("%s %s", msg, function ? function : "???");
1107 return (addr_t)func_addr;
1109 /* ------------------------------------------------------------- */
1111 static int rt_get_caller_pc(addr_t *paddr, rt_frame *f, int level);
1113 int _rt_error(rt_frame *f, const char *msg, const char *fmt, va_list ap)
1115 rt_context *rc;
1116 addr_t pc = 0;
1117 char skip[100];
1118 int i, level, ret, n, one;
1119 const char *a, *b;
1120 addr_t (*printline)(rt_context*, addr_t, const char*, const char*);
1121 addr_t top_func = 0;
1123 skip[0] = 0;
1124 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
1125 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
1126 memcpy(skip, a, b - a), skip[b - a] = 0;
1127 fmt = b + 1;
1129 one = 0;
1130 /* hack for bcheck.c:dprintf(): one level, no newline */
1131 if (fmt[0] == '\001')
1132 ++fmt, one = 1;
1134 rt_wait_sem();
1135 rc = g_rc;
1136 printline = rt_printline, n = 6;
1137 if (rc) {
1138 if (rc->dwarf)
1139 printline = rt_printline_dwarf;
1140 if (rc->num_callers)
1141 n = rc->num_callers;
1142 top_func = (addr_t)rc->top_func;
1145 for (i = level = 0; level < n; i++) {
1146 ret = rt_get_caller_pc(&pc, f, i);
1147 a = "%s";
1148 if (ret != -1) {
1149 pc = printline(rc, pc, level ? "by" : "at", skip);
1150 if (pc == (addr_t)-1)
1151 continue;
1152 a = ": %s";
1154 if (level == 0) {
1155 rt_printf(a, msg);
1156 rt_vprintf(fmt, ap);
1157 } else if (ret == -1)
1158 break;
1159 if (one)
1160 break;
1161 rt_printf("\n");
1162 if (ret == -1 || (pc == top_func && pc))
1163 break;
1164 ++level;
1167 rt_post_sem();
1168 return 0;
1171 /* emit a run time error at position 'pc' */
1172 static int rt_error(rt_frame *f, const char *fmt, ...)
1174 va_list ap;
1175 int ret;
1176 va_start(ap, fmt);
1177 ret = _rt_error(f, "RUNTIME ERROR: ", fmt, ap);
1178 va_end(ap);
1179 return ret;
1182 /* ------------------------------------------------------------- */
1184 #ifndef _WIN32
1185 # include <signal.h>
1186 # ifndef __OpenBSD__
1187 # include <sys/ucontext.h>
1188 # endif
1189 #else
1190 # define ucontext_t CONTEXT
1191 #endif
1193 /* translate from ucontext_t* to internal rt_context * */
1194 static void rt_getcontext(ucontext_t *uc, rt_frame *rc)
1196 #if defined _WIN64
1197 rc->ip = uc->Rip;
1198 rc->fp = uc->Rbp;
1199 rc->sp = uc->Rsp;
1200 #elif defined _WIN32
1201 rc->ip = uc->Eip;
1202 rc->fp = uc->Ebp;
1203 rc->sp = uc->Esp;
1204 #elif defined __i386__
1205 # if defined(__APPLE__)
1206 rc->ip = uc->uc_mcontext->__ss.__eip;
1207 rc->fp = uc->uc_mcontext->__ss.__ebp;
1208 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1209 rc->ip = uc->uc_mcontext.mc_eip;
1210 rc->fp = uc->uc_mcontext.mc_ebp;
1211 # elif defined(__dietlibc__)
1212 rc->ip = uc->uc_mcontext.eip;
1213 rc->fp = uc->uc_mcontext.ebp;
1214 # elif defined(__NetBSD__)
1215 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
1216 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
1217 # elif defined(__OpenBSD__)
1218 rc->ip = uc->sc_eip;
1219 rc->fp = uc->sc_ebp;
1220 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
1221 rc->ip = uc->uc_mcontext.gregs[EIP];
1222 rc->fp = uc->uc_mcontext.gregs[EBP];
1223 # else
1224 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
1225 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
1226 # endif
1227 #elif defined(__x86_64__)
1228 # if defined(__APPLE__)
1229 rc->ip = uc->uc_mcontext->__ss.__rip;
1230 rc->fp = uc->uc_mcontext->__ss.__rbp;
1231 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1232 rc->ip = uc->uc_mcontext.mc_rip;
1233 rc->fp = uc->uc_mcontext.mc_rbp;
1234 # elif defined(__NetBSD__)
1235 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
1236 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
1237 # elif defined(__OpenBSD__)
1238 rc->ip = uc->sc_rip;
1239 rc->fp = uc->sc_rbp;
1240 # else
1241 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
1242 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
1243 # endif
1244 #elif defined(__arm__) && defined(__NetBSD__)
1245 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1246 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1247 #elif defined(__arm__) && defined(__OpenBSD__)
1248 rc->ip = uc->sc_pc;
1249 rc->fp = uc->sc_r11;
1250 #elif defined(__arm__) && defined(__FreeBSD__)
1251 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1252 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1253 #elif defined(__arm__)
1254 rc->ip = uc->uc_mcontext.arm_pc;
1255 rc->fp = uc->uc_mcontext.arm_fp;
1256 #elif defined(__aarch64__) && defined(__APPLE__)
1257 // see:
1258 // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h
1259 rc->ip = uc->uc_mcontext->__ss.__pc;
1260 rc->fp = uc->uc_mcontext->__ss.__fp;
1261 #elif defined(__aarch64__) && defined(__FreeBSD__)
1262 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
1263 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
1264 #elif defined(__aarch64__) && defined(__NetBSD__)
1265 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1266 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1267 #elif defined(__aarch64__) && defined(__OpenBSD__)
1268 rc->ip = uc->sc_elr;
1269 rc->fp = uc->sc_x[29];
1270 #elif defined(__aarch64__)
1271 rc->ip = uc->uc_mcontext.pc;
1272 rc->fp = uc->uc_mcontext.regs[29];
1273 #elif defined(__riscv) && defined(__OpenBSD__)
1274 rc->ip = uc->sc_sepc;
1275 rc->fp = uc->sc_s[0];
1276 #elif defined(__riscv)
1277 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
1278 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
1279 #endif
1282 /* ------------------------------------------------------------- */
1283 #ifndef _WIN32
1284 /* signal handler for fatal errors */
1285 static void sig_error(int signum, siginfo_t *siginf, void *puc)
1287 rt_frame f;
1288 rt_getcontext(puc, &f);
1290 switch(signum) {
1291 case SIGFPE:
1292 switch(siginf->si_code) {
1293 case FPE_INTDIV:
1294 case FPE_FLTDIV:
1295 rt_error(&f, "division by zero");
1296 break;
1297 default:
1298 rt_error(&f, "floating point exception");
1299 break;
1301 break;
1302 case SIGBUS:
1303 case SIGSEGV:
1304 rt_error(&f, "invalid memory access");
1305 break;
1306 case SIGILL:
1307 rt_error(&f, "illegal instruction");
1308 break;
1309 case SIGABRT:
1310 rt_error(&f, "abort() called");
1311 break;
1312 default:
1313 rt_error(&f, "caught signal %d", signum);
1314 break;
1316 rt_exit(255);
1319 #ifndef SA_SIGINFO
1320 # define SA_SIGINFO 0x00000004u
1321 #endif
1323 /* Generate a stack backtrace when a CPU exception occurs. */
1324 static void set_exception_handler(void)
1326 struct sigaction sigact;
1327 /* install TCC signal handlers to print debug info on fatal
1328 runtime errors */
1329 sigemptyset (&sigact.sa_mask);
1330 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
1331 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
1332 sigact.sa_flags |= SA_ONSTACK;
1333 #endif
1334 sigact.sa_sigaction = sig_error;
1335 sigemptyset(&sigact.sa_mask);
1336 sigaction(SIGFPE, &sigact, NULL);
1337 sigaction(SIGILL, &sigact, NULL);
1338 sigaction(SIGSEGV, &sigact, NULL);
1339 sigaction(SIGBUS, &sigact, NULL);
1340 sigaction(SIGABRT, &sigact, NULL);
1341 #if 0//def SIGSTKSZ
1342 /* This allows stack overflow to be reported instead of a SEGV */
1344 stack_t ss;
1345 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
1347 ss.ss_sp = stack;
1348 ss.ss_size = SIGSTKSZ;
1349 ss.ss_flags = 0;
1350 sigaltstack(&ss, NULL);
1352 #endif
1355 #else /* WIN32 */
1357 /* signal handler for fatal errors */
1358 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
1360 rt_frame f;
1361 unsigned code;
1363 rt_getcontext(ex_info->ContextRecord, &f);
1365 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
1366 case EXCEPTION_ACCESS_VIOLATION:
1367 rt_error(&f, "invalid memory access");
1368 break;
1369 case EXCEPTION_STACK_OVERFLOW:
1370 rt_error(&f, "stack overflow");
1371 break;
1372 case EXCEPTION_INT_DIVIDE_BY_ZERO:
1373 rt_error(&f, "division by zero");
1374 break;
1375 case EXCEPTION_BREAKPOINT:
1376 case EXCEPTION_SINGLE_STEP:
1377 f.ip = *(addr_t*)f.sp;
1378 rt_error(&f, "breakpoint/single-step exception:");
1379 return EXCEPTION_CONTINUE_SEARCH;
1380 default:
1381 rt_error(&f, "caught exception %08x", code);
1382 break;
1384 if (rt_do_jmp)
1385 rt_exit(255);
1386 return EXCEPTION_EXECUTE_HANDLER;
1389 /* Generate a stack backtrace when a CPU exception occurs. */
1390 static void set_exception_handler(void)
1392 SetUnhandledExceptionFilter(cpu_exception_handler);
1395 #endif
1397 /* ------------------------------------------------------------- */
1398 /* return the PC at frame level 'level'. Return negative if not found */
1399 #if defined(__i386__) || defined(__x86_64__)
1400 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1402 addr_t ip, fp;
1403 if (level == 0) {
1404 ip = rc->ip;
1405 } else {
1406 ip = 0;
1407 fp = rc->fp;
1408 while (--level) {
1409 /* XXX: check address validity with program info */
1410 if (fp <= 0x1000)
1411 break;
1412 fp = ((addr_t *)fp)[0];
1414 if (fp > 0x1000)
1415 ip = ((addr_t *)fp)[1];
1417 if (ip <= 0x1000)
1418 return -1;
1419 *paddr = ip;
1420 return 0;
1423 #elif defined(__arm__)
1424 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1426 /* XXX: only supports linux/bsd */
1427 #if !defined(__linux__) && \
1428 !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
1429 return -1;
1430 #else
1431 if (level == 0) {
1432 *paddr = rc->ip;
1433 } else {
1434 addr_t fp = rc->fp;
1435 while (--level)
1436 fp = ((addr_t *)fp)[0];
1437 *paddr = ((addr_t *)fp)[2];
1439 return 0;
1440 #endif
1443 #elif defined(__aarch64__)
1444 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1446 if (level == 0) {
1447 *paddr = rc->ip;
1448 } else {
1449 addr_t *fp = (addr_t*)rc->fp;
1450 while (--level)
1451 fp = (addr_t *)fp[0];
1452 *paddr = fp[1];
1454 return 0;
1457 #elif defined(__riscv)
1458 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1460 if (level == 0) {
1461 *paddr = rc->ip;
1462 } else {
1463 addr_t *fp = (addr_t*)rc->fp;
1464 while (--level && fp >= (addr_t*)0x1000)
1465 fp = (addr_t *)fp[-2];
1466 if (fp < (addr_t*)0x1000)
1467 return -1;
1468 *paddr = fp[-1];
1470 return 0;
1473 #else
1474 #warning add arch specific rt_get_caller_pc()
1475 static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level)
1477 return -1;
1480 #endif
1481 #endif /* CONFIG_TCC_BACKTRACE */
1482 /* ------------------------------------------------------------- */
1483 #ifdef CONFIG_TCC_STATIC
1485 /* dummy function for profiling */
1486 ST_FUNC void *dlopen(const char *filename, int flag)
1488 return NULL;
1491 ST_FUNC void dlclose(void *p)
1495 ST_FUNC const char *dlerror(void)
1497 return "error";
1500 typedef struct TCCSyms {
1501 char *str;
1502 void *ptr;
1503 } TCCSyms;
1506 /* add the symbol you want here if no dynamic linking is done */
1507 static TCCSyms tcc_syms[] = {
1508 #if !defined(CONFIG_TCCBOOT)
1509 #define TCCSYM(a) { #a, &a, },
1510 TCCSYM(printf)
1511 TCCSYM(fprintf)
1512 TCCSYM(fopen)
1513 TCCSYM(fclose)
1514 #undef TCCSYM
1515 #endif
1516 { NULL, NULL },
1519 ST_FUNC void *dlsym(void *handle, const char *symbol)
1521 TCCSyms *p;
1522 p = tcc_syms;
1523 while (p->str != NULL) {
1524 if (!strcmp(p->str, symbol))
1525 return p->ptr;
1526 p++;
1528 return NULL;
1531 #endif /* CONFIG_TCC_STATIC */
1532 #endif /* TCC_IS_NATIVE */
1533 /* ------------------------------------------------------------- */