1 | /* |
2 | * TCC - Tiny C Compiler - Support for -run switch |
3 | * |
4 | * Copyright (c) 2001-2004 Fabrice Bellard |
5 | * |
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. |
10 | * |
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. |
15 | * |
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 |
19 | */ |
20 | |
21 | #include "tcc.h" |
22 | |
23 | /* only native compiler supports -run */ |
24 | #ifdef TCC_IS_NATIVE |
25 | |
26 | #ifdef CONFIG_TCC_BACKTRACE |
27 | typedef struct rt_context |
28 | { |
29 | /* --> tccelf.c:tcc_add_btstub wants those below in that order: */ |
30 | Stab_Sym *stab_sym, *stab_sym_end; |
31 | char *stab_str; |
32 | ElfW(Sym) *esym_start, *esym_end; |
33 | char *elf_str; |
34 | addr_t prog_base; |
35 | void *bounds_start; |
36 | struct rt_context *next; |
37 | /* <-- */ |
38 | int num_callers; |
39 | addr_t ip, fp, sp; |
40 | void *top_func; |
41 | jmp_buf jmp_buf; |
42 | char do_jmp; |
43 | } rt_context; |
44 | |
45 | static rt_context g_rtctxt; |
46 | static void set_exception_handler(void); |
47 | static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap); |
48 | static void rt_exit(int code); |
49 | #endif /* CONFIG_TCC_BACKTRACE */ |
50 | |
51 | /* defined when included from lib/bt-exe.c */ |
52 | #ifndef CONFIG_TCC_BACKTRACE_ONLY |
53 | |
54 | #ifndef _WIN32 |
55 | # include <sys/mman.h> |
56 | #endif |
57 | |
58 | static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length); |
59 | static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff); |
60 | |
61 | #ifdef _WIN64 |
62 | static void *win64_add_function_table(TCCState *s1); |
63 | static void win64_del_function_table(void *); |
64 | #endif |
65 | |
66 | /* ------------------------------------------------------------- */ |
67 | /* Do all relocations (needed before using tcc_get_symbol()) |
68 | Returns -1 on error. */ |
69 | |
70 | LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr) |
71 | { |
72 | int size; |
73 | addr_t ptr_diff = 0; |
74 | |
75 | if (TCC_RELOCATE_AUTO != ptr) |
76 | return tcc_relocate_ex(s1, ptr, 0); |
77 | |
78 | size = tcc_relocate_ex(s1, NULL, 0); |
79 | if (size < 0) |
80 | return -1; |
81 | |
82 | #ifdef HAVE_SELINUX |
83 | { |
84 | /* Using mmap instead of malloc */ |
85 | void *prx; |
86 | char tmpfname[] = "/tmp/.tccrunXXXXXX" ; |
87 | int fd = mkstemp(tmpfname); |
88 | unlink(tmpfname); |
89 | ftruncate(fd, size); |
90 | |
91 | ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); |
92 | prx = mmap (NULL, size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0); |
93 | if (ptr == MAP_FAILED || prx == MAP_FAILED) |
94 | tcc_error("tccrun: could not map memory" ); |
95 | dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size); |
96 | dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx); |
97 | ptr_diff = (char*)prx - (char*)ptr; |
98 | close(fd); |
99 | } |
100 | #else |
101 | ptr = tcc_malloc(size); |
102 | #endif |
103 | tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */ |
104 | dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr); |
105 | return 0; |
106 | } |
107 | |
108 | ST_FUNC void tcc_run_free(TCCState *s1) |
109 | { |
110 | int i; |
111 | |
112 | for (i = 0; i < s1->nb_runtime_mem; ++i) { |
113 | #ifdef HAVE_SELINUX |
114 | unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++]; |
115 | munmap(s1->runtime_mem[i++], size); |
116 | munmap(s1->runtime_mem[i], size); |
117 | #else |
118 | #ifdef _WIN64 |
119 | win64_del_function_table(*(void**)s1->runtime_mem[i]); |
120 | #endif |
121 | tcc_free(s1->runtime_mem[i]); |
122 | #endif |
123 | } |
124 | tcc_free(s1->runtime_mem); |
125 | } |
126 | |
127 | static void run_cdtors(TCCState *s1, const char *start, const char *end, |
128 | int argc, char **argv, char **envp) |
129 | { |
130 | void **a = (void **)get_sym_addr(s1, start, 0, 0); |
131 | void **b = (void **)get_sym_addr(s1, end, 0, 0); |
132 | while (a != b) |
133 | ((void(*)(int, char **, char **))*a++)(argc, argv, envp); |
134 | } |
135 | |
136 | /* launch the compiled program with the given arguments */ |
137 | LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) |
138 | { |
139 | int (*prog_main)(int, char **, char **), ret; |
140 | #ifdef CONFIG_TCC_BACKTRACE |
141 | rt_context *rc = &g_rtctxt; |
142 | #endif |
143 | # if defined(__APPLE__) |
144 | char **envp = NULL; |
145 | #else |
146 | char **envp = environ; |
147 | #endif |
148 | |
149 | s1->runtime_main = s1->nostdlib ? "_start" : "main" ; |
150 | if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1)) |
151 | return 0; |
152 | #ifdef CONFIG_TCC_BACKTRACE |
153 | if (s1->do_debug) |
154 | tcc_add_symbol(s1, "exit" , rt_exit); |
155 | #endif |
156 | if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0) |
157 | return -1; |
158 | prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1); |
159 | |
160 | #ifdef CONFIG_TCC_BACKTRACE |
161 | memset(rc, 0, sizeof *rc); |
162 | if (s1->do_debug) { |
163 | void *p; |
164 | rc->stab_sym = (Stab_Sym *)stab_section->data; |
165 | rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset); |
166 | rc->stab_str = (char *)stab_section->link->data; |
167 | rc->esym_start = (ElfW(Sym) *)(symtab_section->data); |
168 | rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset); |
169 | rc->elf_str = (char *)symtab_section->link->data; |
170 | #if PTR_SIZE == 8 |
171 | rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL; |
172 | #endif |
173 | rc->top_func = tcc_get_symbol(s1, "main" ); |
174 | rc->num_callers = s1->rt_num_callers; |
175 | rc->do_jmp = 1; |
176 | if ((p = tcc_get_symbol(s1, "__rt_error" ))) |
177 | *(void**)p = _rt_error; |
178 | #ifdef CONFIG_TCC_BCHECK |
179 | if (s1->do_bounds_check) { |
180 | if ((p = tcc_get_symbol(s1, "__bound_init" ))) |
181 | ((void(*)(void*, int))p)(bounds_section->data, 1); |
182 | } |
183 | #endif |
184 | set_exception_handler(); |
185 | } |
186 | #endif |
187 | |
188 | errno = 0; /* clean errno value */ |
189 | fflush(stdout); |
190 | fflush(stderr); |
191 | /* These aren't C symbols, so don't need leading underscore handling. */ |
192 | run_cdtors(s1, "__init_array_start" , "__init_array_end" , argc, argv, envp); |
193 | #ifdef CONFIG_TCC_BACKTRACE |
194 | if (!rc->do_jmp || !(ret = setjmp(rc->jmp_buf))) |
195 | #endif |
196 | { |
197 | ret = prog_main(argc, argv, envp); |
198 | } |
199 | run_cdtors(s1, "__fini_array_start" , "__fini_array_end" , 0, NULL, NULL); |
200 | if ((s1->dflag & 16) && ret) |
201 | fprintf(s1->ppfp, "[returns %d]\n" , ret), fflush(s1->ppfp); |
202 | return ret; |
203 | } |
204 | |
205 | #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 |
206 | /* To avoid that x86 processors would reload cached instructions |
207 | each time when data is written in the near, we need to make |
208 | sure that code and data do not share the same 64 byte unit */ |
209 | #define RUN_SECTION_ALIGNMENT 63 |
210 | #else |
211 | #define RUN_SECTION_ALIGNMENT 0 |
212 | #endif |
213 | |
214 | /* relocate code. Return -1 on error, required size if ptr is NULL, |
215 | otherwise copy code into buffer passed by the caller */ |
216 | static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff) |
217 | { |
218 | Section *s; |
219 | unsigned offset, length, align, max_align, i, k, f; |
220 | addr_t mem, addr; |
221 | |
222 | if (NULL == ptr) { |
223 | s1->nb_errors = 0; |
224 | #ifdef TCC_TARGET_PE |
225 | pe_output_file(s1, NULL); |
226 | #else |
227 | tcc_add_runtime(s1); |
228 | resolve_common_syms(s1); |
229 | build_got_entries(s1); |
230 | #endif |
231 | if (s1->nb_errors) |
232 | return -1; |
233 | } |
234 | |
235 | offset = max_align = 0, mem = (addr_t)ptr; |
236 | #ifdef _WIN64 |
237 | offset += sizeof (void*); /* space for function_table pointer */ |
238 | #endif |
239 | for (k = 0; k < 2; ++k) { |
240 | f = 0, addr = k ? mem : mem + ptr_diff; |
241 | for(i = 1; i < s1->nb_sections; i++) { |
242 | s = s1->sections[i]; |
243 | if (0 == (s->sh_flags & SHF_ALLOC)) |
244 | continue; |
245 | if (k != !(s->sh_flags & SHF_EXECINSTR)) |
246 | continue; |
247 | align = s->sh_addralign - 1; |
248 | if (++f == 1 && align < RUN_SECTION_ALIGNMENT) |
249 | align = RUN_SECTION_ALIGNMENT; |
250 | if (max_align < align) |
251 | max_align = align; |
252 | offset += -(addr + offset) & align; |
253 | s->sh_addr = mem ? addr + offset : 0; |
254 | offset += s->data_offset; |
255 | #if 0 |
256 | if (mem) |
257 | printf("%-16s %p len %04x align %2d\n" , |
258 | s->name, (void*)s->sh_addr, (unsigned)s->data_offset, align + 1); |
259 | #endif |
260 | } |
261 | } |
262 | |
263 | /* relocate symbols */ |
264 | relocate_syms(s1, s1->symtab, !(s1->nostdlib)); |
265 | if (s1->nb_errors) |
266 | return -1; |
267 | |
268 | if (0 == mem) |
269 | return offset + max_align; |
270 | |
271 | #ifdef TCC_TARGET_PE |
272 | s1->pe_imagebase = mem; |
273 | #endif |
274 | |
275 | /* relocate each section */ |
276 | for(i = 1; i < s1->nb_sections; i++) { |
277 | s = s1->sections[i]; |
278 | if (s->reloc) |
279 | relocate_section(s1, s); |
280 | } |
281 | #if !defined(TCC_TARGET_PE) || defined(TCC_TARGET_MACHO) |
282 | relocate_plt(s1); |
283 | #endif |
284 | |
285 | for(i = 1; i < s1->nb_sections; i++) { |
286 | s = s1->sections[i]; |
287 | if (0 == (s->sh_flags & SHF_ALLOC)) |
288 | continue; |
289 | length = s->data_offset; |
290 | ptr = (void*)s->sh_addr; |
291 | if (s->sh_flags & SHF_EXECINSTR) |
292 | ptr = (char*)((addr_t)ptr - ptr_diff); |
293 | if (NULL == s->data || s->sh_type == SHT_NOBITS) |
294 | memset(ptr, 0, length); |
295 | else |
296 | memcpy(ptr, s->data, length); |
297 | /* mark executable sections as executable in memory */ |
298 | if (s->sh_flags & SHF_EXECINSTR) |
299 | set_pages_executable(s1, (char*)((addr_t)ptr + ptr_diff), length); |
300 | } |
301 | |
302 | #ifdef _WIN64 |
303 | *(void**)mem = win64_add_function_table(s1); |
304 | #endif |
305 | |
306 | return 0; |
307 | } |
308 | |
309 | /* ------------------------------------------------------------- */ |
310 | /* allow to run code in memory */ |
311 | |
312 | static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length) |
313 | { |
314 | #ifdef _WIN32 |
315 | unsigned long old_protect; |
316 | VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect); |
317 | #else |
318 | void __clear_cache(void *beginning, void *end); |
319 | # ifndef HAVE_SELINUX |
320 | addr_t start, end; |
321 | # ifndef PAGESIZE |
322 | # define PAGESIZE 4096 |
323 | # endif |
324 | start = (addr_t)ptr & ~(PAGESIZE - 1); |
325 | end = (addr_t)ptr + length; |
326 | end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1); |
327 | if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC)) |
328 | tcc_error("mprotect failed: did you mean to configure --with-selinux?" ); |
329 | # endif |
330 | # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64 |
331 | __clear_cache(ptr, (char *)ptr + length); |
332 | # endif |
333 | #endif |
334 | } |
335 | |
336 | #ifdef _WIN64 |
337 | static void *win64_add_function_table(TCCState *s1) |
338 | { |
339 | void *p = NULL; |
340 | if (s1->uw_pdata) { |
341 | p = (void*)s1->uw_pdata->sh_addr; |
342 | RtlAddFunctionTable( |
343 | (RUNTIME_FUNCTION*)p, |
344 | s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION), |
345 | s1->pe_imagebase |
346 | ); |
347 | s1->uw_pdata = NULL; |
348 | } |
349 | return p; |
350 | } |
351 | |
352 | static void win64_del_function_table(void *p) |
353 | { |
354 | if (p) { |
355 | RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p); |
356 | } |
357 | } |
358 | #endif |
359 | #endif //ndef CONFIG_TCC_BACKTRACE_ONLY |
360 | /* ------------------------------------------------------------- */ |
361 | #ifdef CONFIG_TCC_BACKTRACE |
362 | |
363 | static int rt_vprintf(const char *fmt, va_list ap) |
364 | { |
365 | int ret = vfprintf(stderr, fmt, ap); |
366 | fflush(stderr); |
367 | return ret; |
368 | } |
369 | |
370 | static int rt_printf(const char *fmt, ...) |
371 | { |
372 | va_list ap; |
373 | int r; |
374 | va_start(ap, fmt); |
375 | r = rt_vprintf(fmt, ap); |
376 | va_end(ap); |
377 | return r; |
378 | } |
379 | |
380 | #define INCLUDE_STACK_SIZE 32 |
381 | |
382 | /* print the position in the source file of PC value 'pc' by reading |
383 | the stabs debug information */ |
384 | static addr_t rt_printline (rt_context *rc, addr_t wanted_pc, |
385 | const char *msg, const char *skip) |
386 | { |
387 | char func_name[128]; |
388 | addr_t func_addr, last_pc, pc; |
389 | const char *incl_files[INCLUDE_STACK_SIZE]; |
390 | int incl_index, last_incl_index, len, last_line_num, i; |
391 | const char *str, *p; |
392 | ElfW(Sym) *esym; |
393 | Stab_Sym *sym; |
394 | |
395 | next: |
396 | func_name[0] = '\0'; |
397 | func_addr = 0; |
398 | incl_index = 0; |
399 | last_pc = (addr_t)-1; |
400 | last_line_num = 1; |
401 | last_incl_index = 0; |
402 | |
403 | for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) { |
404 | str = rc->stab_str + sym->n_strx; |
405 | pc = sym->n_value; |
406 | |
407 | switch(sym->n_type) { |
408 | case N_SLINE: |
409 | if (func_addr) |
410 | goto rel_pc; |
411 | case N_SO: |
412 | case N_SOL: |
413 | goto abs_pc; |
414 | case N_FUN: |
415 | if (sym->n_strx == 0) /* end of function */ |
416 | goto rel_pc; |
417 | abs_pc: |
418 | #if PTR_SIZE == 8 |
419 | /* Stab_Sym.n_value is only 32bits */ |
420 | pc += rc->prog_base; |
421 | #endif |
422 | goto check_pc; |
423 | rel_pc: |
424 | pc += func_addr; |
425 | check_pc: |
426 | if (pc >= wanted_pc && wanted_pc >= last_pc) |
427 | goto found; |
428 | break; |
429 | } |
430 | |
431 | switch(sym->n_type) { |
432 | /* function start or end */ |
433 | case N_FUN: |
434 | if (sym->n_strx == 0) |
435 | goto reset_func; |
436 | p = strchr(str, ':'); |
437 | if (0 == p || (len = p - str + 1, len > sizeof func_name)) |
438 | len = sizeof func_name; |
439 | pstrcpy(func_name, len, str); |
440 | func_addr = pc; |
441 | break; |
442 | /* line number info */ |
443 | case N_SLINE: |
444 | last_pc = pc; |
445 | last_line_num = sym->n_desc; |
446 | last_incl_index = incl_index; |
447 | break; |
448 | /* include files */ |
449 | case N_BINCL: |
450 | if (incl_index < INCLUDE_STACK_SIZE) |
451 | incl_files[incl_index++] = str; |
452 | break; |
453 | case N_EINCL: |
454 | if (incl_index > 1) |
455 | incl_index--; |
456 | break; |
457 | /* start/end of translation unit */ |
458 | case N_SO: |
459 | incl_index = 0; |
460 | if (sym->n_strx) { |
461 | /* do not add path */ |
462 | len = strlen(str); |
463 | if (len > 0 && str[len - 1] != '/') |
464 | incl_files[incl_index++] = str; |
465 | } |
466 | reset_func: |
467 | func_name[0] = '\0'; |
468 | func_addr = 0; |
469 | last_pc = (addr_t)-1; |
470 | break; |
471 | /* alternative file name (from #line or #include directives) */ |
472 | case N_SOL: |
473 | if (incl_index) |
474 | incl_files[incl_index-1] = str; |
475 | break; |
476 | } |
477 | } |
478 | |
479 | func_name[0] = '\0'; |
480 | func_addr = 0; |
481 | last_incl_index = 0; |
482 | |
483 | /* we try symtab symbols (no line number info) */ |
484 | for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) { |
485 | int type = ELFW(ST_TYPE)(esym->st_info); |
486 | if (type == STT_FUNC || type == STT_GNU_IFUNC) { |
487 | if (wanted_pc >= esym->st_value && |
488 | wanted_pc < esym->st_value + esym->st_size) { |
489 | pstrcpy(func_name, sizeof(func_name), |
490 | rc->elf_str + esym->st_name); |
491 | func_addr = esym->st_value; |
492 | goto found; |
493 | } |
494 | } |
495 | } |
496 | |
497 | if ((rc = rc->next)) |
498 | goto next; |
499 | |
500 | found: |
501 | i = last_incl_index; |
502 | if (i > 0) { |
503 | str = incl_files[--i]; |
504 | if (skip[0] && strstr(str, skip)) |
505 | return (addr_t)-1; |
506 | rt_printf("%s:%d: " , str, last_line_num); |
507 | } else |
508 | rt_printf("%08llx : " , (long long)wanted_pc); |
509 | rt_printf("%s %s" , msg, func_name[0] ? func_name : "???" ); |
510 | #if 0 |
511 | if (--i >= 0) { |
512 | rt_printf(" (included from " ); |
513 | for (;;) { |
514 | rt_printf("%s" , incl_files[i]); |
515 | if (--i < 0) |
516 | break; |
517 | rt_printf(", " ); |
518 | } |
519 | rt_printf(")" ); |
520 | } |
521 | #endif |
522 | return func_addr; |
523 | } |
524 | |
525 | static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level); |
526 | |
527 | static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap) |
528 | { |
529 | rt_context *rc = &g_rtctxt; |
530 | addr_t pc = 0; |
531 | char skip[100]; |
532 | int i, level, ret, n; |
533 | const char *a, *b, *msg; |
534 | |
535 | if (fp) { |
536 | /* we're called from tcc_backtrace. */ |
537 | rc->fp = (addr_t)fp; |
538 | rc->ip = (addr_t)ip; |
539 | msg = "" ; |
540 | } else { |
541 | /* we're called from signal/exception handler */ |
542 | msg = "RUNTIME ERROR: " ; |
543 | } |
544 | |
545 | skip[0] = 0; |
546 | /* If fmt is like "^file.c^..." then skip calls from 'file.c' */ |
547 | if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) { |
548 | memcpy(skip, a, b - a), skip[b - a] = 0; |
549 | fmt = b + 1; |
550 | } |
551 | |
552 | n = rc->num_callers ? rc->num_callers : 6; |
553 | for (i = level = 0; level < n; i++) { |
554 | ret = rt_get_caller_pc(&pc, rc, i); |
555 | a = "%s" ; |
556 | if (ret != -1) { |
557 | pc = rt_printline(rc, pc, level ? "by" : "at" , skip); |
558 | if (pc == (addr_t)-1) |
559 | continue; |
560 | a = ": %s" ; |
561 | } |
562 | if (level == 0) { |
563 | rt_printf(a, msg); |
564 | rt_vprintf(fmt, ap); |
565 | } else if (ret == -1) |
566 | break; |
567 | rt_printf("\n" ); |
568 | if (ret == -1 || (pc == (addr_t)rc->top_func && pc)) |
569 | break; |
570 | ++level; |
571 | } |
572 | |
573 | rc->ip = rc->fp = 0; |
574 | return 0; |
575 | } |
576 | |
577 | /* emit a run time error at position 'pc' */ |
578 | static int rt_error(const char *fmt, ...) |
579 | { |
580 | va_list ap; |
581 | int ret; |
582 | va_start(ap, fmt); |
583 | ret = _rt_error(0, 0, fmt, ap); |
584 | va_end(ap); |
585 | return ret; |
586 | } |
587 | |
588 | static void rt_exit(int code) |
589 | { |
590 | rt_context *rc = &g_rtctxt; |
591 | if (rc->do_jmp) |
592 | longjmp(rc->jmp_buf, code ? code : 256); |
593 | exit(code); |
594 | } |
595 | |
596 | /* ------------------------------------------------------------- */ |
597 | |
598 | #ifndef _WIN32 |
599 | # include <signal.h> |
600 | # ifndef __OpenBSD__ |
601 | # include <sys/ucontext.h> |
602 | # endif |
603 | #else |
604 | # define ucontext_t CONTEXT |
605 | #endif |
606 | |
607 | /* translate from ucontext_t* to internal rt_context * */ |
608 | static void rt_getcontext(ucontext_t *uc, rt_context *rc) |
609 | { |
610 | #if defined _WIN64 |
611 | rc->ip = uc->Rip; |
612 | rc->fp = uc->Rbp; |
613 | rc->sp = uc->Rsp; |
614 | #elif defined _WIN32 |
615 | rc->ip = uc->Eip; |
616 | rc->fp = uc->Ebp; |
617 | rc->sp = uc->Esp; |
618 | #elif defined __i386__ |
619 | # if defined(__APPLE__) |
620 | rc->ip = uc->uc_mcontext->__ss.__eip; |
621 | rc->fp = uc->uc_mcontext->__ss.__ebp; |
622 | # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
623 | rc->ip = uc->uc_mcontext.mc_eip; |
624 | rc->fp = uc->uc_mcontext.mc_ebp; |
625 | # elif defined(__dietlibc__) |
626 | rc->ip = uc->uc_mcontext.eip; |
627 | rc->fp = uc->uc_mcontext.ebp; |
628 | # elif defined(__NetBSD__) |
629 | rc->ip = uc->uc_mcontext.__gregs[_REG_EIP]; |
630 | rc->fp = uc->uc_mcontext.__gregs[_REG_EBP]; |
631 | # elif defined(__OpenBSD__) |
632 | rc->ip = uc->sc_eip; |
633 | rc->fp = uc->sc_ebp; |
634 | # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */ |
635 | rc->ip = uc->uc_mcontext.gregs[EIP]; |
636 | rc->fp = uc->uc_mcontext.gregs[EBP]; |
637 | # else |
638 | rc->ip = uc->uc_mcontext.gregs[REG_EIP]; |
639 | rc->fp = uc->uc_mcontext.gregs[REG_EBP]; |
640 | # endif |
641 | #elif defined(__x86_64__) |
642 | # if defined(__APPLE__) |
643 | rc->ip = uc->uc_mcontext->__ss.__rip; |
644 | rc->fp = uc->uc_mcontext->__ss.__rbp; |
645 | # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
646 | rc->ip = uc->uc_mcontext.mc_rip; |
647 | rc->fp = uc->uc_mcontext.mc_rbp; |
648 | # elif defined(__NetBSD__) |
649 | rc->ip = uc->uc_mcontext.__gregs[_REG_RIP]; |
650 | rc->fp = uc->uc_mcontext.__gregs[_REG_RBP]; |
651 | # else |
652 | rc->ip = uc->uc_mcontext.gregs[REG_RIP]; |
653 | rc->fp = uc->uc_mcontext.gregs[REG_RBP]; |
654 | # endif |
655 | #elif defined(__arm__) |
656 | rc->ip = uc->uc_mcontext.arm_pc; |
657 | rc->fp = uc->uc_mcontext.arm_fp; |
658 | #elif defined(__aarch64__) |
659 | rc->ip = uc->uc_mcontext.pc; |
660 | rc->fp = uc->uc_mcontext.regs[29]; |
661 | #elif defined(__riscv) |
662 | rc->ip = uc->uc_mcontext.__gregs[REG_PC]; |
663 | rc->fp = uc->uc_mcontext.__gregs[REG_S0]; |
664 | #endif |
665 | } |
666 | |
667 | /* ------------------------------------------------------------- */ |
668 | #ifndef _WIN32 |
669 | /* signal handler for fatal errors */ |
670 | static void sig_error(int signum, siginfo_t *siginf, void *puc) |
671 | { |
672 | rt_context *rc = &g_rtctxt; |
673 | rt_getcontext(puc, rc); |
674 | |
675 | switch(signum) { |
676 | case SIGFPE: |
677 | switch(siginf->si_code) { |
678 | case FPE_INTDIV: |
679 | case FPE_FLTDIV: |
680 | rt_error("division by zero" ); |
681 | break; |
682 | default: |
683 | rt_error("floating point exception" ); |
684 | break; |
685 | } |
686 | break; |
687 | case SIGBUS: |
688 | case SIGSEGV: |
689 | rt_error("invalid memory access" ); |
690 | break; |
691 | case SIGILL: |
692 | rt_error("illegal instruction" ); |
693 | break; |
694 | case SIGABRT: |
695 | rt_error("abort() called" ); |
696 | break; |
697 | default: |
698 | rt_error("caught signal %d" , signum); |
699 | break; |
700 | } |
701 | rt_exit(255); |
702 | } |
703 | |
704 | #ifndef SA_SIGINFO |
705 | # define SA_SIGINFO 0x00000004u |
706 | #endif |
707 | |
708 | /* Generate a stack backtrace when a CPU exception occurs. */ |
709 | static void set_exception_handler(void) |
710 | { |
711 | struct sigaction sigact; |
712 | /* install TCC signal handlers to print debug info on fatal |
713 | runtime errors */ |
714 | sigact.sa_flags = SA_SIGINFO | SA_RESETHAND; |
715 | #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes |
716 | sigact.sa_flags |= SA_ONSTACK; |
717 | #endif |
718 | sigact.sa_sigaction = sig_error; |
719 | sigemptyset(&sigact.sa_mask); |
720 | sigaction(SIGFPE, &sigact, NULL); |
721 | sigaction(SIGILL, &sigact, NULL); |
722 | sigaction(SIGSEGV, &sigact, NULL); |
723 | sigaction(SIGBUS, &sigact, NULL); |
724 | sigaction(SIGABRT, &sigact, NULL); |
725 | #if 0//def SIGSTKSZ |
726 | /* This allows stack overflow to be reported instead of a SEGV */ |
727 | { |
728 | stack_t ss; |
729 | static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16))); |
730 | |
731 | ss.ss_sp = stack; |
732 | ss.ss_size = SIGSTKSZ; |
733 | ss.ss_flags = 0; |
734 | sigaltstack(&ss, NULL); |
735 | } |
736 | #endif |
737 | } |
738 | |
739 | #else /* WIN32 */ |
740 | |
741 | /* signal handler for fatal errors */ |
742 | static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) |
743 | { |
744 | rt_context *rc = &g_rtctxt; |
745 | unsigned code; |
746 | rt_getcontext(ex_info->ContextRecord, rc); |
747 | |
748 | switch (code = ex_info->ExceptionRecord->ExceptionCode) { |
749 | case EXCEPTION_ACCESS_VIOLATION: |
750 | rt_error("invalid memory access" ); |
751 | break; |
752 | case EXCEPTION_STACK_OVERFLOW: |
753 | rt_error("stack overflow" ); |
754 | break; |
755 | case EXCEPTION_INT_DIVIDE_BY_ZERO: |
756 | rt_error("division by zero" ); |
757 | break; |
758 | case EXCEPTION_BREAKPOINT: |
759 | case EXCEPTION_SINGLE_STEP: |
760 | rc->ip = *(addr_t*)rc->sp; |
761 | rt_error("breakpoint/single-step exception:" ); |
762 | return EXCEPTION_CONTINUE_SEARCH; |
763 | default: |
764 | rt_error("caught exception %08x" , code); |
765 | break; |
766 | } |
767 | if (rc->do_jmp) |
768 | rt_exit(255); |
769 | return EXCEPTION_EXECUTE_HANDLER; |
770 | } |
771 | |
772 | /* Generate a stack backtrace when a CPU exception occurs. */ |
773 | static void set_exception_handler(void) |
774 | { |
775 | SetUnhandledExceptionFilter(cpu_exception_handler); |
776 | } |
777 | |
778 | #endif |
779 | |
780 | /* ------------------------------------------------------------- */ |
781 | /* return the PC at frame level 'level'. Return negative if not found */ |
782 | #if defined(__i386__) || defined(__x86_64__) |
783 | static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) |
784 | { |
785 | addr_t ip, fp; |
786 | if (level == 0) { |
787 | ip = rc->ip; |
788 | } else { |
789 | ip = 0; |
790 | fp = rc->fp; |
791 | while (--level) { |
792 | /* XXX: check address validity with program info */ |
793 | if (fp <= 0x1000) |
794 | break; |
795 | fp = ((addr_t *)fp)[0]; |
796 | } |
797 | if (fp > 0x1000) |
798 | ip = ((addr_t *)fp)[1]; |
799 | } |
800 | if (ip <= 0x1000) |
801 | return -1; |
802 | *paddr = ip; |
803 | return 0; |
804 | } |
805 | |
806 | #elif defined(__arm__) |
807 | static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) |
808 | { |
809 | /* XXX: only supports linux */ |
810 | #if !defined(__linux__) |
811 | return -1; |
812 | #else |
813 | if (level == 0) { |
814 | *paddr = rc->ip; |
815 | } else { |
816 | addr_t fp = rc->fp; |
817 | while (--level) |
818 | fp = ((addr_t *)fp)[0]; |
819 | *paddr = ((addr_t *)fp)[2]; |
820 | } |
821 | return 0; |
822 | #endif |
823 | } |
824 | |
825 | #elif defined(__aarch64__) |
826 | static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) |
827 | { |
828 | if (level == 0) { |
829 | *paddr = rc->ip; |
830 | } else { |
831 | addr_t *fp = (addr_t*)rc->fp; |
832 | while (--level) |
833 | fp = (addr_t *)fp[0]; |
834 | *paddr = fp[1]; |
835 | } |
836 | return 0; |
837 | } |
838 | |
839 | #elif defined(__riscv) |
840 | static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) |
841 | { |
842 | if (level == 0) { |
843 | *paddr = rc->ip; |
844 | } else { |
845 | addr_t *fp = (addr_t*)rc->fp; |
846 | while (--level && fp >= (addr_t*)0x1000) |
847 | fp = (addr_t *)fp[-2]; |
848 | if (fp < (addr_t*)0x1000) |
849 | return -1; |
850 | *paddr = fp[-1]; |
851 | } |
852 | return 0; |
853 | } |
854 | |
855 | #else |
856 | #warning add arch specific rt_get_caller_pc() |
857 | static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) |
858 | { |
859 | return -1; |
860 | } |
861 | |
862 | #endif |
863 | #endif /* CONFIG_TCC_BACKTRACE */ |
864 | /* ------------------------------------------------------------- */ |
865 | #ifdef CONFIG_TCC_STATIC |
866 | |
867 | /* dummy function for profiling */ |
868 | ST_FUNC void *dlopen(const char *filename, int flag) |
869 | { |
870 | return NULL; |
871 | } |
872 | |
873 | ST_FUNC void dlclose(void *p) |
874 | { |
875 | } |
876 | |
877 | ST_FUNC const char *dlerror(void) |
878 | { |
879 | return "error" ; |
880 | } |
881 | |
882 | typedef struct TCCSyms { |
883 | char *str; |
884 | void *ptr; |
885 | } TCCSyms; |
886 | |
887 | |
888 | /* add the symbol you want here if no dynamic linking is done */ |
889 | static TCCSyms tcc_syms[] = { |
890 | #if !defined(CONFIG_TCCBOOT) |
891 | #define TCCSYM(a) { #a, &a, }, |
892 | TCCSYM(printf) |
893 | TCCSYM(fprintf) |
894 | TCCSYM(fopen) |
895 | TCCSYM(fclose) |
896 | #undef TCCSYM |
897 | #endif |
898 | { NULL, NULL }, |
899 | }; |
900 | |
901 | ST_FUNC void *dlsym(void *handle, const char *symbol) |
902 | { |
903 | TCCSyms *p; |
904 | p = tcc_syms; |
905 | while (p->str != NULL) { |
906 | if (!strcmp(p->str, symbol)) |
907 | return p->ptr; |
908 | p++; |
909 | } |
910 | return NULL; |
911 | } |
912 | |
913 | #endif /* CONFIG_TCC_STATIC */ |
914 | #endif /* TCC_IS_NATIVE */ |
915 | /* ------------------------------------------------------------- */ |
916 | |