Add support for __FreeBSD_kernel__ kernel
[tinycc.git] / tccrun.c
blobc45aeb6d81d830dd4465fee2f460702d40b57fb8
1 /*
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
21 #include "tcc.h"
23 #ifdef _WIN32
24 #define ucontext_t CONTEXT
25 #endif
27 static void set_pages_executable(void *ptr, unsigned long length);
28 static void set_exception_handler(void);
29 static int rt_get_caller_pc(uplong *paddr, ucontext_t *uc, int level);
30 static void rt_error(ucontext_t *uc, const char *fmt, ...);
31 static int tcc_relocate_ex(TCCState *s1, void *ptr);
33 /* ------------------------------------------------------------- */
34 /* Do all relocations (needed before using tcc_get_symbol())
35 Returns -1 on error. */
37 int tcc_relocate(TCCState *s1)
39 int ret;
40 #ifdef HAVE_SELINUX
41 char tmpfname[] = "/tmp/.tccrunXXXXXX";
42 int fd = mkstemp (tmpfname);
43 unlink (tmpfname); ftruncate (fd, 1000);
44 if ((ret= tcc_relocate_ex(s1,NULL)) < 0)return -1;
45 s1->mem_size=ret;
46 /* Use mmap instead of malloc for Selinux */
47 s1->write_mem = mmap (NULL, ret, PROT_READ|PROT_WRITE,
48 MAP_SHARED, fd, 0);
49 if(s1->write_mem == MAP_FAILED){
50 error("/tmp not writeable");
51 return -1;
53 s1->runtime_mem = mmap (NULL, ret, PROT_READ|PROT_EXEC,
54 MAP_SHARED, fd, 0);
55 if(s1->runtime_mem == MAP_FAILED){
56 error("/tmp not executable");
57 return -1;
59 ret = tcc_relocate_ex(s1, s1->write_mem);
60 #else
61 ret = tcc_relocate_ex(s1, NULL);
62 if (-1 != ret) {
63 s1->runtime_mem = tcc_malloc(ret);
64 ret = tcc_relocate_ex(s1, s1->runtime_mem);
66 #endif
67 return ret;
70 /* launch the compiled program with the given arguments */
71 int tcc_run(TCCState *s1, int argc, char **argv)
73 int (*prog_main)(int, char **);
75 if (tcc_relocate(s1) < 0)
76 return -1;
78 prog_main = tcc_get_symbol_err(s1, "main");
80 #ifdef CONFIG_TCC_BACKTRACE
81 if (s1->do_debug)
82 set_exception_handler();
83 #endif
85 #ifdef CONFIG_TCC_BCHECK
86 if (s1->do_bounds_check) {
87 void (*bound_init)(void);
88 void (*bound_exit)(void);
89 int ret;
90 /* set error function */
91 rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
92 rt_prog_main = prog_main;
93 /* XXX: use .init section so that it also work in binary ? */
94 bound_init = tcc_get_symbol_err(s1, "__bound_init");
95 bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
96 bound_init();
97 ret = (*prog_main)(argc, argv);
98 bound_exit();
99 return ret;
101 #endif
103 #ifdef TCC_TARGET_PE
105 unsigned char *p = tcc_get_symbol(s1, "tinyc_no_getbp");
106 if (p) *p = 0;
108 #endif
109 return (*prog_main)(argc, argv);
113 /* relocate code. Return -1 on error, required size if ptr is NULL,
114 otherwise copy code into buffer passed by the caller */
115 static int tcc_relocate_ex(TCCState *s1, void *ptr)
117 Section *s;
118 unsigned long offset, length;
119 uplong mem;
120 int i;
122 if (0 == s1->runtime_added) {
123 s1->runtime_added = 1;
124 s1->nb_errors = 0;
125 #ifdef TCC_TARGET_PE
126 pe_output_file(s1, NULL);
127 #else
128 tcc_add_runtime(s1);
129 relocate_common_syms();
130 tcc_add_linker_symbols(s1);
131 build_got_entries(s1);
132 #endif
133 if (s1->nb_errors)
134 return -1;
137 offset = 0, mem = (uplong)ptr;
138 for(i = 1; i < s1->nb_sections; i++) {
139 s = s1->sections[i];
140 if (0 == (s->sh_flags & SHF_ALLOC))
141 continue;
142 length = s->data_offset;
143 s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0;
144 offset = (offset + length + 15) & ~15;
146 offset += 16;
148 /* relocate symbols */
149 relocate_syms(s1, 1);
150 if (s1->nb_errors)
151 return -1;
153 #if (defined TCC_TARGET_X86_64 || defined TCC_TARGET_ARM) && !defined TCC_TARGET_PE
154 s1->runtime_plt_and_got_offset = 0;
155 s1->runtime_plt_and_got = (char *)(mem + offset);
156 /* double the size of the buffer for got and plt entries
157 XXX: calculate exact size for them? */
158 offset *= 2;
159 #endif
161 if (0 == mem)
162 return offset;
164 /* relocate each section */
165 for(i = 1; i < s1->nb_sections; i++) {
166 s = s1->sections[i];
167 if (s->reloc)
168 relocate_section(s1, s);
171 for(i = 1; i < s1->nb_sections; i++) {
172 s = s1->sections[i];
173 if (0 == (s->sh_flags & SHF_ALLOC))
174 continue;
175 length = s->data_offset;
176 // printf("%-12s %08x %04x\n", s->name, s->sh_addr, length);
177 ptr = (void*)(uplong)s->sh_addr;
178 if (NULL == s->data || s->sh_type == SHT_NOBITS)
179 memset(ptr, 0, length);
180 else
181 memcpy(ptr, s->data, length);
182 /* mark executable sections as executable in memory */
183 if (s->sh_flags & SHF_EXECINSTR)
184 set_pages_executable(ptr, length);
187 #if (defined TCC_TARGET_X86_64 || defined TCC_TARGET_ARM) && !defined TCC_TARGET_PE
188 set_pages_executable(s1->runtime_plt_and_got,
189 s1->runtime_plt_and_got_offset);
190 #endif
191 return 0;
194 /* ------------------------------------------------------------- */
195 /* allow to run code in memory */
197 static void set_pages_executable(void *ptr, unsigned long length)
199 #ifdef _WIN32
200 unsigned long old_protect;
201 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
202 #else
203 unsigned long start, end;
204 start = (uplong)ptr & ~(PAGESIZE - 1);
205 end = (uplong)ptr + length;
206 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
207 mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC);
208 #endif
211 /* ------------------------------------------------------------- */
212 #ifdef CONFIG_TCC_BACKTRACE
214 /* print the position in the source file of PC value 'pc' by reading
215 the stabs debug information */
216 static uplong rt_printline(uplong wanted_pc)
218 Stab_Sym *sym, *sym_end;
219 char func_name[128], last_func_name[128];
220 unsigned long func_addr, last_pc, pc;
221 const char *incl_files[INCLUDE_STACK_SIZE];
222 int incl_index, len, last_line_num, i;
223 const char *str, *p;
225 fprintf(stderr, "0x%08lx:", (unsigned long)wanted_pc);
227 func_name[0] = '\0';
228 func_addr = 0;
229 incl_index = 0;
230 last_func_name[0] = '\0';
231 last_pc = 0xffffffff;
232 last_line_num = 1;
233 sym = (Stab_Sym *)stab_section->data + 1;
234 sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
235 while (sym < sym_end) {
236 switch(sym->n_type) {
237 /* function start or end */
238 case N_FUN:
239 if (sym->n_strx == 0) {
240 /* we test if between last line and end of function */
241 pc = sym->n_value + func_addr;
242 if (wanted_pc >= last_pc && wanted_pc < pc)
243 goto found;
244 func_name[0] = '\0';
245 func_addr = 0;
246 } else {
247 str = stabstr_section->data + sym->n_strx;
248 p = strchr(str, ':');
249 if (!p) {
250 pstrcpy(func_name, sizeof(func_name), str);
251 } else {
252 len = p - str;
253 if (len > sizeof(func_name) - 1)
254 len = sizeof(func_name) - 1;
255 memcpy(func_name, str, len);
256 func_name[len] = '\0';
258 func_addr = sym->n_value;
260 break;
261 /* line number info */
262 case N_SLINE:
263 pc = sym->n_value + func_addr;
264 if (wanted_pc >= last_pc && wanted_pc < pc)
265 goto found;
266 last_pc = pc;
267 last_line_num = sym->n_desc;
268 /* XXX: slow! */
269 strcpy(last_func_name, func_name);
270 break;
271 /* include files */
272 case N_BINCL:
273 str = stabstr_section->data + sym->n_strx;
274 add_incl:
275 if (incl_index < INCLUDE_STACK_SIZE) {
276 incl_files[incl_index++] = str;
278 break;
279 case N_EINCL:
280 if (incl_index > 1)
281 incl_index--;
282 break;
283 case N_SO:
284 if (sym->n_strx == 0) {
285 incl_index = 0; /* end of translation unit */
286 } else {
287 str = stabstr_section->data + sym->n_strx;
288 /* do not add path */
289 len = strlen(str);
290 if (len > 0 && str[len - 1] != '/')
291 goto add_incl;
293 break;
295 sym++;
298 /* second pass: we try symtab symbols (no line number info) */
299 incl_index = 0;
301 ElfW(Sym) *sym, *sym_end;
302 int type;
304 sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
305 for(sym = (ElfW(Sym) *)symtab_section->data + 1;
306 sym < sym_end;
307 sym++) {
308 type = ELFW(ST_TYPE)(sym->st_info);
309 if (type == STT_FUNC
310 #ifdef STT_GNU_IFUNC
311 || type == STT_GNU_IFUNC
312 #endif
314 if (wanted_pc >= sym->st_value &&
315 wanted_pc < sym->st_value + sym->st_size) {
316 pstrcpy(last_func_name, sizeof(last_func_name),
317 strtab_section->data + sym->st_name);
318 func_addr = sym->st_value;
319 goto found;
324 /* did not find any info: */
325 fprintf(stderr, " ???\n");
326 return 0;
327 found:
328 if (last_func_name[0] != '\0') {
329 fprintf(stderr, " %s()", last_func_name);
331 if (incl_index > 0) {
332 fprintf(stderr, " (%s:%d",
333 incl_files[incl_index - 1], last_line_num);
334 for(i = incl_index - 2; i >= 0; i--)
335 fprintf(stderr, ", included from %s", incl_files[i]);
336 fprintf(stderr, ")");
338 fprintf(stderr, "\n");
339 return func_addr;
342 /* emit a run time error at position 'pc' */
343 static void rt_error(ucontext_t *uc, const char *fmt, ...)
345 va_list ap;
346 uplong pc;
347 int i;
349 va_start(ap, fmt);
350 fprintf(stderr, "Runtime error: ");
351 vfprintf(stderr, fmt, ap);
352 fprintf(stderr, "\n");
354 for(i=0;i<num_callers;i++) {
355 if (rt_get_caller_pc(&pc, uc, i) < 0)
356 break;
357 if (i == 0)
358 fprintf(stderr, "at ");
359 else
360 fprintf(stderr, "by ");
361 pc = rt_printline(pc);
362 if (pc == (uplong)rt_prog_main && pc)
363 break;
365 exit(255);
366 va_end(ap);
369 /* ------------------------------------------------------------- */
370 #ifndef _WIN32
372 /* signal handler for fatal errors */
373 static void sig_error(int signum, siginfo_t *siginf, void *puc)
375 ucontext_t *uc = puc;
377 switch(signum) {
378 case SIGFPE:
379 switch(siginf->si_code) {
380 case FPE_INTDIV:
381 case FPE_FLTDIV:
382 rt_error(uc, "division by zero");
383 break;
384 default:
385 rt_error(uc, "floating point exception");
386 break;
388 break;
389 case SIGBUS:
390 case SIGSEGV:
391 if (rt_bound_error_msg && *rt_bound_error_msg)
392 rt_error(uc, *rt_bound_error_msg);
393 else
394 rt_error(uc, "dereferencing invalid pointer");
395 break;
396 case SIGILL:
397 rt_error(uc, "illegal instruction");
398 break;
399 case SIGABRT:
400 rt_error(uc, "abort() called");
401 break;
402 default:
403 rt_error(uc, "caught signal %d", signum);
404 break;
406 exit(255);
409 /* Generate a stack backtrace when a CPU exception occurs. */
410 static void set_exception_handler(void)
412 struct sigaction sigact;
413 /* install TCC signal handlers to print debug info on fatal
414 runtime errors */
415 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
416 sigact.sa_sigaction = sig_error;
417 sigemptyset(&sigact.sa_mask);
418 sigaction(SIGFPE, &sigact, NULL);
419 sigaction(SIGILL, &sigact, NULL);
420 sigaction(SIGSEGV, &sigact, NULL);
421 sigaction(SIGBUS, &sigact, NULL);
422 sigaction(SIGABRT, &sigact, NULL);
425 /* ------------------------------------------------------------- */
426 #ifdef __i386__
428 /* fix for glibc 2.1 */
429 #ifndef REG_EIP
430 #define REG_EIP EIP
431 #define REG_EBP EBP
432 #endif
434 /* return the PC at frame level 'level'. Return non zero if not found */
435 static int rt_get_caller_pc(unsigned long *paddr, ucontext_t *uc, int level)
437 unsigned long fp;
438 int i;
440 if (level == 0) {
441 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
442 *paddr = uc->uc_mcontext.mc_eip;
443 #elif defined(__dietlibc__)
444 *paddr = uc->uc_mcontext.eip;
445 #else
446 *paddr = uc->uc_mcontext.gregs[REG_EIP];
447 #endif
448 return 0;
449 } else {
450 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
451 fp = uc->uc_mcontext.mc_ebp;
452 #elif defined(__dietlibc__)
453 fp = uc->uc_mcontext.ebp;
454 #else
455 fp = uc->uc_mcontext.gregs[REG_EBP];
456 #endif
457 for(i=1;i<level;i++) {
458 /* XXX: check address validity with program info */
459 if (fp <= 0x1000 || fp >= 0xc0000000)
460 return -1;
461 fp = ((unsigned long *)fp)[0];
463 *paddr = ((unsigned long *)fp)[1];
464 return 0;
468 /* ------------------------------------------------------------- */
469 #elif defined(__x86_64__)
471 /* return the PC at frame level 'level'. Return non zero if not found */
472 static int rt_get_caller_pc(unsigned long *paddr,
473 ucontext_t *uc, int level)
475 unsigned long fp;
476 int i;
478 if (level == 0) {
479 /* XXX: only support linux */
480 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
481 *paddr = uc->uc_mcontext.mc_rip;
482 #else
483 *paddr = uc->uc_mcontext.gregs[REG_RIP];
484 #endif
485 return 0;
486 } else {
487 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
488 fp = uc->uc_mcontext.mc_rbp;
489 #else
490 fp = uc->uc_mcontext.gregs[REG_RBP];
491 #endif
492 for(i=1;i<level;i++) {
493 /* XXX: check address validity with program info */
494 if (fp <= 0x1000)
495 return -1;
496 fp = ((unsigned long *)fp)[0];
498 *paddr = ((unsigned long *)fp)[1];
499 return 0;
503 /* ------------------------------------------------------------- */
504 #elif defined(__arm__)
506 /* return the PC at frame level 'level'. Return negative if not found */
507 static int rt_get_caller_pc(unsigned long *paddr,
508 ucontext_t *uc, int level)
510 uint32_t fp, sp;
511 int i;
513 if (level == 0) {
514 /* XXX: only supports linux */
515 #if defined(__linux__)
516 *paddr = uc->uc_mcontext.arm_pc;
517 #else
518 return -1;
519 #endif
520 return 0;
521 } else {
522 #if defined(__linux__)
523 fp = uc->uc_mcontext.arm_fp;
524 sp = uc->uc_mcontext.arm_sp;
525 if (sp < 0x1000)
526 sp = 0x1000;
527 #else
528 return -1;
529 #endif
530 /* XXX: specific to tinycc stack frames */
531 if (fp < sp + 12 || fp & 3)
532 return -1;
533 for(i = 1; i < level; i++) {
534 sp = ((uint32_t *)fp)[-2];
535 if (sp < fp || sp - fp > 16 || sp & 3)
536 return -1;
537 fp = ((uint32_t *)fp)[-3];
538 if (fp <= sp || fp - sp < 12 || fp & 3)
539 return -1;
541 /* XXX: check address validity with program info */
542 *paddr = ((uint32_t *)fp)[-1];
543 return 0;
547 /* ------------------------------------------------------------- */
548 #else
550 #warning add arch specific rt_get_caller_pc()
551 static int rt_get_caller_pc(unsigned long *paddr,
552 ucontext_t *uc, int level)
554 return -1;
557 #endif /* !__i386__ */
559 /* ------------------------------------------------------------- */
560 #else /* WIN32 */
562 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
564 CONTEXT *uc = ex_info->ContextRecord;
566 EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
567 printf("CPU exception: code=%08lx addr=%p\n",
568 er->ExceptionCode, er->ExceptionAddress);
570 if (rt_bound_error_msg && *rt_bound_error_msg)
571 rt_error(uc, *rt_bound_error_msg);
572 else
573 rt_error(uc, "dereferencing invalid pointer");
574 exit(255);
575 //return EXCEPTION_CONTINUE_SEARCH;
578 /* Generate a stack backtrace when a CPU exception occurs. */
579 static void set_exception_handler(void)
581 SetUnhandledExceptionFilter(cpu_exception_handler);
584 #ifdef _WIN64
585 #define Eip Rip
586 #define Ebp Rbp
587 #endif
589 /* return the PC at frame level 'level'. Return non zero if not found */
590 static int rt_get_caller_pc(uplong *paddr, CONTEXT *uc, int level)
592 uplong fp;
593 int i;
595 if (level == 0) {
596 *paddr = uc->Eip;
597 return 0;
598 } else {
599 fp = uc->Ebp;
600 for(i=1;i<level;i++) {
601 /* XXX: check address validity with program info */
602 if (fp <= 0x1000 || fp >= 0xc0000000)
603 return -1;
604 fp = ((uplong*)fp)[0];
606 *paddr = ((uplong*)fp)[1];
607 return 0;
611 #undef Eip
612 #undef Ebp
614 #endif /* _WIN32 */
615 #endif /* CONFIG_TCC_BACKTRACE */
616 /* ------------------------------------------------------------- */
618 #ifdef CONFIG_TCC_STATIC
620 #define RTLD_LAZY 0x001
621 #define RTLD_NOW 0x002
622 #define RTLD_GLOBAL 0x100
623 #define RTLD_DEFAULT NULL
625 /* dummy function for profiling */
626 void *dlopen(const char *filename, int flag)
628 return NULL;
631 void dlclose(void *p)
635 const char *dlerror(void)
637 return "error";
640 typedef struct TCCSyms {
641 char *str;
642 void *ptr;
643 } TCCSyms;
645 #define TCCSYM(a) { #a, &a, },
647 /* add the symbol you want here if no dynamic linking is done */
648 static TCCSyms tcc_syms[] = {
649 #if !defined(CONFIG_TCCBOOT)
650 TCCSYM(printf)
651 TCCSYM(fprintf)
652 TCCSYM(fopen)
653 TCCSYM(fclose)
654 #endif
655 { NULL, NULL },
658 void *resolve_sym(TCCState *s1, const char *symbol)
660 TCCSyms *p;
661 p = tcc_syms;
662 while (p->str != NULL) {
663 if (!strcmp(p->str, symbol))
664 return p->ptr;
665 p++;
667 return NULL;
670 #elif !defined(_WIN32)
672 void *resolve_sym(TCCState *s1, const char *sym)
674 return dlsym(RTLD_DEFAULT, sym);
677 #endif /* CONFIG_TCC_STATIC */
679 /* ------------------------------------------------------------- */