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
= "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 #define RUN_SECTION_ALIGNMENT 63
174 #define RUN_SECTION_ALIGNMENT 15
177 /* relocate code. Return -1 on error, required size if ptr is NULL,
178 otherwise copy code into buffer passed by the caller */
179 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
)
182 unsigned offset
, length
, fill
, i
, k
;
188 pe_output_file(s1
, NULL
);
191 resolve_common_syms(s1
);
192 build_got_entries(s1
);
198 offset
= 0, mem
= (addr_t
)ptr
;
199 fill
= -mem
& RUN_SECTION_ALIGNMENT
;
201 offset
+= sizeof (void*);
203 for (k
= 0; k
< 2; ++k
) {
204 for(i
= 1; i
< s1
->nb_sections
; i
++) {
206 if (0 == (s
->sh_flags
& SHF_ALLOC
))
208 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
213 else if (s
->sh_flags
& SHF_EXECINSTR
)
214 s
->sh_addr
= mem
+ offset
+ ptr_diff
;
216 s
->sh_addr
= mem
+ offset
;
219 printf("%-16s +%02lx %p %04x\n",
220 s
->name
, fill
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
);
222 offset
+= s
->data_offset
;
223 fill
= -(mem
+ offset
) & 15;
225 #if RUN_SECTION_ALIGNMENT > 15
226 /* To avoid that x86 processors would reload cached instructions each time
227 when data is written in the near, we need to make sure that code and data
228 do not share the same 64 byte unit */
229 fill
= -(mem
+ offset
) & RUN_SECTION_ALIGNMENT
;
233 /* relocate symbols */
234 relocate_syms(s1
, s1
->symtab
, 1);
239 return offset
+ RUN_SECTION_ALIGNMENT
;
242 s1
->pe_imagebase
= mem
;
245 /* relocate each section */
246 for(i
= 1; i
< s1
->nb_sections
; i
++) {
249 relocate_section(s1
, s
);
253 for(i
= 1; i
< s1
->nb_sections
; i
++) {
255 if (0 == (s
->sh_flags
& SHF_ALLOC
))
257 length
= s
->data_offset
;
258 ptr
= (void*)s
->sh_addr
;
259 if (s
->sh_flags
& SHF_EXECINSTR
)
260 ptr
= (char*)ptr
- ptr_diff
;
261 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
262 memset(ptr
, 0, length
);
264 memcpy(ptr
, s
->data
, length
);
265 /* mark executable sections as executable in memory */
266 if (s
->sh_flags
& SHF_EXECINSTR
)
267 set_pages_executable((char*)ptr
+ ptr_diff
, length
);
271 *(void**)mem
= win64_add_function_table(s1
);
277 /* ------------------------------------------------------------- */
278 /* allow to run code in memory */
280 static void set_pages_executable(void *ptr
, unsigned long length
)
283 unsigned long old_protect
;
284 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
286 void __clear_cache(void *beginning
, void *end
);
287 # ifndef HAVE_SELINUX
290 # define PAGESIZE 4096
292 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
293 end
= (addr_t
)ptr
+ length
;
294 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
295 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
296 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
298 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
299 __clear_cache(ptr
, (char *)ptr
+ length
);
305 static void *win64_add_function_table(TCCState
*s1
)
309 p
= (void*)s1
->uw_pdata
->sh_addr
;
311 (RUNTIME_FUNCTION
*)p
,
312 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
320 static void win64_del_function_table(void *p
)
323 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
328 /* ------------------------------------------------------------- */
329 #ifdef CONFIG_TCC_BACKTRACE
331 ST_FUNC
void tcc_set_num_callers(int n
)
336 /* print the position in the source file of PC value 'pc' by reading
337 the stabs debug information */
338 static addr_t
rt_printline(addr_t wanted_pc
, const char *msg
)
340 char func_name
[128], last_func_name
[128];
341 addr_t func_addr
, last_pc
, pc
;
342 const char *incl_files
[INCLUDE_STACK_SIZE
];
343 int incl_index
, len
, last_line_num
, i
;
346 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
, *sym
;
348 char *stab_str
= NULL
;
351 stab_len
= stab_section
->data_offset
;
352 stab_sym
= (Stab_Sym
*)stab_section
->data
;
353 stab_str
= (char *) stabstr_section
->data
;
359 last_func_name
[0] = '\0';
360 last_pc
= (addr_t
)-1;
366 stab_sym_end
= (Stab_Sym
*)((char*)stab_sym
+ stab_len
);
367 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
368 switch(sym
->n_type
) {
369 /* function start or end */
371 if (sym
->n_strx
== 0) {
372 /* we test if between last line and end of function */
373 pc
= sym
->n_value
+ func_addr
;
374 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
379 str
= stab_str
+ sym
->n_strx
;
380 p
= strchr(str
, ':');
382 pstrcpy(func_name
, sizeof(func_name
), str
);
385 if (len
> sizeof(func_name
) - 1)
386 len
= sizeof(func_name
) - 1;
387 memcpy(func_name
, str
, len
);
388 func_name
[len
] = '\0';
390 func_addr
= sym
->n_value
;
393 /* line number info */
395 pc
= sym
->n_value
+ func_addr
;
396 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
399 last_line_num
= sym
->n_desc
;
401 strcpy(last_func_name
, func_name
);
405 str
= stab_str
+ sym
->n_strx
;
407 if (incl_index
< INCLUDE_STACK_SIZE
) {
408 incl_files
[incl_index
++] = str
;
416 if (sym
->n_strx
== 0) {
417 incl_index
= 0; /* end of translation unit */
419 str
= stab_str
+ sym
->n_strx
;
420 /* do not add path */
422 if (len
> 0 && str
[len
- 1] != '/')
430 /* second pass: we try symtab symbols (no line number info) */
434 ElfW(Sym
) *sym
, *sym_end
;
437 sym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
438 for(sym
= (ElfW(Sym
) *)symtab_section
->data
+ 1;
441 type
= ELFW(ST_TYPE
)(sym
->st_info
);
442 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
443 if (wanted_pc
>= sym
->st_value
&&
444 wanted_pc
< sym
->st_value
+ sym
->st_size
) {
445 pstrcpy(last_func_name
, sizeof(last_func_name
),
446 (char *) symtab_section
->link
->data
+ sym
->st_name
);
447 func_addr
= sym
->st_value
;
453 /* did not find any info: */
454 fprintf(stderr
, "%s %p ???\n", msg
, (void*)wanted_pc
);
460 fprintf(stderr
, "%s:%d: ", incl_files
[--i
], last_line_num
);
461 fprintf(stderr
, "%s %p", msg
, (void*)wanted_pc
);
462 if (last_func_name
[0] != '\0')
463 fprintf(stderr
, " %s()", last_func_name
);
465 fprintf(stderr
, " (included from ");
467 fprintf(stderr
, "%s", incl_files
[i
]);
470 fprintf(stderr
, ", ");
472 fprintf(stderr
, ")");
474 fprintf(stderr
, "\n");
479 /* emit a run time error at position 'pc' */
480 static void rt_error(ucontext_t
*uc
, const char *fmt
, ...)
486 fprintf(stderr
, "Runtime error: ");
488 vfprintf(stderr
, fmt
, ap
);
490 fprintf(stderr
, "\n");
492 for(i
=0;i
<rt_num_callers
;i
++) {
493 if (rt_get_caller_pc(&pc
, uc
, i
) < 0)
495 pc
= rt_printline(pc
, i
? "by" : "at");
496 if (pc
== (addr_t
)rt_prog_main
&& pc
)
501 /* ------------------------------------------------------------- */
504 /* signal handler for fatal errors */
505 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
507 ucontext_t
*uc
= puc
;
511 switch(siginf
->si_code
) {
514 rt_error(uc
, "division by zero");
517 rt_error(uc
, "floating point exception");
523 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
524 rt_error(uc
, *rt_bound_error_msg
);
526 rt_error(uc
, "dereferencing invalid pointer");
529 rt_error(uc
, "illegal instruction");
532 rt_error(uc
, "abort() called");
535 rt_error(uc
, "caught signal %d", signum
);
542 # define SA_SIGINFO 0x00000004u
545 /* Generate a stack backtrace when a CPU exception occurs. */
546 static void set_exception_handler(void)
548 struct sigaction sigact
;
549 /* install TCC signal handlers to print debug info on fatal
551 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
552 sigact
.sa_sigaction
= sig_error
;
553 sigemptyset(&sigact
.sa_mask
);
554 sigaction(SIGFPE
, &sigact
, NULL
);
555 sigaction(SIGILL
, &sigact
, NULL
);
556 sigaction(SIGSEGV
, &sigact
, NULL
);
557 sigaction(SIGBUS
, &sigact
, NULL
);
558 sigaction(SIGABRT
, &sigact
, NULL
);
561 /* ------------------------------------------------------------- */
564 /* fix for glibc 2.1 */
570 /* return the PC at frame level 'level'. Return negative if not found */
571 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
577 #if defined(__APPLE__)
578 *paddr
= uc
->uc_mcontext
->__ss
.__eip
;
579 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
580 *paddr
= uc
->uc_mcontext
.mc_eip
;
581 #elif defined(__dietlibc__)
582 *paddr
= uc
->uc_mcontext
.eip
;
583 #elif defined(__NetBSD__)
584 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
585 #elif defined(__OpenBSD__)
588 *paddr
= uc
->uc_mcontext
.gregs
[REG_EIP
];
592 #if defined(__APPLE__)
593 fp
= uc
->uc_mcontext
->__ss
.__ebp
;
594 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
595 fp
= uc
->uc_mcontext
.mc_ebp
;
596 #elif defined(__dietlibc__)
597 fp
= uc
->uc_mcontext
.ebp
;
598 #elif defined(__NetBSD__)
599 fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
600 #elif defined(__OpenBSD__)
603 fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
605 for(i
=1;i
<level
;i
++) {
606 /* XXX: check address validity with program info */
607 if (fp
<= 0x1000 || fp
>= 0xc0000000)
609 fp
= ((addr_t
*)fp
)[0];
611 *paddr
= ((addr_t
*)fp
)[1];
616 /* ------------------------------------------------------------- */
617 #elif defined(__x86_64__)
619 /* return the PC at frame level 'level'. Return negative if not found */
620 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
626 /* XXX: only support linux */
627 #if defined(__APPLE__)
628 *paddr
= uc
->uc_mcontext
->__ss
.__rip
;
629 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
630 *paddr
= uc
->uc_mcontext
.mc_rip
;
631 #elif defined(__NetBSD__)
632 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
634 *paddr
= uc
->uc_mcontext
.gregs
[REG_RIP
];
638 #if defined(__APPLE__)
639 fp
= uc
->uc_mcontext
->__ss
.__rbp
;
640 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
641 fp
= uc
->uc_mcontext
.mc_rbp
;
642 #elif defined(__NetBSD__)
643 fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
645 fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
647 for(i
=1;i
<level
;i
++) {
648 /* XXX: check address validity with program info */
651 fp
= ((addr_t
*)fp
)[0];
653 *paddr
= ((addr_t
*)fp
)[1];
658 /* ------------------------------------------------------------- */
659 #elif defined(__arm__)
661 /* return the PC at frame level 'level'. Return negative if not found */
662 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
668 /* XXX: only supports linux */
669 #if defined(__linux__)
670 *paddr
= uc
->uc_mcontext
.arm_pc
;
676 #if defined(__linux__)
677 fp
= uc
->uc_mcontext
.arm_fp
;
678 sp
= uc
->uc_mcontext
.arm_sp
;
684 /* XXX: specific to tinycc stack frames */
685 if (fp
< sp
+ 12 || fp
& 3)
687 for(i
= 1; i
< level
; i
++) {
688 sp
= ((addr_t
*)fp
)[-2];
689 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
691 fp
= ((addr_t
*)fp
)[-3];
692 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
695 /* XXX: check address validity with program info */
696 *paddr
= ((addr_t
*)fp
)[-1];
701 /* ------------------------------------------------------------- */
702 #elif defined(__aarch64__)
704 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
708 else if (level
== 0) {
709 *paddr
= uc
->uc_mcontext
.pc
;
713 addr_t
*fp
= (addr_t
*)uc
->uc_mcontext
.regs
[29];
715 for (i
= 1; i
< level
; i
++)
716 fp
= (addr_t
*)fp
[0];
722 /* ------------------------------------------------------------- */
725 #warning add arch specific rt_get_caller_pc()
726 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
731 #endif /* !__i386__ */
733 /* ------------------------------------------------------------- */
736 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
738 EXCEPTION_RECORD
*er
= ex_info
->ExceptionRecord
;
739 CONTEXT
*uc
= ex_info
->ContextRecord
;
740 switch (er
->ExceptionCode
) {
741 case EXCEPTION_ACCESS_VIOLATION
:
742 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
743 rt_error(uc
, *rt_bound_error_msg
);
745 rt_error(uc
, "access violation");
747 case EXCEPTION_STACK_OVERFLOW
:
748 rt_error(uc
, "stack overflow");
750 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
751 rt_error(uc
, "division by zero");
754 rt_error(uc
, "exception caught");
757 return EXCEPTION_EXECUTE_HANDLER
;
760 /* Generate a stack backtrace when a CPU exception occurs. */
761 static void set_exception_handler(void)
763 SetUnhandledExceptionFilter(cpu_exception_handler
);
766 /* return the PC at frame level 'level'. Return non zero if not found */
767 static int rt_get_caller_pc(addr_t
*paddr
, CONTEXT
*uc
, int level
)
779 for(i
=1;i
<level
;i
++) {
780 /* XXX: check address validity with program info */
781 if (fp
<= 0x1000 || fp
>= 0xc0000000)
783 fp
= ((addr_t
*)fp
)[0];
785 pc
= ((addr_t
*)fp
)[1];
792 #endif /* CONFIG_TCC_BACKTRACE */
793 /* ------------------------------------------------------------- */
794 #ifdef CONFIG_TCC_STATIC
796 /* dummy function for profiling */
797 ST_FUNC
void *dlopen(const char *filename
, int flag
)
802 ST_FUNC
void dlclose(void *p
)
806 ST_FUNC
const char *dlerror(void)
811 typedef struct TCCSyms
{
817 /* add the symbol you want here if no dynamic linking is done */
818 static TCCSyms tcc_syms
[] = {
819 #if !defined(CONFIG_TCCBOOT)
820 #define TCCSYM(a) { #a, &a, },
830 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
834 while (p
->str
!= NULL
) {
835 if (!strcmp(p
->str
, symbol
))
842 #endif /* CONFIG_TCC_STATIC */
843 #endif /* TCC_IS_NATIVE */
844 /* ------------------------------------------------------------- */