Fix default_reallocator declaration
[tinycc.git] / tccrun.c
blob3ad9a41c231566c2153ab67259b69af1eca1a52b
1 /*
2 * TCC - Tiny C Compiler - Support for -run switch
4 * Copyright (c) 2001-2004 Fabrice Bellard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "tcc.h"
23 /* only native compiler supports -run */
24 #ifdef TCC_IS_NATIVE
26 typedef struct rt_context
28 #ifdef CONFIG_TCC_BACKTRACE
29 /* --> tccelf.c:tcc_add_btstub wants those below in that order: */
30 union {
31 struct {
32 Stab_Sym *stab_sym, *stab_sym_end;
33 char *stab_str;
35 struct {
36 unsigned char *dwarf_line, *dwarf_line_end, *dwarf_line_str;
39 addr_t dwarf;
40 ElfW(Sym) *esym_start, *esym_end;
41 char *elf_str;
42 addr_t prog_base;
43 void *bounds_start;
44 struct rt_context *next;
45 /* <-- */
46 int num_callers;
47 addr_t ip, fp, sp;
48 void *top_func;
49 #endif
50 jmp_buf jb;
51 int do_jmp;
52 # define NR_AT_EXIT 32
53 int nr_exit;
54 void *exitfunc[NR_AT_EXIT];
55 void *exitarg[NR_AT_EXIT];
56 } rt_context;
58 static rt_context g_rtctxt;
59 static void rt_exit(int code)
61 rt_context *rc = &g_rtctxt;
62 if (rc->do_jmp)
63 longjmp(rc->jb, code ? code : 256);
64 exit(code);
67 #ifdef CONFIG_TCC_BACKTRACE
68 static void set_exception_handler(void);
69 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap);
70 #endif /* CONFIG_TCC_BACKTRACE */
72 /* defined when included from lib/bt-exe.c */
73 #ifndef CONFIG_TCC_BACKTRACE_ONLY
75 #ifndef _WIN32
76 # include <sys/mman.h>
77 #endif
79 static int set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length);
80 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff);
82 #ifdef _WIN64
83 static void *win64_add_function_table(TCCState *s1);
84 static void win64_del_function_table(void *);
85 #endif
87 /* ------------------------------------------------------------- */
88 /* Do all relocations (needed before using tcc_get_symbol())
89 Returns -1 on error. */
91 LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
93 int size;
94 addr_t ptr_diff = 0;
96 if (TCC_RELOCATE_AUTO != ptr)
97 return tcc_relocate_ex(s1, ptr, 0);
99 size = tcc_relocate_ex(s1, NULL, 0);
100 if (size < 0)
101 return -1;
103 #ifdef HAVE_SELINUX
105 /* Using mmap instead of malloc */
106 void *prx;
107 char tmpfname[] = "/tmp/.tccrunXXXXXX";
108 int fd = mkstemp(tmpfname);
109 unlink(tmpfname);
110 ftruncate(fd, size);
112 size = (size + (PAGESIZE-1)) & ~(PAGESIZE-1);
113 ptr = mmap(NULL, size * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
114 /* mmap RX memory at a fixed distance */
115 prx = mmap((char*)ptr + size, size, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_FIXED, fd, 0);
116 close(fd);
117 if (ptr == MAP_FAILED || prx == MAP_FAILED)
118 return tcc_error_noabort("tccrun: could not map memory");
119 ptr_diff = (char*)prx - (char*)ptr;
120 //printf("map %p %p %p\n", ptr, prx, (void*)ptr_diff);
122 #else
123 ptr = tcc_malloc(size);
124 #endif
125 if (tcc_relocate_ex(s1, ptr, ptr_diff))
126 return -1;
127 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
128 dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
129 return 0;
132 ST_FUNC void tcc_run_free(TCCState *s1)
134 int i;
136 for (i = 0; i < s1->nb_runtime_mem; i += 2) {
137 unsigned size = (unsigned)(addr_t)s1->runtime_mem[i];
138 void *ptr = s1->runtime_mem[i+1];
139 #ifdef HAVE_SELINUX
140 munmap(ptr, size * 2);
141 #else
142 /* unprotect memory to make it usable for malloc again */
143 set_pages_executable(s1, 2, ptr, size);
144 #ifdef _WIN64
145 win64_del_function_table(*(void**)ptr);
146 #endif
147 tcc_free(ptr);
148 #endif
150 tcc_free(s1->runtime_mem);
153 static void run_cdtors(TCCState *s1, const char *start, const char *end,
154 int argc, char **argv, char **envp)
156 void **a = (void **)get_sym_addr(s1, start, 0, 0);
157 void **b = (void **)get_sym_addr(s1, end, 0, 0);
158 while (a != b)
159 ((void(*)(int, char **, char **))*a++)(argc, argv, envp);
162 static void run_on_exit(int ret)
164 rt_context *rc = &g_rtctxt;
165 int n = rc->nr_exit;
166 while (n)
167 --n, ((void(*)(int,void*))rc->exitfunc[n])(ret, rc->exitarg[n]);
170 static int rt_on_exit(void *function, void *arg)
172 rt_context *rc = &g_rtctxt;
173 if (rc->nr_exit < NR_AT_EXIT) {
174 rc->exitfunc[rc->nr_exit] = function;
175 rc->exitarg[rc->nr_exit++] = arg;
176 return 0;
178 return 1;
181 static int rt_atexit(void *function)
183 return rt_on_exit(function, NULL);
186 /* launch the compiled program with the given arguments */
187 LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
189 int (*prog_main)(int, char **, char **), ret;
190 rt_context *rc = &g_rtctxt;
192 #if defined(__APPLE__) || defined(__FreeBSD__)
193 char **envp = NULL;
194 #elif defined(__OpenBSD__) || defined(__NetBSD__)
195 extern char **environ;
196 char **envp = environ;
197 #else
198 char **envp = environ;
199 #endif
201 s1->runtime_main = s1->nostdlib ? "_start" : "main";
202 if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1))
203 return 0;
205 tcc_add_symbol(s1, "exit", rt_exit);
206 tcc_add_symbol(s1, "atexit", rt_atexit);
207 tcc_add_symbol(s1, "on_exit", rt_on_exit);
208 if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
209 return -1;
211 prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1);
212 if ((addr_t)-1 == (addr_t)prog_main)
213 return -1;
215 memset(rc, 0, sizeof *rc);
216 rc->do_jmp = 1;
218 #ifdef CONFIG_TCC_BACKTRACE
219 if (s1->do_debug) {
220 void *p;
221 if (s1->dwarf) {
222 rc->dwarf_line = dwarf_line_section->data;
223 rc->dwarf_line_end = dwarf_line_section->data + dwarf_line_section->data_offset;
224 if (dwarf_line_str_section)
225 rc->dwarf_line_str = dwarf_line_str_section->data;
227 else
229 rc->stab_sym = (Stab_Sym *)stab_section->data;
230 rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
231 rc->stab_str = (char *)stab_section->link->data;
233 rc->dwarf = s1->dwarf;
234 rc->esym_start = (ElfW(Sym) *)(symtab_section->data);
235 rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
236 rc->elf_str = (char *)symtab_section->link->data;
237 #if PTR_SIZE == 8
238 rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL;
239 #if defined TCC_TARGET_MACHO
240 if (s1->dwarf)
241 rc->prog_base = (addr_t) -1;
242 #else
243 #endif
244 #endif
245 rc->top_func = tcc_get_symbol(s1, "main");
246 rc->num_callers = s1->rt_num_callers;
247 if ((p = tcc_get_symbol(s1, "__rt_error")))
248 *(void**)p = _rt_error;
249 #ifdef CONFIG_TCC_BCHECK
250 if (s1->do_bounds_check) {
251 rc->bounds_start = (void*)bounds_section->sh_addr;
252 if ((p = tcc_get_symbol(s1, "__bound_init")))
253 ((void(*)(void*,int))p)(rc->bounds_start, 1);
255 #endif
256 set_exception_handler();
258 #endif
260 errno = 0; /* clean errno value */
261 fflush(stdout);
262 fflush(stderr);
264 /* These aren't C symbols, so don't need leading underscore handling. */
265 run_cdtors(s1, "__init_array_start", "__init_array_end", argc, argv, envp);
266 ret = setjmp(rc->jb);
267 if (0 == ret)
268 ret = prog_main(argc, argv, envp);
269 else if (256 == ret)
270 ret = 0;
271 run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL);
272 run_on_exit(ret);
273 if (s1->dflag & 16 && ret) /* tcc -dt -run ... */
274 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
275 return ret;
278 #define DEBUG_RUNMEN 0
280 /* enable rx/ro/rw permissions */
281 #define CONFIG_RUNMEM_RO 1
283 #if CONFIG_RUNMEM_RO
284 # define PAGE_ALIGN PAGESIZE
285 #elif defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
286 /* To avoid that x86 processors would reload cached instructions
287 each time when data is written in the near, we need to make
288 sure that code and data do not share the same 64 byte unit */
289 # define PAGE_ALIGN 64
290 #else
291 # define PAGE_ALIGN 1
292 #endif
294 /* relocate code. Return -1 on error, required size if ptr is NULL,
295 otherwise copy code into buffer passed by the caller */
296 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
298 Section *s;
299 unsigned offset, length, align, max_align, i, k, f;
300 unsigned n, copy;
301 addr_t mem, addr;
303 if (NULL == ptr) {
304 s1->nb_errors = 0;
305 #ifdef TCC_TARGET_PE
306 pe_output_file(s1, NULL);
307 #else
308 tcc_add_runtime(s1);
309 resolve_common_syms(s1);
310 build_got_entries(s1, 0);
311 #endif
312 if (s1->nb_errors)
313 return -1;
316 offset = max_align = 0, mem = (addr_t)ptr;
317 #ifdef _WIN64
318 offset += sizeof (void*); /* space for function_table pointer */
319 #endif
320 copy = 0;
321 redo:
322 for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
323 n = 0; addr = 0;
324 for(i = 1; i < s1->nb_sections; i++) {
325 static const char shf[] = {
326 SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
328 s = s1->sections[i];
329 if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
330 continue;
331 length = s->data_offset;
332 if (copy) {
333 if (addr == 0)
334 addr = s->sh_addr;
335 n = (s->sh_addr - addr) + length;
336 ptr = (void*)s->sh_addr;
337 if (k == 0)
338 ptr = (void*)(s->sh_addr - ptr_diff);
339 if (NULL == s->data || s->sh_type == SHT_NOBITS)
340 memset(ptr, 0, length);
341 else
342 memcpy(ptr, s->data, length);
343 #ifdef _WIN64
344 if (s == s1->uw_pdata)
345 *(void**)mem = win64_add_function_table(s1);
346 #endif
347 if (s->data) {
348 tcc_free(s->data);
349 s->data = NULL;
350 s->data_allocated = 0;
352 s->data_offset = 0;
353 continue;
355 align = s->sh_addralign - 1;
356 if (++n == 1 && align < (PAGE_ALIGN - 1))
357 align = (PAGE_ALIGN - 1);
358 if (max_align < align)
359 max_align = align;
360 addr = k ? mem : mem + ptr_diff;
361 offset += -(addr + offset) & align;
362 s->sh_addr = mem ? addr + offset : 0;
363 offset += length;
364 #if DEBUG_RUNMEN
365 if (mem)
366 printf("%d: %-16s %p len %04x align %04x\n",
367 k, s->name, (void*)s->sh_addr, length, align + 1);
368 #endif
370 if (copy) { /* set permissions */
371 if (k == 0 && ptr_diff)
372 continue; /* not with HAVE_SELINUX */
373 f = k;
374 #if !CONFIG_RUNMEM_RO
375 if (f != 0)
376 continue;
377 f = 3; /* change only SHF_EXECINSTR to rwx */
378 #endif
379 #if DEBUG_RUNMEN
380 printf("protect %d %p %04x\n", f, (void*)addr, n);
381 #endif
382 if (n) {
383 if (set_pages_executable(s1, f, (void*)addr, n))
384 return -1;
389 if (copy)
390 return 0;
392 /* relocate symbols */
393 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
394 if (s1->nb_errors)
395 return -1;
396 if (0 == mem)
397 return offset + max_align;
399 #ifdef TCC_TARGET_PE
400 s1->pe_imagebase = mem;
401 #endif
403 /* relocate sections */
404 #ifndef TCC_TARGET_PE
405 relocate_plt(s1);
406 #endif
407 relocate_sections(s1);
408 copy = 1;
409 goto redo;
412 /* ------------------------------------------------------------- */
413 /* allow to run code in memory */
415 static int set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length)
417 #ifdef _WIN32
418 static const unsigned char protect[] = {
419 PAGE_EXECUTE_READ,
420 PAGE_READONLY,
421 PAGE_READWRITE,
422 PAGE_EXECUTE_READWRITE
424 DWORD old;
425 if (!VirtualProtect(ptr, length, protect[mode], &old))
426 return -1;
427 return 0;
428 #else
429 static const unsigned char protect[] = {
430 PROT_READ | PROT_EXEC,
431 PROT_READ,
432 PROT_READ | PROT_WRITE,
433 PROT_READ | PROT_WRITE | PROT_EXEC
435 addr_t start, end;
436 start = (addr_t)ptr & ~(PAGESIZE - 1);
437 end = (addr_t)ptr + length;
438 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
439 if (mprotect((void *)start, end - start, protect[mode]))
440 return tcc_error_noabort("mprotect failed: did you mean to configure --with-selinux?");
441 /* XXX: BSD sometimes dump core with bad system call */
442 # if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
443 if (mode == 0 || mode == 3) {
444 void __clear_cache(void *beginning, void *end);
445 __clear_cache(ptr, (char *)ptr + length);
447 # endif
448 return 0;
449 #endif
452 #ifdef _WIN64
453 static void *win64_add_function_table(TCCState *s1)
455 void *p = NULL;
456 if (s1->uw_pdata) {
457 p = (void*)s1->uw_pdata->sh_addr;
458 RtlAddFunctionTable(
459 (RUNTIME_FUNCTION*)p,
460 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
461 s1->pe_imagebase
463 s1->uw_pdata = NULL;
465 return p;
468 static void win64_del_function_table(void *p)
470 if (p) {
471 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
474 #endif
475 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
476 /* ------------------------------------------------------------- */
477 #ifdef CONFIG_TCC_BACKTRACE
479 static int rt_vprintf(const char *fmt, va_list ap)
481 int ret = vfprintf(stderr, fmt, ap);
482 fflush(stderr);
483 return ret;
486 static int rt_printf(const char *fmt, ...)
488 va_list ap;
489 int r;
490 va_start(ap, fmt);
491 r = rt_vprintf(fmt, ap);
492 va_end(ap);
493 return r;
496 static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr)
498 ElfW(Sym) *esym;
499 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
500 int type = ELFW(ST_TYPE)(esym->st_info);
501 if ((type == STT_FUNC || type == STT_GNU_IFUNC)
502 && wanted_pc >= esym->st_value
503 && wanted_pc < esym->st_value + esym->st_size) {
504 *func_addr = esym->st_value;
505 return rc->elf_str + esym->st_name;
508 return NULL;
512 /* print the position in the source file of PC value 'pc' by reading
513 the stabs debug information */
514 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
515 const char *msg, const char *skip)
517 char func_name[128];
518 addr_t func_addr, last_pc, pc;
519 const char *incl_files[INCLUDE_STACK_SIZE];
520 int incl_index, last_incl_index, len, last_line_num, i;
521 const char *str, *p;
522 Stab_Sym *sym;
524 next:
525 func_name[0] = '\0';
526 func_addr = 0;
527 incl_index = 0;
528 last_pc = (addr_t)-1;
529 last_line_num = 1;
530 last_incl_index = 0;
532 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
533 str = rc->stab_str + sym->n_strx;
534 pc = sym->n_value;
536 switch(sym->n_type) {
537 case N_SLINE:
538 if (func_addr)
539 goto rel_pc;
540 case N_SO:
541 case N_SOL:
542 goto abs_pc;
543 case N_FUN:
544 if (sym->n_strx == 0) /* end of function */
545 goto rel_pc;
546 abs_pc:
547 #if PTR_SIZE == 8
548 /* Stab_Sym.n_value is only 32bits */
549 pc += rc->prog_base;
550 #endif
551 goto check_pc;
552 rel_pc:
553 pc += func_addr;
554 check_pc:
555 if (pc >= wanted_pc && wanted_pc >= last_pc)
556 goto found;
557 break;
560 switch(sym->n_type) {
561 /* function start or end */
562 case N_FUN:
563 if (sym->n_strx == 0)
564 goto reset_func;
565 p = strchr(str, ':');
566 if (0 == p || (len = p - str + 1, len > sizeof func_name))
567 len = sizeof func_name;
568 pstrcpy(func_name, len, str);
569 func_addr = pc;
570 break;
571 /* line number info */
572 case N_SLINE:
573 last_pc = pc;
574 last_line_num = sym->n_desc;
575 last_incl_index = incl_index;
576 break;
577 /* include files */
578 case N_BINCL:
579 if (incl_index < INCLUDE_STACK_SIZE)
580 incl_files[incl_index++] = str;
581 break;
582 case N_EINCL:
583 if (incl_index > 1)
584 incl_index--;
585 break;
586 /* start/end of translation unit */
587 case N_SO:
588 incl_index = 0;
589 if (sym->n_strx) {
590 /* do not add path */
591 len = strlen(str);
592 if (len > 0 && str[len - 1] != '/')
593 incl_files[incl_index++] = str;
595 reset_func:
596 func_name[0] = '\0';
597 func_addr = 0;
598 last_pc = (addr_t)-1;
599 break;
600 /* alternative file name (from #line or #include directives) */
601 case N_SOL:
602 if (incl_index)
603 incl_files[incl_index-1] = str;
604 break;
608 func_name[0] = '\0';
609 func_addr = 0;
610 last_incl_index = 0;
611 /* we try symtab symbols (no line number info) */
612 p = rt_elfsym(rc, wanted_pc, &func_addr);
613 if (p) {
614 pstrcpy(func_name, sizeof func_name, p);
615 goto found;
617 if ((rc = rc->next))
618 goto next;
619 found:
620 i = last_incl_index;
621 if (i > 0) {
622 str = incl_files[--i];
623 if (skip[0] && strstr(str, skip))
624 return (addr_t)-1;
625 rt_printf("%s:%d: ", str, last_line_num);
626 } else
627 rt_printf("%08llx : ", (long long)wanted_pc);
628 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
629 #if 0
630 if (--i >= 0) {
631 rt_printf(" (included from ");
632 for (;;) {
633 rt_printf("%s", incl_files[i]);
634 if (--i < 0)
635 break;
636 rt_printf(", ");
638 rt_printf(")");
640 #endif
641 return func_addr;
644 /* ------------------------------------------------------------- */
645 /* rt_printline - dwarf version */
647 #define MAX_128 ((8 * sizeof (long long) + 6) / 7)
649 #define DIR_TABLE_SIZE (64)
650 #define FILE_TABLE_SIZE (512)
652 #define dwarf_read_1(ln,end) \
653 ((ln) < (end) ? *(ln)++ : 0)
654 #define dwarf_read_2(ln,end) \
655 ((ln) + 2 < (end) ? (ln) += 2, read16le((ln) - 2) : 0)
656 #define dwarf_read_4(ln,end) \
657 ((ln) + 4 < (end) ? (ln) += 4, read32le((ln) - 4) : 0)
658 #define dwarf_read_8(ln,end) \
659 ((ln) + 8 < (end) ? (ln) += 8, read64le((ln) - 8) : 0)
660 #define dwarf_ignore_type(ln, end) /* timestamp/size/md5/... */ \
661 switch (entry_format[j].form) { \
662 case DW_FORM_data1: (ln) += 1; break; \
663 case DW_FORM_data2: (ln) += 2; break; \
664 case DW_FORM_data4: (ln) += 3; break; \
665 case DW_FORM_data8: (ln) += 8; break; \
666 case DW_FORM_data16: (ln) += 16; break; \
667 case DW_FORM_udata: dwarf_read_uleb128(&(ln), (end)); break; \
668 default: goto next_line; \
671 static unsigned long long
672 dwarf_read_uleb128(unsigned char **ln, unsigned char *end)
674 unsigned char *cp = *ln;
675 unsigned long long retval = 0;
676 int i;
678 for (i = 0; i < MAX_128; i++) {
679 unsigned long long byte = dwarf_read_1(cp, end);
681 retval |= (byte & 0x7f) << (i * 7);
682 if ((byte & 0x80) == 0)
683 break;
685 *ln = cp;
686 return retval;
689 static long long
690 dwarf_read_sleb128(unsigned char **ln, unsigned char *end)
692 unsigned char *cp = *ln;
693 long long retval = 0;
694 int i;
696 for (i = 0; i < MAX_128; i++) {
697 unsigned long long byte = dwarf_read_1(cp, end);
699 retval |= (byte & 0x7f) << (i * 7);
700 if ((byte & 0x80) == 0) {
701 if ((byte & 0x40) && (i + 1) * 7 < 64)
702 retval |= -1LL << ((i + 1) * 7);
703 break;
706 *ln = cp;
707 return retval;
710 static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc,
711 const char *msg, const char *skip)
713 unsigned char *ln;
714 unsigned char *cp;
715 unsigned char *end;
716 unsigned char *opcode_length;
717 unsigned long long size;
718 unsigned int length;
719 unsigned char version;
720 unsigned int min_insn_length;
721 unsigned int max_ops_per_insn;
722 int line_base;
723 unsigned int line_range;
724 unsigned int opcode_base;
725 unsigned int opindex;
726 unsigned int col;
727 unsigned int i;
728 unsigned int j;
729 unsigned int len;
730 unsigned long long value;
731 struct {
732 unsigned int type;
733 unsigned int form;
734 } entry_format[256];
735 unsigned int dir_size;
736 #if 0
737 char *dirs[DIR_TABLE_SIZE];
738 #endif
739 unsigned int filename_size;
740 struct dwarf_filename_struct {
741 unsigned int dir_entry;
742 char *name;
743 } filename_table[FILE_TABLE_SIZE];
744 addr_t last_pc;
745 addr_t pc;
746 addr_t func_addr;
747 int line;
748 char *filename;
749 char *function;
751 next:
752 ln = rc->dwarf_line;
753 while (ln < rc->dwarf_line_end) {
754 dir_size = 0;
755 filename_size = 0;
756 last_pc = 0;
757 pc = 0;
758 func_addr = 0;
759 line = 1;
760 filename = NULL;
761 function = NULL;
762 length = 4;
763 size = dwarf_read_4(ln, rc->dwarf_line_end);
764 if (size == 0xffffffffu) // dwarf 64
765 length = 8, size = dwarf_read_8(ln, rc->dwarf_line_end);
766 end = ln + size;
767 if (end < ln || end > rc->dwarf_line_end)
768 break;
769 version = dwarf_read_2(ln, end);
770 if (version >= 5)
771 ln += length + 2; // address size, segment selector, prologue Length
772 else
773 ln += length; // prologue Length
774 min_insn_length = dwarf_read_1(ln, end);
775 if (version >= 4)
776 max_ops_per_insn = dwarf_read_1(ln, end);
777 else
778 max_ops_per_insn = 1;
779 ln++; // Initial value of 'is_stmt'
780 line_base = dwarf_read_1(ln, end);
781 line_base |= line_base >= 0x80 ? ~0xff : 0;
782 line_range = dwarf_read_1(ln, end);
783 opcode_base = dwarf_read_1(ln, end);
784 opcode_length = ln;
785 ln += opcode_base - 1;
786 opindex = 0;
787 if (version >= 5) {
788 col = dwarf_read_1(ln, end);
789 for (i = 0; i < col; i++) {
790 entry_format[i].type = dwarf_read_uleb128(&ln, end);
791 entry_format[i].form = dwarf_read_uleb128(&ln, end);
793 dir_size = dwarf_read_uleb128(&ln, end);
794 for (i = 0; i < dir_size; i++) {
795 for (j = 0; j < col; j++) {
796 if (entry_format[j].type == DW_LNCT_path) {
797 if (entry_format[j].form != DW_FORM_line_strp)
798 goto next_line;
799 #if 0
800 value = length == 4 ? dwarf_read_4(ln, end)
801 : dwarf_read_8(ln, end);
802 if (i < DIR_TABLE_SIZE)
803 dirs[i] = (char *)rc->dwarf_line_str + value;
804 #else
805 length == 4 ? dwarf_read_4(ln, end)
806 : dwarf_read_8(ln, end);
807 #endif
809 else
810 dwarf_ignore_type(ln, end);
813 col = dwarf_read_1(ln, end);
814 for (i = 0; i < col; i++) {
815 entry_format[i].type = dwarf_read_uleb128(&ln, end);
816 entry_format[i].form = dwarf_read_uleb128(&ln, end);
818 filename_size = dwarf_read_uleb128(&ln, end);
819 for (i = 0; i < filename_size; i++)
820 for (j = 0; j < col; j++) {
821 if (entry_format[j].type == DW_LNCT_path) {
822 if (entry_format[j].form != DW_FORM_line_strp)
823 goto next_line;
824 value = length == 4 ? dwarf_read_4(ln, end)
825 : dwarf_read_8(ln, end);
826 if (i < FILE_TABLE_SIZE)
827 filename_table[i].name =
828 (char *)rc->dwarf_line_str + value;
830 else if (entry_format[j].type == DW_LNCT_directory_index) {
831 switch (entry_format[j].form) {
832 case DW_FORM_data1: value = dwarf_read_1(ln, end); break;
833 case DW_FORM_data2: value = dwarf_read_2(ln, end); break;
834 case DW_FORM_data4: value = dwarf_read_4(ln, end); break;
835 case DW_FORM_udata: value = dwarf_read_uleb128(&ln, end); break;
836 default: goto next_line;
838 if (i < FILE_TABLE_SIZE)
839 filename_table[i].dir_entry = value;
841 else
842 dwarf_ignore_type(ln, end);
845 else {
846 while ((dwarf_read_1(ln, end))) {
847 #if 0
848 if (++dir_size < DIR_TABLE_SIZE)
849 dirs[dir_size - 1] = (char *)ln - 1;
850 #endif
851 while (dwarf_read_1(ln, end)) {}
853 while ((dwarf_read_1(ln, end))) {
854 if (++filename_size < FILE_TABLE_SIZE) {
855 filename_table[filename_size - 1].name = (char *)ln - 1;
856 while (dwarf_read_1(ln, end)) {}
857 filename_table[filename_size - 1].dir_entry =
858 dwarf_read_uleb128(&ln, end);
860 else {
861 while (dwarf_read_1(ln, end)) {}
862 dwarf_read_uleb128(&ln, end);
864 dwarf_read_uleb128(&ln, end); // time
865 dwarf_read_uleb128(&ln, end); // size
868 if (filename_size >= 1)
869 filename = filename_table[0].name;
870 while (ln < end) {
871 last_pc = pc;
872 i = dwarf_read_1(ln, end);
873 if (i >= opcode_base) {
874 if (max_ops_per_insn == 1)
875 pc += ((i - opcode_base) / line_range) * min_insn_length;
876 else {
877 pc += (opindex + (i - opcode_base) / line_range) /
878 max_ops_per_insn * min_insn_length;
879 opindex = (opindex + (i - opcode_base) / line_range) %
880 max_ops_per_insn;
882 i = (int)((i - opcode_base) % line_range) + line_base;
883 check_pc:
884 if (pc >= wanted_pc && wanted_pc >= last_pc)
885 goto found;
886 line += i;
888 else {
889 switch (i) {
890 case 0:
891 len = dwarf_read_uleb128(&ln, end);
892 cp = ln;
893 ln += len;
894 if (len == 0)
895 goto next_line;
896 switch (dwarf_read_1(cp, end)) {
897 case DW_LNE_end_sequence:
898 break;
899 case DW_LNE_set_address:
900 #if PTR_SIZE == 4
901 pc = dwarf_read_4(cp, end);
902 #else
903 pc = dwarf_read_8(cp, end);
904 #endif
905 #if defined TCC_TARGET_MACHO
906 if (rc->prog_base != (addr_t) -1)
907 pc += rc->prog_base;
908 #endif
909 opindex = 0;
910 break;
911 case DW_LNE_define_file: /* deprecated */
912 if (++filename_size < FILE_TABLE_SIZE) {
913 filename_table[filename_size - 1].name = (char *)ln - 1;
914 while (dwarf_read_1(ln, end)) {}
915 filename_table[filename_size - 1].dir_entry =
916 dwarf_read_uleb128(&ln, end);
918 else {
919 while (dwarf_read_1(ln, end)) {}
920 dwarf_read_uleb128(&ln, end);
922 dwarf_read_uleb128(&ln, end); // time
923 dwarf_read_uleb128(&ln, end); // size
924 break;
925 case DW_LNE_hi_user - 1:
926 function = (char *)cp;
927 func_addr = pc;
928 break;
929 default:
930 break;
932 break;
933 case DW_LNS_advance_pc:
934 if (max_ops_per_insn == 1)
935 pc += dwarf_read_uleb128(&ln, end) * min_insn_length;
936 else {
937 unsigned long long off = dwarf_read_uleb128(&ln, end);
939 pc += (opindex + off) / max_ops_per_insn *
940 min_insn_length;
941 opindex = (opindex + off) % max_ops_per_insn;
943 i = 0;
944 goto check_pc;
945 case DW_LNS_advance_line:
946 line += dwarf_read_sleb128(&ln, end);
947 break;
948 case DW_LNS_set_file:
949 i = dwarf_read_uleb128(&ln, end);
950 i -= i > 0 && version < 5;
951 if (i < FILE_TABLE_SIZE && i < filename_size)
952 filename = filename_table[i].name;
953 break;
954 case DW_LNS_const_add_pc:
955 if (max_ops_per_insn == 1)
956 pc += ((255 - opcode_base) / line_range) * min_insn_length;
957 else {
958 unsigned int off = (255 - opcode_base) / line_range;
960 pc += ((opindex + off) / max_ops_per_insn) *
961 min_insn_length;
962 opindex = (opindex + off) % max_ops_per_insn;
964 i = 0;
965 goto check_pc;
966 case DW_LNS_fixed_advance_pc:
967 i = dwarf_read_2(ln, end);
968 pc += i;
969 opindex = 0;
970 i = 0;
971 goto check_pc;
972 default:
973 for (j = 0; j < opcode_length[i - 1]; j++)
974 dwarf_read_uleb128 (&ln, end);
975 break;
979 next_line:
980 ln = end;
983 filename = NULL;
984 func_addr = 0;
985 /* we try symtab symbols (no line number info) */
986 function = rt_elfsym(rc, wanted_pc, &func_addr);
987 if (function)
988 goto found;
989 if ((rc = rc->next))
990 goto next;
991 found:
992 if (filename) {
993 if (skip[0] && strstr(filename, skip))
994 return (addr_t)-1;
995 rt_printf("%s:%d: ", filename, line);
997 else
998 rt_printf("0x%08llx : ", (long long)wanted_pc);
999 rt_printf("%s %s", msg, function ? function : "???");
1000 return (addr_t)func_addr;
1002 /* ------------------------------------------------------------- */
1004 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level);
1006 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap)
1008 rt_context *rc = &g_rtctxt;
1009 addr_t pc = 0;
1010 char skip[100];
1011 int i, level, ret, n, one;
1012 const char *a, *b, *msg;
1014 if (fp) {
1015 /* we're called from tcc_backtrace. */
1016 rc->fp = (addr_t)fp;
1017 rc->ip = (addr_t)ip;
1018 msg = "";
1019 } else {
1020 /* we're called from signal/exception handler */
1021 msg = "RUNTIME ERROR: ";
1024 skip[0] = 0;
1025 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
1026 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
1027 memcpy(skip, a, b - a), skip[b - a] = 0;
1028 fmt = b + 1;
1030 one = 0;
1031 /* hack for bcheck.c:dprintf(): one level, no newline */
1032 if (fmt[0] == '\001')
1033 ++fmt, one = 1;
1035 n = rc->num_callers ? rc->num_callers : 6;
1036 for (i = level = 0; level < n; i++) {
1037 ret = rt_get_caller_pc(&pc, rc, i);
1038 a = "%s";
1039 if (ret != -1) {
1040 if (rc->dwarf)
1041 pc = rt_printline_dwarf(rc, pc, level ? "by" : "at", skip);
1042 else
1043 pc = rt_printline(rc, pc, level ? "by" : "at", skip);
1044 if (pc == (addr_t)-1)
1045 continue;
1046 a = ": %s";
1048 if (level == 0) {
1049 rt_printf(a, msg);
1050 rt_vprintf(fmt, ap);
1051 } else if (ret == -1)
1052 break;
1053 if (one)
1054 break;
1055 rt_printf("\n");
1056 if (ret == -1 || (pc == (addr_t)rc->top_func && pc))
1057 break;
1058 ++level;
1061 rc->ip = rc->fp = 0;
1062 return 0;
1065 /* emit a run time error at position 'pc' */
1066 static int rt_error(const char *fmt, ...)
1068 va_list ap;
1069 int ret;
1070 va_start(ap, fmt);
1071 ret = _rt_error(0, 0, fmt, ap);
1072 va_end(ap);
1073 return ret;
1076 /* ------------------------------------------------------------- */
1078 #ifndef _WIN32
1079 # include <signal.h>
1080 # ifndef __OpenBSD__
1081 # include <sys/ucontext.h>
1082 # endif
1083 #else
1084 # define ucontext_t CONTEXT
1085 #endif
1087 /* translate from ucontext_t* to internal rt_context * */
1088 static void rt_getcontext(ucontext_t *uc, rt_context *rc)
1090 #if defined _WIN64
1091 rc->ip = uc->Rip;
1092 rc->fp = uc->Rbp;
1093 rc->sp = uc->Rsp;
1094 #elif defined _WIN32
1095 rc->ip = uc->Eip;
1096 rc->fp = uc->Ebp;
1097 rc->sp = uc->Esp;
1098 #elif defined __i386__
1099 # if defined(__APPLE__)
1100 rc->ip = uc->uc_mcontext->__ss.__eip;
1101 rc->fp = uc->uc_mcontext->__ss.__ebp;
1102 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1103 rc->ip = uc->uc_mcontext.mc_eip;
1104 rc->fp = uc->uc_mcontext.mc_ebp;
1105 # elif defined(__dietlibc__)
1106 rc->ip = uc->uc_mcontext.eip;
1107 rc->fp = uc->uc_mcontext.ebp;
1108 # elif defined(__NetBSD__)
1109 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
1110 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
1111 # elif defined(__OpenBSD__)
1112 rc->ip = uc->sc_eip;
1113 rc->fp = uc->sc_ebp;
1114 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
1115 rc->ip = uc->uc_mcontext.gregs[EIP];
1116 rc->fp = uc->uc_mcontext.gregs[EBP];
1117 # else
1118 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
1119 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
1120 # endif
1121 #elif defined(__x86_64__)
1122 # if defined(__APPLE__)
1123 rc->ip = uc->uc_mcontext->__ss.__rip;
1124 rc->fp = uc->uc_mcontext->__ss.__rbp;
1125 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1126 rc->ip = uc->uc_mcontext.mc_rip;
1127 rc->fp = uc->uc_mcontext.mc_rbp;
1128 # elif defined(__NetBSD__)
1129 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
1130 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
1131 # elif defined(__OpenBSD__)
1132 rc->ip = uc->sc_rip;
1133 rc->fp = uc->sc_rbp;
1134 # else
1135 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
1136 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
1137 # endif
1138 #elif defined(__arm__) && defined(__NetBSD__)
1139 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1140 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1141 #elif defined(__arm__) && defined(__OpenBSD__)
1142 rc->ip = uc->sc_pc;
1143 rc->fp = uc->sc_r11;
1144 #elif defined(__arm__) && defined(__FreeBSD__)
1145 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1146 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1147 #elif defined(__arm__)
1148 rc->ip = uc->uc_mcontext.arm_pc;
1149 rc->fp = uc->uc_mcontext.arm_fp;
1150 #elif defined(__aarch64__) && defined(__APPLE__)
1151 // see:
1152 // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h
1153 rc->ip = uc->uc_mcontext->__ss.__pc;
1154 rc->fp = uc->uc_mcontext->__ss.__fp;
1155 #elif defined(__aarch64__) && defined(__FreeBSD__)
1156 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
1157 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
1158 #elif defined(__aarch64__) && defined(__NetBSD__)
1159 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1160 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1161 #elif defined(__aarch64__) && defined(__OpenBSD__)
1162 rc->ip = uc->sc_elr;
1163 rc->fp = uc->sc_x[29];
1164 #elif defined(__aarch64__)
1165 rc->ip = uc->uc_mcontext.pc;
1166 rc->fp = uc->uc_mcontext.regs[29];
1167 #elif defined(__riscv) && defined(__OpenBSD__)
1168 rc->ip = uc->sc_sepc;
1169 rc->fp = uc->sc_s[0];
1170 #elif defined(__riscv)
1171 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
1172 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
1173 #endif
1176 /* ------------------------------------------------------------- */
1177 #ifndef _WIN32
1178 /* signal handler for fatal errors */
1179 static void sig_error(int signum, siginfo_t *siginf, void *puc)
1181 rt_context *rc = &g_rtctxt;
1182 rt_getcontext(puc, rc);
1184 switch(signum) {
1185 case SIGFPE:
1186 switch(siginf->si_code) {
1187 case FPE_INTDIV:
1188 case FPE_FLTDIV:
1189 rt_error("division by zero");
1190 break;
1191 default:
1192 rt_error("floating point exception");
1193 break;
1195 break;
1196 case SIGBUS:
1197 case SIGSEGV:
1198 rt_error("invalid memory access");
1199 break;
1200 case SIGILL:
1201 rt_error("illegal instruction");
1202 break;
1203 case SIGABRT:
1204 rt_error("abort() called");
1205 break;
1206 default:
1207 rt_error("caught signal %d", signum);
1208 break;
1210 rt_exit(255);
1213 #ifndef SA_SIGINFO
1214 # define SA_SIGINFO 0x00000004u
1215 #endif
1217 /* Generate a stack backtrace when a CPU exception occurs. */
1218 static void set_exception_handler(void)
1220 struct sigaction sigact;
1221 /* install TCC signal handlers to print debug info on fatal
1222 runtime errors */
1223 sigemptyset (&sigact.sa_mask);
1224 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
1225 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
1226 sigact.sa_flags |= SA_ONSTACK;
1227 #endif
1228 sigact.sa_sigaction = sig_error;
1229 sigemptyset(&sigact.sa_mask);
1230 sigaction(SIGFPE, &sigact, NULL);
1231 sigaction(SIGILL, &sigact, NULL);
1232 sigaction(SIGSEGV, &sigact, NULL);
1233 sigaction(SIGBUS, &sigact, NULL);
1234 sigaction(SIGABRT, &sigact, NULL);
1235 #if 0//def SIGSTKSZ
1236 /* This allows stack overflow to be reported instead of a SEGV */
1238 stack_t ss;
1239 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
1241 ss.ss_sp = stack;
1242 ss.ss_size = SIGSTKSZ;
1243 ss.ss_flags = 0;
1244 sigaltstack(&ss, NULL);
1246 #endif
1249 #else /* WIN32 */
1251 /* signal handler for fatal errors */
1252 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
1254 rt_context *rc = &g_rtctxt;
1255 unsigned code;
1256 rt_getcontext(ex_info->ContextRecord, rc);
1258 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
1259 case EXCEPTION_ACCESS_VIOLATION:
1260 rt_error("invalid memory access");
1261 break;
1262 case EXCEPTION_STACK_OVERFLOW:
1263 rt_error("stack overflow");
1264 break;
1265 case EXCEPTION_INT_DIVIDE_BY_ZERO:
1266 rt_error("division by zero");
1267 break;
1268 case EXCEPTION_BREAKPOINT:
1269 case EXCEPTION_SINGLE_STEP:
1270 rc->ip = *(addr_t*)rc->sp;
1271 rt_error("breakpoint/single-step exception:");
1272 return EXCEPTION_CONTINUE_SEARCH;
1273 default:
1274 rt_error("caught exception %08x", code);
1275 break;
1277 if (rc->do_jmp)
1278 rt_exit(255);
1279 return EXCEPTION_EXECUTE_HANDLER;
1282 /* Generate a stack backtrace when a CPU exception occurs. */
1283 static void set_exception_handler(void)
1285 SetUnhandledExceptionFilter(cpu_exception_handler);
1288 #endif
1290 /* ------------------------------------------------------------- */
1291 /* return the PC at frame level 'level'. Return negative if not found */
1292 #if defined(__i386__) || defined(__x86_64__)
1293 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1295 addr_t ip, fp;
1296 if (level == 0) {
1297 ip = rc->ip;
1298 } else {
1299 ip = 0;
1300 fp = rc->fp;
1301 while (--level) {
1302 /* XXX: check address validity with program info */
1303 if (fp <= 0x1000)
1304 break;
1305 fp = ((addr_t *)fp)[0];
1307 if (fp > 0x1000)
1308 ip = ((addr_t *)fp)[1];
1310 if (ip <= 0x1000)
1311 return -1;
1312 *paddr = ip;
1313 return 0;
1316 #elif defined(__arm__)
1317 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1319 /* XXX: only supports linux/bsd */
1320 #if !defined(__linux__) && \
1321 !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
1322 return -1;
1323 #else
1324 if (level == 0) {
1325 *paddr = rc->ip;
1326 } else {
1327 addr_t fp = rc->fp;
1328 while (--level)
1329 fp = ((addr_t *)fp)[0];
1330 *paddr = ((addr_t *)fp)[2];
1332 return 0;
1333 #endif
1336 #elif defined(__aarch64__)
1337 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1339 if (level == 0) {
1340 *paddr = rc->ip;
1341 } else {
1342 addr_t *fp = (addr_t*)rc->fp;
1343 while (--level)
1344 fp = (addr_t *)fp[0];
1345 *paddr = fp[1];
1347 return 0;
1350 #elif defined(__riscv)
1351 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1353 if (level == 0) {
1354 *paddr = rc->ip;
1355 } else {
1356 addr_t *fp = (addr_t*)rc->fp;
1357 while (--level && fp >= (addr_t*)0x1000)
1358 fp = (addr_t *)fp[-2];
1359 if (fp < (addr_t*)0x1000)
1360 return -1;
1361 *paddr = fp[-1];
1363 return 0;
1366 #else
1367 #warning add arch specific rt_get_caller_pc()
1368 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1370 return -1;
1373 #endif
1374 #endif /* CONFIG_TCC_BACKTRACE */
1375 /* ------------------------------------------------------------- */
1376 #ifdef CONFIG_TCC_STATIC
1378 /* dummy function for profiling */
1379 ST_FUNC void *dlopen(const char *filename, int flag)
1381 return NULL;
1384 ST_FUNC void dlclose(void *p)
1388 ST_FUNC const char *dlerror(void)
1390 return "error";
1393 typedef struct TCCSyms {
1394 char *str;
1395 void *ptr;
1396 } TCCSyms;
1399 /* add the symbol you want here if no dynamic linking is done */
1400 static TCCSyms tcc_syms[] = {
1401 #if !defined(CONFIG_TCCBOOT)
1402 #define TCCSYM(a) { #a, &a, },
1403 TCCSYM(printf)
1404 TCCSYM(fprintf)
1405 TCCSYM(fopen)
1406 TCCSYM(fclose)
1407 #undef TCCSYM
1408 #endif
1409 { NULL, NULL },
1412 ST_FUNC void *dlsym(void *handle, const char *symbol)
1414 TCCSyms *p;
1415 p = tcc_syms;
1416 while (p->str != NULL) {
1417 if (!strcmp(p->str, symbol))
1418 return p->ptr;
1419 p++;
1421 return NULL;
1424 #endif /* CONFIG_TCC_STATIC */
1425 #endif /* TCC_IS_NATIVE */
1426 /* ------------------------------------------------------------- */