Use mmap instead of exec mem for Selinux machines. Fixes crash on Fedora.
[tinycc.git] / tccrun.c
blobf2946a053cd0fa9bedfbd861b7e920ada7650f38
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;
41 ret = tcc_relocate_ex(s1, NULL);
42 if (-1 != ret) {
43 s1->runtime_mem = tcc_malloc(ret);
44 ret = tcc_relocate_ex(s1, s1->runtime_mem);
46 return ret;
49 /* launch the compiled program with the given arguments */
50 int tcc_run(TCCState *s1, int argc, char **argv)
52 int (*prog_main)(int, char **);
53 int ret;
54 #ifdef HAVE_SELINUX
55 int rret;
56 void *ptr,*writep;
57 char tmpfname[] = "/tmp/.tccrunXXXXXX";
58 int fd = mkstemp (tmpfname);
59 unlink (tmpfname);
60 ftruncate (fd, 1000);
61 if ((rret= tcc_relocate_ex(s1,NULL)) < 0)
62 return -1;
63 /* Use mmap instead of malloc for Selinux */
64 writep = mmap (NULL, rret, PROT_READ|PROT_WRITE,
65 MAP_SHARED, fd, 0);
66 if(writep == MAP_FAILED){
67 error("/tmp not writeable");
68 return -1;
70 ptr = mmap (NULL, rret, PROT_READ|PROT_EXEC,
71 MAP_SHARED, fd, 0);
72 if(ptr == MAP_FAILED){
73 error("/tmp not executable");
74 return -1;
76 tcc_relocate_ex(s1, writep);
77 #else
78 if (tcc_relocate(s1) < 0)
79 return -1;
80 #endif
81 prog_main = tcc_get_symbol_err(s1, "main");
83 #ifdef CONFIG_TCC_BACKTRACE
84 if (s1->do_debug)
85 set_exception_handler();
86 #endif
88 #ifdef CONFIG_TCC_BCHECK
89 if (s1->do_bounds_check) {
90 void (*bound_init)(void);
91 void (*bound_exit)(void);
92 /* set error function */
93 rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
94 rt_prog_main = prog_main;
95 /* XXX: use .init section so that it also work in binary ? */
96 bound_init = tcc_get_symbol_err(s1, "__bound_init");
97 bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
98 bound_init();
99 ret = (*prog_main)(argc, argv);
100 bound_exit();
101 return ret;
103 #endif
105 #ifdef TCC_TARGET_PE
107 unsigned char *p = tcc_get_symbol(s1, "tinyc_no_getbp");
108 if (p) *p = 0;
110 #endif
111 ret=(*prog_main)(argc, argv);
112 #ifdef HAVE_SELINUX
113 munmap (writep, rret);
114 munmap (ptr, rret);
116 #endif
117 return ret;
121 /* relocate code. Return -1 on error, required size if ptr is NULL,
122 otherwise copy code into buffer passed by the caller */
123 static int tcc_relocate_ex(TCCState *s1, void *ptr)
125 Section *s;
126 unsigned long offset, length;
127 uplong mem;
128 int i;
130 if (0 == s1->runtime_added) {
131 s1->runtime_added = 1;
132 s1->nb_errors = 0;
133 #ifdef TCC_TARGET_PE
134 pe_output_file(s1, NULL);
135 #else
136 tcc_add_runtime(s1);
137 relocate_common_syms();
138 tcc_add_linker_symbols(s1);
139 build_got_entries(s1);
140 #endif
141 if (s1->nb_errors)
142 return -1;
145 offset = 0, mem = (uplong)ptr;
146 for(i = 1; i < s1->nb_sections; i++) {
147 s = s1->sections[i];
148 if (0 == (s->sh_flags & SHF_ALLOC))
149 continue;
150 length = s->data_offset;
151 s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0;
152 offset = (offset + length + 15) & ~15;
154 offset += 16;
156 /* relocate symbols */
157 relocate_syms(s1, 1);
158 if (s1->nb_errors)
159 return -1;
161 #if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE
162 s1->runtime_plt_and_got_offset = 0;
163 s1->runtime_plt_and_got = (char *)(mem + offset);
164 /* double the size of the buffer for got and plt entries
165 XXX: calculate exact size for them? */
166 offset *= 2;
167 #endif
169 if (0 == mem)
170 return offset;
172 /* relocate each section */
173 for(i = 1; i < s1->nb_sections; i++) {
174 s = s1->sections[i];
175 if (s->reloc)
176 relocate_section(s1, s);
179 for(i = 1; i < s1->nb_sections; i++) {
180 s = s1->sections[i];
181 if (0 == (s->sh_flags & SHF_ALLOC))
182 continue;
183 length = s->data_offset;
184 // printf("%-12s %08x %04x\n", s->name, s->sh_addr, length);
185 ptr = (void*)(uplong)s->sh_addr;
186 if (NULL == s->data || s->sh_type == SHT_NOBITS)
187 memset(ptr, 0, length);
188 else
189 memcpy(ptr, s->data, length);
190 /* mark executable sections as executable in memory */
191 if (s->sh_flags & SHF_EXECINSTR)
192 set_pages_executable(ptr, length);
195 #if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE
196 set_pages_executable(s1->runtime_plt_and_got,
197 s1->runtime_plt_and_got_offset);
198 #endif
199 return 0;
202 /* ------------------------------------------------------------- */
203 /* allow to run code in memory */
205 static void set_pages_executable(void *ptr, unsigned long length)
207 #ifdef _WIN32
208 unsigned long old_protect;
209 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
210 #else
211 unsigned long start, end;
212 start = (uplong)ptr & ~(PAGESIZE - 1);
213 end = (uplong)ptr + length;
214 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
215 mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC);
216 #endif
219 /* ------------------------------------------------------------- */
220 #ifdef CONFIG_TCC_BACKTRACE
222 /* print the position in the source file of PC value 'pc' by reading
223 the stabs debug information */
224 static uplong rt_printline(uplong wanted_pc)
226 Stab_Sym *sym, *sym_end;
227 char func_name[128], last_func_name[128];
228 unsigned long func_addr, last_pc, pc;
229 const char *incl_files[INCLUDE_STACK_SIZE];
230 int incl_index, len, last_line_num, i;
231 const char *str, *p;
233 fprintf(stderr, "0x%08lx:", (unsigned long)wanted_pc);
235 func_name[0] = '\0';
236 func_addr = 0;
237 incl_index = 0;
238 last_func_name[0] = '\0';
239 last_pc = 0xffffffff;
240 last_line_num = 1;
241 sym = (Stab_Sym *)stab_section->data + 1;
242 sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
243 while (sym < sym_end) {
244 switch(sym->n_type) {
245 /* function start or end */
246 case N_FUN:
247 if (sym->n_strx == 0) {
248 /* we test if between last line and end of function */
249 pc = sym->n_value + func_addr;
250 if (wanted_pc >= last_pc && wanted_pc < pc)
251 goto found;
252 func_name[0] = '\0';
253 func_addr = 0;
254 } else {
255 str = stabstr_section->data + sym->n_strx;
256 p = strchr(str, ':');
257 if (!p) {
258 pstrcpy(func_name, sizeof(func_name), str);
259 } else {
260 len = p - str;
261 if (len > sizeof(func_name) - 1)
262 len = sizeof(func_name) - 1;
263 memcpy(func_name, str, len);
264 func_name[len] = '\0';
266 func_addr = sym->n_value;
268 break;
269 /* line number info */
270 case N_SLINE:
271 pc = sym->n_value + func_addr;
272 if (wanted_pc >= last_pc && wanted_pc < pc)
273 goto found;
274 last_pc = pc;
275 last_line_num = sym->n_desc;
276 /* XXX: slow! */
277 strcpy(last_func_name, func_name);
278 break;
279 /* include files */
280 case N_BINCL:
281 str = stabstr_section->data + sym->n_strx;
282 add_incl:
283 if (incl_index < INCLUDE_STACK_SIZE) {
284 incl_files[incl_index++] = str;
286 break;
287 case N_EINCL:
288 if (incl_index > 1)
289 incl_index--;
290 break;
291 case N_SO:
292 if (sym->n_strx == 0) {
293 incl_index = 0; /* end of translation unit */
294 } else {
295 str = stabstr_section->data + sym->n_strx;
296 /* do not add path */
297 len = strlen(str);
298 if (len > 0 && str[len - 1] != '/')
299 goto add_incl;
301 break;
303 sym++;
306 /* second pass: we try symtab symbols (no line number info) */
307 incl_index = 0;
309 ElfW(Sym) *sym, *sym_end;
310 int type;
312 sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
313 for(sym = (ElfW(Sym) *)symtab_section->data + 1;
314 sym < sym_end;
315 sym++) {
316 type = ELFW(ST_TYPE)(sym->st_info);
317 if (type == STT_FUNC) {
318 if (wanted_pc >= sym->st_value &&
319 wanted_pc < sym->st_value + sym->st_size) {
320 pstrcpy(last_func_name, sizeof(last_func_name),
321 strtab_section->data + sym->st_name);
322 func_addr = sym->st_value;
323 goto found;
328 /* did not find any info: */
329 fprintf(stderr, " ???\n");
330 return 0;
331 found:
332 if (last_func_name[0] != '\0') {
333 fprintf(stderr, " %s()", last_func_name);
335 if (incl_index > 0) {
336 fprintf(stderr, " (%s:%d",
337 incl_files[incl_index - 1], last_line_num);
338 for(i = incl_index - 2; i >= 0; i--)
339 fprintf(stderr, ", included from %s", incl_files[i]);
340 fprintf(stderr, ")");
342 fprintf(stderr, "\n");
343 return func_addr;
346 /* emit a run time error at position 'pc' */
347 static void rt_error(ucontext_t *uc, const char *fmt, ...)
349 va_list ap;
350 uplong pc;
351 int i;
353 va_start(ap, fmt);
354 fprintf(stderr, "Runtime error: ");
355 vfprintf(stderr, fmt, ap);
356 fprintf(stderr, "\n");
358 for(i=0;i<num_callers;i++) {
359 if (rt_get_caller_pc(&pc, uc, i) < 0)
360 break;
361 if (i == 0)
362 fprintf(stderr, "at ");
363 else
364 fprintf(stderr, "by ");
365 pc = rt_printline(pc);
366 if (pc == (uplong)rt_prog_main && pc)
367 break;
369 exit(255);
370 va_end(ap);
373 /* ------------------------------------------------------------- */
374 #ifndef _WIN32
376 /* signal handler for fatal errors */
377 static void sig_error(int signum, siginfo_t *siginf, void *puc)
379 ucontext_t *uc = puc;
381 switch(signum) {
382 case SIGFPE:
383 switch(siginf->si_code) {
384 case FPE_INTDIV:
385 case FPE_FLTDIV:
386 rt_error(uc, "division by zero");
387 break;
388 default:
389 rt_error(uc, "floating point exception");
390 break;
392 break;
393 case SIGBUS:
394 case SIGSEGV:
395 if (rt_bound_error_msg && *rt_bound_error_msg)
396 rt_error(uc, *rt_bound_error_msg);
397 else
398 rt_error(uc, "dereferencing invalid pointer");
399 break;
400 case SIGILL:
401 rt_error(uc, "illegal instruction");
402 break;
403 case SIGABRT:
404 rt_error(uc, "abort() called");
405 break;
406 default:
407 rt_error(uc, "caught signal %d", signum);
408 break;
410 exit(255);
413 /* Generate a stack backtrace when a CPU exception occurs. */
414 static void set_exception_handler(void)
416 struct sigaction sigact;
417 /* install TCC signal handlers to print debug info on fatal
418 runtime errors */
419 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
420 sigact.sa_sigaction = sig_error;
421 sigemptyset(&sigact.sa_mask);
422 sigaction(SIGFPE, &sigact, NULL);
423 sigaction(SIGILL, &sigact, NULL);
424 sigaction(SIGSEGV, &sigact, NULL);
425 sigaction(SIGBUS, &sigact, NULL);
426 sigaction(SIGABRT, &sigact, NULL);
429 /* ------------------------------------------------------------- */
430 #ifdef __i386__
432 /* fix for glibc 2.1 */
433 #ifndef REG_EIP
434 #define REG_EIP EIP
435 #define REG_EBP EBP
436 #endif
438 /* return the PC at frame level 'level'. Return non zero if not found */
439 static int rt_get_caller_pc(unsigned long *paddr, ucontext_t *uc, int level)
441 unsigned long fp;
442 int i;
444 if (level == 0) {
445 #if defined(__FreeBSD__)
446 *paddr = uc->uc_mcontext.mc_eip;
447 #elif defined(__dietlibc__)
448 *paddr = uc->uc_mcontext.eip;
449 #else
450 *paddr = uc->uc_mcontext.gregs[REG_EIP];
451 #endif
452 return 0;
453 } else {
454 #if defined(__FreeBSD__)
455 fp = uc->uc_mcontext.mc_ebp;
456 #elif defined(__dietlibc__)
457 fp = uc->uc_mcontext.ebp;
458 #else
459 fp = uc->uc_mcontext.gregs[REG_EBP];
460 #endif
461 for(i=1;i<level;i++) {
462 /* XXX: check address validity with program info */
463 if (fp <= 0x1000 || fp >= 0xc0000000)
464 return -1;
465 fp = ((unsigned long *)fp)[0];
467 *paddr = ((unsigned long *)fp)[1];
468 return 0;
472 /* ------------------------------------------------------------- */
473 #elif defined(__x86_64__)
475 /* return the PC at frame level 'level'. Return non zero if not found */
476 static int rt_get_caller_pc(unsigned long *paddr,
477 ucontext_t *uc, int level)
479 unsigned long fp;
480 int i;
482 if (level == 0) {
483 /* XXX: only support linux */
484 #if defined(__FreeBSD__)
485 *paddr = uc->uc_mcontext.mc_rip;
486 #else
487 *paddr = uc->uc_mcontext.gregs[REG_RIP];
488 #endif
489 return 0;
490 } else {
491 #if defined(__FreeBSD__)
492 fp = uc->uc_mcontext.mc_rbp;
493 #else
494 fp = uc->uc_mcontext.gregs[REG_RBP];
495 #endif
496 for(i=1;i<level;i++) {
497 /* XXX: check address validity with program info */
498 if (fp <= 0x1000)
499 return -1;
500 fp = ((unsigned long *)fp)[0];
502 *paddr = ((unsigned long *)fp)[1];
503 return 0;
507 /* ------------------------------------------------------------- */
508 #else
510 #warning add arch specific rt_get_caller_pc()
511 static int rt_get_caller_pc(unsigned long *paddr,
512 ucontext_t *uc, int level)
514 return -1;
517 #endif /* !__i386__ */
519 /* ------------------------------------------------------------- */
520 #else /* WIN32 */
522 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
524 CONTEXT *uc = ex_info->ContextRecord;
526 EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
527 printf("CPU exception: code=%08lx addr=%p\n",
528 er->ExceptionCode, er->ExceptionAddress);
530 if (rt_bound_error_msg && *rt_bound_error_msg)
531 rt_error(uc, *rt_bound_error_msg);
532 else
533 rt_error(uc, "dereferencing invalid pointer");
534 exit(255);
535 //return EXCEPTION_CONTINUE_SEARCH;
538 /* Generate a stack backtrace when a CPU exception occurs. */
539 static void set_exception_handler(void)
541 SetUnhandledExceptionFilter(cpu_exception_handler);
544 #ifdef _WIN64
545 #define Eip Rip
546 #define Ebp Rbp
547 #endif
549 /* return the PC at frame level 'level'. Return non zero if not found */
550 static int rt_get_caller_pc(uplong *paddr, CONTEXT *uc, int level)
552 uplong fp;
553 int i;
555 if (level == 0) {
556 *paddr = uc->Eip;
557 return 0;
558 } else {
559 fp = uc->Ebp;
560 for(i=1;i<level;i++) {
561 /* XXX: check address validity with program info */
562 if (fp <= 0x1000 || fp >= 0xc0000000)
563 return -1;
564 fp = ((uplong*)fp)[0];
566 *paddr = ((uplong*)fp)[1];
567 return 0;
571 #undef Eip
572 #undef Ebp
574 #endif /* _WIN32 */
575 #endif /* CONFIG_TCC_BACKTRACE */
576 /* ------------------------------------------------------------- */
578 #ifdef CONFIG_TCC_STATIC
580 #define RTLD_LAZY 0x001
581 #define RTLD_NOW 0x002
582 #define RTLD_GLOBAL 0x100
583 #define RTLD_DEFAULT NULL
585 /* dummy function for profiling */
586 void *dlopen(const char *filename, int flag)
588 return NULL;
591 void dlclose(void *p)
595 const char *dlerror(void)
597 return "error";
600 typedef struct TCCSyms {
601 char *str;
602 void *ptr;
603 } TCCSyms;
605 #define TCCSYM(a) { #a, &a, },
607 /* add the symbol you want here if no dynamic linking is done */
608 static TCCSyms tcc_syms[] = {
609 #if !defined(CONFIG_TCCBOOT)
610 TCCSYM(printf)
611 TCCSYM(fprintf)
612 TCCSYM(fopen)
613 TCCSYM(fclose)
614 #endif
615 { NULL, NULL },
618 void *resolve_sym(TCCState *s1, const char *symbol)
620 TCCSyms *p;
621 p = tcc_syms;
622 while (p->str != NULL) {
623 if (!strcmp(p->str, symbol))
624 return p->ptr;
625 p++;
627 return NULL;
630 #elif !defined(_WIN32)
632 void *resolve_sym(TCCState *s1, const char *sym)
634 return dlsym(RTLD_DEFAULT, sym);
637 #endif /* CONFIG_TCC_STATIC */
639 /* ------------------------------------------------------------- */