| 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 | |