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
40 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
);
41 static void rt_error(ucontext_t
*uc
, const char *fmt
, ...);
42 static void set_exception_handler(void);
45 static DWORD s1_for_run_idx
;
46 void set_s1_for_run(TCCState
*s
)
49 s1_for_run_idx
= TlsAlloc();
50 TlsSetValue(s1_for_run_idx
, s
);
52 #define get_s1_for_run() ((TCCState*)TlsGetValue(s1_for_run_idx))
54 /* XXX: add tls support for linux */
55 static TCCState
*s1_for_run
;
56 #define set_s1_for_run(s) (s1_for_run = s)
57 #define get_s1_for_run() s1_for_run
61 static void set_pages_executable(TCCState
*s1
, void *ptr
, unsigned long length
);
62 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
);
65 static void *win64_add_function_table(TCCState
*s1
);
66 static void win64_del_function_table(void *);
69 /* ------------------------------------------------------------- */
70 /* Do all relocations (needed before using tcc_get_symbol())
71 Returns -1 on error. */
73 LIBTCCAPI
int tcc_relocate(TCCState
*s1
, void *ptr
)
78 if (TCC_RELOCATE_AUTO
!= ptr
)
79 return tcc_relocate_ex(s1
, ptr
, 0);
81 size
= tcc_relocate_ex(s1
, NULL
, 0);
87 /* Using mmap instead of malloc */
89 char tmpfname
[] = "/tmp/.tccrunXXXXXX";
90 int fd
= mkstemp(tmpfname
);
94 ptr
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
95 prx
= mmap (NULL
, size
, PROT_READ
|PROT_EXEC
, MAP_SHARED
, fd
, 0);
96 if (ptr
== MAP_FAILED
|| prx
== MAP_FAILED
)
97 tcc_error("tccrun: could not map memory");
98 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, (void*)(addr_t
)size
);
99 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, prx
);
100 ptr_diff
= (char*)prx
- (char*)ptr
;
103 ptr
= tcc_malloc(size
);
105 tcc_relocate_ex(s1
, ptr
, ptr_diff
); /* no more errors expected */
106 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, ptr
);
110 ST_FUNC
void tcc_run_free(TCCState
*s1
)
114 for (i
= 0; i
< s1
->nb_runtime_mem
; ++i
) {
116 unsigned size
= (unsigned)(addr_t
)s1
->runtime_mem
[i
++];
117 munmap(s1
->runtime_mem
[i
++], size
);
118 munmap(s1
->runtime_mem
[i
], size
);
121 win64_del_function_table(*(void**)s1
->runtime_mem
[i
]);
123 tcc_free(s1
->runtime_mem
[i
]);
126 tcc_free(s1
->runtime_mem
);
127 if (get_s1_for_run() == s1
)
128 set_s1_for_run(NULL
);
131 /* launch the compiled program with the given arguments */
132 LIBTCCAPI
int tcc_run(TCCState
*s1
, int argc
, char **argv
)
134 int (*prog_main
)(int, char **);
136 s1
->runtime_main
= s1
->nostdlib
? "_start" : "main";
137 if ((s1
->dflag
& 16) && !find_elf_sym(s1
->symtab
, s1
->runtime_main
))
139 if (tcc_relocate(s1
, TCC_RELOCATE_AUTO
) < 0)
141 prog_main
= tcc_get_symbol_err(s1
, s1
->runtime_main
);
143 #ifdef CONFIG_TCC_BACKTRACE
145 set_exception_handler();
146 s1
->rt_prog_main
= prog_main
;
147 /* set global state pointer for exception handlers*/
152 errno
= 0; /* clean errno value */
154 #ifdef CONFIG_TCC_BCHECK
155 if (s1
->do_bounds_check
) {
156 void (*bound_init
)(void);
157 void (*bound_exit
)(void);
158 void (*bounds_add_static_var
)(size_t *p
);
159 size_t *bounds_start
;
162 /* set error function */
163 s1
->rt_bound_error_msg
= tcc_get_symbol_err(s1
, "__bound_error_msg");
164 /* XXX: use .init section so that it also work in binary ? */
165 bound_init
= tcc_get_symbol_err(s1
, "__bound_init");
166 bound_exit
= tcc_get_symbol_err(s1
, "__bound_exit");
167 bounds_add_static_var
= tcc_get_symbol_err(s1
, "__bounds_add_static_var");
168 bounds_start
= tcc_get_symbol_err(s1
, "__bounds_start");
171 bounds_add_static_var (bounds_start
);
173 ret
= (*prog_main
)(argc
, argv
);
179 return (*prog_main
)(argc
, argv
);
182 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
183 /* To avoid that x86 processors would reload cached instructions
184 each time when data is written in the near, we need to make
185 sure that code and data do not share the same 64 byte unit */
186 #define RUN_SECTION_ALIGNMENT 63
188 #define RUN_SECTION_ALIGNMENT 0
191 /* relocate code. Return -1 on error, required size if ptr is NULL,
192 otherwise copy code into buffer passed by the caller */
193 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
)
196 unsigned offset
, length
, align
, max_align
, i
, k
, f
;
202 pe_output_file(s1
, NULL
);
205 resolve_common_syms(s1
);
206 build_got_entries(s1
);
212 offset
= max_align
= 0, mem
= (addr_t
)ptr
;
214 offset
+= sizeof (void*); /* space for function_table pointer */
216 for (k
= 0; k
< 2; ++k
) {
217 f
= 0, addr
= k
? mem
: mem
+ ptr_diff
;
218 for(i
= 1; i
< s1
->nb_sections
; i
++) {
220 if (0 == (s
->sh_flags
& SHF_ALLOC
))
222 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
224 align
= s
->sh_addralign
- 1;
225 if (++f
== 1 && align
< RUN_SECTION_ALIGNMENT
)
226 align
= RUN_SECTION_ALIGNMENT
;
227 if (max_align
< align
)
229 offset
+= -(addr
+ offset
) & align
;
230 s
->sh_addr
= mem
? addr
+ offset
: 0;
231 offset
+= s
->data_offset
;
234 printf("%-16s %p len %04x align %2d\n",
235 s
->name
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
, align
+ 1);
240 /* relocate symbols */
241 relocate_syms(s1
, s1
->symtab
, 1);
246 return offset
+ max_align
;
249 s1
->pe_imagebase
= mem
;
252 /* relocate each section */
253 for(i
= 1; i
< s1
->nb_sections
; i
++) {
256 relocate_section(s1
, s
);
258 #ifndef TCC_TARGET_PE
262 for(i
= 1; i
< s1
->nb_sections
; i
++) {
264 if (0 == (s
->sh_flags
& SHF_ALLOC
))
266 length
= s
->data_offset
;
267 ptr
= (void*)s
->sh_addr
;
268 if (s
->sh_flags
& SHF_EXECINSTR
)
269 ptr
= (char*)ptr
- ptr_diff
;
270 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
271 memset(ptr
, 0, length
);
273 memcpy(ptr
, s
->data
, length
);
274 /* mark executable sections as executable in memory */
275 if (s
->sh_flags
& SHF_EXECINSTR
)
276 set_pages_executable(s1
, (char*)ptr
+ ptr_diff
, length
);
280 *(void**)mem
= win64_add_function_table(s1
);
286 /* ------------------------------------------------------------- */
287 /* allow to run code in memory */
289 static void set_pages_executable(TCCState
*s1
, void *ptr
, unsigned long length
)
292 unsigned long old_protect
;
293 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
295 void __clear_cache(void *beginning
, void *end
);
296 # ifndef HAVE_SELINUX
299 # define PAGESIZE 4096
301 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
302 end
= (addr_t
)ptr
+ length
;
303 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
304 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
305 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
307 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
308 __clear_cache(ptr
, (char *)ptr
+ length
);
314 static void *win64_add_function_table(TCCState
*s1
)
318 p
= (void*)s1
->uw_pdata
->sh_addr
;
320 (RUNTIME_FUNCTION
*)p
,
321 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
329 static void win64_del_function_table(void *p
)
332 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
337 /* ------------------------------------------------------------- */
338 #ifdef CONFIG_TCC_BACKTRACE
340 /* print the position in the source file of PC value 'pc' by reading
341 the stabs debug information */
342 static addr_t
rt_printline(TCCState
*s1
, addr_t wanted_pc
, const char *msg
)
344 char func_name
[128], last_func_name
[128];
345 addr_t func_addr
, last_pc
, pc
;
346 const char *incl_files
[INCLUDE_STACK_SIZE
];
347 int incl_index
, len
, last_line_num
, i
;
350 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
, *sym
;
352 char *stab_str
= NULL
;
355 stab_len
= stab_section
->data_offset
;
356 stab_sym
= (Stab_Sym
*)stab_section
->data
;
357 stab_str
= (char *) stab_section
->link
->data
;
363 last_func_name
[0] = '\0';
364 last_pc
= (addr_t
)-1;
370 stab_sym_end
= (Stab_Sym
*)((char*)stab_sym
+ stab_len
);
371 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
372 switch(sym
->n_type
) {
373 /* function start or end */
375 if (sym
->n_strx
== 0) {
376 /* we test if between last line and end of function */
377 pc
= sym
->n_value
+ func_addr
;
378 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
383 str
= stab_str
+ sym
->n_strx
;
384 p
= strchr(str
, ':');
386 pstrcpy(func_name
, sizeof(func_name
), str
);
389 if (len
> sizeof(func_name
) - 1)
390 len
= sizeof(func_name
) - 1;
391 memcpy(func_name
, str
, len
);
392 func_name
[len
] = '\0';
394 func_addr
= sym
->n_value
;
397 /* line number info */
399 pc
= sym
->n_value
+ func_addr
;
400 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
403 last_line_num
= sym
->n_desc
;
405 strcpy(last_func_name
, func_name
);
409 str
= stab_str
+ sym
->n_strx
;
411 if (incl_index
< INCLUDE_STACK_SIZE
) {
412 incl_files
[incl_index
++] = str
;
420 if (sym
->n_strx
== 0) {
421 incl_index
= 0; /* end of translation unit */
423 str
= stab_str
+ sym
->n_strx
;
424 /* do not add path */
426 if (len
> 0 && str
[len
- 1] != '/')
434 /* second pass: we try symtab symbols (no line number info) */
438 ElfW(Sym
) *sym
, *sym_end
;
441 sym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
442 for(sym
= (ElfW(Sym
) *)symtab_section
->data
+ 1;
445 type
= ELFW(ST_TYPE
)(sym
->st_info
);
446 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
447 if (wanted_pc
>= sym
->st_value
&&
448 wanted_pc
< sym
->st_value
+ sym
->st_size
) {
449 pstrcpy(last_func_name
, sizeof(last_func_name
),
450 (char *) symtab_section
->link
->data
+ sym
->st_name
);
451 func_addr
= sym
->st_value
;
457 /* did not find any info: */
458 fprintf(stderr
, "%s %p ???\n", msg
, (void*)wanted_pc
);
464 fprintf(stderr
, "%s:%d: ", incl_files
[--i
], last_line_num
);
465 fprintf(stderr
, "%s %p", msg
, (void*)wanted_pc
);
466 if (last_func_name
[0] != '\0')
467 fprintf(stderr
, " %s()", last_func_name
);
469 fprintf(stderr
, " (included from ");
471 fprintf(stderr
, "%s", incl_files
[i
]);
474 fprintf(stderr
, ", ");
476 fprintf(stderr
, ")");
478 fprintf(stderr
, "\n");
483 /* emit a run time error at position 'pc' */
484 static void rt_error(ucontext_t
*uc
, const char *fmt
, ...)
489 TCCState
*s1
= get_s1_for_run();
491 fprintf(stderr
, "Runtime error: ");
493 vfprintf(stderr
, fmt
, ap
);
495 fprintf(stderr
, "\n");
500 for(i
=0;i
<s1
->rt_num_callers
;i
++) {
501 if (rt_get_caller_pc(&pc
, uc
, i
) < 0)
503 pc
= rt_printline(s1
, pc
, i
? "by" : "at");
504 if (pc
== (addr_t
)s1
->rt_prog_main
&& pc
)
509 /* ------------------------------------------------------------- */
512 /* signal handler for fatal errors */
513 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
515 ucontext_t
*uc
= puc
;
520 switch(siginf
->si_code
) {
523 rt_error(uc
, "division by zero");
526 rt_error(uc
, "floating point exception");
532 s1
= get_s1_for_run();
533 if (s1
&& s1
->rt_bound_error_msg
&& *s1
->rt_bound_error_msg
)
534 rt_error(uc
, *s1
->rt_bound_error_msg
);
536 rt_error(uc
, "dereferencing invalid pointer");
539 rt_error(uc
, "illegal instruction");
542 rt_error(uc
, "abort() called");
545 rt_error(uc
, "caught signal %d", signum
);
552 # define SA_SIGINFO 0x00000004u
555 /* Generate a stack backtrace when a CPU exception occurs. */
556 static void set_exception_handler(void)
558 struct sigaction sigact
;
559 /* install TCC signal handlers to print debug info on fatal
561 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
562 sigact
.sa_sigaction
= sig_error
;
563 sigemptyset(&sigact
.sa_mask
);
564 sigaction(SIGFPE
, &sigact
, NULL
);
565 sigaction(SIGILL
, &sigact
, NULL
);
566 sigaction(SIGSEGV
, &sigact
, NULL
);
567 sigaction(SIGBUS
, &sigact
, NULL
);
568 sigaction(SIGABRT
, &sigact
, NULL
);
571 /* ------------------------------------------------------------- */
574 /* fix for glibc 2.1 */
580 /* return the PC at frame level 'level'. Return negative if not found */
581 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
587 #if defined(__APPLE__)
588 *paddr
= uc
->uc_mcontext
->__ss
.__eip
;
589 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
590 *paddr
= uc
->uc_mcontext
.mc_eip
;
591 #elif defined(__dietlibc__)
592 *paddr
= uc
->uc_mcontext
.eip
;
593 #elif defined(__NetBSD__)
594 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
595 #elif defined(__OpenBSD__)
598 *paddr
= uc
->uc_mcontext
.gregs
[REG_EIP
];
602 #if defined(__APPLE__)
603 fp
= uc
->uc_mcontext
->__ss
.__ebp
;
604 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
605 fp
= uc
->uc_mcontext
.mc_ebp
;
606 #elif defined(__dietlibc__)
607 fp
= uc
->uc_mcontext
.ebp
;
608 #elif defined(__NetBSD__)
609 fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
610 #elif defined(__OpenBSD__)
613 fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
615 for(i
=1;i
<level
;i
++) {
616 /* XXX: check address validity with program info */
617 if (fp
<= 0x1000 || fp
>= 0xc0000000)
619 fp
= ((addr_t
*)fp
)[0];
621 *paddr
= ((addr_t
*)fp
)[1];
626 /* ------------------------------------------------------------- */
627 #elif defined(__x86_64__)
629 /* return the PC at frame level 'level'. Return negative if not found */
630 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
636 /* XXX: only support linux */
637 #if defined(__APPLE__)
638 *paddr
= uc
->uc_mcontext
->__ss
.__rip
;
639 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
640 *paddr
= uc
->uc_mcontext
.mc_rip
;
641 #elif defined(__NetBSD__)
642 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
644 *paddr
= uc
->uc_mcontext
.gregs
[REG_RIP
];
648 #if defined(__APPLE__)
649 fp
= uc
->uc_mcontext
->__ss
.__rbp
;
650 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
651 fp
= uc
->uc_mcontext
.mc_rbp
;
652 #elif defined(__NetBSD__)
653 fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
655 fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
657 for(i
=1;i
<level
;i
++) {
658 /* XXX: check address validity with program info */
661 fp
= ((addr_t
*)fp
)[0];
663 *paddr
= ((addr_t
*)fp
)[1];
668 /* ------------------------------------------------------------- */
669 #elif defined(__arm__)
671 /* return the PC at frame level 'level'. Return negative if not found */
672 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
678 /* XXX: only supports linux */
679 #if defined(__linux__)
680 *paddr
= uc
->uc_mcontext
.arm_pc
;
686 #if defined(__linux__)
687 fp
= uc
->uc_mcontext
.arm_fp
;
688 sp
= uc
->uc_mcontext
.arm_sp
;
694 /* XXX: specific to tinycc stack frames */
695 if (fp
< sp
+ 12 || fp
& 3)
697 for(i
= 1; i
< level
; i
++) {
698 sp
= ((addr_t
*)fp
)[-2];
699 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
701 fp
= ((addr_t
*)fp
)[-3];
702 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
705 /* XXX: check address validity with program info */
706 *paddr
= ((addr_t
*)fp
)[-1];
711 /* ------------------------------------------------------------- */
712 #elif defined(__aarch64__)
714 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
718 else if (level
== 0) {
719 *paddr
= uc
->uc_mcontext
.pc
;
723 addr_t
*fp
= (addr_t
*)uc
->uc_mcontext
.regs
[29];
725 for (i
= 1; i
< level
; i
++)
726 fp
= (addr_t
*)fp
[0];
732 /* ------------------------------------------------------------- */
735 #warning add arch specific rt_get_caller_pc()
736 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
741 #endif /* !__i386__ */
743 /* ------------------------------------------------------------- */
746 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
748 EXCEPTION_RECORD
*er
= ex_info
->ExceptionRecord
;
749 CONTEXT
*uc
= ex_info
->ContextRecord
;
751 switch (er
->ExceptionCode
) {
752 case EXCEPTION_ACCESS_VIOLATION
:
753 s1
= get_s1_for_run();
754 if (s1
&& s1
->rt_bound_error_msg
&& *s1
->rt_bound_error_msg
)
755 rt_error(uc
, *s1
->rt_bound_error_msg
);
757 rt_error(uc
, "access violation");
759 case EXCEPTION_STACK_OVERFLOW
:
760 rt_error(uc
, "stack overflow");
762 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
763 rt_error(uc
, "division by zero");
766 rt_error(uc
, "exception caught");
769 return EXCEPTION_EXECUTE_HANDLER
;
772 /* Generate a stack backtrace when a CPU exception occurs. */
773 static void set_exception_handler(void)
775 SetUnhandledExceptionFilter(cpu_exception_handler
);
778 /* return the PC at frame level 'level'. Return non zero if not found */
779 static int rt_get_caller_pc(addr_t
*paddr
, CONTEXT
*uc
, int level
)
791 for(i
=1;i
<level
;i
++) {
792 /* XXX: check address validity with program info */
793 if (fp
<= 0x1000 || fp
>= 0xc0000000)
795 fp
= ((addr_t
*)fp
)[0];
797 pc
= ((addr_t
*)fp
)[1];
804 #endif /* CONFIG_TCC_BACKTRACE */
805 /* ------------------------------------------------------------- */
806 #ifdef CONFIG_TCC_STATIC
808 /* dummy function for profiling */
809 ST_FUNC
void *dlopen(const char *filename
, int flag
)
814 ST_FUNC
void dlclose(void *p
)
818 ST_FUNC
const char *dlerror(void)
823 typedef struct TCCSyms
{
829 /* add the symbol you want here if no dynamic linking is done */
830 static TCCSyms tcc_syms
[] = {
831 #if !defined(CONFIG_TCCBOOT)
832 #define TCCSYM(a) { #a, &a, },
842 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
846 while (p
->str
!= NULL
) {
847 if (!strcmp(p
->str
, symbol
))
854 #endif /* CONFIG_TCC_STATIC */
855 #endif /* TCC_IS_NATIVE */
856 /* ------------------------------------------------------------- */