x86-64: Fix psABI stdarg prologue
[tinycc.git] / tccrun.c
blob950ffb00ebec10af92280ad8f7849d378d8034ee
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 #ifndef _WIN32
27 # include <sys/mman.h>
28 #endif
30 #ifdef CONFIG_TCC_BACKTRACE
31 # ifndef _WIN32
32 # include <signal.h>
33 # ifndef __OpenBSD__
34 # include <sys/ucontext.h>
35 # endif
36 # else
37 # define ucontext_t CONTEXT
38 # endif
39 ST_DATA int rt_num_callers = 6;
40 ST_DATA const char **rt_bound_error_msg;
41 ST_DATA void *rt_prog_main;
42 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
43 static void rt_error(ucontext_t *uc, const char *fmt, ...);
44 static void set_exception_handler(void);
45 #endif
47 static void set_pages_executable(void *ptr, unsigned long length);
48 static int tcc_relocate_ex(TCCState *s1, void *ptr);
50 #ifdef _WIN64
51 static void *win64_add_function_table(TCCState *s1);
52 static void win64_del_function_table(void *);
53 #endif
55 // #define HAVE_SELINUX
57 /* ------------------------------------------------------------- */
58 /* Do all relocations (needed before using tcc_get_symbol())
59 Returns -1 on error. */
61 LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
63 int size;
65 if (TCC_RELOCATE_AUTO != ptr)
66 return tcc_relocate_ex(s1, ptr);
68 size = tcc_relocate_ex(s1, NULL);
69 if (size < 0)
70 return -1;
72 #ifdef HAVE_SELINUX
73 /* Use mmap instead of malloc for Selinux. */
74 ptr = mmap (NULL, size, PROT_READ|PROT_WRITE,
75 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
76 if (ptr == MAP_FAILED)
77 tcc_error("tccrun: could not map memory");
78 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
79 #else
80 ptr = tcc_malloc(size);
81 #endif
82 tcc_relocate_ex(s1, ptr); /* no more errors expected */
83 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
84 return 0;
87 ST_FUNC void tcc_run_free(TCCState *s1)
89 int i;
91 for (i = 0; i < s1->nb_runtime_mem; ++i) {
92 #ifdef HAVE_SELINUX
93 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
94 munmap(s1->runtime_mem[i], size);
95 #else
96 #ifdef _WIN64
97 win64_del_function_table(*(void**)s1->runtime_mem[i]);
98 #endif
99 tcc_free(s1->runtime_mem[i]);
100 #endif
102 tcc_free(s1->runtime_mem);
105 /* launch the compiled program with the given arguments */
106 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
108 int (*prog_main)(int, char **);
110 s1->runtime_main = "main";
111 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
112 return -1;
113 prog_main = tcc_get_symbol_err(s1, s1->runtime_main);
115 #ifdef CONFIG_TCC_BACKTRACE
116 if (s1->do_debug) {
117 set_exception_handler();
118 rt_prog_main = prog_main;
120 #endif
122 errno = 0; /* clean errno value */
124 #ifdef CONFIG_TCC_BCHECK
125 if (s1->do_bounds_check) {
126 void (*bound_init)(void);
127 void (*bound_exit)(void);
128 void (*bound_new_region)(void *p, addr_t size);
129 int (*bound_delete_region)(void *p);
130 int i, ret;
132 /* set error function */
133 rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
134 /* XXX: use .init section so that it also work in binary ? */
135 bound_init = tcc_get_symbol_err(s1, "__bound_init");
136 bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
137 bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region");
138 bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region");
140 bound_init();
141 /* mark argv area as valid */
142 bound_new_region(argv, argc*sizeof(argv[0]));
143 for (i=0; i<argc; ++i)
144 bound_new_region(argv[i], strlen(argv[i]) + 1);
146 ret = (*prog_main)(argc, argv);
148 /* unmark argv area */
149 for (i=0; i<argc; ++i)
150 bound_delete_region(argv[i]);
151 bound_delete_region(argv);
152 bound_exit();
153 return ret;
155 #endif
156 return (*prog_main)(argc, argv);
159 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
160 #define RUN_SECTION_ALIGNMENT 63
161 #else
162 #define RUN_SECTION_ALIGNMENT 15
163 #endif
165 /* relocate code. Return -1 on error, required size if ptr is NULL,
166 otherwise copy code into buffer passed by the caller */
167 static int tcc_relocate_ex(TCCState *s1, void *ptr)
169 Section *s;
170 unsigned offset, length, fill, i, k;
171 addr_t mem;
173 if (NULL == ptr) {
174 s1->nb_errors = 0;
175 #ifdef TCC_TARGET_PE
176 pe_output_file(s1, NULL);
177 #else
178 tcc_add_runtime(s1);
179 relocate_common_syms();
180 tcc_add_linker_symbols(s1);
181 build_got_entries(s1);
182 #endif
183 if (s1->nb_errors)
184 return -1;
187 offset = 0, mem = (addr_t)ptr;
188 fill = -mem & RUN_SECTION_ALIGNMENT;
189 #ifdef _WIN64
190 offset += sizeof (void*);
191 #endif
192 for (k = 0; k < 2; ++k) {
193 for(i = 1; i < s1->nb_sections; i++) {
194 s = s1->sections[i];
195 if (0 == (s->sh_flags & SHF_ALLOC))
196 continue;
197 if (k != !(s->sh_flags & SHF_EXECINSTR))
198 continue;
199 offset += fill;
200 s->sh_addr = mem ? mem + offset : 0;
201 #if 0
202 if (mem)
203 printf("%-16s +%02lx %p %04x\n",
204 s->name, fill, (void*)s->sh_addr, (unsigned)s->data_offset);
205 #endif
206 offset += s->data_offset;
207 fill = -(mem + offset) & 15;
209 #if RUN_SECTION_ALIGNMENT > 15
210 /* To avoid that x86 processors would reload cached instructions each time
211 when data is written in the near, we need to make sure that code and data
212 do not share the same 64 byte unit */
213 fill = -(mem + offset) & RUN_SECTION_ALIGNMENT;
214 #endif
217 /* relocate symbols */
218 relocate_syms(s1, s1->symtab, 1);
219 if (s1->nb_errors)
220 return -1;
222 if (0 == mem)
223 return offset + RUN_SECTION_ALIGNMENT;
225 /* relocate each section */
226 for(i = 1; i < s1->nb_sections; i++) {
227 s = s1->sections[i];
228 if (s->reloc)
229 relocate_section(s1, s);
231 relocate_plt(s1);
233 #ifdef _WIN64
234 *(void**)ptr = win64_add_function_table(s1);
235 #endif
237 for(i = 1; i < s1->nb_sections; i++) {
238 s = s1->sections[i];
239 if (0 == (s->sh_flags & SHF_ALLOC))
240 continue;
241 length = s->data_offset;
242 ptr = (void*)s->sh_addr;
243 if (NULL == s->data || s->sh_type == SHT_NOBITS)
244 memset(ptr, 0, length);
245 else
246 memcpy(ptr, s->data, length);
247 /* mark executable sections as executable in memory */
248 if (s->sh_flags & SHF_EXECINSTR)
249 set_pages_executable(ptr, length);
251 return 0;
254 /* ------------------------------------------------------------- */
255 /* allow to run code in memory */
257 static void set_pages_executable(void *ptr, unsigned long length)
259 #ifdef _WIN32
260 unsigned long old_protect;
261 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
262 #else
263 void __clear_cache(void *beginning, void *end);
264 addr_t start, end;
265 #ifndef PAGESIZE
266 # define PAGESIZE 4096
267 #endif
268 start = (addr_t)ptr & ~(PAGESIZE - 1);
269 end = (addr_t)ptr + length;
270 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
271 if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
272 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
273 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
274 __clear_cache(ptr, (char *)ptr + length);
275 # endif
276 #endif
279 #ifdef _WIN64
280 static void *win64_add_function_table(TCCState *s1)
282 void *p = NULL;
283 if (s1->uw_pdata) {
284 p = (void*)s1->uw_pdata->sh_addr;
285 RtlAddFunctionTable(
286 (RUNTIME_FUNCTION*)p,
287 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
288 text_section->sh_addr
290 s1->uw_pdata = NULL;
292 return p;;
295 static void win64_del_function_table(void *p)
297 if (p) {
298 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
301 #endif
303 /* ------------------------------------------------------------- */
304 #ifdef CONFIG_TCC_BACKTRACE
306 ST_FUNC void tcc_set_num_callers(int n)
308 rt_num_callers = n;
311 /* print the position in the source file of PC value 'pc' by reading
312 the stabs debug information */
313 static addr_t rt_printline(addr_t wanted_pc, const char *msg)
315 char func_name[128], last_func_name[128];
316 addr_t func_addr, last_pc, pc;
317 const char *incl_files[INCLUDE_STACK_SIZE];
318 int incl_index, len, last_line_num, i;
319 const char *str, *p;
321 Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
322 int stab_len = 0;
323 char *stab_str = NULL;
325 if (stab_section) {
326 stab_len = stab_section->data_offset;
327 stab_sym = (Stab_Sym *)stab_section->data;
328 stab_str = (char *) stabstr_section->data;
331 func_name[0] = '\0';
332 func_addr = 0;
333 incl_index = 0;
334 last_func_name[0] = '\0';
335 last_pc = (addr_t)-1;
336 last_line_num = 1;
338 if (!stab_sym)
339 goto no_stabs;
341 stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
342 for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
343 switch(sym->n_type) {
344 /* function start or end */
345 case N_FUN:
346 if (sym->n_strx == 0) {
347 /* we test if between last line and end of function */
348 pc = sym->n_value + func_addr;
349 if (wanted_pc >= last_pc && wanted_pc < pc)
350 goto found;
351 func_name[0] = '\0';
352 func_addr = 0;
353 } else {
354 str = stab_str + sym->n_strx;
355 p = strchr(str, ':');
356 if (!p) {
357 pstrcpy(func_name, sizeof(func_name), str);
358 } else {
359 len = p - str;
360 if (len > sizeof(func_name) - 1)
361 len = sizeof(func_name) - 1;
362 memcpy(func_name, str, len);
363 func_name[len] = '\0';
365 func_addr = sym->n_value;
367 break;
368 /* line number info */
369 case N_SLINE:
370 pc = sym->n_value + func_addr;
371 if (wanted_pc >= last_pc && wanted_pc < pc)
372 goto found;
373 last_pc = pc;
374 last_line_num = sym->n_desc;
375 /* XXX: slow! */
376 strcpy(last_func_name, func_name);
377 break;
378 /* include files */
379 case N_BINCL:
380 str = stab_str + sym->n_strx;
381 add_incl:
382 if (incl_index < INCLUDE_STACK_SIZE) {
383 incl_files[incl_index++] = str;
385 break;
386 case N_EINCL:
387 if (incl_index > 1)
388 incl_index--;
389 break;
390 case N_SO:
391 if (sym->n_strx == 0) {
392 incl_index = 0; /* end of translation unit */
393 } else {
394 str = stab_str + sym->n_strx;
395 /* do not add path */
396 len = strlen(str);
397 if (len > 0 && str[len - 1] != '/')
398 goto add_incl;
400 break;
404 no_stabs:
405 /* second pass: we try symtab symbols (no line number info) */
406 incl_index = 0;
407 if (symtab_section)
409 ElfW(Sym) *sym, *sym_end;
410 int type;
412 sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
413 for(sym = (ElfW(Sym) *)symtab_section->data + 1;
414 sym < sym_end;
415 sym++) {
416 type = ELFW(ST_TYPE)(sym->st_info);
417 if (type == STT_FUNC || type == STT_GNU_IFUNC) {
418 if (wanted_pc >= sym->st_value &&
419 wanted_pc < sym->st_value + sym->st_size) {
420 pstrcpy(last_func_name, sizeof(last_func_name),
421 (char *) strtab_section->data + sym->st_name);
422 func_addr = sym->st_value;
423 goto found;
428 /* did not find any info: */
429 fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
430 fflush(stderr);
431 return 0;
432 found:
433 i = incl_index;
434 if (i > 0)
435 fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
436 fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
437 if (last_func_name[0] != '\0')
438 fprintf(stderr, " %s()", last_func_name);
439 if (--i >= 0) {
440 fprintf(stderr, " (included from ");
441 for (;;) {
442 fprintf(stderr, "%s", incl_files[i]);
443 if (--i < 0)
444 break;
445 fprintf(stderr, ", ");
447 fprintf(stderr, ")");
449 fprintf(stderr, "\n");
450 fflush(stderr);
451 return func_addr;
454 /* emit a run time error at position 'pc' */
455 static void rt_error(ucontext_t *uc, const char *fmt, ...)
457 va_list ap;
458 addr_t pc;
459 int i;
461 fprintf(stderr, "Runtime error: ");
462 va_start(ap, fmt);
463 vfprintf(stderr, fmt, ap);
464 va_end(ap);
465 fprintf(stderr, "\n");
467 for(i=0;i<rt_num_callers;i++) {
468 if (rt_get_caller_pc(&pc, uc, i) < 0)
469 break;
470 pc = rt_printline(pc, i ? "by" : "at");
471 if (pc == (addr_t)rt_prog_main && pc)
472 break;
476 /* ------------------------------------------------------------- */
477 #ifndef _WIN32
479 /* signal handler for fatal errors */
480 static void sig_error(int signum, siginfo_t *siginf, void *puc)
482 ucontext_t *uc = puc;
484 switch(signum) {
485 case SIGFPE:
486 switch(siginf->si_code) {
487 case FPE_INTDIV:
488 case FPE_FLTDIV:
489 rt_error(uc, "division by zero");
490 break;
491 default:
492 rt_error(uc, "floating point exception");
493 break;
495 break;
496 case SIGBUS:
497 case SIGSEGV:
498 if (rt_bound_error_msg && *rt_bound_error_msg)
499 rt_error(uc, *rt_bound_error_msg);
500 else
501 rt_error(uc, "dereferencing invalid pointer");
502 break;
503 case SIGILL:
504 rt_error(uc, "illegal instruction");
505 break;
506 case SIGABRT:
507 rt_error(uc, "abort() called");
508 break;
509 default:
510 rt_error(uc, "caught signal %d", signum);
511 break;
513 exit(255);
516 #ifndef SA_SIGINFO
517 # define SA_SIGINFO 0x00000004u
518 #endif
520 /* Generate a stack backtrace when a CPU exception occurs. */
521 static void set_exception_handler(void)
523 struct sigaction sigact;
524 /* install TCC signal handlers to print debug info on fatal
525 runtime errors */
526 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
527 sigact.sa_sigaction = sig_error;
528 sigemptyset(&sigact.sa_mask);
529 sigaction(SIGFPE, &sigact, NULL);
530 sigaction(SIGILL, &sigact, NULL);
531 sigaction(SIGSEGV, &sigact, NULL);
532 sigaction(SIGBUS, &sigact, NULL);
533 sigaction(SIGABRT, &sigact, NULL);
536 /* ------------------------------------------------------------- */
537 #ifdef __i386__
539 /* fix for glibc 2.1 */
540 #ifndef REG_EIP
541 #define REG_EIP EIP
542 #define REG_EBP EBP
543 #endif
545 /* return the PC at frame level 'level'. Return negative if not found */
546 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
548 addr_t fp;
549 int i;
551 if (level == 0) {
552 #if defined(__APPLE__)
553 *paddr = uc->uc_mcontext->__ss.__eip;
554 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
555 *paddr = uc->uc_mcontext.mc_eip;
556 #elif defined(__dietlibc__)
557 *paddr = uc->uc_mcontext.eip;
558 #elif defined(__NetBSD__)
559 *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
560 #elif defined(__OpenBSD__)
561 *paddr = uc->sc_eip;
562 #else
563 *paddr = uc->uc_mcontext.gregs[REG_EIP];
564 #endif
565 return 0;
566 } else {
567 #if defined(__APPLE__)
568 fp = uc->uc_mcontext->__ss.__ebp;
569 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
570 fp = uc->uc_mcontext.mc_ebp;
571 #elif defined(__dietlibc__)
572 fp = uc->uc_mcontext.ebp;
573 #elif defined(__NetBSD__)
574 fp = uc->uc_mcontext.__gregs[_REG_EBP];
575 #elif defined(__OpenBSD__)
576 *paddr = uc->sc_ebp;
577 #else
578 fp = uc->uc_mcontext.gregs[REG_EBP];
579 #endif
580 for(i=1;i<level;i++) {
581 /* XXX: check address validity with program info */
582 if (fp <= 0x1000 || fp >= 0xc0000000)
583 return -1;
584 fp = ((addr_t *)fp)[0];
586 *paddr = ((addr_t *)fp)[1];
587 return 0;
591 /* ------------------------------------------------------------- */
592 #elif defined(__x86_64__)
594 /* return the PC at frame level 'level'. Return negative if not found */
595 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
597 addr_t fp;
598 int i;
600 if (level == 0) {
601 /* XXX: only support linux */
602 #if defined(__APPLE__)
603 *paddr = uc->uc_mcontext->__ss.__rip;
604 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
605 *paddr = uc->uc_mcontext.mc_rip;
606 #elif defined(__NetBSD__)
607 *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
608 #else
609 *paddr = uc->uc_mcontext.gregs[REG_RIP];
610 #endif
611 return 0;
612 } else {
613 #if defined(__APPLE__)
614 fp = uc->uc_mcontext->__ss.__rbp;
615 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
616 fp = uc->uc_mcontext.mc_rbp;
617 #elif defined(__NetBSD__)
618 fp = uc->uc_mcontext.__gregs[_REG_RBP];
619 #else
620 fp = uc->uc_mcontext.gregs[REG_RBP];
621 #endif
622 for(i=1;i<level;i++) {
623 /* XXX: check address validity with program info */
624 if (fp <= 0x1000)
625 return -1;
626 fp = ((addr_t *)fp)[0];
628 *paddr = ((addr_t *)fp)[1];
629 return 0;
633 /* ------------------------------------------------------------- */
634 #elif defined(__arm__)
636 /* return the PC at frame level 'level'. Return negative if not found */
637 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
639 addr_t fp, sp;
640 int i;
642 if (level == 0) {
643 /* XXX: only supports linux */
644 #if defined(__linux__)
645 *paddr = uc->uc_mcontext.arm_pc;
646 #else
647 return -1;
648 #endif
649 return 0;
650 } else {
651 #if defined(__linux__)
652 fp = uc->uc_mcontext.arm_fp;
653 sp = uc->uc_mcontext.arm_sp;
654 if (sp < 0x1000)
655 sp = 0x1000;
656 #else
657 return -1;
658 #endif
659 /* XXX: specific to tinycc stack frames */
660 if (fp < sp + 12 || fp & 3)
661 return -1;
662 for(i = 1; i < level; i++) {
663 sp = ((addr_t *)fp)[-2];
664 if (sp < fp || sp - fp > 16 || sp & 3)
665 return -1;
666 fp = ((addr_t *)fp)[-3];
667 if (fp <= sp || fp - sp < 12 || fp & 3)
668 return -1;
670 /* XXX: check address validity with program info */
671 *paddr = ((addr_t *)fp)[-1];
672 return 0;
676 /* ------------------------------------------------------------- */
677 #elif defined(__aarch64__)
679 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
681 if (level < 0)
682 return -1;
683 else if (level == 0) {
684 *paddr = uc->uc_mcontext.pc;
685 return 0;
687 else {
688 addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
689 int i;
690 for (i = 1; i < level; i++)
691 fp = (addr_t *)fp[0];
692 *paddr = fp[1];
693 return 0;
697 /* ------------------------------------------------------------- */
698 #else
700 #warning add arch specific rt_get_caller_pc()
701 static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
703 return -1;
706 #endif /* !__i386__ */
708 /* ------------------------------------------------------------- */
709 #else /* WIN32 */
711 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
713 EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
714 CONTEXT *uc = ex_info->ContextRecord;
715 switch (er->ExceptionCode) {
716 case EXCEPTION_ACCESS_VIOLATION:
717 if (rt_bound_error_msg && *rt_bound_error_msg)
718 rt_error(uc, *rt_bound_error_msg);
719 else
720 rt_error(uc, "access violation");
721 break;
722 case EXCEPTION_STACK_OVERFLOW:
723 rt_error(uc, "stack overflow");
724 break;
725 case EXCEPTION_INT_DIVIDE_BY_ZERO:
726 rt_error(uc, "division by zero");
727 break;
728 default:
729 rt_error(uc, "exception caught");
730 break;
732 return EXCEPTION_EXECUTE_HANDLER;
735 /* Generate a stack backtrace when a CPU exception occurs. */
736 static void set_exception_handler(void)
738 SetUnhandledExceptionFilter(cpu_exception_handler);
741 /* return the PC at frame level 'level'. Return non zero if not found */
742 static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
744 addr_t fp, pc;
745 int i;
746 #ifdef _WIN64
747 pc = uc->Rip;
748 fp = uc->Rbp;
749 #else
750 pc = uc->Eip;
751 fp = uc->Ebp;
752 #endif
753 if (level > 0) {
754 for(i=1;i<level;i++) {
755 /* XXX: check address validity with program info */
756 if (fp <= 0x1000 || fp >= 0xc0000000)
757 return -1;
758 fp = ((addr_t*)fp)[0];
760 pc = ((addr_t*)fp)[1];
762 *paddr = pc;
763 return 0;
766 #endif /* _WIN32 */
767 #endif /* CONFIG_TCC_BACKTRACE */
768 /* ------------------------------------------------------------- */
769 #ifdef CONFIG_TCC_STATIC
771 /* dummy function for profiling */
772 ST_FUNC void *dlopen(const char *filename, int flag)
774 return NULL;
777 ST_FUNC void dlclose(void *p)
781 ST_FUNC const char *dlerror(void)
783 return "error";
786 typedef struct TCCSyms {
787 char *str;
788 void *ptr;
789 } TCCSyms;
792 /* add the symbol you want here if no dynamic linking is done */
793 static TCCSyms tcc_syms[] = {
794 #if !defined(CONFIG_TCCBOOT)
795 #define TCCSYM(a) { #a, &a, },
796 TCCSYM(printf)
797 TCCSYM(fprintf)
798 TCCSYM(fopen)
799 TCCSYM(fclose)
800 #undef TCCSYM
801 #endif
802 { NULL, NULL },
805 ST_FUNC void *dlsym(void *handle, const char *symbol)
807 TCCSyms *p;
808 p = tcc_syms;
809 while (p->str != NULL) {
810 if (!strcmp(p->str, symbol))
811 return p->ptr;
812 p++;
814 return NULL;
817 #endif /* CONFIG_TCC_STATIC */
818 #endif /* TCC_IS_NATIVE */
819 /* ------------------------------------------------------------- */