/* * TCC - Tiny C Compiler - Support for -run switch * * Copyright (c) 2001-2004 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "tcc.h" /* only native compiler supports -run */ #ifdef TCC_IS_NATIVE #ifndef _WIN32 # include <sys/mman.h> #endif #ifdef CONFIG_TCC_BACKTRACE # ifndef _WIN32 # include <signal.h> # ifndef __OpenBSD__ # include <sys/ucontext.h> # endif # else # define ucontext_t CONTEXT # endif ST_DATA int rt_num_callers = 6; ST_DATA const char **rt_bound_error_msg; ST_DATA void *rt_prog_main; static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level); static void rt_error(ucontext_t *uc, const char *fmt, ...); static void set_exception_handler(void); #endif static void set_pages_executable(void *ptr, unsigned long length); static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff); #ifdef _WIN64 static void *win64_add_function_table(TCCState *s1); static void win64_del_function_table(void *); #endif /* ------------------------------------------------------------- */ /* Do all relocations (needed before using tcc_get_symbol()) Returns -1 on error. */ LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr) { int size; addr_t ptr_diff = 0; if (TCC_RELOCATE_AUTO != ptr) return tcc_relocate_ex(s1, ptr, 0); size = tcc_relocate_ex(s1, NULL, 0); if (size < 0) return -1; #ifdef HAVE_SELINUX { /* Using mmap instead of malloc */ void *prx; char tmpfname[] = "/tmp/.tccrunXXXXXX"; int fd = mkstemp(tmpfname); unlink(tmpfname); ftruncate(fd, size); ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); prx = mmap (NULL, size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED || prx == MAP_FAILED) tcc_error("tccrun: could not map memory"); dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size); dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx); ptr_diff = (char*)prx - (char*)ptr; } #else ptr = tcc_malloc(size); #endif tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */ dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr); return 0; } ST_FUNC void tcc_run_free(TCCState *s1) { int i; for (i = 0; i < s1->nb_runtime_mem; ++i) { #ifdef HAVE_SELINUX unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++]; munmap(s1->runtime_mem[i++], size); munmap(s1->runtime_mem[i], size); #else #ifdef _WIN64 win64_del_function_table(*(void**)s1->runtime_mem[i]); #endif tcc_free(s1->runtime_mem[i]); #endif } tcc_free(s1->runtime_mem); } /* launch the compiled program with the given arguments */ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) <style>pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span><span class="k">discard</span><span class="w"> </span><span class="s2">"""</span> <span class="s"> output = "И\n"</span> <span class="s2">"""</span> <span class="k">let</span><span class="w"> </span><span class="n">s</span><span class="p">:</span><span class="w"> </span><span class="nb">string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"И</span><span class="se">\n</span><span class="s">"</span> <span class="k">let</span><span class="w"> </span><span class="n">cs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="n">cstring</span> <span class="n">echo</span><span class="w"> </span><span class="o">$</span><span class="n">s</span> </pre></div> </code></pre></td></tr></table> </div> <!-- class=content --> <div class='footer'>generated by <a href='https://git.causal.agency/cgit-pink/about/'>cgit-pink 1.4.1-2-gfad0</a> (<a href='https://git-scm.com/'>git 2.36.2.497.gbbea4dcf42</a>) at 2025-02-13 23:28:18 +0000</div> </div> <!-- id=cgit --> </body> </html> str, *p; Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym; int stab_len = 0; char *stab_str = NULL; if (stab_section) { stab_len = stab_section->data_offset; stab_sym = (Stab_Sym *)stab_section->data; stab_str = (char *) stabstr_section->data; } func_name[0] = '\0'; func_addr = 0; incl_index = 0; last_func_name[0] = '\0'; last_pc = (addr_t)-1; last_line_num = 1; if (!stab_sym) goto no_stabs; stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len); for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) { switch(sym->n_type) { /* function start or end */ case N_FUN: if (sym->n_strx == 0) { /* we test if between last line and end of function */ pc = sym->n_value + func_addr; if (wanted_pc >= last_pc && wanted_pc < pc) goto found; func_name[0] = '\0'; func_addr = 0; } else { str = stab_str + sym->n_strx; p = strchr(str, ':'); if (!p) { pstrcpy(func_name, sizeof(func_name), str); } else { len = p - str; if (len > sizeof(func_name) - 1) len = sizeof(func_name) - 1; memcpy(func_name, str, len); func_name[len] = '\0'; } func_addr = sym->n_value; } break; /* line number info */ case N_SLINE: pc = sym->n_value + func_addr; if (wanted_pc >= last_pc && wanted_pc < pc) goto found; last_pc = pc; last_line_num = sym->n_desc; /* XXX: slow! */ strcpy(last_func_name, func_name); break; /* include files */ case N_BINCL: str = stab_str + sym->n_strx; add_incl: if (incl_index < INCLUDE_STACK_SIZE) { incl_files[incl_index++] = str; } break; case N_EINCL: if (incl_index > 1) incl_index--; break; case N_SO: if (sym->n_strx == 0) { incl_index = 0; /* end of translation unit */ } else { str = stab_str + sym->n_strx; /* do not add path */ len = strlen(str); if (len > 0 && str[len - 1] != '/') goto add_incl; } break; } } no_stabs: /* second pass: we try symtab symbols (no line number info) */ incl_index = 0; if (symtab_section) { ElfW(Sym) *sym, *sym_end; int type; sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset); for(sym = (ElfW(Sym) *)symtab_section->data + 1; sym < sym_end; sym++) { type = ELFW(ST_TYPE)(sym->st_info); if (type == STT_FUNC || type == STT_GNU_IFUNC) { if (wanted_pc >= sym->st_value && wanted_pc < sym->st_value + sym->st_size) { pstrcpy(last_func_name, sizeof(last_func_name), (char *) strtab_section->data + sym->st_name); func_addr = sym->st_value; goto found; } } } } /* did not find any info: */ fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc); fflush(stderr); return 0; found: i = incl_index; if (i > 0) fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num); fprintf(stderr, "%s %p", msg, (void*)wanted_pc); if (last_func_name[0] != '\0') fprintf(stderr, " %s()", last_func_name); if (--i >= 0) { fprintf(stderr, " (included from "); for (;;) { fprintf(stderr, "%s", incl_files[i]); if (--i < 0) break; fprintf(stderr, ", "); } fprintf(stderr, ")"); } fprintf(stderr, "\n"); fflush(stderr); return func_addr; } /* emit a run time error at position 'pc' */ static void rt_error(ucontext_t *uc, const char *fmt, ...) { va_list ap; addr_t pc; int i; fprintf(stderr, "Runtime error: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); for(i=0;i<rt_num_callers;i++) { if (rt_get_caller_pc(&pc, uc, i) < 0) break; pc = rt_printline(pc, i ? "by" : "at"); if (pc == (addr_t)rt_prog_main && pc) break; } } /* ------------------------------------------------------------- */ #ifndef _WIN32 /* signal handler for fatal errors */ static void sig_error(int signum, siginfo_t *siginf, void *puc) { ucontext_t *uc = puc; switch(signum) { case SIGFPE: switch(siginf->si_code) { case FPE_INTDIV: case FPE_FLTDIV: rt_error(uc, "division by zero"); break; default: rt_error(uc, "floating point exception"); break; } break; case SIGBUS: case SIGSEGV: if (rt_bound_error_msg && *rt_bound_error_msg) rt_error(uc, *rt_bound_error_msg); else rt_error(uc, "dereferencing invalid pointer"); break; case SIGILL: rt_error(uc, "illegal instruction"); break; case SIGABRT: rt_error(uc, "abort() called"); break; default: rt_error(uc, "caught signal %d", signum); break; } exit(255); } #ifndef SA_SIGINFO # define SA_SIGINFO 0x00000004u #endif /* Generate a stack backtrace when a CPU exception occurs. */ static void set_exception_handler(void) { struct sigaction sigact; /* install TCC signal handlers to print debug info on fatal runtime errors */ sigact.sa_flags = SA_SIGINFO | SA_RESETHAND; sigact.sa_sigaction = sig_error; sigemptyset(&sigact.sa_mask); sigaction(SIGFPE, &sigact, NULL); sigaction(SIGILL, &sigact, NULL); sigaction(SIGSEGV, &sigact, NULL); sigaction(SIGBUS, &sigact, NULL); sigaction(SIGABRT, &sigact, NULL); } /* ------------------------------------------------------------- */ #ifdef __i386__ /* fix for glibc 2.1 */ #ifndef REG_EIP #define REG_EIP EIP #define REG_EBP EBP #endif /* return the PC at frame level 'level'. Return negative if not found */ static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) { addr_t fp; int i; if (level == 0) { #if defined(__APPLE__) *paddr = uc->uc_mcontext->__ss.__eip; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) *paddr = uc->uc_mcontext.mc_eip; #elif defined(__dietlibc__) *paddr = uc->uc_mcontext.eip; #elif defined(__NetBSD__) *paddr = uc->uc_mcontext.__gregs[_REG_EIP]; #elif defined(__OpenBSD__) *paddr = uc->sc_eip; #else *paddr = uc->uc_mcontext.gregs[REG_EIP]; #endif return 0; } else { #if defined(__APPLE__) fp = uc->uc_mcontext->__ss.__ebp; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) fp = uc->uc_mcontext.mc_ebp; #elif defined(__dietlibc__) fp = uc->uc_mcontext.ebp; #elif defined(__NetBSD__) fp = uc->uc_mcontext.__gregs[_REG_EBP]; #elif defined(__OpenBSD__) *paddr = uc->sc_ebp; #else fp = uc->uc_mcontext.gregs[REG_EBP]; #endif for(i=1;i<level;i++) { /* XXX: check address validity with program info */ if (fp <= 0x1000 || fp >= 0xc0000000) return -1; fp = ((addr_t *)fp)[0]; } *paddr = ((addr_t *)fp)[1]; return 0; } } /* ------------------------------------------------------------- */ #elif defined(__x86_64__) /* return the PC at frame level 'level'. Return negative if not found */ static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) { addr_t fp; int i; if (level == 0) { /* XXX: only support linux */ #if defined(__APPLE__) *paddr = uc->uc_mcontext->__ss.__rip; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) *paddr = uc->uc_mcontext.mc_rip; #elif defined(__NetBSD__) *paddr = uc->uc_mcontext.__gregs[_REG_RIP]; #else *paddr = uc->uc_mcontext.gregs[REG_RIP]; #endif return 0; } else { #if defined(__APPLE__) fp = uc->uc_mcontext->__ss.__rbp; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) fp = uc->uc_mcontext.mc_rbp; #elif defined(__NetBSD__) fp = uc->uc_mcontext.__gregs[_REG_RBP]; #else fp = uc->uc_mcontext.gregs[REG_RBP]; #endif for(i=1;i<level;i++) { /* XXX: check address validity with program info */ if (fp <= 0x1000) return -1; fp = ((addr_t *)fp)[0]; } *paddr = ((addr_t *)fp)[1]; return 0; } } /* ------------------------------------------------------------- */ #elif defined(__arm__) /* return the PC at frame level 'level'. Return negative if not found */ static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) { addr_t fp, sp; int i; if (level == 0) { /* XXX: only supports linux */ #if defined(__linux__) *paddr = uc->uc_mcontext.arm_pc; #else return -1; #endif return 0; } else { #if defined(__linux__) fp = uc->uc_mcontext.arm_fp; sp = uc->uc_mcontext.arm_sp; if (sp < 0x1000) sp = 0x1000; #else return -1; #endif /* XXX: specific to tinycc stack frames */ if (fp < sp + 12 || fp & 3) return -1; for(i = 1; i < level; i++) { sp = ((addr_t *)fp)[-2]; if (sp < fp || sp - fp > 16 || sp & 3) return -1; fp = ((addr_t *)fp)[-3]; if (fp <= sp || fp - sp < 12 || fp & 3) return -1; } /* XXX: check address validity with program info */ *paddr = ((addr_t *)fp)[-1]; return 0; } } /* ------------------------------------------------------------- */ #elif defined(__aarch64__) static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) { if (level < 0) return -1; else if (level == 0) { *paddr = uc->uc_mcontext.pc; return 0; } else { addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29]; int i; for (i = 1; i < level; i++) fp = (addr_t *)fp[0]; *paddr = fp[1]; return 0; } } /* ------------------------------------------------------------- */ #else #warning add arch specific rt_get_caller_pc() static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) { return -1; } #endif /* !__i386__ */ /* ------------------------------------------------------------- */ #else /* WIN32 */ static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) { EXCEPTION_RECORD *er = ex_info->ExceptionRecord; CONTEXT *uc = ex_info->ContextRecord; switch (er->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: if (rt_bound_error_msg && *rt_bound_error_msg) rt_error(uc, *rt_bound_error_msg); else rt_error(uc, "access violation"); break; case EXCEPTION_STACK_OVERFLOW: rt_error(uc, "stack overflow"); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: rt_error(uc, "division by zero"); break; default: rt_error(uc, "exception caught"); break; } return EXCEPTION_EXECUTE_HANDLER; } /* Generate a stack backtrace when a CPU exception occurs. */ static void set_exception_handler(void) { SetUnhandledExceptionFilter(cpu_exception_handler); } /* return the PC at frame level 'level'. Return non zero if not found */ static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level) { addr_t fp, pc; int i; #ifdef _WIN64 pc = uc->Rip; fp = uc->Rbp; #else pc = uc->Eip; fp = uc->Ebp; #endif if (level > 0) { for(i=1;i<level;i++) { /* XXX: check address validity with program info */ if (fp <= 0x1000 || fp >= 0xc0000000) return -1; fp = ((addr_t*)fp)[0]; } pc = ((addr_t*)fp)[1]; } *paddr = pc; return 0; } #endif /* _WIN32 */ #endif /* CONFIG_TCC_BACKTRACE */ /* ------------------------------------------------------------- */ #ifdef CONFIG_TCC_STATIC /* dummy function for profiling */ ST_FUNC void *dlopen(const char *filename, int flag) { return NULL; } ST_FUNC void dlclose(void *p) { } ST_FUNC const char *dlerror(void) { return "error"; } typedef struct TCCSyms { char *str; void *ptr; } TCCSyms; /* add the symbol you want here if no dynamic linking is done */ static TCCSyms tcc_syms[] = { #if !defined(CONFIG_TCCBOOT) #define TCCSYM(a) { #a, &a, }, TCCSYM(printf) TCCSYM(fprintf) TCCSYM(fopen) TCCSYM(fclose) #undef TCCSYM #endif { NULL, NULL }, }; ST_FUNC void *dlsym(void *handle, const char *symbol) { TCCSyms *p; p = tcc_syms; while (p->str != NULL) { if (!strcmp(p->str, symbol)) return p->ptr; p++; } return NULL; } #endif /* CONFIG_TCC_STATIC */ #endif /* TCC_IS_NATIVE */ /* ------------------------------------------------------------- */