Avoid crash with "Avoid a crash with weak symbols for "make test""
[tinycc.git] / tccrun.c
blob9ef903d053910d34a6376c1609085ecee6209375
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 if (wanted_pc >= sym->st_value &&
311 wanted_pc < sym->st_value + sym->st_size) {
312 pstrcpy(last_func_name, sizeof(last_func_name),
313 strtab_section->data + sym->st_name);
314 func_addr = sym->st_value;
315 goto found;
320 /* did not find any info: */
321 fprintf(stderr, " ???\n");
322 return 0;
323 found:
324 if (last_func_name[0] != '\0') {
325 fprintf(stderr, " %s()", last_func_name);
327 if (incl_index > 0) {
328 fprintf(stderr, " (%s:%d",
329 incl_files[incl_index - 1], last_line_num);
330 for(i = incl_index - 2; i >= 0; i--)
331 fprintf(stderr, ", included from %s", incl_files[i]);
332 fprintf(stderr, ")");
334 fprintf(stderr, "\n");
335 return func_addr;
338 /* emit a run time error at position 'pc' */
339 static void rt_error(ucontext_t *uc, const char *fmt, ...)
341 va_list ap;
342 uplong pc;
343 int i;
345 va_start(ap, fmt);
346 fprintf(stderr, "Runtime error: ");
347 vfprintf(stderr, fmt, ap);
348 fprintf(stderr, "\n");
350 for(i=0;i<num_callers;i++) {
351 if (rt_get_caller_pc(&pc, uc, i) < 0)
352 break;
353 if (i == 0)
354 fprintf(stderr, "at ");
355 else
356 fprintf(stderr, "by ");
357 pc = rt_printline(pc);
358 if (pc == (uplong)rt_prog_main && pc)
359 break;
361 exit(255);
362 va_end(ap);
365 /* ------------------------------------------------------------- */
366 #ifndef _WIN32
368 /* signal handler for fatal errors */
369 static void sig_error(int signum, siginfo_t *siginf, void *puc)
371 ucontext_t *uc = puc;
373 switch(signum) {
374 case SIGFPE:
375 switch(siginf->si_code) {
376 case FPE_INTDIV:
377 case FPE_FLTDIV:
378 rt_error(uc, "division by zero");
379 break;
380 default:
381 rt_error(uc, "floating point exception");
382 break;
384 break;
385 case SIGBUS:
386 case SIGSEGV:
387 if (rt_bound_error_msg && *rt_bound_error_msg)
388 rt_error(uc, *rt_bound_error_msg);
389 else
390 rt_error(uc, "dereferencing invalid pointer");
391 break;
392 case SIGILL:
393 rt_error(uc, "illegal instruction");
394 break;
395 case SIGABRT:
396 rt_error(uc, "abort() called");
397 break;
398 default:
399 rt_error(uc, "caught signal %d", signum);
400 break;
402 exit(255);
405 /* Generate a stack backtrace when a CPU exception occurs. */
406 static void set_exception_handler(void)
408 struct sigaction sigact;
409 /* install TCC signal handlers to print debug info on fatal
410 runtime errors */
411 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
412 sigact.sa_sigaction = sig_error;
413 sigemptyset(&sigact.sa_mask);
414 sigaction(SIGFPE, &sigact, NULL);
415 sigaction(SIGILL, &sigact, NULL);
416 sigaction(SIGSEGV, &sigact, NULL);
417 sigaction(SIGBUS, &sigact, NULL);
418 sigaction(SIGABRT, &sigact, NULL);
421 /* ------------------------------------------------------------- */
422 #ifdef __i386__
424 /* fix for glibc 2.1 */
425 #ifndef REG_EIP
426 #define REG_EIP EIP
427 #define REG_EBP EBP
428 #endif
430 /* return the PC at frame level 'level'. Return non zero if not found */
431 static int rt_get_caller_pc(unsigned long *paddr, ucontext_t *uc, int level)
433 unsigned long fp;
434 int i;
436 if (level == 0) {
437 #if defined(__FreeBSD__)
438 *paddr = uc->uc_mcontext.mc_eip;
439 #elif defined(__dietlibc__)
440 *paddr = uc->uc_mcontext.eip;
441 #else
442 *paddr = uc->uc_mcontext.gregs[REG_EIP];
443 #endif
444 return 0;
445 } else {
446 #if defined(__FreeBSD__)
447 fp = uc->uc_mcontext.mc_ebp;
448 #elif defined(__dietlibc__)
449 fp = uc->uc_mcontext.ebp;
450 #else
451 fp = uc->uc_mcontext.gregs[REG_EBP];
452 #endif
453 for(i=1;i<level;i++) {
454 /* XXX: check address validity with program info */
455 if (fp <= 0x1000 || fp >= 0xc0000000)
456 return -1;
457 fp = ((unsigned long *)fp)[0];
459 *paddr = ((unsigned long *)fp)[1];
460 return 0;
464 /* ------------------------------------------------------------- */
465 #elif defined(__x86_64__)
467 /* return the PC at frame level 'level'. Return non zero if not found */
468 static int rt_get_caller_pc(unsigned long *paddr,
469 ucontext_t *uc, int level)
471 unsigned long fp;
472 int i;
474 if (level == 0) {
475 /* XXX: only support linux */
476 #if defined(__FreeBSD__)
477 *paddr = uc->uc_mcontext.mc_rip;
478 #else
479 *paddr = uc->uc_mcontext.gregs[REG_RIP];
480 #endif
481 return 0;
482 } else {
483 #if defined(__FreeBSD__)
484 fp = uc->uc_mcontext.mc_rbp;
485 #else
486 fp = uc->uc_mcontext.gregs[REG_RBP];
487 #endif
488 for(i=1;i<level;i++) {
489 /* XXX: check address validity with program info */
490 if (fp <= 0x1000)
491 return -1;
492 fp = ((unsigned long *)fp)[0];
494 *paddr = ((unsigned long *)fp)[1];
495 return 0;
499 /* ------------------------------------------------------------- */
500 #elif defined(__arm__)
502 /* return the PC at frame level 'level'. Return negative if not found */
503 static int rt_get_caller_pc(unsigned long *paddr,
504 ucontext_t *uc, int level)
506 uint32_t fp, sp;
507 int i;
509 if (level == 0) {
510 /* XXX: only supports linux */
511 #if defined(__linux__)
512 *paddr = uc->uc_mcontext.arm_pc;
513 #else
514 return -1;
515 #endif
516 return 0;
517 } else {
518 #if defined(__linux__)
519 fp = uc->uc_mcontext.arm_fp;
520 sp = uc->uc_mcontext.arm_sp;
521 if (sp < 0x1000)
522 sp = 0x1000;
523 #else
524 return -1;
525 #endif
526 /* XXX: specific to tinycc stack frames */
527 if (fp < sp + 12 || fp & 3)
528 return -1;
529 for(i = 1; i < level; i++) {
530 sp = ((uint32_t *)fp)[-2];
531 if (sp < fp || sp - fp > 16 || sp & 3)
532 return -1;
533 fp = ((uint32_t *)fp)[-3];
534 if (fp <= sp || fp - sp < 12 || fp & 3)
535 return -1;
537 /* XXX: check address validity with program info */
538 *paddr = ((uint32_t *)fp)[-1];
539 return 0;
543 /* ------------------------------------------------------------- */
544 #else
546 #warning add arch specific rt_get_caller_pc()
547 static int rt_get_caller_pc(unsigned long *paddr,
548 ucontext_t *uc, int level)
550 return -1;
553 #endif /* !__i386__ */
555 /* ------------------------------------------------------------- */
556 #else /* WIN32 */
558 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
560 CONTEXT *uc = ex_info->ContextRecord;
562 EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
563 printf("CPU exception: code=%08lx addr=%p\n",
564 er->ExceptionCode, er->ExceptionAddress);
566 if (rt_bound_error_msg && *rt_bound_error_msg)
567 rt_error(uc, *rt_bound_error_msg);
568 else
569 rt_error(uc, "dereferencing invalid pointer");
570 exit(255);
571 //return EXCEPTION_CONTINUE_SEARCH;
574 /* Generate a stack backtrace when a CPU exception occurs. */
575 static void set_exception_handler(void)
577 SetUnhandledExceptionFilter(cpu_exception_handler);
580 #ifdef _WIN64
581 #define Eip Rip
582 #define Ebp Rbp
583 #endif
585 /* return the PC at frame level 'level'. Return non zero if not found */
586 static int rt_get_caller_pc(uplong *paddr, CONTEXT *uc, int level)
588 uplong fp;
589 int i;
591 if (level == 0) {
592 *paddr = uc->Eip;
593 return 0;
594 } else {
595 fp = uc->Ebp;
596 for(i=1;i<level;i++) {
597 /* XXX: check address validity with program info */
598 if (fp <= 0x1000 || fp >= 0xc0000000)
599 return -1;
600 fp = ((uplong*)fp)[0];
602 *paddr = ((uplong*)fp)[1];
603 return 0;
607 #undef Eip
608 #undef Ebp
610 #endif /* _WIN32 */
611 #endif /* CONFIG_TCC_BACKTRACE */
612 /* ------------------------------------------------------------- */
614 #ifdef CONFIG_TCC_STATIC
616 #define RTLD_LAZY 0x001
617 #define RTLD_NOW 0x002
618 #define RTLD_GLOBAL 0x100
619 #define RTLD_DEFAULT NULL
621 /* dummy function for profiling */
622 void *dlopen(const char *filename, int flag)
624 return NULL;
627 void dlclose(void *p)
631 const char *dlerror(void)
633 return "error";
636 typedef struct TCCSyms {
637 char *str;
638 void *ptr;
639 } TCCSyms;
641 #define TCCSYM(a) { #a, &a, },
643 /* add the symbol you want here if no dynamic linking is done */
644 static TCCSyms tcc_syms[] = {
645 #if !defined(CONFIG_TCCBOOT)
646 TCCSYM(printf)
647 TCCSYM(fprintf)
648 TCCSYM(fopen)
649 TCCSYM(fclose)
650 #endif
651 { NULL, NULL },
654 void *resolve_sym(TCCState *s1, const char *symbol)
656 TCCSyms *p;
657 p = tcc_syms;
658 while (p->str != NULL) {
659 if (!strcmp(p->str, symbol))
660 return p->ptr;
661 p++;
663 return NULL;
666 #elif !defined(_WIN32)
668 void *resolve_sym(TCCState *s1, const char *sym)
670 return dlsym(RTLD_DEFAULT, sym);
673 #endif /* CONFIG_TCC_STATIC */
675 /* ------------------------------------------------------------- */