Support aliasing static global nonfunction objects
[tinycc.git] / tccrun.c
blobb102d829096214d47ec8216b935674922c5ab132
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 if (!(ret = setjmp(rc->jb)))
267 ret = prog_main(argc, argv, envp);
268 run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL);
269 run_on_exit(ret);
270 if (s1->dflag & 16 && ret) /* tcc -dt -run ... */
271 fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp);
272 return ret;
275 #define DEBUG_RUNMEN 0
277 /* enable rx/ro/rw permissions */
278 #define CONFIG_RUNMEM_RO 1
280 #if CONFIG_RUNMEM_RO
281 # define PAGE_ALIGN PAGESIZE
282 #elif defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
283 /* To avoid that x86 processors would reload cached instructions
284 each time when data is written in the near, we need to make
285 sure that code and data do not share the same 64 byte unit */
286 # define PAGE_ALIGN 64
287 #else
288 # define PAGE_ALIGN 1
289 #endif
291 /* relocate code. Return -1 on error, required size if ptr is NULL,
292 otherwise copy code into buffer passed by the caller */
293 static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
295 Section *s;
296 unsigned offset, length, align, max_align, i, k, f;
297 unsigned n, copy;
298 addr_t mem, addr;
300 if (NULL == ptr) {
301 s1->nb_errors = 0;
302 #ifdef TCC_TARGET_PE
303 pe_output_file(s1, NULL);
304 #else
305 tcc_add_runtime(s1);
306 resolve_common_syms(s1);
307 build_got_entries(s1, 0);
308 #endif
309 if (s1->nb_errors)
310 return -1;
313 offset = max_align = 0, mem = (addr_t)ptr;
314 #ifdef _WIN64
315 offset += sizeof (void*); /* space for function_table pointer */
316 #endif
317 copy = 0;
318 redo:
319 for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
320 n = 0; addr = 0;
321 for(i = 1; i < s1->nb_sections; i++) {
322 static const char shf[] = {
323 SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
325 s = s1->sections[i];
326 if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
327 continue;
328 length = s->data_offset;
329 if (copy) {
330 if (addr == 0)
331 addr = s->sh_addr;
332 n = (s->sh_addr - addr) + length;
333 ptr = (void*)s->sh_addr;
334 if (k == 0)
335 ptr = (void*)(s->sh_addr - ptr_diff);
336 if (NULL == s->data || s->sh_type == SHT_NOBITS)
337 memset(ptr, 0, length);
338 else
339 memcpy(ptr, s->data, length);
340 #ifdef _WIN64
341 if (s == s1->uw_pdata)
342 *(void**)mem = win64_add_function_table(s1);
343 #endif
344 if (s->data) {
345 tcc_free(s->data);
346 s->data = NULL;
347 s->data_allocated = 0;
349 s->data_offset = 0;
350 continue;
352 align = s->sh_addralign - 1;
353 if (++n == 1 && align < (PAGE_ALIGN - 1))
354 align = (PAGE_ALIGN - 1);
355 if (max_align < align)
356 max_align = align;
357 addr = k ? mem : mem + ptr_diff;
358 offset += -(addr + offset) & align;
359 s->sh_addr = mem ? addr + offset : 0;
360 offset += length;
361 #if DEBUG_RUNMEN
362 if (mem)
363 printf("%d: %-16s %p len %04x align %04x\n",
364 k, s->name, (void*)s->sh_addr, length, align + 1);
365 #endif
367 if (copy) { /* set permissions */
368 if (k == 0 && ptr_diff)
369 continue; /* not with HAVE_SELINUX */
370 f = k;
371 #if !CONFIG_RUNMEM_RO
372 if (f != 0)
373 continue;
374 f = 3; /* change only SHF_EXECINSTR to rwx */
375 #endif
376 #if DEBUG_RUNMEN
377 printf("protect %d %p %04x\n", f, (void*)addr, n);
378 #endif
379 if (n) {
380 if (set_pages_executable(s1, f, (void*)addr, n))
381 return -1;
386 if (copy)
387 return 0;
389 /* relocate symbols */
390 relocate_syms(s1, s1->symtab, !(s1->nostdlib));
391 if (s1->nb_errors)
392 return -1;
393 if (0 == mem)
394 return offset + max_align;
396 #ifdef TCC_TARGET_PE
397 s1->pe_imagebase = mem;
398 #endif
400 /* relocate sections */
401 #ifndef TCC_TARGET_PE
402 relocate_plt(s1);
403 #endif
404 relocate_sections(s1);
405 copy = 1;
406 goto redo;
409 /* ------------------------------------------------------------- */
410 /* allow to run code in memory */
412 static int set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length)
414 #ifdef _WIN32
415 static const unsigned char protect[] = {
416 PAGE_EXECUTE_READ,
417 PAGE_READONLY,
418 PAGE_READWRITE,
419 PAGE_EXECUTE_READWRITE
421 DWORD old;
422 if (!VirtualProtect(ptr, length, protect[mode], &old))
423 return -1;
424 return 0;
425 #else
426 static const unsigned char protect[] = {
427 PROT_READ | PROT_EXEC,
428 PROT_READ,
429 PROT_READ | PROT_WRITE,
430 PROT_READ | PROT_WRITE | PROT_EXEC
432 addr_t start, end;
433 start = (addr_t)ptr & ~(PAGESIZE - 1);
434 end = (addr_t)ptr + length;
435 end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
436 if (mprotect((void *)start, end - start, protect[mode]))
437 return tcc_error_noabort("mprotect failed: did you mean to configure --with-selinux?");
438 /* XXX: BSD sometimes dump core with bad system call */
439 # if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
440 if (mode == 0 || mode == 3) {
441 void __clear_cache(void *beginning, void *end);
442 __clear_cache(ptr, (char *)ptr + length);
444 # endif
445 return 0;
446 #endif
449 #ifdef _WIN64
450 static void *win64_add_function_table(TCCState *s1)
452 void *p = NULL;
453 if (s1->uw_pdata) {
454 p = (void*)s1->uw_pdata->sh_addr;
455 RtlAddFunctionTable(
456 (RUNTIME_FUNCTION*)p,
457 s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
458 s1->pe_imagebase
460 s1->uw_pdata = NULL;
462 return p;
465 static void win64_del_function_table(void *p)
467 if (p) {
468 RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
471 #endif
472 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
473 /* ------------------------------------------------------------- */
474 #ifdef CONFIG_TCC_BACKTRACE
476 static int rt_vprintf(const char *fmt, va_list ap)
478 int ret = vfprintf(stderr, fmt, ap);
479 fflush(stderr);
480 return ret;
483 static int rt_printf(const char *fmt, ...)
485 va_list ap;
486 int r;
487 va_start(ap, fmt);
488 r = rt_vprintf(fmt, ap);
489 va_end(ap);
490 return r;
493 static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr)
495 ElfW(Sym) *esym;
496 for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) {
497 int type = ELFW(ST_TYPE)(esym->st_info);
498 if ((type == STT_FUNC || type == STT_GNU_IFUNC)
499 && wanted_pc >= esym->st_value
500 && wanted_pc < esym->st_value + esym->st_size) {
501 *func_addr = esym->st_value;
502 return rc->elf_str + esym->st_name;
505 return NULL;
509 /* print the position in the source file of PC value 'pc' by reading
510 the stabs debug information */
511 static addr_t rt_printline (rt_context *rc, addr_t wanted_pc,
512 const char *msg, const char *skip)
514 char func_name[128];
515 addr_t func_addr, last_pc, pc;
516 const char *incl_files[INCLUDE_STACK_SIZE];
517 int incl_index, last_incl_index, len, last_line_num, i;
518 const char *str, *p;
519 Stab_Sym *sym;
521 next:
522 func_name[0] = '\0';
523 func_addr = 0;
524 incl_index = 0;
525 last_pc = (addr_t)-1;
526 last_line_num = 1;
527 last_incl_index = 0;
529 for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) {
530 str = rc->stab_str + sym->n_strx;
531 pc = sym->n_value;
533 switch(sym->n_type) {
534 case N_SLINE:
535 if (func_addr)
536 goto rel_pc;
537 case N_SO:
538 case N_SOL:
539 goto abs_pc;
540 case N_FUN:
541 if (sym->n_strx == 0) /* end of function */
542 goto rel_pc;
543 abs_pc:
544 #if PTR_SIZE == 8
545 /* Stab_Sym.n_value is only 32bits */
546 pc += rc->prog_base;
547 #endif
548 goto check_pc;
549 rel_pc:
550 pc += func_addr;
551 check_pc:
552 if (pc >= wanted_pc && wanted_pc >= last_pc)
553 goto found;
554 break;
557 switch(sym->n_type) {
558 /* function start or end */
559 case N_FUN:
560 if (sym->n_strx == 0)
561 goto reset_func;
562 p = strchr(str, ':');
563 if (0 == p || (len = p - str + 1, len > sizeof func_name))
564 len = sizeof func_name;
565 pstrcpy(func_name, len, str);
566 func_addr = pc;
567 break;
568 /* line number info */
569 case N_SLINE:
570 last_pc = pc;
571 last_line_num = sym->n_desc;
572 last_incl_index = incl_index;
573 break;
574 /* include files */
575 case N_BINCL:
576 if (incl_index < INCLUDE_STACK_SIZE)
577 incl_files[incl_index++] = str;
578 break;
579 case N_EINCL:
580 if (incl_index > 1)
581 incl_index--;
582 break;
583 /* start/end of translation unit */
584 case N_SO:
585 incl_index = 0;
586 if (sym->n_strx) {
587 /* do not add path */
588 len = strlen(str);
589 if (len > 0 && str[len - 1] != '/')
590 incl_files[incl_index++] = str;
592 reset_func:
593 func_name[0] = '\0';
594 func_addr = 0;
595 last_pc = (addr_t)-1;
596 break;
597 /* alternative file name (from #line or #include directives) */
598 case N_SOL:
599 if (incl_index)
600 incl_files[incl_index-1] = str;
601 break;
605 func_name[0] = '\0';
606 func_addr = 0;
607 last_incl_index = 0;
608 /* we try symtab symbols (no line number info) */
609 p = rt_elfsym(rc, wanted_pc, &func_addr);
610 if (p) {
611 pstrcpy(func_name, sizeof func_name, p);
612 goto found;
614 if ((rc = rc->next))
615 goto next;
616 found:
617 i = last_incl_index;
618 if (i > 0) {
619 str = incl_files[--i];
620 if (skip[0] && strstr(str, skip))
621 return (addr_t)-1;
622 rt_printf("%s:%d: ", str, last_line_num);
623 } else
624 rt_printf("%08llx : ", (long long)wanted_pc);
625 rt_printf("%s %s", msg, func_name[0] ? func_name : "???");
626 #if 0
627 if (--i >= 0) {
628 rt_printf(" (included from ");
629 for (;;) {
630 rt_printf("%s", incl_files[i]);
631 if (--i < 0)
632 break;
633 rt_printf(", ");
635 rt_printf(")");
637 #endif
638 return func_addr;
641 /* ------------------------------------------------------------- */
642 /* rt_printline - dwarf version */
644 #define MAX_128 ((8 * sizeof (long long) + 6) / 7)
646 #define DIR_TABLE_SIZE (64)
647 #define FILE_TABLE_SIZE (512)
649 #define dwarf_read_1(ln,end) \
650 ((ln) < (end) ? *(ln)++ : 0)
651 #define dwarf_read_2(ln,end) \
652 ((ln) + 2 < (end) ? (ln) += 2, read16le((ln) - 2) : 0)
653 #define dwarf_read_4(ln,end) \
654 ((ln) + 4 < (end) ? (ln) += 4, read32le((ln) - 4) : 0)
655 #define dwarf_read_8(ln,end) \
656 ((ln) + 8 < (end) ? (ln) += 8, read64le((ln) - 8) : 0)
657 #define dwarf_ignore_type(ln, end) /* timestamp/size/md5/... */ \
658 switch (entry_format[j].form) { \
659 case DW_FORM_data1: (ln) += 1; break; \
660 case DW_FORM_data2: (ln) += 2; break; \
661 case DW_FORM_data4: (ln) += 3; break; \
662 case DW_FORM_data8: (ln) += 8; break; \
663 case DW_FORM_data16: (ln) += 16; break; \
664 case DW_FORM_udata: dwarf_read_uleb128(&(ln), (end)); break; \
665 default: goto next_line; \
668 static unsigned long long
669 dwarf_read_uleb128(unsigned char **ln, unsigned char *end)
671 unsigned char *cp = *ln;
672 unsigned long long retval = 0;
673 int i;
675 for (i = 0; i < MAX_128; i++) {
676 unsigned long long byte = dwarf_read_1(cp, end);
678 retval |= (byte & 0x7f) << (i * 7);
679 if ((byte & 0x80) == 0)
680 break;
682 *ln = cp;
683 return retval;
686 static long long
687 dwarf_read_sleb128(unsigned char **ln, unsigned char *end)
689 unsigned char *cp = *ln;
690 long long retval = 0;
691 int i;
693 for (i = 0; i < MAX_128; i++) {
694 unsigned long long byte = dwarf_read_1(cp, end);
696 retval |= (byte & 0x7f) << (i * 7);
697 if ((byte & 0x80) == 0) {
698 if ((byte & 0x40) && (i + 1) * 7 < 64)
699 retval |= -1LL << ((i + 1) * 7);
700 break;
703 *ln = cp;
704 return retval;
707 static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc,
708 const char *msg, const char *skip)
710 unsigned char *ln;
711 unsigned char *cp;
712 unsigned char *end;
713 unsigned char *opcode_length;
714 unsigned long long size;
715 unsigned int length;
716 unsigned char version;
717 unsigned int min_insn_length;
718 unsigned int max_ops_per_insn;
719 int line_base;
720 unsigned int line_range;
721 unsigned int opcode_base;
722 unsigned int opindex;
723 unsigned int col;
724 unsigned int i;
725 unsigned int j;
726 unsigned int len;
727 unsigned long long value;
728 struct {
729 unsigned int type;
730 unsigned int form;
731 } entry_format[256];
732 unsigned int dir_size;
733 #if 0
734 char *dirs[DIR_TABLE_SIZE];
735 #endif
736 unsigned int filename_size;
737 struct dwarf_filename_struct {
738 unsigned int dir_entry;
739 char *name;
740 } filename_table[FILE_TABLE_SIZE];
741 addr_t last_pc;
742 addr_t pc;
743 addr_t func_addr;
744 int line;
745 char *filename;
746 char *function;
748 next:
749 ln = rc->dwarf_line;
750 while (ln < rc->dwarf_line_end) {
751 dir_size = 0;
752 filename_size = 0;
753 last_pc = 0;
754 pc = 0;
755 func_addr = 0;
756 line = 1;
757 filename = NULL;
758 function = NULL;
759 length = 4;
760 size = dwarf_read_4(ln, rc->dwarf_line_end);
761 if (size == 0xffffffffu) // dwarf 64
762 length = 8, size = dwarf_read_8(ln, rc->dwarf_line_end);
763 end = ln + size;
764 if (end < ln || end > rc->dwarf_line_end)
765 break;
766 version = dwarf_read_2(ln, end);
767 if (version >= 5)
768 ln += length + 2; // address size, segment selector, prologue Length
769 else
770 ln += length; // prologue Length
771 min_insn_length = dwarf_read_1(ln, end);
772 if (version >= 4)
773 max_ops_per_insn = dwarf_read_1(ln, end);
774 else
775 max_ops_per_insn = 1;
776 ln++; // Initial value of 'is_stmt'
777 line_base = dwarf_read_1(ln, end);
778 line_base |= line_base >= 0x80 ? ~0xff : 0;
779 line_range = dwarf_read_1(ln, end);
780 opcode_base = dwarf_read_1(ln, end);
781 opcode_length = ln;
782 ln += opcode_base - 1;
783 opindex = 0;
784 if (version >= 5) {
785 col = dwarf_read_1(ln, end);
786 for (i = 0; i < col; i++) {
787 entry_format[i].type = dwarf_read_uleb128(&ln, end);
788 entry_format[i].form = dwarf_read_uleb128(&ln, end);
790 dir_size = dwarf_read_uleb128(&ln, end);
791 for (i = 0; i < dir_size; i++) {
792 for (j = 0; j < col; j++) {
793 if (entry_format[j].type == DW_LNCT_path) {
794 if (entry_format[j].form != DW_FORM_line_strp)
795 goto next_line;
796 #if 0
797 value = length == 4 ? dwarf_read_4(ln, end)
798 : dwarf_read_8(ln, end);
799 if (i < DIR_TABLE_SIZE)
800 dirs[i] = (char *)rc->dwarf_line_str + value;
801 #else
802 length == 4 ? dwarf_read_4(ln, end)
803 : dwarf_read_8(ln, end);
804 #endif
806 else
807 dwarf_ignore_type(ln, end);
810 col = dwarf_read_1(ln, end);
811 for (i = 0; i < col; i++) {
812 entry_format[i].type = dwarf_read_uleb128(&ln, end);
813 entry_format[i].form = dwarf_read_uleb128(&ln, end);
815 filename_size = dwarf_read_uleb128(&ln, end);
816 for (i = 0; i < filename_size; i++)
817 for (j = 0; j < col; j++) {
818 if (entry_format[j].type == DW_LNCT_path) {
819 if (entry_format[j].form != DW_FORM_line_strp)
820 goto next_line;
821 value = length == 4 ? dwarf_read_4(ln, end)
822 : dwarf_read_8(ln, end);
823 if (i < FILE_TABLE_SIZE)
824 filename_table[i].name =
825 (char *)rc->dwarf_line_str + value;
827 else if (entry_format[j].type == DW_LNCT_directory_index) {
828 switch (entry_format[j].form) {
829 case DW_FORM_data1: value = dwarf_read_1(ln, end); break;
830 case DW_FORM_data2: value = dwarf_read_2(ln, end); break;
831 case DW_FORM_data4: value = dwarf_read_4(ln, end); break;
832 case DW_FORM_udata: value = dwarf_read_uleb128(&ln, end); break;
833 default: goto next_line;
835 if (i < FILE_TABLE_SIZE)
836 filename_table[i].dir_entry = value;
838 else
839 dwarf_ignore_type(ln, end);
842 else {
843 while ((dwarf_read_1(ln, end))) {
844 #if 0
845 if (++dir_size < DIR_TABLE_SIZE)
846 dirs[dir_size - 1] = (char *)ln - 1;
847 #endif
848 while (dwarf_read_1(ln, end)) {}
850 while ((dwarf_read_1(ln, end))) {
851 if (++filename_size < FILE_TABLE_SIZE) {
852 filename_table[filename_size - 1].name = (char *)ln - 1;
853 while (dwarf_read_1(ln, end)) {}
854 filename_table[filename_size - 1].dir_entry =
855 dwarf_read_uleb128(&ln, end);
857 else {
858 while (dwarf_read_1(ln, end)) {}
859 dwarf_read_uleb128(&ln, end);
861 dwarf_read_uleb128(&ln, end); // time
862 dwarf_read_uleb128(&ln, end); // size
865 if (filename_size >= 1)
866 filename = filename_table[0].name;
867 while (ln < end) {
868 last_pc = pc;
869 i = dwarf_read_1(ln, end);
870 if (i >= opcode_base) {
871 if (max_ops_per_insn == 1)
872 pc += ((i - opcode_base) / line_range) * min_insn_length;
873 else {
874 pc += (opindex + (i - opcode_base) / line_range) /
875 max_ops_per_insn * min_insn_length;
876 opindex = (opindex + (i - opcode_base) / line_range) %
877 max_ops_per_insn;
879 i = (int)((i - opcode_base) % line_range) + line_base;
880 check_pc:
881 if (pc >= wanted_pc && wanted_pc >= last_pc)
882 goto found;
883 line += i;
885 else {
886 switch (i) {
887 case 0:
888 len = dwarf_read_uleb128(&ln, end);
889 cp = ln;
890 ln += len;
891 if (len == 0)
892 goto next_line;
893 switch (dwarf_read_1(cp, end)) {
894 case DW_LNE_end_sequence:
895 break;
896 case DW_LNE_set_address:
897 #if PTR_SIZE == 4
898 pc = dwarf_read_4(cp, end);
899 #else
900 pc = dwarf_read_8(cp, end);
901 #endif
902 #if defined TCC_TARGET_MACHO
903 if (rc->prog_base != (addr_t) -1)
904 pc += rc->prog_base;
905 #endif
906 opindex = 0;
907 break;
908 case DW_LNE_define_file: /* deprecated */
909 if (++filename_size < FILE_TABLE_SIZE) {
910 filename_table[filename_size - 1].name = (char *)ln - 1;
911 while (dwarf_read_1(ln, end)) {}
912 filename_table[filename_size - 1].dir_entry =
913 dwarf_read_uleb128(&ln, end);
915 else {
916 while (dwarf_read_1(ln, end)) {}
917 dwarf_read_uleb128(&ln, end);
919 dwarf_read_uleb128(&ln, end); // time
920 dwarf_read_uleb128(&ln, end); // size
921 break;
922 case DW_LNE_hi_user - 1:
923 function = (char *)cp;
924 func_addr = pc;
925 break;
926 default:
927 break;
929 break;
930 case DW_LNS_advance_pc:
931 if (max_ops_per_insn == 1)
932 pc += dwarf_read_uleb128(&ln, end) * min_insn_length;
933 else {
934 unsigned long long off = dwarf_read_uleb128(&ln, end);
936 pc += (opindex + off) / max_ops_per_insn *
937 min_insn_length;
938 opindex = (opindex + off) % max_ops_per_insn;
940 i = 0;
941 goto check_pc;
942 case DW_LNS_advance_line:
943 line += dwarf_read_sleb128(&ln, end);
944 break;
945 case DW_LNS_set_file:
946 i = dwarf_read_uleb128(&ln, end);
947 i -= i > 0 && version < 5;
948 if (i < FILE_TABLE_SIZE && i < filename_size)
949 filename = filename_table[i].name;
950 break;
951 case DW_LNS_const_add_pc:
952 if (max_ops_per_insn == 1)
953 pc += ((255 - opcode_base) / line_range) * min_insn_length;
954 else {
955 unsigned int off = (255 - opcode_base) / line_range;
957 pc += ((opindex + off) / max_ops_per_insn) *
958 min_insn_length;
959 opindex = (opindex + off) % max_ops_per_insn;
961 i = 0;
962 goto check_pc;
963 case DW_LNS_fixed_advance_pc:
964 i = dwarf_read_2(ln, end);
965 pc += i;
966 opindex = 0;
967 i = 0;
968 goto check_pc;
969 default:
970 for (j = 0; j < opcode_length[i - 1]; j++)
971 dwarf_read_uleb128 (&ln, end);
972 break;
976 next_line:
977 ln = end;
980 filename = NULL;
981 func_addr = 0;
982 /* we try symtab symbols (no line number info) */
983 function = rt_elfsym(rc, wanted_pc, &func_addr);
984 if (function)
985 goto found;
986 if ((rc = rc->next))
987 goto next;
988 found:
989 if (filename) {
990 if (skip[0] && strstr(filename, skip))
991 return (addr_t)-1;
992 rt_printf("%s:%d: ", filename, line);
994 else
995 rt_printf("0x%08llx : ", (long long)wanted_pc);
996 rt_printf("%s %s", msg, function ? function : "???");
997 return (addr_t)func_addr;
999 /* ------------------------------------------------------------- */
1001 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level);
1003 static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap)
1005 rt_context *rc = &g_rtctxt;
1006 addr_t pc = 0;
1007 char skip[100];
1008 int i, level, ret, n, one;
1009 const char *a, *b, *msg;
1011 if (fp) {
1012 /* we're called from tcc_backtrace. */
1013 rc->fp = (addr_t)fp;
1014 rc->ip = (addr_t)ip;
1015 msg = "";
1016 } else {
1017 /* we're called from signal/exception handler */
1018 msg = "RUNTIME ERROR: ";
1021 skip[0] = 0;
1022 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
1023 if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) {
1024 memcpy(skip, a, b - a), skip[b - a] = 0;
1025 fmt = b + 1;
1027 one = 0;
1028 /* hack for bcheck.c:dprintf(): one level, no newline */
1029 if (fmt[0] == '\001')
1030 ++fmt, one = 1;
1032 n = rc->num_callers ? rc->num_callers : 6;
1033 for (i = level = 0; level < n; i++) {
1034 ret = rt_get_caller_pc(&pc, rc, i);
1035 a = "%s";
1036 if (ret != -1) {
1037 if (rc->dwarf)
1038 pc = rt_printline_dwarf(rc, pc, level ? "by" : "at", skip);
1039 else
1040 pc = rt_printline(rc, pc, level ? "by" : "at", skip);
1041 if (pc == (addr_t)-1)
1042 continue;
1043 a = ": %s";
1045 if (level == 0) {
1046 rt_printf(a, msg);
1047 rt_vprintf(fmt, ap);
1048 } else if (ret == -1)
1049 break;
1050 if (one)
1051 break;
1052 rt_printf("\n");
1053 if (ret == -1 || (pc == (addr_t)rc->top_func && pc))
1054 break;
1055 ++level;
1058 rc->ip = rc->fp = 0;
1059 return 0;
1062 /* emit a run time error at position 'pc' */
1063 static int rt_error(const char *fmt, ...)
1065 va_list ap;
1066 int ret;
1067 va_start(ap, fmt);
1068 ret = _rt_error(0, 0, fmt, ap);
1069 va_end(ap);
1070 return ret;
1073 /* ------------------------------------------------------------- */
1075 #ifndef _WIN32
1076 # include <signal.h>
1077 # ifndef __OpenBSD__
1078 # include <sys/ucontext.h>
1079 # endif
1080 #else
1081 # define ucontext_t CONTEXT
1082 #endif
1084 /* translate from ucontext_t* to internal rt_context * */
1085 static void rt_getcontext(ucontext_t *uc, rt_context *rc)
1087 #if defined _WIN64
1088 rc->ip = uc->Rip;
1089 rc->fp = uc->Rbp;
1090 rc->sp = uc->Rsp;
1091 #elif defined _WIN32
1092 rc->ip = uc->Eip;
1093 rc->fp = uc->Ebp;
1094 rc->sp = uc->Esp;
1095 #elif defined __i386__
1096 # if defined(__APPLE__)
1097 rc->ip = uc->uc_mcontext->__ss.__eip;
1098 rc->fp = uc->uc_mcontext->__ss.__ebp;
1099 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1100 rc->ip = uc->uc_mcontext.mc_eip;
1101 rc->fp = uc->uc_mcontext.mc_ebp;
1102 # elif defined(__dietlibc__)
1103 rc->ip = uc->uc_mcontext.eip;
1104 rc->fp = uc->uc_mcontext.ebp;
1105 # elif defined(__NetBSD__)
1106 rc->ip = uc->uc_mcontext.__gregs[_REG_EIP];
1107 rc->fp = uc->uc_mcontext.__gregs[_REG_EBP];
1108 # elif defined(__OpenBSD__)
1109 rc->ip = uc->sc_eip;
1110 rc->fp = uc->sc_ebp;
1111 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
1112 rc->ip = uc->uc_mcontext.gregs[EIP];
1113 rc->fp = uc->uc_mcontext.gregs[EBP];
1114 # else
1115 rc->ip = uc->uc_mcontext.gregs[REG_EIP];
1116 rc->fp = uc->uc_mcontext.gregs[REG_EBP];
1117 # endif
1118 #elif defined(__x86_64__)
1119 # if defined(__APPLE__)
1120 rc->ip = uc->uc_mcontext->__ss.__rip;
1121 rc->fp = uc->uc_mcontext->__ss.__rbp;
1122 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
1123 rc->ip = uc->uc_mcontext.mc_rip;
1124 rc->fp = uc->uc_mcontext.mc_rbp;
1125 # elif defined(__NetBSD__)
1126 rc->ip = uc->uc_mcontext.__gregs[_REG_RIP];
1127 rc->fp = uc->uc_mcontext.__gregs[_REG_RBP];
1128 # elif defined(__OpenBSD__)
1129 rc->ip = uc->sc_rip;
1130 rc->fp = uc->sc_rbp;
1131 # else
1132 rc->ip = uc->uc_mcontext.gregs[REG_RIP];
1133 rc->fp = uc->uc_mcontext.gregs[REG_RBP];
1134 # endif
1135 #elif defined(__arm__) && defined(__NetBSD__)
1136 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1137 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1138 #elif defined(__arm__) && defined(__OpenBSD__)
1139 rc->ip = uc->sc_pc;
1140 rc->fp = uc->sc_r11;
1141 #elif defined(__arm__) && defined(__FreeBSD__)
1142 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1143 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1144 #elif defined(__arm__)
1145 rc->ip = uc->uc_mcontext.arm_pc;
1146 rc->fp = uc->uc_mcontext.arm_fp;
1147 #elif defined(__aarch64__) && defined(__APPLE__)
1148 // see:
1149 // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h
1150 rc->ip = uc->uc_mcontext->__ss.__pc;
1151 rc->fp = uc->uc_mcontext->__ss.__fp;
1152 #elif defined(__aarch64__) && defined(__FreeBSD__)
1153 rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */
1154 rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29];
1155 #elif defined(__aarch64__) && defined(__NetBSD__)
1156 rc->ip = uc->uc_mcontext.__gregs[_REG_PC];
1157 rc->fp = uc->uc_mcontext.__gregs[_REG_FP];
1158 #elif defined(__aarch64__) && defined(__OpenBSD__)
1159 rc->ip = uc->sc_elr;
1160 rc->fp = uc->sc_x[29];
1161 #elif defined(__aarch64__)
1162 rc->ip = uc->uc_mcontext.pc;
1163 rc->fp = uc->uc_mcontext.regs[29];
1164 #elif defined(__riscv) && defined(__OpenBSD__)
1165 rc->ip = uc->sc_sepc;
1166 rc->fp = uc->sc_s[0];
1167 #elif defined(__riscv)
1168 rc->ip = uc->uc_mcontext.__gregs[REG_PC];
1169 rc->fp = uc->uc_mcontext.__gregs[REG_S0];
1170 #endif
1173 /* ------------------------------------------------------------- */
1174 #ifndef _WIN32
1175 /* signal handler for fatal errors */
1176 static void sig_error(int signum, siginfo_t *siginf, void *puc)
1178 rt_context *rc = &g_rtctxt;
1179 rt_getcontext(puc, rc);
1181 switch(signum) {
1182 case SIGFPE:
1183 switch(siginf->si_code) {
1184 case FPE_INTDIV:
1185 case FPE_FLTDIV:
1186 rt_error("division by zero");
1187 break;
1188 default:
1189 rt_error("floating point exception");
1190 break;
1192 break;
1193 case SIGBUS:
1194 case SIGSEGV:
1195 rt_error("invalid memory access");
1196 break;
1197 case SIGILL:
1198 rt_error("illegal instruction");
1199 break;
1200 case SIGABRT:
1201 rt_error("abort() called");
1202 break;
1203 default:
1204 rt_error("caught signal %d", signum);
1205 break;
1207 rt_exit(255);
1210 #ifndef SA_SIGINFO
1211 # define SA_SIGINFO 0x00000004u
1212 #endif
1214 /* Generate a stack backtrace when a CPU exception occurs. */
1215 static void set_exception_handler(void)
1217 struct sigaction sigact;
1218 /* install TCC signal handlers to print debug info on fatal
1219 runtime errors */
1220 sigemptyset (&sigact.sa_mask);
1221 sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
1222 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
1223 sigact.sa_flags |= SA_ONSTACK;
1224 #endif
1225 sigact.sa_sigaction = sig_error;
1226 sigemptyset(&sigact.sa_mask);
1227 sigaction(SIGFPE, &sigact, NULL);
1228 sigaction(SIGILL, &sigact, NULL);
1229 sigaction(SIGSEGV, &sigact, NULL);
1230 sigaction(SIGBUS, &sigact, NULL);
1231 sigaction(SIGABRT, &sigact, NULL);
1232 #if 0//def SIGSTKSZ
1233 /* This allows stack overflow to be reported instead of a SEGV */
1235 stack_t ss;
1236 static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16)));
1238 ss.ss_sp = stack;
1239 ss.ss_size = SIGSTKSZ;
1240 ss.ss_flags = 0;
1241 sigaltstack(&ss, NULL);
1243 #endif
1246 #else /* WIN32 */
1248 /* signal handler for fatal errors */
1249 static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
1251 rt_context *rc = &g_rtctxt;
1252 unsigned code;
1253 rt_getcontext(ex_info->ContextRecord, rc);
1255 switch (code = ex_info->ExceptionRecord->ExceptionCode) {
1256 case EXCEPTION_ACCESS_VIOLATION:
1257 rt_error("invalid memory access");
1258 break;
1259 case EXCEPTION_STACK_OVERFLOW:
1260 rt_error("stack overflow");
1261 break;
1262 case EXCEPTION_INT_DIVIDE_BY_ZERO:
1263 rt_error("division by zero");
1264 break;
1265 case EXCEPTION_BREAKPOINT:
1266 case EXCEPTION_SINGLE_STEP:
1267 rc->ip = *(addr_t*)rc->sp;
1268 rt_error("breakpoint/single-step exception:");
1269 return EXCEPTION_CONTINUE_SEARCH;
1270 default:
1271 rt_error("caught exception %08x", code);
1272 break;
1274 if (rc->do_jmp)
1275 rt_exit(255);
1276 return EXCEPTION_EXECUTE_HANDLER;
1279 /* Generate a stack backtrace when a CPU exception occurs. */
1280 static void set_exception_handler(void)
1282 SetUnhandledExceptionFilter(cpu_exception_handler);
1285 #endif
1287 /* ------------------------------------------------------------- */
1288 /* return the PC at frame level 'level'. Return negative if not found */
1289 #if defined(__i386__) || defined(__x86_64__)
1290 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1292 addr_t ip, fp;
1293 if (level == 0) {
1294 ip = rc->ip;
1295 } else {
1296 ip = 0;
1297 fp = rc->fp;
1298 while (--level) {
1299 /* XXX: check address validity with program info */
1300 if (fp <= 0x1000)
1301 break;
1302 fp = ((addr_t *)fp)[0];
1304 if (fp > 0x1000)
1305 ip = ((addr_t *)fp)[1];
1307 if (ip <= 0x1000)
1308 return -1;
1309 *paddr = ip;
1310 return 0;
1313 #elif defined(__arm__)
1314 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1316 /* XXX: only supports linux/bsd */
1317 #if !defined(__linux__) && \
1318 !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
1319 return -1;
1320 #else
1321 if (level == 0) {
1322 *paddr = rc->ip;
1323 } else {
1324 addr_t fp = rc->fp;
1325 while (--level)
1326 fp = ((addr_t *)fp)[0];
1327 *paddr = ((addr_t *)fp)[2];
1329 return 0;
1330 #endif
1333 #elif defined(__aarch64__)
1334 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1336 if (level == 0) {
1337 *paddr = rc->ip;
1338 } else {
1339 addr_t *fp = (addr_t*)rc->fp;
1340 while (--level)
1341 fp = (addr_t *)fp[0];
1342 *paddr = fp[1];
1344 return 0;
1347 #elif defined(__riscv)
1348 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1350 if (level == 0) {
1351 *paddr = rc->ip;
1352 } else {
1353 addr_t *fp = (addr_t*)rc->fp;
1354 while (--level && fp >= (addr_t*)0x1000)
1355 fp = (addr_t *)fp[-2];
1356 if (fp < (addr_t*)0x1000)
1357 return -1;
1358 *paddr = fp[-1];
1360 return 0;
1363 #else
1364 #warning add arch specific rt_get_caller_pc()
1365 static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level)
1367 return -1;
1370 #endif
1371 #endif /* CONFIG_TCC_BACKTRACE */
1372 /* ------------------------------------------------------------- */
1373 #ifdef CONFIG_TCC_STATIC
1375 /* dummy function for profiling */
1376 ST_FUNC void *dlopen(const char *filename, int flag)
1378 return NULL;
1381 ST_FUNC void dlclose(void *p)
1385 ST_FUNC const char *dlerror(void)
1387 return "error";
1390 typedef struct TCCSyms {
1391 char *str;
1392 void *ptr;
1393 } TCCSyms;
1396 /* add the symbol you want here if no dynamic linking is done */
1397 static TCCSyms tcc_syms[] = {
1398 #if !defined(CONFIG_TCCBOOT)
1399 #define TCCSYM(a) { #a, &a, },
1400 TCCSYM(printf)
1401 TCCSYM(fprintf)
1402 TCCSYM(fopen)
1403 TCCSYM(fclose)
1404 #undef TCCSYM
1405 #endif
1406 { NULL, NULL },
1409 ST_FUNC void *dlsym(void *handle, const char *symbol)
1411 TCCSyms *p;
1412 p = tcc_syms;
1413 while (p->str != NULL) {
1414 if (!strcmp(p->str, symbol))
1415 return p->ptr;
1416 p++;
1418 return NULL;
1421 #endif /* CONFIG_TCC_STATIC */
1422 #endif /* TCC_IS_NATIVE */
1423 /* ------------------------------------------------------------- */