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 if (tcc_relocate(s1
, TCC_RELOCATE_AUTO
) < 0)
112 prog_main
= tcc_get_symbol_err(s1
, s1
->runtime_main
);
114 #ifdef CONFIG_TCC_BACKTRACE
116 set_exception_handler();
117 rt_prog_main
= prog_main
;
121 errno
= 0; /* clean errno value */
123 #ifdef CONFIG_TCC_BCHECK
124 if (s1
->do_bounds_check
) {
125 void (*bound_init
)(void);
126 void (*bound_exit
)(void);
127 void (*bound_new_region
)(void *p
, addr_t size
);
128 int (*bound_delete_region
)(void *p
);
131 /* set error function */
132 rt_bound_error_msg
= tcc_get_symbol_err(s1
, "__bound_error_msg");
133 /* XXX: use .init section so that it also work in binary ? */
134 bound_init
= tcc_get_symbol_err(s1
, "__bound_init");
135 bound_exit
= tcc_get_symbol_err(s1
, "__bound_exit");
136 bound_new_region
= tcc_get_symbol_err(s1
, "__bound_new_region");
137 bound_delete_region
= tcc_get_symbol_err(s1
, "__bound_delete_region");
140 /* mark argv area as valid */
141 bound_new_region(argv
, argc
*sizeof(argv
[0]));
142 for (i
=0; i
<argc
; ++i
)
143 bound_new_region(argv
[i
], strlen(argv
[i
]) + 1);
145 ret
= (*prog_main
)(argc
, argv
);
147 /* unmark argv area */
148 for (i
=0; i
<argc
; ++i
)
149 bound_delete_region(argv
[i
]);
150 bound_delete_region(argv
);
155 return (*prog_main
)(argc
, argv
);
158 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
159 #define RUN_SECTION_ALIGNMENT 63
161 #define RUN_SECTION_ALIGNMENT 15
164 /* relocate code. Return -1 on error, required size if ptr is NULL,
165 otherwise copy code into buffer passed by the caller */
166 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
)
169 unsigned offset
, length
, fill
, i
, k
;
175 pe_output_file(s1
, NULL
);
178 relocate_common_syms();
179 tcc_add_linker_symbols(s1
);
180 build_got_entries(s1
);
186 offset
= 0, mem
= (addr_t
)ptr
;
187 fill
= -mem
& RUN_SECTION_ALIGNMENT
;
189 offset
+= sizeof (void*);
191 for (k
= 0; k
< 2; ++k
) {
192 for(i
= 1; i
< s1
->nb_sections
; i
++) {
194 if (0 == (s
->sh_flags
& SHF_ALLOC
))
196 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
199 s
->sh_addr
= mem
? mem
+ offset
: 0;
202 printf("%-16s +%02lx %p %04x\n",
203 s
->name
, fill
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
);
205 offset
+= s
->data_offset
;
206 fill
= -(mem
+ offset
) & 15;
208 #if RUN_SECTION_ALIGNMENT > 15
209 /* To avoid that x86 processors would reload cached instructions each time
210 when data is written in the near, we need to make sure that code and data
211 do not share the same 64 byte unit */
212 fill
= -(mem
+ offset
) & RUN_SECTION_ALIGNMENT
;
216 /* relocate symbols */
217 relocate_syms(s1
, s1
->symtab
, 1);
222 return offset
+ RUN_SECTION_ALIGNMENT
;
224 /* relocate each section */
225 for(i
= 1; i
< s1
->nb_sections
; i
++) {
228 relocate_section(s1
, s
);
233 *(void**)ptr
= win64_add_function_table(s1
);
236 for(i
= 1; i
< s1
->nb_sections
; i
++) {
238 if (0 == (s
->sh_flags
& SHF_ALLOC
))
240 length
= s
->data_offset
;
241 ptr
= (void*)s
->sh_addr
;
242 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
243 memset(ptr
, 0, length
);
245 memcpy(ptr
, s
->data
, length
);
246 /* mark executable sections as executable in memory */
247 if (s
->sh_flags
& SHF_EXECINSTR
)
248 set_pages_executable(ptr
, length
);
253 /* ------------------------------------------------------------- */
254 /* allow to run code in memory */
256 static void set_pages_executable(void *ptr
, unsigned long length
)
259 unsigned long old_protect
;
260 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
262 void __clear_cache(void *beginning
, void *end
);
265 # define PAGESIZE 4096
267 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
268 end
= (addr_t
)ptr
+ length
;
269 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
270 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
271 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
272 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
273 __clear_cache(ptr
, (char *)ptr
+ length
);
279 static void *win64_add_function_table(TCCState
*s1
)
283 p
= (void*)s1
->uw_pdata
->sh_addr
;
285 (RUNTIME_FUNCTION
*)p
,
286 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
287 text_section
->sh_addr
294 static void win64_del_function_table(void *p
)
297 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
302 /* ------------------------------------------------------------- */
303 #ifdef CONFIG_TCC_BACKTRACE
305 ST_FUNC
void tcc_set_num_callers(int n
)
310 /* print the position in the source file of PC value 'pc' by reading
311 the stabs debug information */
312 static addr_t
rt_printline(addr_t wanted_pc
, const char *msg
)
314 char func_name
[128], last_func_name
[128];
315 addr_t func_addr
, last_pc
, pc
;
316 const char *incl_files
[INCLUDE_STACK_SIZE
];
317 int incl_index
, len
, last_line_num
, i
;
320 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
, *sym
;
322 char *stab_str
= NULL
;
325 stab_len
= stab_section
->data_offset
;
326 stab_sym
= (Stab_Sym
*)stab_section
->data
;
327 stab_str
= (char *) stabstr_section
->data
;
333 last_func_name
[0] = '\0';
334 last_pc
= (addr_t
)-1;
340 stab_sym_end
= (Stab_Sym
*)((char*)stab_sym
+ stab_len
);
341 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
342 switch(sym
->n_type
) {
343 /* function start or end */
345 if (sym
->n_strx
== 0) {
346 /* we test if between last line and end of function */
347 pc
= sym
->n_value
+ func_addr
;
348 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
353 str
= stab_str
+ sym
->n_strx
;
354 p
= strchr(str
, ':');
356 pstrcpy(func_name
, sizeof(func_name
), str
);
359 if (len
> sizeof(func_name
) - 1)
360 len
= sizeof(func_name
) - 1;
361 memcpy(func_name
, str
, len
);
362 func_name
[len
] = '\0';
364 func_addr
= sym
->n_value
;
367 /* line number info */
369 pc
= sym
->n_value
+ func_addr
;
370 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
373 last_line_num
= sym
->n_desc
;
375 strcpy(last_func_name
, func_name
);
379 str
= stab_str
+ sym
->n_strx
;
381 if (incl_index
< INCLUDE_STACK_SIZE
) {
382 incl_files
[incl_index
++] = str
;
390 if (sym
->n_strx
== 0) {
391 incl_index
= 0; /* end of translation unit */
393 str
= stab_str
+ sym
->n_strx
;
394 /* do not add path */
396 if (len
> 0 && str
[len
- 1] != '/')
404 /* second pass: we try symtab symbols (no line number info) */
408 ElfW(Sym
) *sym
, *sym_end
;
411 sym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
412 for(sym
= (ElfW(Sym
) *)symtab_section
->data
+ 1;
415 type
= ELFW(ST_TYPE
)(sym
->st_info
);
416 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
417 if (wanted_pc
>= sym
->st_value
&&
418 wanted_pc
< sym
->st_value
+ sym
->st_size
) {
419 pstrcpy(last_func_name
, sizeof(last_func_name
),
420 (char *) strtab_section
->data
+ sym
->st_name
);
421 func_addr
= sym
->st_value
;
427 /* did not find any info: */
428 fprintf(stderr
, "%s %p ???\n", msg
, (void*)wanted_pc
);
434 fprintf(stderr
, "%s:%d: ", incl_files
[--i
], last_line_num
);
435 fprintf(stderr
, "%s %p", msg
, (void*)wanted_pc
);
436 if (last_func_name
[0] != '\0')
437 fprintf(stderr
, " %s()", last_func_name
);
439 fprintf(stderr
, " (included from ");
441 fprintf(stderr
, "%s", incl_files
[i
]);
444 fprintf(stderr
, ", ");
446 fprintf(stderr
, ")");
448 fprintf(stderr
, "\n");
453 /* emit a run time error at position 'pc' */
454 static void rt_error(ucontext_t
*uc
, const char *fmt
, ...)
460 fprintf(stderr
, "Runtime error: ");
462 vfprintf(stderr
, fmt
, ap
);
464 fprintf(stderr
, "\n");
466 for(i
=0;i
<rt_num_callers
;i
++) {
467 if (rt_get_caller_pc(&pc
, uc
, i
) < 0)
469 pc
= rt_printline(pc
, i
? "by" : "at");
470 if (pc
== (addr_t
)rt_prog_main
&& pc
)
475 /* ------------------------------------------------------------- */
478 /* signal handler for fatal errors */
479 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
481 ucontext_t
*uc
= puc
;
485 switch(siginf
->si_code
) {
488 rt_error(uc
, "division by zero");
491 rt_error(uc
, "floating point exception");
497 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
498 rt_error(uc
, *rt_bound_error_msg
);
500 rt_error(uc
, "dereferencing invalid pointer");
503 rt_error(uc
, "illegal instruction");
506 rt_error(uc
, "abort() called");
509 rt_error(uc
, "caught signal %d", signum
);
516 # define SA_SIGINFO 0x00000004u
519 /* Generate a stack backtrace when a CPU exception occurs. */
520 static void set_exception_handler(void)
522 struct sigaction sigact
;
523 /* install TCC signal handlers to print debug info on fatal
525 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
526 sigact
.sa_sigaction
= sig_error
;
527 sigemptyset(&sigact
.sa_mask
);
528 sigaction(SIGFPE
, &sigact
, NULL
);
529 sigaction(SIGILL
, &sigact
, NULL
);
530 sigaction(SIGSEGV
, &sigact
, NULL
);
531 sigaction(SIGBUS
, &sigact
, NULL
);
532 sigaction(SIGABRT
, &sigact
, NULL
);
535 /* ------------------------------------------------------------- */
538 /* fix for glibc 2.1 */
544 /* return the PC at frame level 'level'. Return negative if not found */
545 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
551 #if defined(__APPLE__)
552 *paddr
= uc
->uc_mcontext
->__ss
.__eip
;
553 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
554 *paddr
= uc
->uc_mcontext
.mc_eip
;
555 #elif defined(__dietlibc__)
556 *paddr
= uc
->uc_mcontext
.eip
;
557 #elif defined(__NetBSD__)
558 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
559 #elif defined(__OpenBSD__)
562 *paddr
= uc
->uc_mcontext
.gregs
[REG_EIP
];
566 #if defined(__APPLE__)
567 fp
= uc
->uc_mcontext
->__ss
.__ebp
;
568 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
569 fp
= uc
->uc_mcontext
.mc_ebp
;
570 #elif defined(__dietlibc__)
571 fp
= uc
->uc_mcontext
.ebp
;
572 #elif defined(__NetBSD__)
573 fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
574 #elif defined(__OpenBSD__)
577 fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
579 for(i
=1;i
<level
;i
++) {
580 /* XXX: check address validity with program info */
581 if (fp
<= 0x1000 || fp
>= 0xc0000000)
583 fp
= ((addr_t
*)fp
)[0];
585 *paddr
= ((addr_t
*)fp
)[1];
590 /* ------------------------------------------------------------- */
591 #elif defined(__x86_64__)
593 /* return the PC at frame level 'level'. Return negative if not found */
594 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
600 /* XXX: only support linux */
601 #if defined(__APPLE__)
602 *paddr
= uc
->uc_mcontext
->__ss
.__rip
;
603 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
604 *paddr
= uc
->uc_mcontext
.mc_rip
;
605 #elif defined(__NetBSD__)
606 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
608 *paddr
= uc
->uc_mcontext
.gregs
[REG_RIP
];
612 #if defined(__APPLE__)
613 fp
= uc
->uc_mcontext
->__ss
.__rbp
;
614 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
615 fp
= uc
->uc_mcontext
.mc_rbp
;
616 #elif defined(__NetBSD__)
617 fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
619 fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
621 for(i
=1;i
<level
;i
++) {
622 /* XXX: check address validity with program info */
625 fp
= ((addr_t
*)fp
)[0];
627 *paddr
= ((addr_t
*)fp
)[1];
632 /* ------------------------------------------------------------- */
633 #elif defined(__arm__)
635 /* return the PC at frame level 'level'. Return negative if not found */
636 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
642 /* XXX: only supports linux */
643 #if defined(__linux__)
644 *paddr
= uc
->uc_mcontext
.arm_pc
;
650 #if defined(__linux__)
651 fp
= uc
->uc_mcontext
.arm_fp
;
652 sp
= uc
->uc_mcontext
.arm_sp
;
658 /* XXX: specific to tinycc stack frames */
659 if (fp
< sp
+ 12 || fp
& 3)
661 for(i
= 1; i
< level
; i
++) {
662 sp
= ((addr_t
*)fp
)[-2];
663 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
665 fp
= ((addr_t
*)fp
)[-3];
666 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
669 /* XXX: check address validity with program info */
670 *paddr
= ((addr_t
*)fp
)[-1];
675 /* ------------------------------------------------------------- */
676 #elif defined(__aarch64__)
678 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
682 else if (level
== 0) {
683 *paddr
= uc
->uc_mcontext
.pc
;
687 addr_t
*fp
= (addr_t
*)uc
->uc_mcontext
.regs
[29];
689 for (i
= 1; i
< level
; i
++)
690 fp
= (addr_t
*)fp
[0];
696 /* ------------------------------------------------------------- */
699 #warning add arch specific rt_get_caller_pc()
700 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
705 #endif /* !__i386__ */
707 /* ------------------------------------------------------------- */
710 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
712 EXCEPTION_RECORD
*er
= ex_info
->ExceptionRecord
;
713 CONTEXT
*uc
= ex_info
->ContextRecord
;
714 switch (er
->ExceptionCode
) {
715 case EXCEPTION_ACCESS_VIOLATION
:
716 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
717 rt_error(uc
, *rt_bound_error_msg
);
719 rt_error(uc
, "access violation");
721 case EXCEPTION_STACK_OVERFLOW
:
722 rt_error(uc
, "stack overflow");
724 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
725 rt_error(uc
, "division by zero");
728 rt_error(uc
, "exception caught");
731 return EXCEPTION_EXECUTE_HANDLER
;
734 /* Generate a stack backtrace when a CPU exception occurs. */
735 static void set_exception_handler(void)
737 SetUnhandledExceptionFilter(cpu_exception_handler
);
740 /* return the PC at frame level 'level'. Return non zero if not found */
741 static int rt_get_caller_pc(addr_t
*paddr
, CONTEXT
*uc
, int level
)
753 for(i
=1;i
<level
;i
++) {
754 /* XXX: check address validity with program info */
755 if (fp
<= 0x1000 || fp
>= 0xc0000000)
757 fp
= ((addr_t
*)fp
)[0];
759 pc
= ((addr_t
*)fp
)[1];
766 #endif /* CONFIG_TCC_BACKTRACE */
767 /* ------------------------------------------------------------- */
768 #ifdef CONFIG_TCC_STATIC
770 /* dummy function for profiling */
771 ST_FUNC
void *dlopen(const char *filename
, int flag
)
776 ST_FUNC
void dlclose(void *p
)
780 ST_FUNC
const char *dlerror(void)
785 typedef struct TCCSyms
{
791 /* add the symbol you want here if no dynamic linking is done */
792 static TCCSyms tcc_syms
[] = {
793 #if !defined(CONFIG_TCCBOOT)
794 #define TCCSYM(a) { #a, &a, },
804 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
808 while (p
->str
!= NULL
) {
809 if (!strcmp(p
->str
, symbol
))
816 #endif /* CONFIG_TCC_STATIC */
817 #endif /* TCC_IS_NATIVE */
818 /* ------------------------------------------------------------- */