tcc_relocate: revert to 0.9.24 behavior
[tinycc.git] / tccrun.c
blobd62e92a0c09e7e0f86337d8112e08bacf3f7bd72
1 /*
2 * TCC - Tiny C Compiler
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 /* tccrun.c - support for tcc -run */
24 /********************************************************/
25 #ifdef CONFIG_TCC_STATIC
27 #define RTLD_LAZY 0x001
28 #define RTLD_NOW 0x002
29 #define RTLD_GLOBAL 0x100
30 #define RTLD_DEFAULT NULL
32 /* dummy function for profiling */
33 void *dlopen(const char *filename, int flag)
35 return NULL;
38 void dlclose(void *p)
42 const char *dlerror(void)
44 return "error";
47 typedef struct TCCSyms {
48 char *str;
49 void *ptr;
50 } TCCSyms;
52 #define TCCSYM(a) { #a, &a, },
54 /* add the symbol you want here if no dynamic linking is done */
55 static TCCSyms tcc_syms[] = {
56 #if !defined(CONFIG_TCCBOOT)
57 TCCSYM(printf)
58 TCCSYM(fprintf)
59 TCCSYM(fopen)
60 TCCSYM(fclose)
61 #endif
62 { NULL, NULL },
65 void *resolve_sym(TCCState *s1, const char *symbol)
67 TCCSyms *p;
68 p = tcc_syms;
69 while (p->str != NULL) {
70 if (!strcmp(p->str, symbol))
71 return p->ptr;
72 p++;
74 return NULL;
77 #elif defined(_WIN32)
78 #define dlclose FreeLibrary
80 #else
81 #include <dlfcn.h>
83 void *resolve_sym(TCCState *s1, const char *sym)
85 return dlsym(RTLD_DEFAULT, sym);
88 #endif /* defined CONFIG_TCC_STATIC */
90 /********************************************************/
92 #ifdef CONFIG_TCC_BACKTRACE
94 /* print the position in the source file of PC value 'pc' by reading
95 the stabs debug information */
96 static unsigned long rt_printline(unsigned long wanted_pc)
98 Stab_Sym *sym, *sym_end;
99 char func_name[128], last_func_name[128];
100 unsigned long func_addr, last_pc, pc;
101 const char *incl_files[INCLUDE_STACK_SIZE];
102 int incl_index, len, last_line_num, i;
103 const char *str, *p;
105 fprintf(stderr, "0x%08lx:", wanted_pc);
107 func_name[0] = '\0';
108 func_addr = 0;
109 incl_index = 0;
110 last_func_name[0] = '\0';
111 last_pc = 0xffffffff;
112 last_line_num = 1;
113 sym = (Stab_Sym *)stab_section->data + 1;
114 sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
115 while (sym < sym_end) {
116 switch(sym->n_type) {
117 /* function start or end */
118 case N_FUN:
119 if (sym->n_strx == 0) {
120 /* we test if between last line and end of function */
121 pc = sym->n_value + func_addr;
122 if (wanted_pc >= last_pc && wanted_pc < pc)
123 goto found;
124 func_name[0] = '\0';
125 func_addr = 0;
126 } else {
127 str = stabstr_section->data + sym->n_strx;
128 p = strchr(str, ':');
129 if (!p) {
130 pstrcpy(func_name, sizeof(func_name), str);
131 } else {
132 len = p - str;
133 if (len > sizeof(func_name) - 1)
134 len = sizeof(func_name) - 1;
135 memcpy(func_name, str, len);
136 func_name[len] = '\0';
138 func_addr = sym->n_value;
140 break;
141 /* line number info */
142 case N_SLINE:
143 pc = sym->n_value + func_addr;
144 if (wanted_pc >= last_pc && wanted_pc < pc)
145 goto found;
146 last_pc = pc;
147 last_line_num = sym->n_desc;
148 /* XXX: slow! */
149 strcpy(last_func_name, func_name);
150 break;
151 /* include files */
152 case N_BINCL:
153 str = stabstr_section->data + sym->n_strx;
154 add_incl:
155 if (incl_index < INCLUDE_STACK_SIZE) {
156 incl_files[incl_index++] = str;
158 break;
159 case N_EINCL:
160 if (incl_index > 1)
161 incl_index--;
162 break;
163 case N_SO:
164 if (sym->n_strx == 0) {
165 incl_index = 0; /* end of translation unit */
166 } else {
167 str = stabstr_section->data + sym->n_strx;
168 /* do not add path */
169 len = strlen(str);
170 if (len > 0 && str[len - 1] != '/')
171 goto add_incl;
173 break;
175 sym++;
178 /* second pass: we try symtab symbols (no line number info) */
179 incl_index = 0;
181 ElfW(Sym) *sym, *sym_end;
182 int type;
184 sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
185 for(sym = (ElfW(Sym) *)symtab_section->data + 1;
186 sym < sym_end;
187 sym++) {
188 type = ELFW(ST_TYPE)(sym->st_info);
189 if (type == STT_FUNC) {
190 if (wanted_pc >= sym->st_value &&
191 wanted_pc < sym->st_value + sym->st_size) {
192 pstrcpy(last_func_name, sizeof(last_func_name),
193 strtab_section->data + sym->st_name);
194 func_addr = sym->st_value;
195 goto found;
200 /* did not find any info: */
201 fprintf(stderr, " ???\n");
202 return 0;
203 found:
204 if (last_func_name[0] != '\0') {
205 fprintf(stderr, " %s()", last_func_name);
207 if (incl_index > 0) {
208 fprintf(stderr, " (%s:%d",
209 incl_files[incl_index - 1], last_line_num);
210 for(i = incl_index - 2; i >= 0; i--)
211 fprintf(stderr, ", included from %s", incl_files[i]);
212 fprintf(stderr, ")");
214 fprintf(stderr, "\n");
215 return func_addr;
218 #ifdef __i386__
219 /* fix for glibc 2.1 */
220 #ifndef REG_EIP
221 #define REG_EIP EIP
222 #define REG_EBP EBP
223 #endif
225 /* return the PC at frame level 'level'. Return non zero if not found */
226 static int rt_get_caller_pc(unsigned long *paddr,
227 ucontext_t *uc, int level)
229 unsigned long fp;
230 int i;
232 if (level == 0) {
233 #if defined(__FreeBSD__)
234 *paddr = uc->uc_mcontext.mc_eip;
235 #elif defined(__dietlibc__)
236 *paddr = uc->uc_mcontext.eip;
237 #else
238 *paddr = uc->uc_mcontext.gregs[REG_EIP];
239 #endif
240 return 0;
241 } else {
242 #if defined(__FreeBSD__)
243 fp = uc->uc_mcontext.mc_ebp;
244 #elif defined(__dietlibc__)
245 fp = uc->uc_mcontext.ebp;
246 #else
247 fp = uc->uc_mcontext.gregs[REG_EBP];
248 #endif
249 for(i=1;i<level;i++) {
250 /* XXX: check address validity with program info */
251 if (fp <= 0x1000 || fp >= 0xc0000000)
252 return -1;
253 fp = ((unsigned long *)fp)[0];
255 *paddr = ((unsigned long *)fp)[1];
256 return 0;
259 #elif defined(__x86_64__)
260 /* return the PC at frame level 'level'. Return non zero if not found */
261 static int rt_get_caller_pc(unsigned long *paddr,
262 ucontext_t *uc, int level)
264 unsigned long fp;
265 int i;
267 if (level == 0) {
268 /* XXX: only support linux */
269 #if defined(__FreeBSD__)
270 *paddr = uc->uc_mcontext.mc_rip;
271 #else
272 *paddr = uc->uc_mcontext.gregs[REG_RIP];
273 #endif
274 return 0;
275 } else {
276 #if defined(__FreeBSD__)
277 fp = uc->uc_mcontext.mc_rbp;
278 #else
279 fp = uc->uc_mcontext.gregs[REG_RBP];
280 #endif
281 for(i=1;i<level;i++) {
282 /* XXX: check address validity with program info */
283 if (fp <= 0x1000)
284 return -1;
285 fp = ((unsigned long *)fp)[0];
287 *paddr = ((unsigned long *)fp)[1];
288 return 0;
291 #else
292 #warning add arch specific rt_get_caller_pc()
293 static int rt_get_caller_pc(unsigned long *paddr,
294 ucontext_t *uc, int level)
296 return -1;
298 #endif
300 /* emit a run time error at position 'pc' */
301 void rt_error(ucontext_t *uc, const char *fmt, ...)
303 va_list ap;
304 unsigned long pc;
305 int i;
307 va_start(ap, fmt);
308 fprintf(stderr, "Runtime error: ");
309 vfprintf(stderr, fmt, ap);
310 fprintf(stderr, "\n");
311 for(i=0;i<num_callers;i++) {
312 if (rt_get_caller_pc(&pc, uc, i) < 0)
313 break;
314 if (i == 0)
315 fprintf(stderr, "at ");
316 else
317 fprintf(stderr, "by ");
318 pc = rt_printline(pc);
319 if (pc == rt_prog_main && pc)
320 break;
322 exit(255);
323 va_end(ap);
326 /* signal handler for fatal errors */
327 static void sig_error(int signum, siginfo_t *siginf, void *puc)
329 ucontext_t *uc = puc;
331 switch(signum) {
332 case SIGFPE:
333 switch(siginf->si_code) {
334 case FPE_INTDIV:
335 case FPE_FLTDIV:
336 rt_error(uc, "division by zero");
337 break;
338 default:
339 rt_error(uc, "floating point exception");
340 break;
342 break;
343 case SIGBUS:
344 case SIGSEGV:
345 if (rt_bound_error_msg && *rt_bound_error_msg)
346 rt_error(uc, *rt_bound_error_msg);
347 else
348 rt_error(uc, "dereferencing invalid pointer");
349 break;
350 case SIGILL:
351 rt_error(uc, "illegal instruction");
352 break;
353 case SIGABRT:
354 rt_error(uc, "abort() called");
355 break;
356 default:
357 rt_error(uc, "caught signal %d", signum);
358 break;
360 exit(255);
363 #endif /* defined CONFIG_TCC_BACKTRACE */
365 /********************************************************/
367 void set_pages_executable(void *ptr, unsigned long length)
369 #ifdef _WIN32
370 unsigned long old_protect;
371 VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
372 #else
373 unsigned long start, end;
374 start = (unsigned long)ptr & ~(PAGESIZE - 1);
375 end = (unsigned long)ptr + length;
376 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
377 mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC);
378 #endif
381 /* relocate code. Return -1 on error, required size if ptr is NULL,
382 otherwise copy code into buffer passed by the caller */
383 static int tcc_relocate_ex(TCCState *s1, void *ptr)
385 Section *s;
386 unsigned long offset, length;
387 uplong mem;
388 int i;
390 if (0 == s1->runtime_added) {
391 s1->runtime_added = 1;
392 s1->nb_errors = 0;
393 #ifdef TCC_TARGET_PE
394 pe_output_file(s1, NULL);
395 #else
396 tcc_add_runtime(s1);
397 relocate_common_syms();
398 tcc_add_linker_symbols(s1);
399 build_got_entries(s1);
400 #endif
401 if (s1->nb_errors)
402 return -1;
405 offset = 0, mem = (uplong)ptr;
406 for(i = 1; i < s1->nb_sections; i++) {
407 s = s1->sections[i];
408 if (0 == (s->sh_flags & SHF_ALLOC))
409 continue;
410 length = s->data_offset;
411 s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0;
412 offset = (offset + length + 15) & ~15;
414 offset += 16;
416 /* relocate symbols */
417 relocate_syms(s1, 1);
418 if (s1->nb_errors)
419 return -1;
421 #ifndef TCC_TARGET_PE
422 #ifdef TCC_TARGET_X86_64
423 s1->runtime_plt_and_got_offset = 0;
424 s1->runtime_plt_and_got = (char *)(mem + offset);
425 /* double the size of the buffer for got and plt entries
426 XXX: calculate exact size for them? */
427 offset *= 2;
428 #endif
429 #endif
431 if (0 == mem)
432 return offset;
434 /* relocate each section */
435 for(i = 1; i < s1->nb_sections; i++) {
436 s = s1->sections[i];
437 if (s->reloc)
438 relocate_section(s1, s);
441 for(i = 1; i < s1->nb_sections; i++) {
442 s = s1->sections[i];
443 if (0 == (s->sh_flags & SHF_ALLOC))
444 continue;
445 length = s->data_offset;
446 // printf("%-12s %08x %04x\n", s->name, s->sh_addr, length);
447 ptr = (void*)(uplong)s->sh_addr;
448 if (NULL == s->data || s->sh_type == SHT_NOBITS)
449 memset(ptr, 0, length);
450 else
451 memcpy(ptr, s->data, length);
452 /* mark executable sections as executable in memory */
453 if (s->sh_flags & SHF_EXECINSTR)
454 set_pages_executable(ptr, length);
457 #ifndef TCC_TARGET_PE
458 #ifdef TCC_TARGET_X86_64
459 set_pages_executable(s1->runtime_plt_and_got,
460 s1->runtime_plt_and_got_offset);
461 #endif
462 #endif
463 return 0;
466 /* Do all relocations (needed before using tcc_get_symbol())
467 Returns -1 on error. */
468 int tcc_relocate(TCCState *s1)
470 int ret = tcc_relocate_ex(s1, NULL);
471 if (-1 == ret)
472 return ret;
473 s1->runtime_mem = tcc_malloc(ret);
474 return tcc_relocate_ex(s1, s1->runtime_mem);
477 /* launch the compiled program with the given arguments */
478 int tcc_run(TCCState *s1, int argc, char **argv)
480 int (*prog_main)(int, char **);
482 if (tcc_relocate(s1) < 0)
483 return -1;
485 prog_main = tcc_get_symbol_err(s1, "main");
487 if (s1->do_debug) {
488 #ifdef CONFIG_TCC_BACKTRACE
489 struct sigaction sigact;
490 /* install TCC signal handlers to print debug info on fatal
491 runtime errors */
492 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
493 sigact.sa_sigaction = sig_error;
494 sigemptyset(&sigact.sa_mask);
495 sigaction(SIGFPE, &sigact, NULL);
496 sigaction(SIGILL, &sigact, NULL);
497 sigaction(SIGSEGV, &sigact, NULL);
498 sigaction(SIGBUS, &sigact, NULL);
499 sigaction(SIGABRT, &sigact, NULL);
500 #else
501 error("debug mode not available");
502 #endif
505 #ifdef CONFIG_TCC_BCHECK
506 if (s1->do_bounds_check) {
507 void (*bound_init)(void);
508 void (*bound_exit)(void);
509 /* set error function */
510 rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
511 rt_prog_main = (unsigned long)prog_main;
512 /* XXX: use .init section so that it also work in binary ? */
513 bound_init = tcc_get_symbol_err(s1, "__bound_init");
514 bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
515 bound_init();
516 ret = (*prog_main)(argc, argv);
517 bound_exit();
518 } else
519 #endif
520 return (*prog_main)(argc, argv);
523 /********************************************************/