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
31 static void set_exception_handler(void);
33 static DWORD s1_for_run_idx
;
34 void set_s1_for_run(TCCState
*s
)
37 s1_for_run_idx
= TlsAlloc();
38 TlsSetValue(s1_for_run_idx
, s
);
40 #define get_s1_for_run() ((TCCState*)TlsGetValue(s1_for_run_idx))
42 /* XXX: add tls support for linux */
43 static TCCState
*s1_for_run
;
44 #define set_s1_for_run(s) (s1_for_run = s)
45 #define get_s1_for_run() s1_for_run
49 static void set_pages_executable(TCCState
*s1
, void *ptr
, unsigned long length
);
50 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
);
53 static void *win64_add_function_table(TCCState
*s1
);
54 static void win64_del_function_table(void *);
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
)
66 if (TCC_RELOCATE_AUTO
!= ptr
)
67 return tcc_relocate_ex(s1
, ptr
, 0);
69 size
= tcc_relocate_ex(s1
, NULL
, 0);
75 /* Using mmap instead of malloc */
77 char tmpfname
[] = "/tmp/.tccrunXXXXXX";
78 int fd
= mkstemp(tmpfname
);
82 ptr
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
83 prx
= mmap (NULL
, size
, PROT_READ
|PROT_EXEC
, MAP_SHARED
, fd
, 0);
84 if (ptr
== MAP_FAILED
|| prx
== MAP_FAILED
)
85 tcc_error("tccrun: could not map memory");
86 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, (void*)(addr_t
)size
);
87 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, prx
);
88 ptr_diff
= (char*)prx
- (char*)ptr
;
92 ptr
= tcc_malloc(size
);
94 tcc_relocate_ex(s1
, ptr
, ptr_diff
); /* no more errors expected */
95 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, ptr
);
99 ST_FUNC
void tcc_run_free(TCCState
*s1
)
103 for (i
= 0; i
< s1
->nb_runtime_mem
; ++i
) {
105 unsigned size
= (unsigned)(addr_t
)s1
->runtime_mem
[i
++];
106 munmap(s1
->runtime_mem
[i
++], size
);
107 munmap(s1
->runtime_mem
[i
], size
);
110 win64_del_function_table(*(void**)s1
->runtime_mem
[i
]);
112 tcc_free(s1
->runtime_mem
[i
]);
115 tcc_free(s1
->runtime_mem
);
116 if (get_s1_for_run() == s1
)
117 set_s1_for_run(NULL
);
120 /* launch the compiled program with the given arguments */
121 LIBTCCAPI
int tcc_run(TCCState
*s1
, int argc
, char **argv
)
123 /* PE target overwrites runtime_main */
124 #ifndef TCC_TARGET_PE
125 typedef void (*init_array_func
)(int, char **, char **);
126 typedef void (*fini_array_func
)(void);
127 init_array_func
*__init_array_start
;
128 init_array_func
*__init_array_end
;
129 fini_array_func
*__fini_array_start
;
130 fini_array_func
*__fini_array_end
;
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 /* set error function */
157 s1
->rt_bound_error_msg
= tcc_get_symbol_err(s1
, "__bound_error_msg");
160 #ifndef TCC_TARGET_PE
161 __init_array_start
= tcc_get_symbol_err(s1
, "__init_array_start");
162 __init_array_end
= tcc_get_symbol_err(s1
, "__init_array_end");
163 __fini_array_start
= tcc_get_symbol_err(s1
, "__fini_array_start");
164 __fini_array_end
= tcc_get_symbol_err(s1
, "__fini_array_end");
166 if (__init_array_start
&& __init_array_end
) {
168 while (&__init_array_start
[i
] != __init_array_end
)
169 (*__init_array_start
[i
++])(argc
, argv
, environ
);
173 ret
= (*prog_main
)(argc
, argv
);
175 #ifndef TCC_TARGET_PE
176 if (__fini_array_start
&& __fini_array_end
) {
178 while (&__fini_array_end
[i
] != __fini_array_start
)
179 (*__fini_array_end
[--i
])();
185 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
186 /* To avoid that x86 processors would reload cached instructions
187 each time when data is written in the near, we need to make
188 sure that code and data do not share the same 64 byte unit */
189 #define RUN_SECTION_ALIGNMENT 63
191 #define RUN_SECTION_ALIGNMENT 0
194 /* relocate code. Return -1 on error, required size if ptr is NULL,
195 otherwise copy code into buffer passed by the caller */
196 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
)
199 unsigned offset
, length
, align
, max_align
, i
, k
, f
;
205 pe_output_file(s1
, NULL
);
208 resolve_common_syms(s1
);
209 build_got_entries(s1
);
215 offset
= max_align
= 0, mem
= (addr_t
)ptr
;
217 offset
+= sizeof (void*); /* space for function_table pointer */
219 for (k
= 0; k
< 2; ++k
) {
220 f
= 0, addr
= k
? mem
: mem
+ ptr_diff
;
221 for(i
= 1; i
< s1
->nb_sections
; i
++) {
223 if (0 == (s
->sh_flags
& SHF_ALLOC
))
225 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
227 align
= s
->sh_addralign
- 1;
228 if (++f
== 1 && align
< RUN_SECTION_ALIGNMENT
)
229 align
= RUN_SECTION_ALIGNMENT
;
230 if (max_align
< align
)
232 offset
+= -(addr
+ offset
) & align
;
233 s
->sh_addr
= mem
? addr
+ offset
: 0;
234 offset
+= s
->data_offset
;
237 printf("%-16s %p len %04x align %2d\n",
238 s
->name
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
, align
+ 1);
243 /* relocate symbols */
244 relocate_syms(s1
, s1
->symtab
, 1);
249 return offset
+ max_align
;
252 s1
->pe_imagebase
= mem
;
255 /* relocate each section */
256 for(i
= 1; i
< s1
->nb_sections
; i
++) {
259 relocate_section(s1
, s
);
261 #ifndef TCC_TARGET_PE
265 for(i
= 1; i
< s1
->nb_sections
; i
++) {
267 if (0 == (s
->sh_flags
& SHF_ALLOC
))
269 length
= s
->data_offset
;
270 ptr
= (void*)s
->sh_addr
;
271 if (s
->sh_flags
& SHF_EXECINSTR
)
272 ptr
= (char*)((addr_t
)ptr
- ptr_diff
);
273 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
274 memset(ptr
, 0, length
);
276 memcpy(ptr
, s
->data
, length
);
277 /* mark executable sections as executable in memory */
278 if (s
->sh_flags
& SHF_EXECINSTR
)
279 set_pages_executable(s1
, (char*)((addr_t
)ptr
+ ptr_diff
), length
);
283 *(void**)mem
= win64_add_function_table(s1
);
289 /* ------------------------------------------------------------- */
290 /* allow to run code in memory */
292 static void set_pages_executable(TCCState
*s1
, void *ptr
, unsigned long length
)
295 unsigned long old_protect
;
296 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
298 void __clear_cache(void *beginning
, void *end
);
299 # ifndef HAVE_SELINUX
302 # define PAGESIZE 4096
304 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
305 end
= (addr_t
)ptr
+ length
;
306 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
307 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
308 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
310 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
311 __clear_cache(ptr
, (char *)ptr
+ length
);
317 static void *win64_add_function_table(TCCState
*s1
)
321 p
= (void*)s1
->uw_pdata
->sh_addr
;
323 (RUNTIME_FUNCTION
*)p
,
324 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
332 static void win64_del_function_table(void *p
)
335 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
340 /* ------------------------------------------------------------- */
341 #ifdef CONFIG_TCC_BACKTRACE
343 #define INCLUDE_STACK_SIZE 32
344 static int rt_vprintf(const char *fmt
, va_list ap
)
346 int ret
= vfprintf(stderr
, fmt
, ap
);
351 static int rt_printf(const char *fmt
, ...)
356 r
= rt_vprintf(fmt
, ap
);
361 /* print the position in the source file of PC value 'pc' by reading
362 the stabs debug information */
363 static addr_t
rt_printline(TCCState
*s1
, addr_t wanted_pc
, const char *msg
)
366 addr_t func_addr
, last_pc
, pc
;
367 const char *incl_files
[INCLUDE_STACK_SIZE
];
368 int incl_index
, last_incl_index
, len
, last_line_num
, i
;
371 ElfW(Sym
) *esym_start
= NULL
, *esym_end
= NULL
, *esym
;
372 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
= NULL
, *sym
;
373 char *stab_str
= NULL
;
374 char *elf_str
= NULL
;
379 last_pc
= (addr_t
)-1;
384 stab_sym
= (Stab_Sym
*)stab_section
->data
;
385 stab_sym_end
= (Stab_Sym
*)(stab_section
->data
+ stab_section
->data_offset
);
386 stab_str
= (char *) stab_section
->link
->data
;
388 if (symtab_section
) {
389 esym_start
= (ElfW(Sym
) *)(symtab_section
->data
);
390 esym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
391 elf_str
= (char *) symtab_section
->link
->data
;
394 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
395 str
= stab_str
+ sym
->n_strx
;
398 switch(sym
->n_type
) {
406 if (sym
->n_strx
== 0) /* end of function */
410 /* Stab_Sym.n_value is only 32bits */
411 pc
|= wanted_pc
& 0xffffffff00000000ULL
;
418 if (pc
> wanted_pc
&& wanted_pc
> last_pc
)
421 switch(sym
->n_type
) {
422 /* function start or end */
424 if (sym
->n_strx
== 0)
426 p
= strchr(str
, ':');
427 if (0 == p
|| (len
= p
- str
+ 1, len
> sizeof func_name
))
428 len
= sizeof func_name
;
429 pstrcpy(func_name
, len
, str
);
432 /* line number info */
435 last_line_num
= sym
->n_desc
;
436 last_incl_index
= incl_index
;
442 if (incl_index
< INCLUDE_STACK_SIZE
)
443 incl_files
[incl_index
++] = str
;
449 /* start/end of translation unit */
453 /* do not add path */
455 if (len
> 0 && str
[len
- 1] != '/')
456 incl_files
[incl_index
++] = str
;
461 last_pc
= (addr_t
)-1;
463 /* alternative file name (from #line or #include directives) */
466 incl_files
[incl_index
-1] = str
;
471 /* we try symtab symbols (no line number info) */
472 for (esym
= esym_start
+ 1; esym
< esym_end
; ++esym
) {
473 int type
= ELFW(ST_TYPE
)(esym
->st_info
);
474 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
475 if (wanted_pc
>= esym
->st_value
&&
476 wanted_pc
< esym
->st_value
+ esym
->st_size
) {
477 pstrcpy(func_name
, sizeof(func_name
),
478 elf_str
+ esym
->st_name
);
479 func_addr
= esym
->st_value
;
485 /* did not find any info: */
486 rt_printf("%s %p ???", msg
, (void*)wanted_pc
);
492 rt_printf("%s:%d: ", incl_files
[--i
], last_line_num
);
493 rt_printf("%s %p", msg
, (void*)wanted_pc
);
494 if (func_name
[0] != '\0')
495 rt_printf(" %s()", func_name
);
497 rt_printf(" (included from ");
499 rt_printf("%s", incl_files
[i
]);
509 typedef struct rt_context
{
510 addr_t ip
, fp
, sp
, pc
;
513 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
);
515 /* emit a run time error at position 'pc' */
516 static void rt_error(rt_context
*rc
, const char *fmt
, ...)
523 s1
= get_s1_for_run();
525 if (s1
&& s1
->rt_bound_error_msg
&& *s1
->rt_bound_error_msg
)
526 fmt
= *s1
->rt_bound_error_msg
;
531 rt_printf("Runtime error: ");
540 for(i
=0; i
<s1
->rt_num_callers
; i
++) {
541 if (rt_get_caller_pc(&pc
, rc
, i
) < 0)
543 pc
= rt_printline(s1
, pc
, i
? "by" : "at");
545 if (pc
== (addr_t
)s1
->rt_prog_main
&& pc
)
550 /* ------------------------------------------------------------- */
555 # include <sys/ucontext.h>
558 # define ucontext_t CONTEXT
561 /* translate from ucontext_t* to internal rt_context * */
562 static void rt_getcontext(ucontext_t
*uc
, rt_context
*rc
)
572 #elif defined __i386__
573 # if defined(__APPLE__)
574 rc
->ip
= uc
->uc_mcontext
->__ss
.__eip
;
575 rc
->fp
= uc
->uc_mcontext
->__ss
.__ebp
;
576 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
577 rc
->ip
= uc
->uc_mcontext
.mc_eip
;
578 rc
->fp
= uc
->uc_mcontext
.mc_ebp
;
579 # elif defined(__dietlibc__)
580 rc
->ip
= uc
->uc_mcontext
.eip
;
581 rc
->fp
= uc
->uc_mcontext
.ebp
;
582 # elif defined(__NetBSD__)
583 rc
->ip
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
584 rc
->fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
585 # elif defined(__OpenBSD__)
588 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
589 rc
->ip
= uc
->uc_mcontext
.gregs
[EIP
];
590 rc
->fp
= uc
->uc_mcontext
.gregs
[EBP
];
592 rc
->ip
= uc
->uc_mcontext
.gregs
[REG_EIP
];
593 rc
->fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
595 #elif defined(__x86_64__)
596 # if defined(__APPLE__)
597 rc
->ip
= uc
->uc_mcontext
->__ss
.__rip
;
598 rc
->fp
= uc
->uc_mcontext
->__ss
.__rbp
;
599 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
600 rc
->ip
= uc
->uc_mcontext
.mc_rip
;
601 rc
->fp
= uc
->uc_mcontext
.mc_rbp
;
602 # elif defined(__NetBSD__)
603 rc
->ip
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
604 rc
->fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
606 rc
->ip
= uc
->uc_mcontext
.gregs
[REG_RIP
];
607 rc
->fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
609 #elif defined(__arm__)
610 rc
->ip
= uc
->uc_mcontext
.arm_pc
;
611 rc
->fp
= uc
->uc_mcontext
.arm_fp
;
612 rc
->sp
= uc
->uc_mcontext
.arm_sp
;
613 #elif defined(__aarch64__)
614 rc
->ip
= uc
->uc_mcontext
.pc
;
615 rc
->fp
= uc
->uc_mcontext
.regs
[29];
619 /* ------------------------------------------------------------- */
621 /* signal handler for fatal errors */
622 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
626 rt_getcontext(puc
, &rc
);
629 switch(siginf
->si_code
) {
632 rt_error(&rc
, "division by zero");
635 rt_error(&rc
, "floating point exception");
641 rt_error(&rc
, " dereferencing invalid pointer");
644 rt_error(&rc
, "illegal instruction");
647 rt_error(&rc
, "abort() called");
650 rt_error(&rc
, "caught signal %d", signum
);
657 # define SA_SIGINFO 0x00000004u
660 /* Generate a stack backtrace when a CPU exception occurs. */
661 static void set_exception_handler(void)
663 struct sigaction sigact
;
664 /* install TCC signal handlers to print debug info on fatal
666 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
668 sigact
.sa_flags
|= SA_ONSTACK
;
670 sigact
.sa_sigaction
= sig_error
;
671 sigemptyset(&sigact
.sa_mask
);
672 sigaction(SIGFPE
, &sigact
, NULL
);
673 sigaction(SIGILL
, &sigact
, NULL
);
674 sigaction(SIGSEGV
, &sigact
, NULL
);
675 sigaction(SIGBUS
, &sigact
, NULL
);
676 sigaction(SIGABRT
, &sigact
, NULL
);
678 /* This allows stack overflow to be reported instead of a SEGV */
681 static unsigned char stack
[SIGSTKSZ
] __attribute__((aligned(16)));
684 ss
.ss_size
= SIGSTKSZ
;
686 sigaltstack(&ss
, NULL
);
692 /* signal handler for fatal errors */
693 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
698 rt_getcontext(ex_info
->ContextRecord
, &rc
);
699 switch (code
= ex_info
->ExceptionRecord
->ExceptionCode
) {
700 case EXCEPTION_ACCESS_VIOLATION
:
701 rt_error(&rc
, " access violation");
703 case EXCEPTION_STACK_OVERFLOW
:
704 rt_error(&rc
, "stack overflow");
706 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
707 rt_error(&rc
, "division by zero");
709 case EXCEPTION_BREAKPOINT
:
710 case EXCEPTION_SINGLE_STEP
:
711 rc
.ip
= *(addr_t
*)rc
.sp
;
712 rt_error(&rc
, "^breakpoint/single-step exception:");
713 return EXCEPTION_CONTINUE_SEARCH
;
715 rt_error(&rc
, "caught exception %08x", code
);
718 return EXCEPTION_EXECUTE_HANDLER
;
721 /* Generate a stack backtrace when a CPU exception occurs. */
722 static void set_exception_handler(void)
724 SetUnhandledExceptionFilter(cpu_exception_handler
);
729 /* ------------------------------------------------------------- */
730 /* return the PC at frame level 'level'. Return negative if not found */
731 #if defined(__i386__) || defined(__x86_64__)
732 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
741 /* XXX: check address validity with program info */
744 fp
= ((addr_t
*)fp
)[0];
747 ip
= ((addr_t
*)fp
)[1];
755 #elif defined(__arm__)
756 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
758 /* XXX: only supports linux */
759 #if !defined(__linux__)
769 /* XXX: specific to tinycc stack frames */
770 if (fp
< sp
+ 12 || fp
& 3)
773 sp
= ((addr_t
*)fp
)[-2];
774 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
776 fp
= ((addr_t
*)fp
)[-3];
777 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
780 /* XXX: check address validity with program info */
781 *paddr
= ((addr_t
*)fp
)[-1];
787 #elif defined(__aarch64__)
788 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
793 addr_t
*fp
= (addr_t
*)rc
->fp
;
795 fp
= (addr_t
*)fp
[0];
802 #warning add arch specific rt_get_caller_pc()
803 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
809 #endif /* CONFIG_TCC_BACKTRACE */
811 /* ------------------------------------------------------------- */
812 #ifdef CONFIG_TCC_STATIC
814 /* dummy function for profiling */
815 ST_FUNC
void *dlopen(const char *filename
, int flag
)
820 ST_FUNC
void dlclose(void *p
)
824 ST_FUNC
const char *dlerror(void)
829 typedef struct TCCSyms
{
835 /* add the symbol you want here if no dynamic linking is done */
836 static TCCSyms tcc_syms
[] = {
837 #if !defined(CONFIG_TCCBOOT)
838 #define TCCSYM(a) { #a, &a, },
848 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
852 while (p
->str
!= NULL
) {
853 if (!strcmp(p
->str
, symbol
))
860 #endif /* CONFIG_TCC_STATIC */
861 #endif /* TCC_IS_NATIVE */
862 /* ------------------------------------------------------------- */