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
);
51 static void *win64_add_function_table(TCCState
*s1
);
52 static void win64_del_function_table(void *);
55 // #define HAVE_SELINUX
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
)
65 if (TCC_RELOCATE_AUTO
!= ptr
)
66 return tcc_relocate_ex(s1
, ptr
);
68 size
= tcc_relocate_ex(s1
, NULL
);
73 /* Use mmap instead of malloc for Selinux. */
74 ptr
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
,
75 MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0);
76 if (ptr
== MAP_FAILED
)
77 tcc_error("tccrun: could not map memory");
78 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, (void*)(addr_t
)size
);
80 ptr
= tcc_malloc(size
);
82 tcc_relocate_ex(s1
, ptr
); /* no more errors expected */
83 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, ptr
);
87 ST_FUNC
void tcc_run_free(TCCState
*s1
)
91 for (i
= 0; i
< s1
->nb_runtime_mem
; ++i
) {
93 unsigned size
= (unsigned)(addr_t
)s1
->runtime_mem
[i
++];
94 munmap(s1
->runtime_mem
[i
], size
);
97 win64_del_function_table(*(void**)s1
->runtime_mem
[i
]);
99 tcc_free(s1
->runtime_mem
[i
]);
102 tcc_free(s1
->runtime_mem
);
105 /* launch the compiled program with the given arguments */
106 LIBTCCAPI
int tcc_run(TCCState
*s1
, int argc
, char **argv
)
108 int (*prog_main
)(int, char **);
110 s1
->runtime_main
= "main";
111 if (tcc_relocate(s1
, TCC_RELOCATE_AUTO
) < 0)
113 prog_main
= tcc_get_symbol_err(s1
, s1
->runtime_main
);
115 #ifdef CONFIG_TCC_BACKTRACE
117 set_exception_handler();
118 rt_prog_main
= prog_main
;
122 errno
= 0; /* clean errno value */
124 #ifdef CONFIG_TCC_BCHECK
125 if (s1
->do_bounds_check
) {
126 void (*bound_init
)(void);
127 void (*bound_exit
)(void);
128 void (*bound_new_region
)(void *p
, addr_t size
);
129 int (*bound_delete_region
)(void *p
);
132 /* set error function */
133 rt_bound_error_msg
= tcc_get_symbol_err(s1
, "__bound_error_msg");
134 /* XXX: use .init section so that it also work in binary ? */
135 bound_init
= tcc_get_symbol_err(s1
, "__bound_init");
136 bound_exit
= tcc_get_symbol_err(s1
, "__bound_exit");
137 bound_new_region
= tcc_get_symbol_err(s1
, "__bound_new_region");
138 bound_delete_region
= tcc_get_symbol_err(s1
, "__bound_delete_region");
141 /* mark argv area as valid */
142 bound_new_region(argv
, argc
*sizeof(argv
[0]));
143 for (i
=0; i
<argc
; ++i
)
144 bound_new_region(argv
[i
], strlen(argv
[i
]) + 1);
146 ret
= (*prog_main
)(argc
, argv
);
148 /* unmark argv area */
149 for (i
=0; i
<argc
; ++i
)
150 bound_delete_region(argv
[i
]);
151 bound_delete_region(argv
);
156 return (*prog_main
)(argc
, argv
);
159 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
160 #define RUN_SECTION_ALIGNMENT 63
162 #define RUN_SECTION_ALIGNMENT 15
165 /* relocate code. Return -1 on error, required size if ptr is NULL,
166 otherwise copy code into buffer passed by the caller */
167 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
)
170 unsigned offset
, length
, fill
, i
, k
;
176 pe_output_file(s1
, NULL
);
179 relocate_common_syms();
180 tcc_add_linker_symbols(s1
);
181 build_got_entries(s1
);
187 offset
= 0, mem
= (addr_t
)ptr
;
188 fill
= -mem
& RUN_SECTION_ALIGNMENT
;
190 offset
+= sizeof (void*);
192 for (k
= 0; k
< 2; ++k
) {
193 for(i
= 1; i
< s1
->nb_sections
; i
++) {
195 if (0 == (s
->sh_flags
& SHF_ALLOC
))
197 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
200 s
->sh_addr
= mem
? mem
+ offset
: 0;
203 printf("%-16s +%02lx %p %04x\n",
204 s
->name
, fill
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
);
206 offset
+= s
->data_offset
;
207 fill
= -(mem
+ offset
) & 15;
209 #if RUN_SECTION_ALIGNMENT > 15
210 /* To avoid that x86 processors would reload cached instructions each time
211 when data is written in the near, we need to make sure that code and data
212 do not share the same 64 byte unit */
213 fill
= -(mem
+ offset
) & RUN_SECTION_ALIGNMENT
;
217 /* relocate symbols */
218 relocate_syms(s1
, s1
->symtab
, 1);
223 return offset
+ RUN_SECTION_ALIGNMENT
;
225 /* relocate each section */
226 for(i
= 1; i
< s1
->nb_sections
; i
++) {
229 relocate_section(s1
, s
);
234 *(void**)ptr
= win64_add_function_table(s1
);
237 for(i
= 1; i
< s1
->nb_sections
; i
++) {
239 if (0 == (s
->sh_flags
& SHF_ALLOC
))
241 length
= s
->data_offset
;
242 ptr
= (void*)s
->sh_addr
;
243 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
244 memset(ptr
, 0, length
);
246 memcpy(ptr
, s
->data
, length
);
247 /* mark executable sections as executable in memory */
248 if (s
->sh_flags
& SHF_EXECINSTR
)
249 set_pages_executable(ptr
, length
);
254 /* ------------------------------------------------------------- */
255 /* allow to run code in memory */
257 static void set_pages_executable(void *ptr
, unsigned long length
)
260 unsigned long old_protect
;
261 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
263 void __clear_cache(void *beginning
, void *end
);
266 # define PAGESIZE 4096
268 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
269 end
= (addr_t
)ptr
+ length
;
270 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
271 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
272 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
273 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
274 __clear_cache(ptr
, (char *)ptr
+ length
);
280 static void *win64_add_function_table(TCCState
*s1
)
284 p
= (void*)s1
->uw_pdata
->sh_addr
;
286 (RUNTIME_FUNCTION
*)p
,
287 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
288 text_section
->sh_addr
295 static void win64_del_function_table(void *p
)
298 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
303 /* ------------------------------------------------------------- */
304 #ifdef CONFIG_TCC_BACKTRACE
306 ST_FUNC
void tcc_set_num_callers(int n
)
311 /* print the position in the source file of PC value 'pc' by reading
312 the stabs debug information */
313 static addr_t
rt_printline(addr_t wanted_pc
, const char *msg
)
315 char func_name
[128], last_func_name
[128];
316 addr_t func_addr
, last_pc
, pc
;
317 const char *incl_files
[INCLUDE_STACK_SIZE
];
318 int incl_index
, len
, last_line_num
, i
;
321 Stab_Sym
*stab_sym
= NULL
, *stab_sym_end
, *sym
;
323 char *stab_str
= NULL
;
326 stab_len
= stab_section
->data_offset
;
327 stab_sym
= (Stab_Sym
*)stab_section
->data
;
328 stab_str
= (char *) stabstr_section
->data
;
334 last_func_name
[0] = '\0';
335 last_pc
= (addr_t
)-1;
341 stab_sym_end
= (Stab_Sym
*)((char*)stab_sym
+ stab_len
);
342 for (sym
= stab_sym
+ 1; sym
< stab_sym_end
; ++sym
) {
343 switch(sym
->n_type
) {
344 /* function start or end */
346 if (sym
->n_strx
== 0) {
347 /* we test if between last line and end of function */
348 pc
= sym
->n_value
+ func_addr
;
349 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
354 str
= stab_str
+ sym
->n_strx
;
355 p
= strchr(str
, ':');
357 pstrcpy(func_name
, sizeof(func_name
), str
);
360 if (len
> sizeof(func_name
) - 1)
361 len
= sizeof(func_name
) - 1;
362 memcpy(func_name
, str
, len
);
363 func_name
[len
] = '\0';
365 func_addr
= sym
->n_value
;
368 /* line number info */
370 pc
= sym
->n_value
+ func_addr
;
371 if (wanted_pc
>= last_pc
&& wanted_pc
< pc
)
374 last_line_num
= sym
->n_desc
;
376 strcpy(last_func_name
, func_name
);
380 str
= stab_str
+ sym
->n_strx
;
382 if (incl_index
< INCLUDE_STACK_SIZE
) {
383 incl_files
[incl_index
++] = str
;
391 if (sym
->n_strx
== 0) {
392 incl_index
= 0; /* end of translation unit */
394 str
= stab_str
+ sym
->n_strx
;
395 /* do not add path */
397 if (len
> 0 && str
[len
- 1] != '/')
405 /* second pass: we try symtab symbols (no line number info) */
409 ElfW(Sym
) *sym
, *sym_end
;
412 sym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
413 for(sym
= (ElfW(Sym
) *)symtab_section
->data
+ 1;
416 type
= ELFW(ST_TYPE
)(sym
->st_info
);
417 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
418 if (wanted_pc
>= sym
->st_value
&&
419 wanted_pc
< sym
->st_value
+ sym
->st_size
) {
420 pstrcpy(last_func_name
, sizeof(last_func_name
),
421 (char *) strtab_section
->data
+ sym
->st_name
);
422 func_addr
= sym
->st_value
;
428 /* did not find any info: */
429 fprintf(stderr
, "%s %p ???\n", msg
, (void*)wanted_pc
);
435 fprintf(stderr
, "%s:%d: ", incl_files
[--i
], last_line_num
);
436 fprintf(stderr
, "%s %p", msg
, (void*)wanted_pc
);
437 if (last_func_name
[0] != '\0')
438 fprintf(stderr
, " %s()", last_func_name
);
440 fprintf(stderr
, " (included from ");
442 fprintf(stderr
, "%s", incl_files
[i
]);
445 fprintf(stderr
, ", ");
447 fprintf(stderr
, ")");
449 fprintf(stderr
, "\n");
454 /* emit a run time error at position 'pc' */
455 static void rt_error(ucontext_t
*uc
, const char *fmt
, ...)
461 fprintf(stderr
, "Runtime error: ");
463 vfprintf(stderr
, fmt
, ap
);
465 fprintf(stderr
, "\n");
467 for(i
=0;i
<rt_num_callers
;i
++) {
468 if (rt_get_caller_pc(&pc
, uc
, i
) < 0)
470 pc
= rt_printline(pc
, i
? "by" : "at");
471 if (pc
== (addr_t
)rt_prog_main
&& pc
)
476 /* ------------------------------------------------------------- */
479 /* signal handler for fatal errors */
480 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
482 ucontext_t
*uc
= puc
;
486 switch(siginf
->si_code
) {
489 rt_error(uc
, "division by zero");
492 rt_error(uc
, "floating point exception");
498 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
499 rt_error(uc
, *rt_bound_error_msg
);
501 rt_error(uc
, "dereferencing invalid pointer");
504 rt_error(uc
, "illegal instruction");
507 rt_error(uc
, "abort() called");
510 rt_error(uc
, "caught signal %d", signum
);
517 # define SA_SIGINFO 0x00000004u
520 /* Generate a stack backtrace when a CPU exception occurs. */
521 static void set_exception_handler(void)
523 struct sigaction sigact
;
524 /* install TCC signal handlers to print debug info on fatal
526 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
527 sigact
.sa_sigaction
= sig_error
;
528 sigemptyset(&sigact
.sa_mask
);
529 sigaction(SIGFPE
, &sigact
, NULL
);
530 sigaction(SIGILL
, &sigact
, NULL
);
531 sigaction(SIGSEGV
, &sigact
, NULL
);
532 sigaction(SIGBUS
, &sigact
, NULL
);
533 sigaction(SIGABRT
, &sigact
, NULL
);
536 /* ------------------------------------------------------------- */
539 /* fix for glibc 2.1 */
545 /* return the PC at frame level 'level'. Return negative if not found */
546 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
552 #if defined(__APPLE__)
553 *paddr
= uc
->uc_mcontext
->__ss
.__eip
;
554 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
555 *paddr
= uc
->uc_mcontext
.mc_eip
;
556 #elif defined(__dietlibc__)
557 *paddr
= uc
->uc_mcontext
.eip
;
558 #elif defined(__NetBSD__)
559 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
560 #elif defined(__OpenBSD__)
563 *paddr
= uc
->uc_mcontext
.gregs
[REG_EIP
];
567 #if defined(__APPLE__)
568 fp
= uc
->uc_mcontext
->__ss
.__ebp
;
569 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
570 fp
= uc
->uc_mcontext
.mc_ebp
;
571 #elif defined(__dietlibc__)
572 fp
= uc
->uc_mcontext
.ebp
;
573 #elif defined(__NetBSD__)
574 fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
575 #elif defined(__OpenBSD__)
578 fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
580 for(i
=1;i
<level
;i
++) {
581 /* XXX: check address validity with program info */
582 if (fp
<= 0x1000 || fp
>= 0xc0000000)
584 fp
= ((addr_t
*)fp
)[0];
586 *paddr
= ((addr_t
*)fp
)[1];
591 /* ------------------------------------------------------------- */
592 #elif defined(__x86_64__)
594 /* return the PC at frame level 'level'. Return negative if not found */
595 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
601 /* XXX: only support linux */
602 #if defined(__APPLE__)
603 *paddr
= uc
->uc_mcontext
->__ss
.__rip
;
604 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
605 *paddr
= uc
->uc_mcontext
.mc_rip
;
606 #elif defined(__NetBSD__)
607 *paddr
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
609 *paddr
= uc
->uc_mcontext
.gregs
[REG_RIP
];
613 #if defined(__APPLE__)
614 fp
= uc
->uc_mcontext
->__ss
.__rbp
;
615 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
616 fp
= uc
->uc_mcontext
.mc_rbp
;
617 #elif defined(__NetBSD__)
618 fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
620 fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
622 for(i
=1;i
<level
;i
++) {
623 /* XXX: check address validity with program info */
626 fp
= ((addr_t
*)fp
)[0];
628 *paddr
= ((addr_t
*)fp
)[1];
633 /* ------------------------------------------------------------- */
634 #elif defined(__arm__)
636 /* return the PC at frame level 'level'. Return negative if not found */
637 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
643 /* XXX: only supports linux */
644 #if defined(__linux__)
645 *paddr
= uc
->uc_mcontext
.arm_pc
;
651 #if defined(__linux__)
652 fp
= uc
->uc_mcontext
.arm_fp
;
653 sp
= uc
->uc_mcontext
.arm_sp
;
659 /* XXX: specific to tinycc stack frames */
660 if (fp
< sp
+ 12 || fp
& 3)
662 for(i
= 1; i
< level
; i
++) {
663 sp
= ((addr_t
*)fp
)[-2];
664 if (sp
< fp
|| sp
- fp
> 16 || sp
& 3)
666 fp
= ((addr_t
*)fp
)[-3];
667 if (fp
<= sp
|| fp
- sp
< 12 || fp
& 3)
670 /* XXX: check address validity with program info */
671 *paddr
= ((addr_t
*)fp
)[-1];
676 /* ------------------------------------------------------------- */
677 #elif defined(__aarch64__)
679 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
683 else if (level
== 0) {
684 *paddr
= uc
->uc_mcontext
.pc
;
688 addr_t
*fp
= (addr_t
*)uc
->uc_mcontext
.regs
[29];
690 for (i
= 1; i
< level
; i
++)
691 fp
= (addr_t
*)fp
[0];
697 /* ------------------------------------------------------------- */
700 #warning add arch specific rt_get_caller_pc()
701 static int rt_get_caller_pc(addr_t
*paddr
, ucontext_t
*uc
, int level
)
706 #endif /* !__i386__ */
708 /* ------------------------------------------------------------- */
711 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
713 EXCEPTION_RECORD
*er
= ex_info
->ExceptionRecord
;
714 CONTEXT
*uc
= ex_info
->ContextRecord
;
715 switch (er
->ExceptionCode
) {
716 case EXCEPTION_ACCESS_VIOLATION
:
717 if (rt_bound_error_msg
&& *rt_bound_error_msg
)
718 rt_error(uc
, *rt_bound_error_msg
);
720 rt_error(uc
, "access violation");
722 case EXCEPTION_STACK_OVERFLOW
:
723 rt_error(uc
, "stack overflow");
725 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
726 rt_error(uc
, "division by zero");
729 rt_error(uc
, "exception caught");
732 return EXCEPTION_EXECUTE_HANDLER
;
735 /* Generate a stack backtrace when a CPU exception occurs. */
736 static void set_exception_handler(void)
738 SetUnhandledExceptionFilter(cpu_exception_handler
);
741 /* return the PC at frame level 'level'. Return non zero if not found */
742 static int rt_get_caller_pc(addr_t
*paddr
, CONTEXT
*uc
, int level
)
754 for(i
=1;i
<level
;i
++) {
755 /* XXX: check address validity with program info */
756 if (fp
<= 0x1000 || fp
>= 0xc0000000)
758 fp
= ((addr_t
*)fp
)[0];
760 pc
= ((addr_t
*)fp
)[1];
767 #endif /* CONFIG_TCC_BACKTRACE */
768 /* ------------------------------------------------------------- */
769 #ifdef CONFIG_TCC_STATIC
771 /* dummy function for profiling */
772 ST_FUNC
void *dlopen(const char *filename
, int flag
)
777 ST_FUNC
void dlclose(void *p
)
781 ST_FUNC
const char *dlerror(void)
786 typedef struct TCCSyms
{
792 /* add the symbol you want here if no dynamic linking is done */
793 static TCCSyms tcc_syms
[] = {
794 #if !defined(CONFIG_TCCBOOT)
795 #define TCCSYM(a) { #a, &a, },
805 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
809 while (p
->str
!= NULL
) {
810 if (!strcmp(p
->str
, symbol
))
817 #endif /* CONFIG_TCC_STATIC */
818 #endif /* TCC_IS_NATIVE */
819 /* ------------------------------------------------------------- */