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
23 /* only native compiler supports -run */
27 # include <sys/mman.h>
30 #ifdef CONFIG_TCC_BACKTRACE
34 # include <sys/ucontext.h>
37 # define ucontext_t CONTEXT
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);
47 static void set_pages_executable(void *ptr
, unsigned long length
);
48 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
);
51 static void *win64_add_function_table(TCCState
*s1
);
52 static void win64_del_function_table(void *);
55 /* ------------------------------------------------------------- */
56 /* Do all relocations (needed before using tcc_get_symbol())
57 Returns -1 on error. */
59 LIBTCCAPI
int tcc_relocate(TCCState
*s1
, void *ptr
)
64 if (TCC_RELOCATE_AUTO
!= ptr
)
65 return tcc_relocate_ex(s1
, ptr
, 0);
67 size
= tcc_relocate_ex(s1
, NULL
, 0);
73 /* Using mmap instead of malloc */
75 char tmpfname
[] = "/tmp/.tccrunXXXXXX";
76 int fd
= mkstemp(tmpfname
);
80 ptr
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
81 prx
= mmap (NULL
, size
, PROT_READ
|PROT_EXEC
, MAP_SHARED
, fd
, 0);
82 if (ptr
== MAP_FAILED
|| prx
== MAP_FAILED
)
83 tcc_error("tccrun: could not map memory");
84 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, (void*)(addr_t
)size
);
85 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, prx
);
86 ptr_diff
= (char*)prx
- (char*)ptr
;
89 ptr
= tcc_malloc(size
);
91 tcc_relocate_ex(s1
, ptr
, ptr_diff
); /* no more errors expected */
92 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, ptr
);
96 ST_FUNC
void tcc_run_free(TCCState
*s1
)
100 for (i
= 0; i
< s1
->nb_runtime_mem
; ++i
) {
102 unsigned size
= (unsigned)(addr_t
)s1
->runtime_mem
[i
++];
103 munmap(s1
->runtime_mem
[i
++], size
);
104 munmap(s1
->runtime_mem
[i
], size
);
107 win64_del_function_table(*(void**)s1
->runtime_mem
[i
]);
109 tcc_free(s1
->runtime_mem
[i
]);
112 tcc_free(s1
->runtime_mem
);
115 /* launch the compiled program with the given arguments */
116 LIBTCCAPI
int tcc_run(TCCState
*s1
, int argc
, char **argv
)
118 int (*prog_main
)(int, char **);
120 s1
->runtime_main
= s1
->nostdlib
? "_start" : "main";
121 if ((s1
->dflag
& 16) && !find_elf_sym(s1
->symtab
, s1
->runtime_main
))
123 if (tcc_relocate(s1
, TCC_RELOCATE_AUTO
) < 0)
125 prog_main
= tcc_get_symbol_err(s1
, s1
->runtime_main
);
127 #ifdef CONFIG_TCC_BACKTRACE
129 set_exception_handler();
130 rt_prog_main
= prog_main
;
134 errno
= 0; /* clean errno value */
136 #ifdef CONFIG_TCC_BCHECK
137 if (s1
->do_bounds_check
) {
138 void (*bound_init
)(void);
139 void (*bound_exit
)(void);
140 void (*bound_new_region
)(void *p
, addr_t size
);
141 int (*bound_delete_region
)(void *p
);
144 /* set error function */
145 rt_bound_error_msg
= tcc_get_symbol_err(s1
, "__bound_error_msg");
146 /* XXX: use .init section so that it also work in binary ? */
147 bound_init
= tcc_get_symbol_err(s1
, "__bound_init");
148 bound_exit
= tcc_get_symbol_err(s1
, "__bound_exit");
149 bound_new_region
= tcc_get_symbol_err(s1
, "__bound_new_region");
150 bound_delete_region
= tcc_get_symbol_err(s1
, "__bound_delete_region");
153 /* mark argv area as valid */
154 bound_new_region(argv
, argc
*sizeof(argv
[0]));
155 for (i
=0; i
<argc
; ++i
)
156 bound_new_region(argv
[i
], strlen(argv
[i
]) + 1);
158 ret
= (*prog_main
)(argc
, argv
);
160 /* unmark argv area */
161 for (i
=0; i
<argc
; ++i
)
162 bound_delete_region(argv
[i
]);
163 bound_delete_region(argv
);
168 return (*prog_main
)(argc
, argv
);
171 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
172 /* To avoid that x86 processors would reload cached instructions
173 each time when data is written in the near, we need to make
174 sure that code and data do not share the same 64 byte unit */
175 #define RUN_SECTION_ALIGNMENT 63
177 #define RUN_SECTION_ALIGNMENT 0
180 /* relocate code. Return -1 on error, required size if ptr is NULL,
181 otherwise copy code into buffer passed by the caller */
182 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
)
185 unsigned offset
, length
, align
, max_align
, i
, k
, f
;
191 pe_output_file(s1
, NULL
);
194 resolve_common_syms(s1
);
195 build_got_entries(s1
);
201 offset
= max_align
= 0, mem
= (addr_t
)ptr
;
203 offset
+= sizeof (void*); /* space for function_table pointer */
205 for (k
= 0; k
< 2; ++k
) {
206 f
= 0, addr
= k
? mem
: mem
+ ptr_diff
;
207 for(i
= 1; i
< s1
->nb_sections
; i
++) {
209 if (0 == (s
->sh_flags
& SHF_ALLOC
))
211 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
213 align
= s
->sh_addralign
- 1;
214 if (++f
== 1 && align
< RUN_SECTION_ALIGNMENT
)
215 align
= RUN_SECTION_ALIGNMENT
;
216 if (max_align
< align
)
218 offset
+= -(addr
+ offset
) & align
;
219 s
->sh_addr
= mem
? addr
+ offset
: 0;
220 offset
+= s
->data_offset
;
223 printf("%-16s %p len %04x align %2d\n",
224 s
->name
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
, align
+ 1);
229 /* relocate symbols */
230 relocate_syms(s1
, s1
->symtab
, 1);
235 return offset
+ max_align
;
238 s1
->pe_imagebase
= mem
;
241 /* relocate each section */
242 for(i
= 1; i
< s1
->nb_sections
; i
++) {
245 relocate_section(s1
, s
);
249 for(i
= 1; i
< s1
->nb_sections
; i
++) {
251 if (0 == (s
->sh_flags
& SHF_ALLOC
))
253 length
= s
->data_offset
;
254 ptr
= (void*)s
->sh_addr
;
255 if (s
->sh_flags
& SHF_EXECINSTR
)
256 ptr
= (char*)ptr
- ptr_diff
;
257 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
258 memset(ptr
, 0, length
);
260 memcpy(ptr
, s
->data
, length
);
261 /* mark executable sections as executable in memory */
262 if (s
->sh_flags
& SHF_EXECINSTR
)
263 set_pages_executable((char*)ptr
+ ptr_diff
, length
);
267 *(void**)mem
= win64_add_function_table(s1
);
273 /* ------------------------------------------------------------- */
274 /* allow to run code in memory */
276 static void set_pages_executable(void *ptr
, unsigned long length
)
279 unsigned long old_protect
;
280 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
282 void __clear_cache(void *beginning
, void *end
);
283 # ifndef HAVE_SELINUX
286 # define PAGESIZE 4096
288 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
289 end
= (addr_t
)ptr
+ length
;
290 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
291 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
292 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
294 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
295 __clear_cache(ptr
, (char *)ptr
+ length
);
301 static void *win64_add_function_table(TCCState
*s1
)
305 p
= (void*)s1
->uw_pdata
->sh_addr
;
307 (RUNTIME_FUNCTION
*)p
,
308 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
316 static void win64_del_function_table(void *p
)
319 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
324 /* ------------------------------------------------------------- */
325 #ifdef CONFIG_TCC_BACKTRACE
327 ST_FUNC
void tcc_set_num_callers(int n
)
332 /* print the position in the source file of PC value 'pc' by reading
333 the stabs debug information */
334 static addr_t
rt_printline(addr_t wanted_pc
, const char *msg
)
336 char func_name
[128], last_func_name
[128];
337 addr_t func_addr
, last_pc
, pc
;
338 const char *incl_files
[INCLUDE_STACK_SIZE
];
339 int incl_index
, len
, last_line_num
, i
;
342 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
, *sym
;
344 char *stab_str
= NULL
;
347 stab_len
= stab_section
->data_offset
;
348 stab_sym
= (Stab_Sym
*)stab_section
->data
;
349 stab_str
= (char *) stabstr_section
->data
;
355 last_func_name
[0] = '\0';
356 last_pc
= (addr_t
)-1;
362 stab_sym_end
= (Stab_Sym
*)((char*)stab_sym
+ stab_len
);
363 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
364 switch(sym
->n_type
) {
365 /* function start or end */
367 if (sym
->n_strx
== 0) {
368 /* we test if between last line and end of function */
369 pc
= sym
->n_value
+ func_addr
;
370 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
375 str
= stab_str
+ sym
->n_strx
;
376 p
= strchr(str
, ':');
378 pstrcpy(func_name
, sizeof(func_name
), str
);
381 if (len
> sizeof(func_name
) - 1)
382 len
= sizeof(func_name
) - 1;
383 memcpy(func_name
, str
, len
);
384 func_name
[len
] = '\0';
386 func_addr
= sym
->n_value
;
389 /* line number info */
391 pc
= sym
->n_value
+ func_addr
;
392 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
395 last_line_num
= sym
->n_desc
;
397 strcpy(last_func_name
, func_name
);
401 str
= stab_str
+ sym
->n_strx
;
403 if (incl_index
< INCLUDE_STACK_SIZE
) {
404 incl_files
[incl_index
++] = str
;
412 if (sym
->n_strx
== 0) {
413 incl_index
= 0; /* end of translation unit */
415 str
= stab_str
+ sym
->n_strx
;
416 /* do not add path */
418 if (len
> 0 && str
[len
- 1] != '/')
426 /* second pass: we try symtab symbols (no line number info) */
430 ElfW(Sym
) *sym
, *sym_end
;
433 sym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
434 for(sym
= (ElfW(Sym
) *)symtab_section
->data
+ 1;
437 type
= ELFW(ST_TYPE
)(sym
->st_info
);
438 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
439 if (wanted_pc
>= sym
->st_value
&&
440 wanted_pc
< sym
->st_value
+ sym
->st_size
) {
441 pstrcpy(last_func_name
, sizeof(last_func_name
),
442 (char *) symtab_section
->link
->data
+ sym
->st_name
);
443 func_addr
= sym
->st_value
;
449 /* did not find any info: */
450 fprintf(stderr
, "%s %p ???\n", msg
, (void*)wanted_pc
);
456 fprintf(stderr
, "%s:%d: ", incl_files
[--i
], last_line_num
);
457 fprintf(stderr
, "%s %p", msg
, (void*)wanted_pc
);
458 if (last_func_name
[0] != '\0')
459 fprintf(stderr
, " %s()", last_func_name
);
461 fprintf(stderr
, " (included from ");
463 fprintf(stderr
, "%s", incl_files
[i
]);
466 fprintf(stderr
, ", ");
468 fprintf(stderr
, ")");
470 fprintf(stderr
, "\n");
475 /* emit a run time error at position 'pc' */
476 static void rt_error(ucontext_t
*uc
, const char *fmt
, ...)
482 fprintf(stderr
, "Runtime error: ");
484 vfprintf(stderr
, fmt
, ap
);
486 fprintf(stderr
, "\n");
488 for(i
=0;i
<rt_num_callers
;i
++) {
489 if (rt_get_caller_pc(&pc
, uc
, i
) < 0)
491 pc
= rt_printline(pc
, i
? "by" : "at");
492 if (pc
== (addr_t
)rt_prog_main
&& pc
)
497 /* ------------------------------------------------------------- */
500 /* signal handler for fatal errors */
501 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
503 ucontext_t
*uc
= puc
;
507 switch(siginf
->si_code
) {
510 rt_error(uc
, "division by zero");
513 rt_error(uc
, "floating point exception");
519 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
520 rt_error(uc
, *rt_bound_error_msg
);
522 rt_error(uc
, "dereferencing invalid pointer");
525 rt_error(uc
, "illegal instruction");
528 rt_error(uc
, "abort() called");
531 rt_error(uc
, "caught signal %d", signum
);
538 # define SA_SIGINFO 0x00000004u
541 /* Generate a stack backtrace when a CPU exception occurs. */
542 static void set_exception_handler(void)
544 struct sigaction sigact
;
545 /* install TCC signal handlers to print debug info on fatal
547 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
548 sigact
.sa_sigaction
= sig_error
;
549 sigemptyset(&sigact
.sa_mask
);
550 sigaction(SIGFPE
, &sigact
, NULL
);
551 sigaction(SIGILL
, &sigact
, NULL
);
552 sigaction(SIGSEGV
, &sigact
, NULL
);
553 sigaction(SIGBUS
, &sigact
, NULL
);
554 sigaction(SIGABRT
, &sigact
, NULL
);
557 /* ------------------------------------------------------------- */
560 /* fix for glibc 2.1 */
566 /* return the PC at frame level 'level'. Return negative if not found */
567 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
573 #if defined(__APPLE__)
574 *paddr
= uc
->uc_mcontext
->__ss
.__eip
;
575 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
576 *paddr
= uc
->uc_mcontext
.mc_eip
;
577 #elif defined(__dietlibc__)
578 *paddr
= uc
->uc_mcontext
.eip
;
579 #elif defined(__NetBSD__)
580 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
581 #elif defined(__OpenBSD__)
584 *paddr
= uc
->uc_mcontext
.gregs
[REG_EIP
];
588 #if defined(__APPLE__)
589 fp
= uc
->uc_mcontext
->__ss
.__ebp
;
590 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
591 fp
= uc
->uc_mcontext
.mc_ebp
;
592 #elif defined(__dietlibc__)
593 fp
= uc
->uc_mcontext
.ebp
;
594 #elif defined(__NetBSD__)
595 fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
596 #elif defined(__OpenBSD__)
599 fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
601 for(i
=1;i
<level
;i
++) {
602 /* XXX: check address validity with program info */
603 if (fp
<= 0x1000 || fp
>= 0xc0000000)
605 fp
= ((addr_t
*)fp
)[0];
607 *paddr
= ((addr_t
*)fp
)[1];
612 /* ------------------------------------------------------------- */
613 #elif defined(__x86_64__)
615 /* return the PC at frame level 'level'. Return negative if not found */
616 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
622 /* XXX: only support linux */
623 #if defined(__APPLE__)
624 *paddr
= uc
->uc_mcontext
->__ss
.__rip
;
625 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
626 *paddr
= uc
->uc_mcontext
.mc_rip
;
627 #elif defined(__NetBSD__)
628 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
630 *paddr
= uc
->uc_mcontext
.gregs
[REG_RIP
];
634 #if defined(__APPLE__)
635 fp
= uc
->uc_mcontext
->__ss
.__rbp
;
636 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
637 fp
= uc
->uc_mcontext
.mc_rbp
;
638 #elif defined(__NetBSD__)
639 fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
641 fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
643 for(i
=1;i
<level
;i
++) {
644 /* XXX: check address validity with program info */
647 fp
= ((addr_t
*)fp
)[0];
649 *paddr
= ((addr_t
*)fp
)[1];
654 /* ------------------------------------------------------------- */
655 #elif defined(__arm__)
657 /* return the PC at frame level 'level'. Return negative if not found */
658 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
664 /* XXX: only supports linux */
665 #if defined(__linux__)
666 *paddr
= uc
->uc_mcontext
.arm_pc
;
672 #if defined(__linux__)
673 fp
= uc
->uc_mcontext
.arm_fp
;
674 sp
= uc
->uc_mcontext
.arm_sp
;
680 /* XXX: specific to tinycc stack frames */
681 if (fp
< sp
+ 12 || fp
& 3)
683 for(i
= 1; i
< level
; i
++) {
684 sp
= ((addr_t
*)fp
)[-2];
685 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
687 fp
= ((addr_t
*)fp
)[-3];
688 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
691 /* XXX: check address validity with program info */
692 *paddr
= ((addr_t
*)fp
)[-1];
697 /* ------------------------------------------------------------- */
698 #elif defined(__aarch64__)
700 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
704 else if (level
== 0) {
705 *paddr
= uc
->uc_mcontext
.pc
;
709 addr_t
*fp
= (addr_t
*)uc
->uc_mcontext
.regs
[29];
711 for (i
= 1; i
< level
; i
++)
712 fp
= (addr_t
*)fp
[0];
718 /* ------------------------------------------------------------- */
721 #warning add arch specific rt_get_caller_pc()
722 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
727 #endif /* !__i386__ */
729 /* ------------------------------------------------------------- */
732 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
734 EXCEPTION_RECORD
*er
= ex_info
->ExceptionRecord
;
735 CONTEXT
*uc
= ex_info
->ContextRecord
;
736 switch (er
->ExceptionCode
) {
737 case EXCEPTION_ACCESS_VIOLATION
:
738 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
739 rt_error(uc
, *rt_bound_error_msg
);
741 rt_error(uc
, "access violation");
743 case EXCEPTION_STACK_OVERFLOW
:
744 rt_error(uc
, "stack overflow");
746 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
747 rt_error(uc
, "division by zero");
750 rt_error(uc
, "exception caught");
753 return EXCEPTION_EXECUTE_HANDLER
;
756 /* Generate a stack backtrace when a CPU exception occurs. */
757 static void set_exception_handler(void)
759 SetUnhandledExceptionFilter(cpu_exception_handler
);
762 /* return the PC at frame level 'level'. Return non zero if not found */
763 static int rt_get_caller_pc(addr_t
*paddr
, CONTEXT
*uc
, int level
)
775 for(i
=1;i
<level
;i
++) {
776 /* XXX: check address validity with program info */
777 if (fp
<= 0x1000 || fp
>= 0xc0000000)
779 fp
= ((addr_t
*)fp
)[0];
781 pc
= ((addr_t
*)fp
)[1];
788 #endif /* CONFIG_TCC_BACKTRACE */
789 /* ------------------------------------------------------------- */
790 #ifdef CONFIG_TCC_STATIC
792 /* dummy function for profiling */
793 ST_FUNC
void *dlopen(const char *filename
, int flag
)
798 ST_FUNC
void dlclose(void *p
)
802 ST_FUNC
const char *dlerror(void)
807 typedef struct TCCSyms
{
813 /* add the symbol you want here if no dynamic linking is done */
814 static TCCSyms tcc_syms
[] = {
815 #if !defined(CONFIG_TCCBOOT)
816 #define TCCSYM(a) { #a, &a, },
826 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
830 while (p
->str
!= NULL
) {
831 if (!strcmp(p
->str
, symbol
))
838 #endif /* CONFIG_TCC_STATIC */
839 #endif /* TCC_IS_NATIVE */
840 /* ------------------------------------------------------------- */