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
23 /* only native compiler supports -run */
26 #ifdef CONFIG_TCC_BACKTRACE
27 typedef struct rt_context
29 /* --> tccelf.c:tcc_add_btstub wants those below in that order: */
30 Stab_Sym
*stab_sym
, *stab_sym_end
;
32 ElfW(Sym
) *esym_start
, *esym_end
;
36 struct rt_context
*next
;
45 static rt_context g_rtctxt
;
46 static void set_exception_handler(void);
47 static int _rt_error(void *fp
, void *ip
, const char *fmt
, va_list ap
);
48 static void rt_exit(int code
);
49 #endif /* CONFIG_TCC_BACKTRACE */
51 /* defined when included from lib/bt-exe.c */
52 #ifndef CONFIG_TCC_BACKTRACE_ONLY
55 # include <sys/mman.h>
58 static void set_pages_executable(TCCState
*s1
, void *ptr
, unsigned long length
);
59 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
);
62 static void *win64_add_function_table(TCCState
*s1
);
63 static void win64_del_function_table(void *);
66 /* ------------------------------------------------------------- */
67 /* Do all relocations (needed before using tcc_get_symbol())
68 Returns -1 on error. */
70 LIBTCCAPI
int tcc_relocate(TCCState
*s1
, void *ptr
)
75 if (TCC_RELOCATE_AUTO
!= ptr
)
76 return tcc_relocate_ex(s1
, ptr
, 0);
78 size
= tcc_relocate_ex(s1
, NULL
, 0);
84 /* Using mmap instead of malloc */
86 char tmpfname
[] = "/tmp/.tccrunXXXXXX";
87 int fd
= mkstemp(tmpfname
);
91 ptr
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
92 prx
= mmap (NULL
, size
, PROT_READ
|PROT_EXEC
, MAP_SHARED
, fd
, 0);
93 if (ptr
== MAP_FAILED
|| prx
== MAP_FAILED
)
94 tcc_error("tccrun: could not map memory");
95 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, (void*)(addr_t
)size
);
96 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, prx
);
97 ptr_diff
= (char*)prx
- (char*)ptr
;
101 ptr
= tcc_malloc(size
);
103 tcc_relocate_ex(s1
, ptr
, ptr_diff
); /* no more errors expected */
104 dynarray_add(&s1
->runtime_mem
, &s1
->nb_runtime_mem
, ptr
);
108 ST_FUNC
void tcc_run_free(TCCState
*s1
)
112 for (i
= 0; i
< s1
->nb_runtime_mem
; ++i
) {
114 unsigned size
= (unsigned)(addr_t
)s1
->runtime_mem
[i
++];
115 munmap(s1
->runtime_mem
[i
++], size
);
116 munmap(s1
->runtime_mem
[i
], size
);
119 win64_del_function_table(*(void**)s1
->runtime_mem
[i
]);
121 tcc_free(s1
->runtime_mem
[i
]);
124 tcc_free(s1
->runtime_mem
);
127 static void run_cdtors(TCCState
*s1
, const char *start
, const char *end
,
128 int argc
, char **argv
, char **envp
)
130 void **a
= (void **)get_sym_addr(s1
, start
, 0, 0);
131 void **b
= (void **)get_sym_addr(s1
, end
, 0, 0);
133 ((void(*)(int, char **, char **))*a
++)(argc
, argv
, envp
);
136 /* launch the compiled program with the given arguments */
137 LIBTCCAPI
int tcc_run(TCCState
*s1
, int argc
, char **argv
)
139 int (*prog_main
)(int, char **, char **), ret
;
140 #ifdef CONFIG_TCC_BACKTRACE
141 rt_context
*rc
= &g_rtctxt
;
143 # if defined(__APPLE__)
146 char **envp
= environ
;
149 s1
->runtime_main
= s1
->nostdlib
? "_start" : "main";
150 if ((s1
->dflag
& 16) && (addr_t
)-1 == get_sym_addr(s1
, s1
->runtime_main
, 0, 1))
152 #ifdef CONFIG_TCC_BACKTRACE
154 tcc_add_symbol(s1
, "exit", rt_exit
);
156 if (tcc_relocate(s1
, TCC_RELOCATE_AUTO
) < 0)
158 prog_main
= (void*)get_sym_addr(s1
, s1
->runtime_main
, 1, 1);
160 #ifdef CONFIG_TCC_BACKTRACE
161 memset(rc
, 0, sizeof *rc
);
164 rc
->stab_sym
= (Stab_Sym
*)stab_section
->data
;
165 rc
->stab_sym_end
= (Stab_Sym
*)(stab_section
->data
+ stab_section
->data_offset
);
166 rc
->stab_str
= (char *)stab_section
->link
->data
;
167 rc
->esym_start
= (ElfW(Sym
) *)(symtab_section
->data
);
168 rc
->esym_end
= (ElfW(Sym
) *)(symtab_section
->data
+ symtab_section
->data_offset
);
169 rc
->elf_str
= (char *)symtab_section
->link
->data
;
171 rc
->prog_base
= text_section
->sh_addr
& 0xffffffff00000000ULL
;
173 rc
->top_func
= tcc_get_symbol(s1
, "main");
174 rc
->num_callers
= s1
->rt_num_callers
;
176 if ((p
= tcc_get_symbol(s1
, "__rt_error")))
177 *(void**)p
= _rt_error
;
178 #ifdef CONFIG_TCC_BCHECK
179 if (s1
->do_bounds_check
) {
180 if ((p
= tcc_get_symbol(s1
, "__bound_init")))
181 ((void(*)(void*, int))p
)(bounds_section
->data
, 1);
184 set_exception_handler();
188 errno
= 0; /* clean errno value */
191 /* These aren't C symbols, so don't need leading underscore handling. */
192 run_cdtors(s1
, "__init_array_start", "__init_array_end", argc
, argv
, envp
);
193 #ifdef CONFIG_TCC_BACKTRACE
194 if (!rc
->do_jmp
|| !(ret
= setjmp(rc
->jmp_buf)))
197 ret
= prog_main(argc
, argv
, envp
);
199 run_cdtors(s1
, "__fini_array_start", "__fini_array_end", 0, NULL
, NULL
);
200 if ((s1
->dflag
& 16) && ret
)
201 fprintf(s1
->ppfp
, "[returns %d]\n", ret
), fflush(s1
->ppfp
);
205 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
206 /* To avoid that x86 processors would reload cached instructions
207 each time when data is written in the near, we need to make
208 sure that code and data do not share the same 64 byte unit */
209 #define RUN_SECTION_ALIGNMENT 63
211 #define RUN_SECTION_ALIGNMENT 0
214 /* relocate code. Return -1 on error, required size if ptr is NULL,
215 otherwise copy code into buffer passed by the caller */
216 static int tcc_relocate_ex(TCCState
*s1
, void *ptr
, addr_t ptr_diff
)
219 unsigned offset
, length
, align
, max_align
, i
, k
, f
;
225 pe_output_file(s1
, NULL
);
228 resolve_common_syms(s1
);
229 build_got_entries(s1
);
235 offset
= max_align
= 0, mem
= (addr_t
)ptr
;
237 offset
+= sizeof (void*); /* space for function_table pointer */
239 for (k
= 0; k
< 2; ++k
) {
240 f
= 0, addr
= k
? mem
: mem
+ ptr_diff
;
241 for(i
= 1; i
< s1
->nb_sections
; i
++) {
243 if (0 == (s
->sh_flags
& SHF_ALLOC
))
245 if (k
!= !(s
->sh_flags
& SHF_EXECINSTR
))
247 align
= s
->sh_addralign
- 1;
248 if (++f
== 1 && align
< RUN_SECTION_ALIGNMENT
)
249 align
= RUN_SECTION_ALIGNMENT
;
250 if (max_align
< align
)
252 offset
+= -(addr
+ offset
) & align
;
253 s
->sh_addr
= mem
? addr
+ offset
: 0;
254 offset
+= s
->data_offset
;
257 printf("%-16s %p len %04x align %2d\n",
258 s
->name
, (void*)s
->sh_addr
, (unsigned)s
->data_offset
, align
+ 1);
263 /* relocate symbols */
264 relocate_syms(s1
, s1
->symtab
, !(s1
->nostdlib
));
269 return offset
+ max_align
;
272 s1
->pe_imagebase
= mem
;
275 /* relocate each section */
276 for(i
= 1; i
< s1
->nb_sections
; i
++) {
279 relocate_section(s1
, s
);
281 #if !defined(TCC_TARGET_PE) || defined(TCC_TARGET_MACHO)
285 for(i
= 1; i
< s1
->nb_sections
; i
++) {
287 if (0 == (s
->sh_flags
& SHF_ALLOC
))
289 length
= s
->data_offset
;
290 ptr
= (void*)s
->sh_addr
;
291 if (s
->sh_flags
& SHF_EXECINSTR
)
292 ptr
= (char*)((addr_t
)ptr
- ptr_diff
);
293 if (NULL
== s
->data
|| s
->sh_type
== SHT_NOBITS
)
294 memset(ptr
, 0, length
);
296 memcpy(ptr
, s
->data
, length
);
297 /* mark executable sections as executable in memory */
298 if (s
->sh_flags
& SHF_EXECINSTR
)
299 set_pages_executable(s1
, (char*)((addr_t
)ptr
+ ptr_diff
), length
);
303 *(void**)mem
= win64_add_function_table(s1
);
309 /* ------------------------------------------------------------- */
310 /* allow to run code in memory */
312 static void set_pages_executable(TCCState
*s1
, void *ptr
, unsigned long length
)
315 unsigned long old_protect
;
316 VirtualProtect(ptr
, length
, PAGE_EXECUTE_READWRITE
, &old_protect
);
318 void __clear_cache(void *beginning
, void *end
);
319 # ifndef HAVE_SELINUX
322 # define PAGESIZE 4096
324 start
= (addr_t
)ptr
& ~(PAGESIZE
- 1);
325 end
= (addr_t
)ptr
+ length
;
326 end
= (end
+ PAGESIZE
- 1) & ~(PAGESIZE
- 1);
327 if (mprotect((void *)start
, end
- start
, PROT_READ
| PROT_WRITE
| PROT_EXEC
))
328 tcc_error("mprotect failed: did you mean to configure --with-selinux?");
330 # if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
331 __clear_cache(ptr
, (char *)ptr
+ length
);
337 static void *win64_add_function_table(TCCState
*s1
)
341 p
= (void*)s1
->uw_pdata
->sh_addr
;
343 (RUNTIME_FUNCTION
*)p
,
344 s1
->uw_pdata
->data_offset
/ sizeof (RUNTIME_FUNCTION
),
352 static void win64_del_function_table(void *p
)
355 RtlDeleteFunctionTable((RUNTIME_FUNCTION
*)p
);
359 #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
360 /* ------------------------------------------------------------- */
361 #ifdef CONFIG_TCC_BACKTRACE
363 static int rt_vprintf(const char *fmt
, va_list ap
)
365 int ret
= vfprintf(stderr
, fmt
, ap
);
370 static int rt_printf(const char *fmt
, ...)
375 r
= rt_vprintf(fmt
, ap
);
380 #define INCLUDE_STACK_SIZE 32
382 /* print the position in the source file of PC value 'pc' by reading
383 the stabs debug information */
384 static addr_t
rt_printline (rt_context
*rc
, addr_t wanted_pc
,
385 const char *msg
, const char *skip
)
388 addr_t func_addr
, last_pc
, pc
;
389 const char *incl_files
[INCLUDE_STACK_SIZE
];
390 int incl_index
, last_incl_index
, len
, last_line_num
, i
;
399 last_pc
= (addr_t
)-1;
403 for (sym
= rc
->stab_sym
+ 1; sym
< rc
->stab_sym_end
; ++sym
) {
404 str
= rc
->stab_str
+ sym
->n_strx
;
407 switch(sym
->n_type
) {
415 if (sym
->n_strx
== 0) /* end of function */
419 /* Stab_Sym.n_value is only 32bits */
426 if (pc
>= wanted_pc
&& wanted_pc
>= last_pc
)
431 switch(sym
->n_type
) {
432 /* function start or end */
434 if (sym
->n_strx
== 0)
436 p
= strchr(str
, ':');
437 if (0 == p
|| (len
= p
- str
+ 1, len
> sizeof func_name
))
438 len
= sizeof func_name
;
439 pstrcpy(func_name
, len
, str
);
442 /* line number info */
445 last_line_num
= sym
->n_desc
;
446 last_incl_index
= incl_index
;
450 if (incl_index
< INCLUDE_STACK_SIZE
)
451 incl_files
[incl_index
++] = str
;
457 /* start/end of translation unit */
461 /* do not add path */
463 if (len
> 0 && str
[len
- 1] != '/')
464 incl_files
[incl_index
++] = str
;
469 last_pc
= (addr_t
)-1;
471 /* alternative file name (from #line or #include directives) */
474 incl_files
[incl_index
-1] = str
;
483 /* we try symtab symbols (no line number info) */
484 for (esym
= rc
->esym_start
+ 1; esym
< rc
->esym_end
; ++esym
) {
485 int type
= ELFW(ST_TYPE
)(esym
->st_info
);
486 if (type
== STT_FUNC
|| type
== STT_GNU_IFUNC
) {
487 if (wanted_pc
>= esym
->st_value
&&
488 wanted_pc
< esym
->st_value
+ esym
->st_size
) {
489 pstrcpy(func_name
, sizeof(func_name
),
490 rc
->elf_str
+ esym
->st_name
);
491 func_addr
= esym
->st_value
;
503 str
= incl_files
[--i
];
504 if (skip
[0] && strstr(str
, skip
))
506 rt_printf("%s:%d: ", str
, last_line_num
);
508 rt_printf("%08llx : ", (long long)wanted_pc
);
509 rt_printf("%s %s", msg
, func_name
[0] ? func_name
: "???");
512 rt_printf(" (included from ");
514 rt_printf("%s", incl_files
[i
]);
525 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
);
527 static int _rt_error(void *fp
, void *ip
, const char *fmt
, va_list ap
)
529 rt_context
*rc
= &g_rtctxt
;
532 int i
, level
, ret
, n
;
533 const char *a
, *b
, *msg
;
536 /* we're called from tcc_backtrace. */
541 /* we're called from signal/exception handler */
542 msg
= "RUNTIME ERROR: ";
546 /* If fmt is like "^file.c^..." then skip calls from 'file.c' */
547 if (fmt
[0] == '^' && (b
= strchr(a
= fmt
+ 1, fmt
[0]))) {
548 memcpy(skip
, a
, b
- a
), skip
[b
- a
] = 0;
552 n
= rc
->num_callers
? rc
->num_callers
: 6;
553 for (i
= level
= 0; level
< n
; i
++) {
554 ret
= rt_get_caller_pc(&pc
, rc
, i
);
557 pc
= rt_printline(rc
, pc
, level
? "by" : "at", skip
);
558 if (pc
== (addr_t
)-1)
565 } else if (ret
== -1)
568 if (ret
== -1 || (pc
== (addr_t
)rc
->top_func
&& pc
))
577 /* emit a run time error at position 'pc' */
578 static int rt_error(const char *fmt
, ...)
583 ret
= _rt_error(0, 0, fmt
, ap
);
588 static void rt_exit(int code
)
590 rt_context
*rc
= &g_rtctxt
;
592 longjmp(rc
->jmp_buf, code
? code
: 256);
596 /* ------------------------------------------------------------- */
601 # include <sys/ucontext.h>
604 # define ucontext_t CONTEXT
607 /* translate from ucontext_t* to internal rt_context * */
608 static void rt_getcontext(ucontext_t
*uc
, rt_context
*rc
)
618 #elif defined __i386__
619 # if defined(__APPLE__)
620 rc
->ip
= uc
->uc_mcontext
->__ss
.__eip
;
621 rc
->fp
= uc
->uc_mcontext
->__ss
.__ebp
;
622 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
623 rc
->ip
= uc
->uc_mcontext
.mc_eip
;
624 rc
->fp
= uc
->uc_mcontext
.mc_ebp
;
625 # elif defined(__dietlibc__)
626 rc
->ip
= uc
->uc_mcontext
.eip
;
627 rc
->fp
= uc
->uc_mcontext
.ebp
;
628 # elif defined(__NetBSD__)
629 rc
->ip
= uc
->uc_mcontext
.__gregs
[_REG_EIP
];
630 rc
->fp
= uc
->uc_mcontext
.__gregs
[_REG_EBP
];
631 # elif defined(__OpenBSD__)
634 # elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */
635 rc
->ip
= uc
->uc_mcontext
.gregs
[EIP
];
636 rc
->fp
= uc
->uc_mcontext
.gregs
[EBP
];
638 rc
->ip
= uc
->uc_mcontext
.gregs
[REG_EIP
];
639 rc
->fp
= uc
->uc_mcontext
.gregs
[REG_EBP
];
641 #elif defined(__x86_64__)
642 # if defined(__APPLE__)
643 rc
->ip
= uc
->uc_mcontext
->__ss
.__rip
;
644 rc
->fp
= uc
->uc_mcontext
->__ss
.__rbp
;
645 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
646 rc
->ip
= uc
->uc_mcontext
.mc_rip
;
647 rc
->fp
= uc
->uc_mcontext
.mc_rbp
;
648 # elif defined(__NetBSD__)
649 rc
->ip
= uc
->uc_mcontext
.__gregs
[_REG_RIP
];
650 rc
->fp
= uc
->uc_mcontext
.__gregs
[_REG_RBP
];
652 rc
->ip
= uc
->uc_mcontext
.gregs
[REG_RIP
];
653 rc
->fp
= uc
->uc_mcontext
.gregs
[REG_RBP
];
655 #elif defined(__arm__)
656 rc
->ip
= uc
->uc_mcontext
.arm_pc
;
657 rc
->fp
= uc
->uc_mcontext
.arm_fp
;
658 #elif defined(__aarch64__)
659 rc
->ip
= uc
->uc_mcontext
.pc
;
660 rc
->fp
= uc
->uc_mcontext
.regs
[29];
661 #elif defined(__riscv)
662 rc
->ip
= uc
->uc_mcontext
.__gregs
[REG_PC
];
663 rc
->fp
= uc
->uc_mcontext
.__gregs
[REG_S0
];
667 /* ------------------------------------------------------------- */
669 /* signal handler for fatal errors */
670 static void sig_error(int signum
, siginfo_t
*siginf
, void *puc
)
672 rt_context
*rc
= &g_rtctxt
;
673 rt_getcontext(puc
, rc
);
677 switch(siginf
->si_code
) {
680 rt_error("division by zero");
683 rt_error("floating point exception");
689 rt_error("invalid memory access");
692 rt_error("illegal instruction");
695 rt_error("abort() called");
698 rt_error("caught signal %d", signum
);
705 # define SA_SIGINFO 0x00000004u
708 /* Generate a stack backtrace when a CPU exception occurs. */
709 static void set_exception_handler(void)
711 struct sigaction sigact
;
712 /* install TCC signal handlers to print debug info on fatal
714 sigact
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
715 #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
716 sigact
.sa_flags
|= SA_ONSTACK
;
718 sigact
.sa_sigaction
= sig_error
;
719 sigemptyset(&sigact
.sa_mask
);
720 sigaction(SIGFPE
, &sigact
, NULL
);
721 sigaction(SIGILL
, &sigact
, NULL
);
722 sigaction(SIGSEGV
, &sigact
, NULL
);
723 sigaction(SIGBUS
, &sigact
, NULL
);
724 sigaction(SIGABRT
, &sigact
, NULL
);
726 /* This allows stack overflow to be reported instead of a SEGV */
729 static unsigned char stack
[SIGSTKSZ
] __attribute__((aligned(16)));
732 ss
.ss_size
= SIGSTKSZ
;
734 sigaltstack(&ss
, NULL
);
741 /* signal handler for fatal errors */
742 static long __stdcall
cpu_exception_handler(EXCEPTION_POINTERS
*ex_info
)
744 rt_context
*rc
= &g_rtctxt
;
746 rt_getcontext(ex_info
->ContextRecord
, rc
);
748 switch (code
= ex_info
->ExceptionRecord
->ExceptionCode
) {
749 case EXCEPTION_ACCESS_VIOLATION
:
750 rt_error("invalid memory access");
752 case EXCEPTION_STACK_OVERFLOW
:
753 rt_error("stack overflow");
755 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
756 rt_error("division by zero");
758 case EXCEPTION_BREAKPOINT
:
759 case EXCEPTION_SINGLE_STEP
:
760 rc
->ip
= *(addr_t
*)rc
->sp
;
761 rt_error("breakpoint/single-step exception:");
762 return EXCEPTION_CONTINUE_SEARCH
;
764 rt_error("caught exception %08x", code
);
769 return EXCEPTION_EXECUTE_HANDLER
;
772 /* Generate a stack backtrace when a CPU exception occurs. */
773 static void set_exception_handler(void)
775 SetUnhandledExceptionFilter(cpu_exception_handler
);
780 /* ------------------------------------------------------------- */
781 /* return the PC at frame level 'level'. Return negative if not found */
782 #if defined(__i386__) || defined(__x86_64__)
783 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
792 /* XXX: check address validity with program info */
795 fp
= ((addr_t
*)fp
)[0];
798 ip
= ((addr_t
*)fp
)[1];
806 #elif defined(__arm__)
807 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
809 /* XXX: only supports linux */
810 #if !defined(__linux__)
818 fp
= ((addr_t
*)fp
)[0];
819 *paddr
= ((addr_t
*)fp
)[2];
825 #elif defined(__aarch64__)
826 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
831 addr_t
*fp
= (addr_t
*)rc
->fp
;
833 fp
= (addr_t
*)fp
[0];
839 #elif defined(__riscv)
840 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
845 addr_t
*fp
= (addr_t
*)rc
->fp
;
846 while (--level
&& fp
>= (addr_t
*)0x1000)
847 fp
= (addr_t
*)fp
[-2];
848 if (fp
< (addr_t
*)0x1000)
856 #warning add arch specific rt_get_caller_pc()
857 static int rt_get_caller_pc(addr_t
*paddr
, rt_context
*rc
, int level
)
863 #endif /* CONFIG_TCC_BACKTRACE */
864 /* ------------------------------------------------------------- */
865 #ifdef CONFIG_TCC_STATIC
867 /* dummy function for profiling */
868 ST_FUNC
void *dlopen(const char *filename
, int flag
)
873 ST_FUNC
void dlclose(void *p
)
877 ST_FUNC
const char *dlerror(void)
882 typedef struct TCCSyms
{
888 /* add the symbol you want here if no dynamic linking is done */
889 static TCCSyms tcc_syms
[] = {
890 #if !defined(CONFIG_TCCBOOT)
891 #define TCCSYM(a) { #a, &a, },
901 ST_FUNC
void *dlsym(void *handle
, const char *symbol
)
905 while (p
->str
!= NULL
) {
906 if (!strcmp(p
->str
, symbol
))
913 #endif /* CONFIG_TCC_STATIC */
914 #endif /* TCC_IS_NATIVE */
915 /* ------------------------------------------------------------- */