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
);
51 static void *win64_add_function_table(TCCState
*s1
);
52 static void win64_del_function_table(void *);
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
)
65 if (TCC_RELOCATE_AUTO
!= ptr
)
66 return tcc_relocate_ex(s1
, ptr
);
68 size
= tcc_relocate_ex(s1
, NULL
);
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
);
80 ptr
= tcc_malloc(size
);
82 tcc_relocate_ex(s1
, ptr
); /* no more errors expected */
83 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, ptr
);
87 ST_FUNC
void tcc_run_free(TCCState
*s1
)
91 for (i
= 0; i
< s1
->nb_runtime_mem
; ++i
) {
93 unsigned size
= (unsigned)(addr_t
)s1
->runtime_mem
[i
++];
94 munmap(s1
->runtime_mem
[i
], size
);
97 win64_del_function_table(*(void**)s1
->runtime_mem
[i
]);
99 tcc_free(s1
->runtime_mem
[i
]);
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 ((s1
->dflag
& 16) && !find_elf_sym(s1
->symtab
, s1
->runtime_main
))
113 if (tcc_relocate(s1
, TCC_RELOCATE_AUTO
) < 0)
115 prog_main
= tcc_get_symbol_err(s1
, s1
->runtime_main
);
117 #ifdef CONFIG_TCC_BACKTRACE
119 set_exception_handler();
120 rt_prog_main
= prog_main
;
124 errno
= 0; /* clean errno value */
126 #ifdef CONFIG_TCC_BCHECK
127 if (s1
->do_bounds_check
) {
128 void (*bound_init
)(void);
129 void (*bound_exit
)(void);
130 void (*bound_new_region
)(void *p
, addr_t size
);
131 int (*bound_delete_region
)(void *p
);
134 /* set error function */
135 rt_bound_error_msg
= tcc_get_symbol_err(s1
, "__bound_error_msg");
136 /* XXX: use .init section so that it also work in binary ? */
137 bound_init
= tcc_get_symbol_err(s1
, "__bound_init");
138 bound_exit
= tcc_get_symbol_err(s1
, "__bound_exit");
139 bound_new_region
= tcc_get_symbol_err(s1
, "__bound_new_region");
140 bound_delete_region
= tcc_get_symbol_err(s1
, "__bound_delete_region");
143 /* mark argv area as valid */
144 bound_new_region(argv
, argc
*sizeof(argv
[0]));
145 for (i
=0; i
<argc
; ++i
)
146 bound_new_region(argv
[i
], strlen(argv
[i
]) + 1);
148 ret
= (*prog_main
)(argc
, argv
);
150 /* unmark argv area */
151 for (i
=0; i
<argc
; ++i
)
152 bound_delete_region(argv
[i
]);
153 bound_delete_region(argv
);
158 return (*prog_main
)(argc
, argv
);
161 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
162 #define RUN_SECTION_ALIGNMENT 63
164 #define RUN_SECTION_ALIGNMENT 15
167 /* relocate code. Return -1 on error, required size if ptr is NULL,
168 otherwise copy code into buffer passed by the caller */
169 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
)
172 unsigned offset
, length
, fill
, i
, k
;
178 pe_output_file(s1
, NULL
);
181 relocate_common_syms();
182 tcc_add_linker_symbols(s1
);
183 build_got_entries(s1
);
189 offset
= 0, mem
= (addr_t
)ptr
;
190 fill
= -mem
& RUN_SECTION_ALIGNMENT
;
192 offset
+= sizeof (void*);
194 for (k
= 0; k
< 2; ++k
) {
195 for(i
= 1; i
< s1
->nb_sections
; i
++) {
197 if (0 == (s
->sh_flags
& SHF_ALLOC
))
199 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
202 s
->sh_addr
= mem
? mem
+ offset
: 0;
205 printf("%-16s +%02lx %p %04x\n",
206 s
->name
, fill
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
);
208 offset
+= s
->data_offset
;
209 fill
= -(mem
+ offset
) & 15;
211 #if RUN_SECTION_ALIGNMENT > 15
212 /* To avoid that x86 processors would reload cached instructions each time
213 when data is written in the near, we need to make sure that code and data
214 do not share the same 64 byte unit */
215 fill
= -(mem
+ offset
) & RUN_SECTION_ALIGNMENT
;
219 /* relocate symbols */
220 relocate_syms(s1
, s1
->symtab
, 1);
225 return offset
+ RUN_SECTION_ALIGNMENT
;
227 /* relocate each section */
228 for(i
= 1; i
< s1
->nb_sections
; i
++) {
231 relocate_section(s1
, s
);
236 *(void**)ptr
= win64_add_function_table(s1
);
239 for(i
= 1; i
< s1
->nb_sections
; i
++) {
241 if (0 == (s
->sh_flags
& SHF_ALLOC
))
243 length
= s
->data_offset
;
244 ptr
= (void*)s
->sh_addr
;
245 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
246 memset(ptr
, 0, length
);
248 memcpy(ptr
, s
->data
, length
);
249 /* mark executable sections as executable in memory */
250 if (s
->sh_flags
& SHF_EXECINSTR
)
251 set_pages_executable(ptr
, length
);
256 /* ------------------------------------------------------------- */
257 /* allow to run code in memory */
259 static void set_pages_executable(void *ptr
, unsigned long length
)
262 unsigned long old_protect
;
263 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
265 void __clear_cache(void *beginning
, void *end
);
268 # define PAGESIZE 4096
270 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
271 end
= (addr_t
)ptr
+ length
;
272 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
273 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
274 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
275 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
276 __clear_cache(ptr
, (char *)ptr
+ length
);
282 static void *win64_add_function_table(TCCState
*s1
)
286 p
= (void*)s1
->uw_pdata
->sh_addr
;
288 (RUNTIME_FUNCTION
*)p
,
289 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
290 text_section
->sh_addr
297 static void win64_del_function_table(void *p
)
300 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
305 /* ------------------------------------------------------------- */
306 #ifdef CONFIG_TCC_BACKTRACE
308 ST_FUNC
void tcc_set_num_callers(int n
)
313 /* print the position in the source file of PC value 'pc' by reading
314 the stabs debug information */
315 static addr_t
rt_printline(addr_t wanted_pc
, const char *msg
)
317 char func_name
[128], last_func_name
[128];
318 addr_t func_addr
, last_pc
, pc
;
319 const char *incl_files
[INCLUDE_STACK_SIZE
];
320 int incl_index
, len
, last_line_num
, i
;
323 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
, *sym
;
325 char *stab_str
= NULL
;
328 stab_len
= stab_section
->data_offset
;
329 stab_sym
= (Stab_Sym
*)stab_section
->data
;
330 stab_str
= (char *) stabstr_section
->data
;
336 last_func_name
[0] = '\0';
337 last_pc
= (addr_t
)-1;
343 stab_sym_end
= (Stab_Sym
*)((char*)stab_sym
+ stab_len
);
344 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
345 switch(sym
->n_type
) {
346 /* function start or end */
348 if (sym
->n_strx
== 0) {
349 /* we test if between last line and end of function */
350 pc
= sym
->n_value
+ func_addr
;
351 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
356 str
= stab_str
+ sym
->n_strx
;
357 p
= strchr(str
, ':');
359 pstrcpy(func_name
, sizeof(func_name
), str
);
362 if (len
> sizeof(func_name
) - 1)
363 len
= sizeof(func_name
) - 1;
364 memcpy(func_name
, str
, len
);
365 func_name
[len
] = '\0';
367 func_addr
= sym
->n_value
;
370 /* line number info */
372 pc
= sym
->n_value
+ func_addr
;
373 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
376 last_line_num
= sym
->n_desc
;
378 strcpy(last_func_name
, func_name
);
382 str
= stab_str
+ sym
->n_strx
;
384 if (incl_index
< INCLUDE_STACK_SIZE
) {
385 incl_files
[incl_index
++] = str
;
393 if (sym
->n_strx
== 0) {
394 incl_index
= 0; /* end of translation unit */
396 str
= stab_str
+ sym
->n_strx
;
397 /* do not add path */
399 if (len
> 0 && str
[len
- 1] != '/')
407 /* second pass: we try symtab symbols (no line number info) */
411 ElfW(Sym
) *sym
, *sym_end
;
414 sym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
415 for(sym
= (ElfW(Sym
) *)symtab_section
->data
+ 1;
418 type
= ELFW(ST_TYPE
)(sym
->st_info
);
419 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
420 if (wanted_pc
>= sym
->st_value
&&
421 wanted_pc
< sym
->st_value
+ sym
->st_size
) {
422 pstrcpy(last_func_name
, sizeof(last_func_name
),
423 (char *) strtab_section
->data
+ sym
->st_name
);
424 func_addr
= sym
->st_value
;
430 /* did not find any info: */
431 fprintf(stderr
, "%s %p ???\n", msg
, (void*)wanted_pc
);
437 fprintf(stderr
, "%s:%d: ", incl_files
[--i
], last_line_num
);
438 fprintf(stderr
, "%s %p", msg
, (void*)wanted_pc
);
439 if (last_func_name
[0] != '\0')
440 fprintf(stderr
, " %s()", last_func_name
);
442 fprintf(stderr
, " (included from ");
444 fprintf(stderr
, "%s", incl_files
[i
]);
447 fprintf(stderr
, ", ");
449 fprintf(stderr
, ")");
451 fprintf(stderr
, "\n");
456 /* emit a run time error at position 'pc' */
457 static void rt_error(ucontext_t
*uc
, const char *fmt
, ...)
463 fprintf(stderr
, "Runtime error: ");
465 vfprintf(stderr
, fmt
, ap
);
467 fprintf(stderr
, "\n");
469 for(i
=0;i
<rt_num_callers
;i
++) {
470 if (rt_get_caller_pc(&pc
, uc
, i
) < 0)
472 pc
= rt_printline(pc
, i
? "by" : "at");
473 if (pc
== (addr_t
)rt_prog_main
&& pc
)
478 /* ------------------------------------------------------------- */
481 /* signal handler for fatal errors */
482 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
484 ucontext_t
*uc
= puc
;
488 switch(siginf
->si_code
) {
491 rt_error(uc
, "division by zero");
494 rt_error(uc
, "floating point exception");
500 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
501 rt_error(uc
, *rt_bound_error_msg
);
503 rt_error(uc
, "dereferencing invalid pointer");
506 rt_error(uc
, "illegal instruction");
509 rt_error(uc
, "abort() called");
512 rt_error(uc
, "caught signal %d", signum
);
519 # define SA_SIGINFO 0x00000004u
522 /* Generate a stack backtrace when a CPU exception occurs. */
523 static void set_exception_handler(void)
525 struct sigaction sigact
;
526 /* install TCC signal handlers to print debug info on fatal
528 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
529 sigact
.sa_sigaction
= sig_error
;
530 sigemptyset(&sigact
.sa_mask
);
531 sigaction(SIGFPE
, &sigact
, NULL
);
532 sigaction(SIGILL
, &sigact
, NULL
);
533 sigaction(SIGSEGV
, &sigact
, NULL
);
534 sigaction(SIGBUS
, &sigact
, NULL
);
535 sigaction(SIGABRT
, &sigact
, NULL
);
538 /* ------------------------------------------------------------- */
541 /* fix for glibc 2.1 */
547 /* return the PC at frame level 'level'. Return negative if not found */
548 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
554 #if defined(__APPLE__)
555 *paddr
= uc
->uc_mcontext
->__ss
.__eip
;
556 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
557 *paddr
= uc
->uc_mcontext
.mc_eip
;
558 #elif defined(__dietlibc__)
559 *paddr
= uc
->uc_mcontext
.eip
;
560 #elif defined(__NetBSD__)
561 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
562 #elif defined(__OpenBSD__)
565 *paddr
= uc
->uc_mcontext
.gregs
[REG_EIP
];
569 #if defined(__APPLE__)
570 fp
= uc
->uc_mcontext
->__ss
.__ebp
;
571 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
572 fp
= uc
->uc_mcontext
.mc_ebp
;
573 #elif defined(__dietlibc__)
574 fp
= uc
->uc_mcontext
.ebp
;
575 #elif defined(__NetBSD__)
576 fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
577 #elif defined(__OpenBSD__)
580 fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
582 for(i
=1;i
<level
;i
++) {
583 /* XXX: check address validity with program info */
584 if (fp
<= 0x1000 || fp
>= 0xc0000000)
586 fp
= ((addr_t
*)fp
)[0];
588 *paddr
= ((addr_t
*)fp
)[1];
593 /* ------------------------------------------------------------- */
594 #elif defined(__x86_64__)
596 /* return the PC at frame level 'level'. Return negative if not found */
597 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
603 /* XXX: only support linux */
604 #if defined(__APPLE__)
605 *paddr
= uc
->uc_mcontext
->__ss
.__rip
;
606 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
607 *paddr
= uc
->uc_mcontext
.mc_rip
;
608 #elif defined(__NetBSD__)
609 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
611 *paddr
= uc
->uc_mcontext
.gregs
[REG_RIP
];
615 #if defined(__APPLE__)
616 fp
= uc
->uc_mcontext
->__ss
.__rbp
;
617 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
618 fp
= uc
->uc_mcontext
.mc_rbp
;
619 #elif defined(__NetBSD__)
620 fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
622 fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
624 for(i
=1;i
<level
;i
++) {
625 /* XXX: check address validity with program info */
628 fp
= ((addr_t
*)fp
)[0];
630 *paddr
= ((addr_t
*)fp
)[1];
635 /* ------------------------------------------------------------- */
636 #elif defined(__arm__)
638 /* return the PC at frame level 'level'. Return negative if not found */
639 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
645 /* XXX: only supports linux */
646 #if defined(__linux__)
647 *paddr
= uc
->uc_mcontext
.arm_pc
;
653 #if defined(__linux__)
654 fp
= uc
->uc_mcontext
.arm_fp
;
655 sp
= uc
->uc_mcontext
.arm_sp
;
661 /* XXX: specific to tinycc stack frames */
662 if (fp
< sp
+ 12 || fp
& 3)
664 for(i
= 1; i
< level
; i
++) {
665 sp
= ((addr_t
*)fp
)[-2];
666 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
668 fp
= ((addr_t
*)fp
)[-3];
669 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
672 /* XXX: check address validity with program info */
673 *paddr
= ((addr_t
*)fp
)[-1];
678 /* ------------------------------------------------------------- */
679 #elif defined(__aarch64__)
681 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
685 else if (level
== 0) {
686 *paddr
= uc
->uc_mcontext
.pc
;
690 addr_t
*fp
= (addr_t
*)uc
->uc_mcontext
.regs
[29];
692 for (i
= 1; i
< level
; i
++)
693 fp
= (addr_t
*)fp
[0];
699 /* ------------------------------------------------------------- */
702 #warning add arch specific rt_get_caller_pc()
703 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
708 #endif /* !__i386__ */
710 /* ------------------------------------------------------------- */
713 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
715 EXCEPTION_RECORD
*er
= ex_info
->ExceptionRecord
;
716 CONTEXT
*uc
= ex_info
->ContextRecord
;
717 switch (er
->ExceptionCode
) {
718 case EXCEPTION_ACCESS_VIOLATION
:
719 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
720 rt_error(uc
, *rt_bound_error_msg
);
722 rt_error(uc
, "access violation");
724 case EXCEPTION_STACK_OVERFLOW
:
725 rt_error(uc
, "stack overflow");
727 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
728 rt_error(uc
, "division by zero");
731 rt_error(uc
, "exception caught");
734 return EXCEPTION_EXECUTE_HANDLER
;
737 /* Generate a stack backtrace when a CPU exception occurs. */
738 static void set_exception_handler(void)
740 SetUnhandledExceptionFilter(cpu_exception_handler
);
743 /* return the PC at frame level 'level'. Return non zero if not found */
744 static int rt_get_caller_pc(addr_t
*paddr
, CONTEXT
*uc
, int level
)
756 for(i
=1;i
<level
;i
++) {
757 /* XXX: check address validity with program info */
758 if (fp
<= 0x1000 || fp
>= 0xc0000000)
760 fp
= ((addr_t
*)fp
)[0];
762 pc
= ((addr_t
*)fp
)[1];
769 #endif /* CONFIG_TCC_BACKTRACE */
770 /* ------------------------------------------------------------- */
771 #ifdef CONFIG_TCC_STATIC
773 /* dummy function for profiling */
774 ST_FUNC
void *dlopen(const char *filename
, int flag
)
779 ST_FUNC
void dlclose(void *p
)
783 ST_FUNC
const char *dlerror(void)
788 typedef struct TCCSyms
{
794 /* add the symbol you want here if no dynamic linking is done */
795 static TCCSyms tcc_syms
[] = {
796 #if !defined(CONFIG_TCCBOOT)
797 #define TCCSYM(a) { #a, &a, },
807 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
811 while (p
->str
!= NULL
) {
812 if (!strcmp(p
->str
, symbol
))
819 #endif /* CONFIG_TCC_STATIC */
820 #endif /* TCC_IS_NATIVE */
821 /* ------------------------------------------------------------- */