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
;
91 ptr
= tcc_malloc(size
);
93 tcc_relocate_ex(s1
, ptr
, ptr_diff
); /* no more errors expected */
94 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, ptr
);
98 ST_FUNC
void tcc_run_free(TCCState
*s1
)
102 for (i
= 0; i
< s1
->nb_runtime_mem
; ++i
) {
104 unsigned size
= (unsigned)(addr_t
)s1
->runtime_mem
[i
++];
105 munmap(s1
->runtime_mem
[i
++], size
);
106 munmap(s1
->runtime_mem
[i
], size
);
109 win64_del_function_table(*(void**)s1
->runtime_mem
[i
]);
111 tcc_free(s1
->runtime_mem
[i
]);
114 tcc_free(s1
->runtime_mem
);
115 if (get_s1_for_run() == s1
)
116 set_s1_for_run(NULL
);
119 /* launch the compiled program with the given arguments */
120 LIBTCCAPI
int tcc_run(TCCState
*s1
, int argc
, char **argv
)
122 int (*prog_main
)(int, char **);
124 s1
->runtime_main
= s1
->nostdlib
? "_start" : "main";
125 if ((s1
->dflag
& 16) && !find_elf_sym(s1
->symtab
, s1
->runtime_main
))
127 if (tcc_relocate(s1
, TCC_RELOCATE_AUTO
) < 0)
129 prog_main
= tcc_get_symbol_err(s1
, s1
->runtime_main
);
131 #ifdef CONFIG_TCC_BACKTRACE
133 set_exception_handler();
134 s1
->rt_prog_main
= prog_main
;
135 /* set global state pointer for exception handlers*/
140 errno
= 0; /* clean errno value */
142 #ifdef CONFIG_TCC_BCHECK
143 if (s1
->do_bounds_check
) {
144 void (*bound_init
)(void);
145 void (*bound_exit
)(void);
146 void (*bounds_add_static_var
)(size_t *p
);
147 size_t *bounds_start
;
150 /* set error function */
151 s1
->rt_bound_error_msg
= tcc_get_symbol_err(s1
, "__bound_error_msg");
152 /* XXX: use .init section so that it also work in binary ? */
153 bound_init
= tcc_get_symbol_err(s1
, "__bound_init");
154 bound_exit
= tcc_get_symbol_err(s1
, "__bound_exit");
155 bounds_add_static_var
= tcc_get_symbol_err(s1
, "__bounds_add_static_var");
156 bounds_start
= tcc_get_symbol_err(s1
, "__bounds_start");
159 bounds_add_static_var (bounds_start
);
161 ret
= (*prog_main
)(argc
, argv
);
167 return (*prog_main
)(argc
, argv
);
170 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
171 /* To avoid that x86 processors would reload cached instructions
172 each time when data is written in the near, we need to make
173 sure that code and data do not share the same 64 byte unit */
174 #define RUN_SECTION_ALIGNMENT 63
176 #define RUN_SECTION_ALIGNMENT 0
179 /* relocate code. Return -1 on error, required size if ptr is NULL,
180 otherwise copy code into buffer passed by the caller */
181 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
)
184 unsigned offset
, length
, align
, max_align
, i
, k
, f
;
190 pe_output_file(s1
, NULL
);
193 resolve_common_syms(s1
);
194 build_got_entries(s1
);
200 offset
= max_align
= 0, mem
= (addr_t
)ptr
;
202 offset
+= sizeof (void*); /* space for function_table pointer */
204 for (k
= 0; k
< 2; ++k
) {
205 f
= 0, addr
= k
? mem
: mem
+ ptr_diff
;
206 for(i
= 1; i
< s1
->nb_sections
; i
++) {
208 if (0 == (s
->sh_flags
& SHF_ALLOC
))
210 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
212 align
= s
->sh_addralign
- 1;
213 if (++f
== 1 && align
< RUN_SECTION_ALIGNMENT
)
214 align
= RUN_SECTION_ALIGNMENT
;
215 if (max_align
< align
)
217 offset
+= -(addr
+ offset
) & align
;
218 s
->sh_addr
= mem
? addr
+ offset
: 0;
219 offset
+= s
->data_offset
;
222 printf("%-16s %p len %04x align %2d\n",
223 s
->name
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
, align
+ 1);
228 /* relocate symbols */
229 relocate_syms(s1
, s1
->symtab
, 1);
234 return offset
+ max_align
;
237 s1
->pe_imagebase
= mem
;
240 /* relocate each section */
241 for(i
= 1; i
< s1
->nb_sections
; i
++) {
244 relocate_section(s1
, s
);
246 #ifndef TCC_TARGET_PE
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(s1
, (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(TCCState
*s1
, 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
),
317 static void win64_del_function_table(void *p
)
320 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
325 /* ------------------------------------------------------------- */
326 #ifdef CONFIG_TCC_BACKTRACE
328 #define INCLUDE_STACK_SIZE 32
329 static int rt_vprintf(const char *fmt
, va_list ap
)
331 int ret
= vfprintf(stderr
, fmt
, ap
);
336 static int rt_printf(const char *fmt
, ...)
341 r
= rt_vprintf(fmt
, ap
);
346 /* print the position in the source file of PC value 'pc' by reading
347 the stabs debug information */
348 static addr_t
rt_printline(TCCState
*s1
, addr_t wanted_pc
, const char *msg
)
351 addr_t func_addr
, last_pc
, pc
;
352 const char *incl_files
[INCLUDE_STACK_SIZE
];
353 int incl_index
, last_incl_index
, len
, last_line_num
, i
;
356 ElfW(Sym
) *esym_start
= NULL
, *esym_end
= NULL
, *esym
;
357 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
= NULL
, *sym
;
358 char *stab_str
= NULL
;
359 char *elf_str
= NULL
;
364 last_pc
= (addr_t
)-1;
369 stab_sym
= (Stab_Sym
*)stab_section
->data
;
370 stab_sym_end
= (Stab_Sym
*)(stab_section
->data
+ stab_section
->data_offset
);
371 stab_str
= (char *) stab_section
->link
->data
;
373 if (symtab_section
) {
374 esym_start
= (ElfW(Sym
) *)(symtab_section
->data
);
375 esym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
376 elf_str
= symtab_section
->link
->data
;
379 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
380 str
= stab_str
+ sym
->n_strx
;
383 switch(sym
->n_type
) {
391 if (sym
->n_strx
== 0) /* end of function */
395 /* Stab_Sym.n_value is only 32bits */
396 pc
|= wanted_pc
& 0xffffffff00000000ULL
;
403 if (pc
> wanted_pc
&& wanted_pc
> last_pc
)
406 switch(sym
->n_type
) {
407 /* function start or end */
409 if (sym
->n_strx
== 0)
411 p
= strchr(str
, ':');
412 if (0 == p
|| (len
= p
- str
+ 1, len
> sizeof func_name
))
413 len
= sizeof func_name
;
414 pstrcpy(func_name
, len
, str
);
417 /* line number info */
420 last_line_num
= sym
->n_desc
;
421 last_incl_index
= incl_index
;
427 if (incl_index
< INCLUDE_STACK_SIZE
)
428 incl_files
[incl_index
++] = str
;
434 /* start/end of translation unit */
438 /* do not add path */
440 if (len
> 0 && str
[len
- 1] != '/')
441 incl_files
[incl_index
++] = str
;
446 last_pc
= (addr_t
)-1;
448 /* alternative file name (from #line or #include directives) */
451 incl_files
[incl_index
-1] = str
;
456 /* we try symtab symbols (no line number info) */
457 for (esym
= esym_start
+ 1; esym
< esym_end
; ++esym
) {
458 int type
= ELFW(ST_TYPE
)(esym
->st_info
);
459 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
460 if (wanted_pc
>= esym
->st_value
&&
461 wanted_pc
< esym
->st_value
+ esym
->st_size
) {
462 pstrcpy(func_name
, sizeof(func_name
),
463 elf_str
+ esym
->st_name
);
464 func_addr
= esym
->st_value
;
470 /* did not find any info: */
471 rt_printf("%s %p ???", msg
, (void*)wanted_pc
);
477 rt_printf("%s:%d: ", incl_files
[--i
], last_line_num
);
478 rt_printf("%s %p", msg
, (void*)wanted_pc
);
479 if (func_name
[0] != '\0')
480 rt_printf(" %s()", func_name
);
482 rt_printf(" (included from ");
484 rt_printf("%s", incl_files
[i
]);
494 typedef struct rt_context
{
495 addr_t ip
, fp
, sp
, pc
;
498 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
);
500 /* emit a run time error at position 'pc' */
501 static void rt_error(rt_context
*rc
, const char *fmt
, ...)
508 s1
= get_s1_for_run();
510 if (s1
&& s1
->rt_bound_error_msg
&& *s1
->rt_bound_error_msg
)
511 fmt
= *s1
->rt_bound_error_msg
;
516 rt_printf("Runtime error: ");
525 for(i
=0; i
<s1
->rt_num_callers
; i
++) {
526 if (rt_get_caller_pc(&pc
, rc
, i
) < 0)
528 pc
= rt_printline(s1
, pc
, i
? "by" : "at");
530 if (pc
== (addr_t
)s1
->rt_prog_main
&& pc
)
535 /* ------------------------------------------------------------- */
540 # include <sys/ucontext.h>
543 # define ucontext_t CONTEXT
546 /* translate from ucontext_t* to internal rt_context * */
547 static void rt_getcontext(ucontext_t
*uc
, rt_context
*rc
)
557 #elif defined __i386__
558 # if defined(__APPLE__)
559 rc
->ip
= uc
->uc_mcontext
->__ss
.__eip
;
560 rc
->fp
= uc
->uc_mcontext
->__ss
.__ebp
;
561 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
562 rc
->ip
= uc
->uc_mcontext
.mc_eip
;
563 rc
->fp
= uc
->uc_mcontext
.mc_ebp
;
564 # elif defined(__dietlibc__)
565 rc
->ip
= uc
->uc_mcontext
.eip
;
566 rc
->fp
= uc
->uc_mcontext
.ebp
;
567 # elif defined(__NetBSD__)
568 rc
->ip
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
569 rc
->fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
570 # elif defined(__OpenBSD__)
573 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
574 rc
->ip
= uc
->uc_mcontext
.gregs
[EIP
];
575 rc
->fp
= uc
->uc_mcontext
.gregs
[EBP
];
577 rc
->ip
= uc
->uc_mcontext
.gregs
[REG_EIP
];
578 rc
->fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
580 #elif defined(__x86_64__)
581 # if defined(__APPLE__)
582 rc
->ip
= uc
->uc_mcontext
->__ss
.__rip
;
583 rc
->fp
= uc
->uc_mcontext
->__ss
.__rbp
;
584 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
585 rc
->ip
= uc
->uc_mcontext
.mc_rip
;
586 rc
->fp
= uc
->uc_mcontext
.mc_rbp
;
587 # elif defined(__NetBSD__)
588 rc
->ip
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
589 rc
->fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
591 rc
->ip
= uc
->uc_mcontext
.gregs
[REG_RIP
];
592 rc
->fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
594 #elif defined(__arm__)
595 rc
->ip
= uc
->uc_mcontext
.arm_pc
;
596 rc
->fp
= uc
->uc_mcontext
.arm_fp
;
597 rc
->sp
= uc
->uc_mcontext
.arm_sp
;
598 #elif defined(__aarch64__)
599 rc
->ip
= uc
->uc_mcontext
.pc
;
600 rc
->fp
= (addr_t
*)uc
->uc_mcontext
.regs
[29];
604 /* ------------------------------------------------------------- */
606 /* signal handler for fatal errors */
607 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
611 rt_getcontext(puc
, &rc
);
614 switch(siginf
->si_code
) {
617 rt_error(&rc
, "division by zero");
620 rt_error(&rc
, "floating point exception");
626 rt_error(&rc
, " dereferencing invalid pointer");
629 rt_error(&rc
, "illegal instruction");
632 rt_error(&rc
, "abort() called");
635 rt_error(&rc
, "caught signal %d", signum
);
642 # define SA_SIGINFO 0x00000004u
645 /* Generate a stack backtrace when a CPU exception occurs. */
646 static void set_exception_handler(void)
648 struct sigaction sigact
;
649 /* install TCC signal handlers to print debug info on fatal
651 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
652 sigact
.sa_sigaction
= sig_error
;
653 sigemptyset(&sigact
.sa_mask
);
654 sigaction(SIGFPE
, &sigact
, NULL
);
655 sigaction(SIGILL
, &sigact
, NULL
);
656 sigaction(SIGSEGV
, &sigact
, NULL
);
657 sigaction(SIGBUS
, &sigact
, NULL
);
658 sigaction(SIGABRT
, &sigact
, NULL
);
662 /* signal handler for fatal errors */
663 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
668 rt_getcontext(ex_info
->ContextRecord
, &rc
);
669 switch (code
= ex_info
->ExceptionRecord
->ExceptionCode
) {
670 case EXCEPTION_ACCESS_VIOLATION
:
671 rt_error(&rc
, " access violation");
673 case EXCEPTION_STACK_OVERFLOW
:
674 rt_error(&rc
, "stack overflow");
676 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
677 rt_error(&rc
, "division by zero");
679 case EXCEPTION_BREAKPOINT
:
680 case EXCEPTION_SINGLE_STEP
:
681 rc
.ip
= *(addr_t
*)rc
.sp
;
682 rt_error(&rc
, "^breakpoint/single-step exception:");
683 return EXCEPTION_CONTINUE_SEARCH
;
685 rt_error(&rc
, "caught exception %08x", code
);
688 return EXCEPTION_EXECUTE_HANDLER
;
691 /* Generate a stack backtrace when a CPU exception occurs. */
692 static void set_exception_handler(void)
694 SetUnhandledExceptionFilter(cpu_exception_handler
);
699 /* ------------------------------------------------------------- */
700 /* return the PC at frame level 'level'. Return negative if not found */
701 #if defined(__i386__) || defined(__x86_64__)
702 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
711 /* XXX: check address validity with program info */
714 fp
= ((addr_t
*)fp
)[0];
717 ip
= ((addr_t
*)fp
)[1];
725 #elif defined(__arm__)
726 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
728 /* XXX: only supports linux */
729 #if !defined(__linux__)
739 /* XXX: specific to tinycc stack frames */
740 if (fp
< sp
+ 12 || fp
& 3)
743 sp
= ((addr_t
*)fp
)[-2];
744 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
746 fp
= ((addr_t
*)fp
)[-3];
747 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
750 /* XXX: check address validity with program info */
751 *paddr
= ((addr_t
*)fp
)[-1];
757 #elif defined(__aarch64__)
758 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
765 fp
= (addr_t
*)fp
[0];
772 #warning add arch specific rt_get_caller_pc()
773 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
779 #endif /* CONFIG_TCC_BACKTRACE */
781 /* ------------------------------------------------------------- */
782 #ifdef CONFIG_TCC_STATIC
784 /* dummy function for profiling */
785 ST_FUNC
void *dlopen(const char *filename
, int flag
)
790 ST_FUNC
void dlclose(void *p
)
794 ST_FUNC
const char *dlerror(void)
799 typedef struct TCCSyms
{
805 /* add the symbol you want here if no dynamic linking is done */
806 static TCCSyms tcc_syms
[] = {
807 #if !defined(CONFIG_TCCBOOT)
808 #define TCCSYM(a) { #a, &a, },
818 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
822 while (p
->str
!= NULL
) {
823 if (!strcmp(p
->str
, symbol
))
830 #endif /* CONFIG_TCC_STATIC */
831 #endif /* TCC_IS_NATIVE */
832 /* ------------------------------------------------------------- */