Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6429 | siemargl | 1 | /* |
2 | * TCC - Tiny C Compiler - Support for -run switch |
||
3 | * |
||
4 | * Copyright (c) 2001-2004 Fabrice Bellard |
||
5 | * |
||
6 | * This library is free software; you can redistribute it and/or |
||
7 | * modify it under the terms of the GNU Lesser General Public |
||
8 | * License as published by the Free Software Foundation; either |
||
9 | * version 2 of the License, or (at your option) any later version. |
||
10 | * |
||
11 | * This library is distributed in the hope that it will be useful, |
||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
14 | * Lesser General Public License for more details. |
||
15 | * |
||
16 | * You should have received a copy of the GNU Lesser General Public |
||
17 | * License along with this library; if not, write to the Free Software |
||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
19 | */ |
||
20 | |||
21 | #include "tcc.h" |
||
22 | |||
23 | /* only native compiler supports -run */ |
||
24 | #ifdef TCC_IS_NATIVE |
||
25 | |||
26 | #ifdef CONFIG_TCC_BACKTRACE |
||
27 | ST_DATA int rt_num_callers = 6; |
||
28 | ST_DATA const char **rt_bound_error_msg; |
||
29 | ST_DATA void *rt_prog_main; |
||
30 | #endif |
||
31 | |||
32 | #ifdef _WIN32 |
||
33 | #define ucontext_t CONTEXT |
||
34 | #endif |
||
35 | |||
36 | static void set_pages_executable(void *ptr, unsigned long length); |
||
37 | static void set_exception_handler(void); |
||
38 | static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level); |
||
39 | static void rt_error(ucontext_t *uc, const char *fmt, ...); |
||
40 | static int tcc_relocate_ex(TCCState *s1, void *ptr); |
||
41 | |||
42 | #ifdef _WIN64 |
||
43 | static void win64_add_function_table(TCCState *s1); |
||
44 | #endif |
||
45 | |||
46 | /* ------------------------------------------------------------- */ |
||
47 | /* Do all relocations (needed before using tcc_get_symbol()) |
||
48 | Returns -1 on error. */ |
||
49 | |||
50 | LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr) |
||
51 | { |
||
52 | int ret; |
||
53 | |||
54 | if (TCC_RELOCATE_AUTO != ptr) |
||
55 | return tcc_relocate_ex(s1, ptr); |
||
56 | |||
57 | ret = tcc_relocate_ex(s1, NULL); |
||
58 | if (ret < 0) |
||
59 | return ret; |
||
60 | |||
61 | #ifdef HAVE_SELINUX |
||
62 | { /* Use mmap instead of malloc for Selinux. Ref: |
||
63 | http://www.gnu.org/s/libc/manual/html_node/File-Size.html */ |
||
64 | |||
65 | char tmpfname[] = "/tmp/.tccrunXXXXXX"; |
||
66 | int fd = mkstemp (tmpfname); |
||
67 | |||
68 | s1->mem_size = ret; |
||
69 | unlink (tmpfname); |
||
70 | ftruncate (fd, s1->mem_size); |
||
71 | |||
72 | s1->write_mem = mmap (NULL, ret, PROT_READ|PROT_WRITE, |
||
73 | MAP_SHARED, fd, 0); |
||
74 | if (s1->write_mem == MAP_FAILED) |
||
75 | tcc_error("/tmp not writeable"); |
||
76 | |||
77 | s1->runtime_mem = mmap (NULL, ret, PROT_READ|PROT_EXEC, |
||
78 | MAP_SHARED, fd, 0); |
||
79 | if (s1->runtime_mem == MAP_FAILED) |
||
80 | tcc_error("/tmp not executable"); |
||
81 | |||
82 | ret = tcc_relocate_ex(s1, s1->write_mem); |
||
83 | } |
||
84 | #else |
||
85 | s1->runtime_mem = tcc_malloc(ret); |
||
86 | ret = tcc_relocate_ex(s1, s1->runtime_mem); |
||
87 | #endif |
||
88 | return ret; |
||
89 | } |
||
90 | |||
91 | /* launch the compiled program with the given arguments */ |
||
92 | LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) |
||
93 | { |
||
94 | int (*prog_main)(int, char **); |
||
95 | int ret; |
||
96 | |||
97 | if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0) |
||
98 | return -1; |
||
99 | |||
100 | prog_main = tcc_get_symbol_err(s1, s1->runtime_main); |
||
101 | |||
102 | #ifdef CONFIG_TCC_BACKTRACE |
||
103 | if (s1->do_debug) { |
||
104 | set_exception_handler(); |
||
105 | rt_prog_main = prog_main; |
||
106 | } |
||
107 | #endif |
||
108 | |||
109 | #ifdef CONFIG_TCC_BCHECK |
||
110 | if (s1->do_bounds_check) { |
||
111 | void (*bound_init)(void); |
||
112 | void (*bound_exit)(void); |
||
113 | void (*bound_new_region)(void *p, addr_t size); |
||
114 | int (*bound_delete_region)(void *p); |
||
115 | int i; |
||
116 | |||
117 | /* set error function */ |
||
118 | rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg"); |
||
119 | /* XXX: use .init section so that it also work in binary ? */ |
||
120 | bound_init = tcc_get_symbol_err(s1, "__bound_init"); |
||
121 | bound_exit = tcc_get_symbol_err(s1, "__bound_exit"); |
||
122 | bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region"); |
||
123 | bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region"); |
||
124 | bound_init(); |
||
125 | /* mark argv area as valid */ |
||
126 | bound_new_region(argv, argc*sizeof(argv[0])); |
||
127 | for (i=0; i |
||
128 | bound_new_region(argv[i], strlen(argv[i])); |
||
129 | |||
130 | errno = 0; /* clean errno value */ |
||
131 | ret = (*prog_main)(argc, argv); |
||
132 | |||
133 | /* unmark argv area */ |
||
134 | for (i=0; i |
||
135 | bound_delete_region(argv[i]); |
||
136 | bound_delete_region(argv); |
||
137 | |||
138 | bound_exit(); |
||
139 | } else |
||
140 | #endif |
||
141 | { |
||
142 | errno = 0; /* clean errno value */ |
||
143 | ret = (*prog_main)(argc, argv); |
||
144 | } |
||
145 | return ret; |
||
146 | } |
||
147 | |||
148 | /* relocate code. Return -1 on error, required size if ptr is NULL, |
||
149 | otherwise copy code into buffer passed by the caller */ |
||
150 | static int tcc_relocate_ex(TCCState *s1, void *ptr) |
||
151 | { |
||
152 | Section *s; |
||
153 | unsigned long offset, length; |
||
154 | addr_t mem; |
||
155 | int i; |
||
156 | |||
157 | if (NULL == ptr) { |
||
158 | s1->nb_errors = 0; |
||
159 | #ifdef TCC_TARGET_PE |
||
160 | pe_output_file(s1, NULL); |
||
161 | #else |
||
162 | tcc_add_runtime(s1); |
||
163 | relocate_common_syms(); |
||
164 | tcc_add_linker_symbols(s1); |
||
165 | build_got_entries(s1); |
||
166 | #endif |
||
167 | if (s1->nb_errors) |
||
168 | return -1; |
||
169 | } |
||
170 | |||
171 | offset = 0, mem = (addr_t)ptr; |
||
172 | for(i = 1; i < s1->nb_sections; i++) { |
||
173 | s = s1->sections[i]; |
||
174 | if (0 == (s->sh_flags & SHF_ALLOC)) |
||
175 | continue; |
||
176 | length = s->data_offset; |
||
177 | s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0; |
||
178 | offset = (offset + length + 15) & ~15; |
||
179 | } |
||
180 | offset += 16; |
||
181 | |||
182 | /* relocate symbols */ |
||
183 | relocate_syms(s1, 1); |
||
184 | if (s1->nb_errors) |
||
185 | return -1; |
||
186 | |||
187 | if (0 == mem) |
||
188 | return offset; |
||
189 | |||
190 | /* relocate each section */ |
||
191 | for(i = 1; i < s1->nb_sections; i++) { |
||
192 | s = s1->sections[i]; |
||
193 | if (s->reloc) |
||
194 | relocate_section(s1, s); |
||
195 | } |
||
196 | relocate_plt(s1); |
||
197 | |||
198 | for(i = 1; i < s1->nb_sections; i++) { |
||
199 | s = s1->sections[i]; |
||
200 | if (0 == (s->sh_flags & SHF_ALLOC)) |
||
201 | continue; |
||
202 | length = s->data_offset; |
||
203 | // printf("%-12s %08lx %04x\n", s->name, s->sh_addr, length); |
||
204 | ptr = (void*)s->sh_addr; |
||
205 | if (NULL == s->data || s->sh_type == SHT_NOBITS) |
||
206 | memset(ptr, 0, length); |
||
207 | else |
||
208 | memcpy(ptr, s->data, length); |
||
209 | /* mark executable sections as executable in memory */ |
||
210 | if (s->sh_flags & SHF_EXECINSTR) |
||
211 | set_pages_executable(ptr, length); |
||
212 | } |
||
213 | |||
214 | #ifdef _WIN64 |
||
215 | win64_add_function_table(s1); |
||
216 | #endif |
||
217 | return 0; |
||
218 | } |
||
219 | |||
220 | /* ------------------------------------------------------------- */ |
||
221 | /* allow to run code in memory */ |
||
222 | |||
223 | static void set_pages_executable(void *ptr, unsigned long length) |
||
224 | { |
||
225 | #ifdef _WIN32 |
||
226 | unsigned long old_protect; |
||
227 | VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect); |
||
228 | #else |
||
229 | extern void __clear_cache(void *beginning, void *end); |
||
230 | #ifndef PAGESIZE |
||
231 | # define PAGESIZE 4096 |
||
232 | #endif |
||
233 | addr_t start, end; |
||
234 | start = (addr_t)ptr & ~(PAGESIZE - 1); |
||
235 | end = (addr_t)ptr + length; |
||
236 | end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1); |
||
237 | mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC); |
||
238 | #ifndef __PCC__ |
||
239 | __clear_cache(ptr, (char *)ptr + length); |
||
240 | #else |
||
241 | /* pcc 1.2.0.DEVEL 20141206 don't have such proc */ |
||
242 | #endif |
||
243 | #endif |
||
244 | } |
||
245 | |||
246 | /* ------------------------------------------------------------- */ |
||
247 | #ifdef CONFIG_TCC_BACKTRACE |
||
248 | |||
249 | ST_FUNC void tcc_set_num_callers(int n) |
||
250 | { |
||
251 | rt_num_callers = n; |
||
252 | } |
||
253 | |||
254 | /* print the position in the source file of PC value 'pc' by reading |
||
255 | the stabs debug information */ |
||
256 | static addr_t rt_printline(addr_t wanted_pc, const char *msg) |
||
257 | { |
||
258 | char func_name[128], last_func_name[128]; |
||
259 | addr_t func_addr, last_pc, pc; |
||
260 | const char *incl_files[INCLUDE_STACK_SIZE]; |
||
261 | int incl_index, len, last_line_num, i; |
||
262 | const char *str, *p; |
||
263 | |||
264 | Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym; |
||
265 | int stab_len = 0; |
||
266 | char *stab_str = NULL; |
||
267 | |||
268 | if (stab_section) { |
||
269 | stab_len = stab_section->data_offset; |
||
270 | stab_sym = (Stab_Sym *)stab_section->data; |
||
271 | stab_str = (char *) stabstr_section->data; |
||
272 | } |
||
273 | |||
274 | func_name[0] = '\0'; |
||
275 | func_addr = 0; |
||
276 | incl_index = 0; |
||
277 | last_func_name[0] = '\0'; |
||
278 | last_pc = (addr_t)-1; |
||
279 | last_line_num = 1; |
||
280 | |||
281 | if (!stab_sym) |
||
282 | goto no_stabs; |
||
283 | |||
284 | stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len); |
||
285 | for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) { |
||
286 | switch(sym->n_type) { |
||
287 | /* function start or end */ |
||
288 | case N_FUN: |
||
289 | if (sym->n_strx == 0) { |
||
290 | /* we test if between last line and end of function */ |
||
291 | pc = sym->n_value + func_addr; |
||
292 | if (wanted_pc >= last_pc && wanted_pc < pc) |
||
293 | goto found; |
||
294 | func_name[0] = '\0'; |
||
295 | func_addr = 0; |
||
296 | } else { |
||
297 | str = stab_str + sym->n_strx; |
||
298 | p = strchr(str, ':'); |
||
299 | if (!p) { |
||
300 | pstrcpy(func_name, sizeof(func_name), str); |
||
301 | } else { |
||
302 | len = p - str; |
||
303 | if (len > sizeof(func_name) - 1) |
||
304 | len = sizeof(func_name) - 1; |
||
305 | memcpy(func_name, str, len); |
||
306 | func_name[len] = '\0'; |
||
307 | } |
||
308 | func_addr = sym->n_value; |
||
309 | } |
||
310 | break; |
||
311 | /* line number info */ |
||
312 | case N_SLINE: |
||
313 | pc = sym->n_value + func_addr; |
||
314 | if (wanted_pc >= last_pc && wanted_pc < pc) |
||
315 | goto found; |
||
316 | last_pc = pc; |
||
317 | last_line_num = sym->n_desc; |
||
318 | /* XXX: slow! */ |
||
319 | strcpy(last_func_name, func_name); |
||
320 | break; |
||
321 | /* include files */ |
||
322 | case N_BINCL: |
||
323 | str = stab_str + sym->n_strx; |
||
324 | add_incl: |
||
325 | if (incl_index < INCLUDE_STACK_SIZE) { |
||
326 | incl_files[incl_index++] = str; |
||
327 | } |
||
328 | break; |
||
329 | case N_EINCL: |
||
330 | if (incl_index > 1) |
||
331 | incl_index--; |
||
332 | break; |
||
333 | case N_SO: |
||
334 | if (sym->n_strx == 0) { |
||
335 | incl_index = 0; /* end of translation unit */ |
||
336 | } else { |
||
337 | str = stab_str + sym->n_strx; |
||
338 | /* do not add path */ |
||
339 | len = strlen(str); |
||
340 | if (len > 0 && str[len - 1] != '/') |
||
341 | goto add_incl; |
||
342 | } |
||
343 | break; |
||
344 | } |
||
345 | } |
||
346 | |||
347 | no_stabs: |
||
348 | /* second pass: we try symtab symbols (no line number info) */ |
||
349 | incl_index = 0; |
||
350 | if (symtab_section) |
||
351 | { |
||
352 | ElfW(Sym) *sym, *sym_end; |
||
353 | int type; |
||
354 | |||
355 | sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset); |
||
356 | for(sym = (ElfW(Sym) *)symtab_section->data + 1; |
||
357 | sym < sym_end; |
||
358 | sym++) { |
||
359 | type = ELFW(ST_TYPE)(sym->st_info); |
||
360 | if (type == STT_FUNC || type == STT_GNU_IFUNC) { |
||
361 | if (wanted_pc >= sym->st_value && |
||
362 | wanted_pc < sym->st_value + sym->st_size) { |
||
363 | pstrcpy(last_func_name, sizeof(last_func_name), |
||
364 | (char *) strtab_section->data + sym->st_name); |
||
365 | func_addr = sym->st_value; |
||
366 | goto found; |
||
367 | } |
||
368 | } |
||
369 | } |
||
370 | } |
||
371 | /* did not find any info: */ |
||
372 | fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc); |
||
373 | fflush(stderr); |
||
374 | return 0; |
||
375 | found: |
||
376 | i = incl_index; |
||
377 | if (i > 0) |
||
378 | fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num); |
||
379 | fprintf(stderr, "%s %p", msg, (void*)wanted_pc); |
||
380 | if (last_func_name[0] != '\0') |
||
381 | fprintf(stderr, " %s()", last_func_name); |
||
382 | if (--i >= 0) { |
||
383 | fprintf(stderr, " (included from "); |
||
384 | for (;;) { |
||
385 | fprintf(stderr, "%s", incl_files[i]); |
||
386 | if (--i < 0) |
||
387 | break; |
||
388 | fprintf(stderr, ", "); |
||
389 | } |
||
390 | fprintf(stderr, ")"); |
||
391 | } |
||
392 | fprintf(stderr, "\n"); |
||
393 | fflush(stderr); |
||
394 | return func_addr; |
||
395 | } |
||
396 | |||
397 | /* emit a run time error at position 'pc' */ |
||
398 | static void rt_error(ucontext_t *uc, const char *fmt, ...) |
||
399 | { |
||
400 | va_list ap; |
||
401 | addr_t pc; |
||
402 | int i; |
||
403 | |||
404 | fprintf(stderr, "Runtime error: "); |
||
405 | va_start(ap, fmt); |
||
406 | vfprintf(stderr, fmt, ap); |
||
407 | va_end(ap); |
||
408 | fprintf(stderr, "\n"); |
||
409 | |||
410 | for(i=0;i |
||
411 | if (rt_get_caller_pc(&pc, uc, i) < 0) |
||
412 | break; |
||
413 | pc = rt_printline(pc, i ? "by" : "at"); |
||
414 | if (pc == (addr_t)rt_prog_main && pc) |
||
415 | break; |
||
416 | } |
||
417 | } |
||
418 | |||
419 | /* ------------------------------------------------------------- */ |
||
420 | #ifndef _WIN32 |
||
421 | |||
422 | /* signal handler for fatal errors */ |
||
423 | static void sig_error(int signum, siginfo_t *siginf, void *puc) |
||
424 | { |
||
425 | ucontext_t *uc = puc; |
||
426 | |||
427 | switch(signum) { |
||
428 | case SIGFPE: |
||
429 | switch(siginf->si_code) { |
||
430 | case FPE_INTDIV: |
||
431 | case FPE_FLTDIV: |
||
432 | rt_error(uc, "division by zero"); |
||
433 | break; |
||
434 | default: |
||
435 | rt_error(uc, "floating point exception"); |
||
436 | break; |
||
437 | } |
||
438 | break; |
||
439 | case SIGBUS: |
||
440 | case SIGSEGV: |
||
441 | if (rt_bound_error_msg && *rt_bound_error_msg) |
||
442 | rt_error(uc, *rt_bound_error_msg); |
||
443 | else |
||
444 | rt_error(uc, "dereferencing invalid pointer"); |
||
445 | break; |
||
446 | case SIGILL: |
||
447 | rt_error(uc, "illegal instruction"); |
||
448 | break; |
||
449 | case SIGABRT: |
||
450 | rt_error(uc, "abort() called"); |
||
451 | break; |
||
452 | default: |
||
453 | rt_error(uc, "caught signal %d", signum); |
||
454 | break; |
||
455 | } |
||
456 | exit(255); |
||
457 | } |
||
458 | |||
459 | #ifndef SA_SIGINFO |
||
460 | # define SA_SIGINFO 0x00000004u |
||
461 | #endif |
||
462 | |||
463 | /* Generate a stack backtrace when a CPU exception occurs. */ |
||
464 | static void set_exception_handler(void) |
||
465 | { |
||
466 | struct sigaction sigact; |
||
467 | /* install TCC signal handlers to print debug info on fatal |
||
468 | runtime errors */ |
||
469 | sigact.sa_flags = SA_SIGINFO | SA_RESETHAND; |
||
470 | sigact.sa_sigaction = sig_error; |
||
471 | sigemptyset(&sigact.sa_mask); |
||
472 | sigaction(SIGFPE, &sigact, NULL); |
||
473 | sigaction(SIGILL, &sigact, NULL); |
||
474 | sigaction(SIGSEGV, &sigact, NULL); |
||
475 | sigaction(SIGBUS, &sigact, NULL); |
||
476 | sigaction(SIGABRT, &sigact, NULL); |
||
477 | } |
||
478 | |||
479 | /* ------------------------------------------------------------- */ |
||
480 | #ifdef __i386__ |
||
481 | |||
482 | /* fix for glibc 2.1 */ |
||
483 | #ifndef REG_EIP |
||
484 | #define REG_EIP EIP |
||
485 | #define REG_EBP EBP |
||
486 | #endif |
||
487 | |||
488 | /* return the PC at frame level 'level'. Return negative if not found */ |
||
489 | static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) |
||
490 | { |
||
491 | addr_t fp; |
||
492 | int i; |
||
493 | |||
494 | if (level == 0) { |
||
495 | #if defined(__APPLE__) |
||
496 | *paddr = uc->uc_mcontext->__ss.__eip; |
||
497 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
||
498 | *paddr = uc->uc_mcontext.mc_eip; |
||
499 | #elif defined(__dietlibc__) |
||
500 | *paddr = uc->uc_mcontext.eip; |
||
501 | #elif defined(__NetBSD__) |
||
502 | *paddr = uc->uc_mcontext.__gregs[_REG_EIP]; |
||
503 | #else |
||
504 | *paddr = uc->uc_mcontext.gregs[REG_EIP]; |
||
505 | #endif |
||
506 | return 0; |
||
507 | } else { |
||
508 | #if defined(__APPLE__) |
||
509 | fp = uc->uc_mcontext->__ss.__ebp; |
||
510 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
||
511 | fp = uc->uc_mcontext.mc_ebp; |
||
512 | #elif defined(__dietlibc__) |
||
513 | fp = uc->uc_mcontext.ebp; |
||
514 | #elif defined(__NetBSD__) |
||
515 | fp = uc->uc_mcontext.__gregs[_REG_EBP]; |
||
516 | #else |
||
517 | fp = uc->uc_mcontext.gregs[REG_EBP]; |
||
518 | #endif |
||
519 | for(i=1;i |
||
520 | /* XXX: check address validity with program info */ |
||
521 | if (fp <= 0x1000 || fp >= 0xc0000000) |
||
522 | return -1; |
||
523 | fp = ((addr_t *)fp)[0]; |
||
524 | } |
||
525 | *paddr = ((addr_t *)fp)[1]; |
||
526 | return 0; |
||
527 | } |
||
528 | } |
||
529 | |||
530 | /* ------------------------------------------------------------- */ |
||
531 | #elif defined(__x86_64__) |
||
532 | |||
533 | /* return the PC at frame level 'level'. Return negative if not found */ |
||
534 | static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) |
||
535 | { |
||
536 | addr_t fp; |
||
537 | int i; |
||
538 | |||
539 | if (level == 0) { |
||
540 | /* XXX: only support linux */ |
||
541 | #if defined(__APPLE__) |
||
542 | *paddr = uc->uc_mcontext->__ss.__rip; |
||
543 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
||
544 | *paddr = uc->uc_mcontext.mc_rip; |
||
545 | #elif defined(__NetBSD__) |
||
546 | *paddr = uc->uc_mcontext.__gregs[_REG_RIP]; |
||
547 | #else |
||
548 | *paddr = uc->uc_mcontext.gregs[REG_RIP]; |
||
549 | #endif |
||
550 | return 0; |
||
551 | } else { |
||
552 | #if defined(__APPLE__) |
||
553 | fp = uc->uc_mcontext->__ss.__rbp; |
||
554 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
||
555 | fp = uc->uc_mcontext.mc_rbp; |
||
556 | #elif defined(__NetBSD__) |
||
557 | fp = uc->uc_mcontext.__gregs[_REG_RBP]; |
||
558 | #else |
||
559 | fp = uc->uc_mcontext.gregs[REG_RBP]; |
||
560 | #endif |
||
561 | for(i=1;i |
||
562 | /* XXX: check address validity with program info */ |
||
563 | if (fp <= 0x1000) |
||
564 | return -1; |
||
565 | fp = ((addr_t *)fp)[0]; |
||
566 | } |
||
567 | *paddr = ((addr_t *)fp)[1]; |
||
568 | return 0; |
||
569 | } |
||
570 | } |
||
571 | |||
572 | /* ------------------------------------------------------------- */ |
||
573 | #elif defined(__arm__) |
||
574 | |||
575 | /* return the PC at frame level 'level'. Return negative if not found */ |
||
576 | static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) |
||
577 | { |
||
578 | addr_t fp, sp; |
||
579 | int i; |
||
580 | |||
581 | if (level == 0) { |
||
582 | /* XXX: only supports linux */ |
||
583 | #if defined(__linux__) |
||
584 | *paddr = uc->uc_mcontext.arm_pc; |
||
585 | #else |
||
586 | return -1; |
||
587 | #endif |
||
588 | return 0; |
||
589 | } else { |
||
590 | #if defined(__linux__) |
||
591 | fp = uc->uc_mcontext.arm_fp; |
||
592 | sp = uc->uc_mcontext.arm_sp; |
||
593 | if (sp < 0x1000) |
||
594 | sp = 0x1000; |
||
595 | #else |
||
596 | return -1; |
||
597 | #endif |
||
598 | /* XXX: specific to tinycc stack frames */ |
||
599 | if (fp < sp + 12 || fp & 3) |
||
600 | return -1; |
||
601 | for(i = 1; i < level; i++) { |
||
602 | sp = ((addr_t *)fp)[-2]; |
||
603 | if (sp < fp || sp - fp > 16 || sp & 3) |
||
604 | return -1; |
||
605 | fp = ((addr_t *)fp)[-3]; |
||
606 | if (fp <= sp || fp - sp < 12 || fp & 3) |
||
607 | return -1; |
||
608 | } |
||
609 | /* XXX: check address validity with program info */ |
||
610 | *paddr = ((addr_t *)fp)[-1]; |
||
611 | return 0; |
||
612 | } |
||
613 | } |
||
614 | |||
615 | /* ------------------------------------------------------------- */ |
||
616 | #elif defined(__aarch64__) |
||
617 | |||
618 | static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) |
||
619 | { |
||
620 | if (level < 0) |
||
621 | return -1; |
||
622 | else if (level == 0) { |
||
623 | *paddr = uc->uc_mcontext.pc; |
||
624 | return 0; |
||
625 | } |
||
626 | else { |
||
627 | addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29]; |
||
628 | int i; |
||
629 | for (i = 1; i < level; i++) |
||
630 | fp = (addr_t *)fp[0]; |
||
631 | *paddr = fp[1]; |
||
632 | return 0; |
||
633 | } |
||
634 | } |
||
635 | |||
636 | /* ------------------------------------------------------------- */ |
||
637 | #else |
||
638 | |||
639 | #warning add arch specific rt_get_caller_pc() |
||
640 | static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) |
||
641 | { |
||
642 | return -1; |
||
643 | } |
||
644 | |||
645 | #endif /* !__i386__ */ |
||
646 | |||
647 | /* ------------------------------------------------------------- */ |
||
648 | #else /* WIN32 */ |
||
649 | |||
650 | static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) |
||
651 | { |
||
652 | EXCEPTION_RECORD *er = ex_info->ExceptionRecord; |
||
653 | CONTEXT *uc = ex_info->ContextRecord; |
||
654 | switch (er->ExceptionCode) { |
||
655 | case EXCEPTION_ACCESS_VIOLATION: |
||
656 | if (rt_bound_error_msg && *rt_bound_error_msg) |
||
657 | rt_error(uc, *rt_bound_error_msg); |
||
658 | else |
||
659 | rt_error(uc, "access violation"); |
||
660 | break; |
||
661 | case EXCEPTION_STACK_OVERFLOW: |
||
662 | rt_error(uc, "stack overflow"); |
||
663 | break; |
||
664 | case EXCEPTION_INT_DIVIDE_BY_ZERO: |
||
665 | rt_error(uc, "division by zero"); |
||
666 | break; |
||
667 | default: |
||
668 | rt_error(uc, "exception caught"); |
||
669 | break; |
||
670 | } |
||
671 | return EXCEPTION_EXECUTE_HANDLER; |
||
672 | } |
||
673 | |||
674 | /* Generate a stack backtrace when a CPU exception occurs. */ |
||
675 | static void set_exception_handler(void) |
||
676 | { |
||
677 | SetUnhandledExceptionFilter(cpu_exception_handler); |
||
678 | } |
||
679 | |||
680 | #ifdef _WIN64 |
||
681 | static void win64_add_function_table(TCCState *s1) |
||
682 | { |
||
683 | RtlAddFunctionTable( |
||
684 | (RUNTIME_FUNCTION*)s1->uw_pdata->sh_addr, |
||
685 | s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION), |
||
686 | text_section->sh_addr |
||
687 | ); |
||
688 | } |
||
689 | #endif |
||
690 | |||
691 | /* return the PC at frame level 'level'. Return non zero if not found */ |
||
692 | static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level) |
||
693 | { |
||
694 | addr_t fp, pc; |
||
695 | int i; |
||
696 | #ifdef _WIN64 |
||
697 | pc = uc->Rip; |
||
698 | fp = uc->Rbp; |
||
699 | #else |
||
700 | pc = uc->Eip; |
||
701 | fp = uc->Ebp; |
||
702 | #endif |
||
703 | if (level > 0) { |
||
704 | for(i=1;i |
||
705 | /* XXX: check address validity with program info */ |
||
706 | if (fp <= 0x1000 || fp >= 0xc0000000) |
||
707 | return -1; |
||
708 | fp = ((addr_t*)fp)[0]; |
||
709 | } |
||
710 | pc = ((addr_t*)fp)[1]; |
||
711 | } |
||
712 | *paddr = pc; |
||
713 | return 0; |
||
714 | } |
||
715 | |||
716 | #endif /* _WIN32 */ |
||
717 | #endif /* CONFIG_TCC_BACKTRACE */ |
||
718 | /* ------------------------------------------------------------- */ |
||
719 | #ifdef CONFIG_TCC_STATIC |
||
720 | |||
721 | /* dummy function for profiling */ |
||
722 | ST_FUNC void *dlopen(const char *filename, int flag) |
||
723 | { |
||
724 | return NULL; |
||
725 | } |
||
726 | |||
727 | ST_FUNC void dlclose(void *p) |
||
728 | { |
||
729 | } |
||
730 | |||
731 | ST_FUNC const char *dlerror(void) |
||
732 | { |
||
733 | return "error"; |
||
734 | } |
||
735 | |||
736 | typedef struct TCCSyms { |
||
737 | char *str; |
||
738 | void *ptr; |
||
739 | } TCCSyms; |
||
740 | |||
741 | |||
742 | /* add the symbol you want here if no dynamic linking is done */ |
||
743 | static TCCSyms tcc_syms[] = { |
||
744 | #if !defined(CONFIG_TCCBOOT) |
||
745 | #define TCCSYM(a) { #a, &a, }, |
||
746 | TCCSYM(printf) |
||
747 | TCCSYM(fprintf) |
||
748 | TCCSYM(fopen) |
||
749 | TCCSYM(fclose) |
||
750 | #undef TCCSYM |
||
751 | #endif |
||
752 | { NULL, NULL }, |
||
753 | }; |
||
754 | |||
755 | ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol) |
||
756 | { |
||
757 | TCCSyms *p; |
||
758 | p = tcc_syms; |
||
759 | while (p->str != NULL) { |
||
760 | if (!strcmp(p->str, symbol)) |
||
761 | return p->ptr; |
||
762 | p++; |
||
763 | } |
||
764 | return NULL; |
||
765 | } |
||
766 | |||
767 | #elif !defined(_WIN32) |
||
768 | |||
769 | ST_FUNC void *resolve_sym(TCCState *s1, const char *sym) |
||
770 | { |
||
771 | return dlsym(RTLD_DEFAULT, sym); |
||
772 | } |
||
773 | |||
774 | #endif /* CONFIG_TCC_STATIC */ |
||
775 | #endif /* TCC_IS_NATIVE */ |
||
776 | /* ------------------------------------------------------------- */=>>>>=>>>>>=>=>>>>>>>>>>>>>> |