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 relocate_common_syms();
192 tcc_add_linker_symbols(s1
);
193 build_got_entries(s1
);
199 offset
= 0, mem
= (addr_t
)ptr
;
200 fill
= -mem
& RUN_SECTION_ALIGNMENT
;
202 offset
+= sizeof (void*);
204 for (k
= 0; k
< 2; ++k
) {
205 for(i
= 1; i
< s1
->nb_sections
; i
++) {
207 if (0 == (s
->sh_flags
& SHF_ALLOC
))
209 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
214 else if (s
->sh_flags
& SHF_EXECINSTR
)
215 s
->sh_addr
= mem
+ offset
+ ptr_diff
;
217 s
->sh_addr
= mem
+ offset
;
220 printf("%-16s +%02lx %p %04x\n",
221 s
->name
, fill
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
);
223 offset
+= s
->data_offset
;
224 fill
= -(mem
+ offset
) & 15;
226 #if RUN_SECTION_ALIGNMENT > 15
227 /* To avoid that x86 processors would reload cached instructions each time
228 when data is written in the near, we need to make sure that code and data
229 do not share the same 64 byte unit */
230 fill
= -(mem
+ offset
) & RUN_SECTION_ALIGNMENT
;
234 /* relocate symbols */
235 relocate_syms(s1
, s1
->symtab
, 1);
240 return offset
+ RUN_SECTION_ALIGNMENT
;
242 /* relocate each section */
243 for(i
= 1; i
< s1
->nb_sections
; i
++) {
246 relocate_section(s1
, s
);
250 for(i
= 1; i
< s1
->nb_sections
; i
++) {
252 if (0 == (s
->sh_flags
& SHF_ALLOC
))
254 length
= s
->data_offset
;
255 ptr
= (void*)s
->sh_addr
;
256 if (s
->sh_flags
& SHF_EXECINSTR
)
257 ptr
= (char*)ptr
- ptr_diff
;
258 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
259 memset(ptr
, 0, length
);
261 memcpy(ptr
, s
->data
, length
);
262 /* mark executable sections as executable in memory */
263 if (s
->sh_flags
& SHF_EXECINSTR
)
264 set_pages_executable((char*)ptr
+ ptr_diff
, length
);
268 *(void**)mem
= win64_add_function_table(s1
);
274 /* ------------------------------------------------------------- */
275 /* allow to run code in memory */
277 static void set_pages_executable(void *ptr
, unsigned long length
)
280 unsigned long old_protect
;
281 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
283 void __clear_cache(void *beginning
, void *end
);
284 # ifndef HAVE_SELINUX
287 # define PAGESIZE 4096
289 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
290 end
= (addr_t
)ptr
+ length
;
291 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
292 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
293 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
295 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
296 __clear_cache(ptr
, (char *)ptr
+ length
);
302 static void *win64_add_function_table(TCCState
*s1
)
306 p
= (void*)s1
->uw_pdata
->sh_addr
;
308 (RUNTIME_FUNCTION
*)p
,
309 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
310 text_section
->sh_addr
317 static void win64_del_function_table(void *p
)
320 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
325 /* ------------------------------------------------------------- */
326 #ifdef CONFIG_TCC_BACKTRACE
328 ST_FUNC
void tcc_set_num_callers(int n
)
333 /* print the position in the source file of PC value 'pc' by reading
334 the stabs debug information */
335 static addr_t
rt_printline(addr_t wanted_pc
, const char *msg
)
337 char func_name
[128], last_func_name
[128];
338 addr_t func_addr
, last_pc
, pc
;
339 const char *incl_files
[INCLUDE_STACK_SIZE
];
340 int incl_index
, len
, last_line_num
, i
;
343 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
, *sym
;
345 char *stab_str
= NULL
;
348 stab_len
= stab_section
->data_offset
;
349 stab_sym
= (Stab_Sym
*)stab_section
->data
;
350 stab_str
= (char *) stabstr_section
->data
;
356 last_func_name
[0] = '\0';
357 last_pc
= (addr_t
)-1;
363 stab_sym_end
= (Stab_Sym
*)((char*)stab_sym
+ stab_len
);
364 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
365 switch(sym
->n_type
) {
366 /* function start or end */
368 if (sym
->n_strx
== 0) {
369 /* we test if between last line and end of function */
370 pc
= sym
->n_value
+ func_addr
;
371 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
376 str
= stab_str
+ sym
->n_strx
;
377 p
= strchr(str
, ':');
379 pstrcpy(func_name
, sizeof(func_name
), str
);
382 if (len
> sizeof(func_name
) - 1)
383 len
= sizeof(func_name
) - 1;
384 memcpy(func_name
, str
, len
);
385 func_name
[len
] = '\0';
387 func_addr
= sym
->n_value
;
390 /* line number info */
392 pc
= sym
->n_value
+ func_addr
;
393 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
396 last_line_num
= sym
->n_desc
;
398 strcpy(last_func_name
, func_name
);
402 str
= stab_str
+ sym
->n_strx
;
404 if (incl_index
< INCLUDE_STACK_SIZE
) {
405 incl_files
[incl_index
++] = str
;
413 if (sym
->n_strx
== 0) {
414 incl_index
= 0; /* end of translation unit */
416 str
= stab_str
+ sym
->n_strx
;
417 /* do not add path */
419 if (len
> 0 && str
[len
- 1] != '/')
427 /* second pass: we try symtab symbols (no line number info) */
431 ElfW(Sym
) *sym
, *sym_end
;
434 sym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
435 for(sym
= (ElfW(Sym
) *)symtab_section
->data
+ 1;
438 type
= ELFW(ST_TYPE
)(sym
->st_info
);
439 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
440 if (wanted_pc
>= sym
->st_value
&&
441 wanted_pc
< sym
->st_value
+ sym
->st_size
) {
442 pstrcpy(last_func_name
, sizeof(last_func_name
),
443 (char *) strtab_section
->data
+ sym
->st_name
);
444 func_addr
= sym
->st_value
;
450 /* did not find any info: */
451 fprintf(stderr
, "%s %p ???\n", msg
, (void*)wanted_pc
);
457 fprintf(stderr
, "%s:%d: ", incl_files
[--i
], last_line_num
);
458 fprintf(stderr
, "%s %p", msg
, (void*)wanted_pc
);
459 if (last_func_name
[0] != '\0')
460 fprintf(stderr
, " %s()", last_func_name
);
462 fprintf(stderr
, " (included from ");
464 fprintf(stderr
, "%s", incl_files
[i
]);
467 fprintf(stderr
, ", ");
469 fprintf(stderr
, ")");
471 fprintf(stderr
, "\n");
476 /* emit a run time error at position 'pc' */
477 static void rt_error(ucontext_t
*uc
, const char *fmt
, ...)
483 fprintf(stderr
, "Runtime error: ");
485 vfprintf(stderr
, fmt
, ap
);
487 fprintf(stderr
, "\n");
489 for(i
=0;i
<rt_num_callers
;i
++) {
490 if (rt_get_caller_pc(&pc
, uc
, i
) < 0)
492 pc
= rt_printline(pc
, i
? "by" : "at");
493 if (pc
== (addr_t
)rt_prog_main
&& pc
)
498 /* ------------------------------------------------------------- */
501 /* signal handler for fatal errors */
502 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
504 ucontext_t
*uc
= puc
;
508 switch(siginf
->si_code
) {
511 rt_error(uc
, "division by zero");
514 rt_error(uc
, "floating point exception");
520 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
521 rt_error(uc
, *rt_bound_error_msg
);
523 rt_error(uc
, "dereferencing invalid pointer");
526 rt_error(uc
, "illegal instruction");
529 rt_error(uc
, "abort() called");
532 rt_error(uc
, "caught signal %d", signum
);
539 # define SA_SIGINFO 0x00000004u
542 /* Generate a stack backtrace when a CPU exception occurs. */
543 static void set_exception_handler(void)
545 struct sigaction sigact
;
546 /* install TCC signal handlers to print debug info on fatal
548 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
549 sigact
.sa_sigaction
= sig_error
;
550 sigemptyset(&sigact
.sa_mask
);
551 sigaction(SIGFPE
, &sigact
, NULL
);
552 sigaction(SIGILL
, &sigact
, NULL
);
553 sigaction(SIGSEGV
, &sigact
, NULL
);
554 sigaction(SIGBUS
, &sigact
, NULL
);
555 sigaction(SIGABRT
, &sigact
, NULL
);
558 /* ------------------------------------------------------------- */
561 /* fix for glibc 2.1 */
567 /* return the PC at frame level 'level'. Return negative if not found */
568 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
574 #if defined(__APPLE__)
575 *paddr
= uc
->uc_mcontext
->__ss
.__eip
;
576 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
577 *paddr
= uc
->uc_mcontext
.mc_eip
;
578 #elif defined(__dietlibc__)
579 *paddr
= uc
->uc_mcontext
.eip
;
580 #elif defined(__NetBSD__)
581 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
582 #elif defined(__OpenBSD__)
585 *paddr
= uc
->uc_mcontext
.gregs
[REG_EIP
];
589 #if defined(__APPLE__)
590 fp
= uc
->uc_mcontext
->__ss
.__ebp
;
591 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
592 fp
= uc
->uc_mcontext
.mc_ebp
;
593 #elif defined(__dietlibc__)
594 fp
= uc
->uc_mcontext
.ebp
;
595 #elif defined(__NetBSD__)
596 fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
597 #elif defined(__OpenBSD__)
600 fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
602 for(i
=1;i
<level
;i
++) {
603 /* XXX: check address validity with program info */
604 if (fp
<= 0x1000 || fp
>= 0xc0000000)
606 fp
= ((addr_t
*)fp
)[0];
608 *paddr
= ((addr_t
*)fp
)[1];
613 /* ------------------------------------------------------------- */
614 #elif defined(__x86_64__)
616 /* return the PC at frame level 'level'. Return negative if not found */
617 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
623 /* XXX: only support linux */
624 #if defined(__APPLE__)
625 *paddr
= uc
->uc_mcontext
->__ss
.__rip
;
626 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
627 *paddr
= uc
->uc_mcontext
.mc_rip
;
628 #elif defined(__NetBSD__)
629 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
631 *paddr
= uc
->uc_mcontext
.gregs
[REG_RIP
];
635 #if defined(__APPLE__)
636 fp
= uc
->uc_mcontext
->__ss
.__rbp
;
637 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
638 fp
= uc
->uc_mcontext
.mc_rbp
;
639 #elif defined(__NetBSD__)
640 fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
642 fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
644 for(i
=1;i
<level
;i
++) {
645 /* XXX: check address validity with program info */
648 fp
= ((addr_t
*)fp
)[0];
650 *paddr
= ((addr_t
*)fp
)[1];
655 /* ------------------------------------------------------------- */
656 #elif defined(__arm__)
658 /* return the PC at frame level 'level'. Return negative if not found */
659 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
665 /* XXX: only supports linux */
666 #if defined(__linux__)
667 *paddr
= uc
->uc_mcontext
.arm_pc
;
673 #if defined(__linux__)
674 fp
= uc
->uc_mcontext
.arm_fp
;
675 sp
= uc
->uc_mcontext
.arm_sp
;
681 /* XXX: specific to tinycc stack frames */
682 if (fp
< sp
+ 12 || fp
& 3)
684 for(i
= 1; i
< level
; i
++) {
685 sp
= ((addr_t
*)fp
)[-2];
686 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
688 fp
= ((addr_t
*)fp
)[-3];
689 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
692 /* XXX: check address validity with program info */
693 *paddr
= ((addr_t
*)fp
)[-1];
698 /* ------------------------------------------------------------- */
699 #elif defined(__aarch64__)
701 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
705 else if (level
== 0) {
706 *paddr
= uc
->uc_mcontext
.pc
;
710 addr_t
*fp
= (addr_t
*)uc
->uc_mcontext
.regs
[29];
712 for (i
= 1; i
< level
; i
++)
713 fp
= (addr_t
*)fp
[0];
719 /* ------------------------------------------------------------- */
722 #warning add arch specific rt_get_caller_pc()
723 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
728 #endif /* !__i386__ */
730 /* ------------------------------------------------------------- */
733 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
735 EXCEPTION_RECORD
*er
= ex_info
->ExceptionRecord
;
736 CONTEXT
*uc
= ex_info
->ContextRecord
;
737 switch (er
->ExceptionCode
) {
738 case EXCEPTION_ACCESS_VIOLATION
:
739 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
740 rt_error(uc
, *rt_bound_error_msg
);
742 rt_error(uc
, "access violation");
744 case EXCEPTION_STACK_OVERFLOW
:
745 rt_error(uc
, "stack overflow");
747 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
748 rt_error(uc
, "division by zero");
751 rt_error(uc
, "exception caught");
754 return EXCEPTION_EXECUTE_HANDLER
;
757 /* Generate a stack backtrace when a CPU exception occurs. */
758 static void set_exception_handler(void)
760 SetUnhandledExceptionFilter(cpu_exception_handler
);
763 /* return the PC at frame level 'level'. Return non zero if not found */
764 static int rt_get_caller_pc(addr_t
*paddr
, CONTEXT
*uc
, int level
)
776 for(i
=1;i
<level
;i
++) {
777 /* XXX: check address validity with program info */
778 if (fp
<= 0x1000 || fp
>= 0xc0000000)
780 fp
= ((addr_t
*)fp
)[0];
782 pc
= ((addr_t
*)fp
)[1];
789 #endif /* CONFIG_TCC_BACKTRACE */
790 /* ------------------------------------------------------------- */
791 #ifdef CONFIG_TCC_STATIC
793 /* dummy function for profiling */
794 ST_FUNC
void *dlopen(const char *filename
, int flag
)
799 ST_FUNC
void dlclose(void *p
)
803 ST_FUNC
const char *dlerror(void)
808 typedef struct TCCSyms
{
814 /* add the symbol you want here if no dynamic linking is done */
815 static TCCSyms tcc_syms
[] = {
816 #if !defined(CONFIG_TCCBOOT)
817 #define TCCSYM(a) { #a, &a, },
827 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
831 while (p
->str
!= NULL
) {
832 if (!strcmp(p
->str
, symbol
))
839 #endif /* CONFIG_TCC_STATIC */
840 #endif /* TCC_IS_NATIVE */
841 /* ------------------------------------------------------------- */