1 | /* |
2 | * TCC - Tiny C Compiler |
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 | #if !defined ONE_SOURCE || ONE_SOURCE |
22 | #include "tccpp.c" |
23 | #include "tccgen.c" |
24 | #include "tccelf.c" |
25 | #include "tccrun.c" |
26 | #ifdef TCC_TARGET_I386 |
27 | #include "i386-gen.c" |
28 | #include "i386-link.c" |
29 | #include "i386-asm.c" |
30 | #elif defined(TCC_TARGET_ARM) |
31 | #include "arm-gen.c" |
32 | #include "arm-link.c" |
33 | #include "arm-asm.c" |
34 | #elif defined(TCC_TARGET_ARM64) |
35 | #include "arm64-gen.c" |
36 | #include "arm64-link.c" |
37 | #include "arm-asm.c" |
38 | #elif defined(TCC_TARGET_C67) |
39 | #include "c67-gen.c" |
40 | #include "c67-link.c" |
41 | #include "tcccoff.c" |
42 | #elif defined(TCC_TARGET_X86_64) |
43 | #include "x86_64-gen.c" |
44 | #include "x86_64-link.c" |
45 | #include "i386-asm.c" |
46 | #elif defined(TCC_TARGET_RISCV64) |
47 | #include "riscv64-gen.c" |
48 | #include "riscv64-link.c" |
49 | #include "riscv64-asm.c" |
50 | #else |
51 | #error unknown target |
52 | #endif |
53 | #ifdef CONFIG_TCC_ASM |
54 | #include "tccasm.c" |
55 | #endif |
56 | #ifdef TCC_TARGET_PE |
57 | #include "tccpe.c" |
58 | #endif |
59 | #ifdef TCC_TARGET_MACHO |
60 | #include "tccmacho.c" |
61 | #endif |
62 | #endif /* ONE_SOURCE */ |
63 | |
64 | #include "tcc.h" |
65 | |
66 | /********************************************************/ |
67 | /* global variables */ |
68 | |
69 | /* XXX: get rid of this ASAP (or maybe not) */ |
70 | ST_DATA struct TCCState *tcc_state; |
71 | |
72 | #ifdef MEM_DEBUG |
73 | static int nb_states; |
74 | #endif |
75 | |
76 | /********************************************************/ |
77 | #ifdef _WIN32 |
78 | ST_FUNC char *normalize_slashes(char *path) |
79 | { |
80 | char *p; |
81 | for (p = path; *p; ++p) |
82 | if (*p == '\\') |
83 | *p = '/'; |
84 | return path; |
85 | } |
86 | |
87 | static HMODULE tcc_module; |
88 | |
89 | /* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */ |
90 | static void tcc_set_lib_path_w32(TCCState *s) |
91 | { |
92 | char path[1024], *p; |
93 | GetModuleFileNameA(tcc_module, path, sizeof path); |
94 | p = tcc_basename(normalize_slashes(strlwr(path))); |
95 | if (p > path) |
96 | --p; |
97 | *p = 0; |
98 | tcc_set_lib_path(s, path); |
99 | } |
100 | |
101 | #ifdef TCC_TARGET_PE |
102 | static void tcc_add_systemdir(TCCState *s) |
103 | { |
104 | char buf[1000]; |
105 | GetSystemDirectory(buf, sizeof buf); |
106 | tcc_add_library_path(s, normalize_slashes(buf)); |
107 | } |
108 | #endif |
109 | |
110 | #ifdef LIBTCC_AS_DLL |
111 | BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved) |
112 | { |
113 | if (DLL_PROCESS_ATTACH == dwReason) |
114 | tcc_module = hDll; |
115 | return TRUE; |
116 | } |
117 | #endif |
118 | #endif |
119 | |
120 | /********************************************************/ |
121 | #ifndef CONFIG_TCC_SEMLOCK |
122 | #define WAIT_SEM() |
123 | #define POST_SEM() |
124 | #elif defined _WIN32 |
125 | static int tcc_sem_init; |
126 | static CRITICAL_SECTION tcc_cr; |
127 | static void wait_sem(void) |
128 | { |
129 | if (!tcc_sem_init) |
130 | InitializeCriticalSection(&tcc_cr), tcc_sem_init = 1; |
131 | EnterCriticalSection(&tcc_cr); |
132 | } |
133 | #define WAIT_SEM() wait_sem() |
134 | #define POST_SEM() LeaveCriticalSection(&tcc_cr); |
135 | #elif defined __APPLE__ |
136 | /* Half-compatible MacOS doesn't have non-shared (process local) |
137 | semaphores. Use the dispatch framework for lightweight locks. */ |
138 | #include <dispatch/dispatch.h> |
139 | static int tcc_sem_init; |
140 | static dispatch_semaphore_t tcc_sem; |
141 | static void wait_sem(void) |
142 | { |
143 | if (!tcc_sem_init) |
144 | tcc_sem = dispatch_semaphore_create(1), tcc_sem_init = 1; |
145 | dispatch_semaphore_wait(tcc_sem, DISPATCH_TIME_FOREVER); |
146 | } |
147 | #define WAIT_SEM() wait_sem() |
148 | #define POST_SEM() dispatch_semaphore_signal(tcc_sem) |
149 | #else |
150 | #include <semaphore.h> |
151 | static int tcc_sem_init; |
152 | static sem_t tcc_sem; |
153 | static void wait_sem(void) |
154 | { |
155 | if (!tcc_sem_init) |
156 | sem_init(&tcc_sem, 0, 1), tcc_sem_init = 1; |
157 | while (sem_wait (&tcc_sem) < 0 && errno == EINTR); |
158 | } |
159 | #define WAIT_SEM() wait_sem() |
160 | #define POST_SEM() sem_post(&tcc_sem) |
161 | #endif |
162 | |
163 | /********************************************************/ |
164 | /* copy a string and truncate it. */ |
165 | ST_FUNC char *pstrcpy(char *buf, size_t buf_size, const char *s) |
166 | { |
167 | char *q, *q_end; |
168 | int c; |
169 | |
170 | if (buf_size > 0) { |
171 | q = buf; |
172 | q_end = buf + buf_size - 1; |
173 | while (q < q_end) { |
174 | c = *s++; |
175 | if (c == '\0') |
176 | break; |
177 | *q++ = c; |
178 | } |
179 | *q = '\0'; |
180 | } |
181 | return buf; |
182 | } |
183 | |
184 | /* strcat and truncate. */ |
185 | ST_FUNC char *pstrcat(char *buf, size_t buf_size, const char *s) |
186 | { |
187 | size_t len; |
188 | len = strlen(buf); |
189 | if (len < buf_size) |
190 | pstrcpy(buf + len, buf_size - len, s); |
191 | return buf; |
192 | } |
193 | |
194 | ST_FUNC char *pstrncpy(char *out, const char *in, size_t num) |
195 | { |
196 | memcpy(out, in, num); |
197 | out[num] = '\0'; |
198 | return out; |
199 | } |
200 | |
201 | /* extract the basename of a file */ |
202 | PUB_FUNC char *tcc_basename(const char *name) |
203 | { |
204 | char *p = strchr(name, 0); |
205 | while (p > name && !IS_DIRSEP(p[-1])) |
206 | --p; |
207 | return p; |
208 | } |
209 | |
210 | /* extract extension part of a file |
211 | * |
212 | * (if no extension, return pointer to end-of-string) |
213 | */ |
214 | PUB_FUNC char *tcc_fileextension (const char *name) |
215 | { |
216 | char *b = tcc_basename(name); |
217 | char *e = strrchr(b, '.'); |
218 | return e ? e : strchr(b, 0); |
219 | } |
220 | |
221 | /********************************************************/ |
222 | /* memory management */ |
223 | |
224 | #ifndef MEM_DEBUG |
225 | |
226 | PUB_FUNC void tcc_free(void *ptr) |
227 | { |
228 | free(ptr); |
229 | } |
230 | |
231 | PUB_FUNC void *tcc_malloc(unsigned long size) |
232 | { |
233 | void *ptr; |
234 | ptr = malloc(size); |
235 | if (!ptr && size) |
236 | _tcc_error("memory full (malloc)" ); |
237 | return ptr; |
238 | } |
239 | |
240 | PUB_FUNC void *tcc_mallocz(unsigned long size) |
241 | { |
242 | void *ptr; |
243 | ptr = tcc_malloc(size); |
244 | if (size) |
245 | memset(ptr, 0, size); |
246 | return ptr; |
247 | } |
248 | |
249 | PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size) |
250 | { |
251 | void *ptr1; |
252 | ptr1 = realloc(ptr, size); |
253 | if (!ptr1 && size) |
254 | _tcc_error("memory full (realloc)" ); |
255 | return ptr1; |
256 | } |
257 | |
258 | PUB_FUNC char *tcc_strdup(const char *str) |
259 | { |
260 | char *ptr; |
261 | ptr = tcc_malloc(strlen(str) + 1); |
262 | strcpy(ptr, str); |
263 | return ptr; |
264 | } |
265 | |
266 | #else |
267 | |
268 | #define MEM_DEBUG_MAGIC1 0xFEEDDEB1 |
269 | #define MEM_DEBUG_MAGIC2 0xFEEDDEB2 |
270 | #define MEM_DEBUG_MAGIC3 0xFEEDDEB3 |
271 | #define MEM_DEBUG_FILE_LEN 40 |
272 | #define MEM_DEBUG_CHECK3(header) \ |
273 | ((mem_debug_header_t*)((char*)header + header->size))->magic3 |
274 | #define MEM_USER_PTR(header) \ |
275 | ((char *)header + offsetof(mem_debug_header_t, magic3)) |
276 | #define MEM_HEADER_PTR(ptr) \ |
277 | (mem_debug_header_t *)((char*)ptr - offsetof(mem_debug_header_t, magic3)) |
278 | |
279 | struct mem_debug_header { |
280 | unsigned magic1; |
281 | unsigned size; |
282 | struct mem_debug_header *prev; |
283 | struct mem_debug_header *next; |
284 | int line_num; |
285 | char file_name[MEM_DEBUG_FILE_LEN + 1]; |
286 | unsigned magic2; |
287 | ALIGNED(16) unsigned magic3; |
288 | }; |
289 | |
290 | typedef struct mem_debug_header mem_debug_header_t; |
291 | |
292 | static mem_debug_header_t *mem_debug_chain; |
293 | static unsigned mem_cur_size; |
294 | static unsigned mem_max_size; |
295 | |
296 | static mem_debug_header_t *malloc_check(void *ptr, const char *msg) |
297 | { |
298 | mem_debug_header_t * header = MEM_HEADER_PTR(ptr); |
299 | if (header->magic1 != MEM_DEBUG_MAGIC1 || |
300 | header->magic2 != MEM_DEBUG_MAGIC2 || |
301 | MEM_DEBUG_CHECK3(header) != MEM_DEBUG_MAGIC3 || |
302 | header->size == (unsigned)-1) { |
303 | fprintf(stderr, "%s check failed\n" , msg); |
304 | if (header->magic1 == MEM_DEBUG_MAGIC1) |
305 | fprintf(stderr, "%s:%u: block allocated here.\n" , |
306 | header->file_name, header->line_num); |
307 | exit(1); |
308 | } |
309 | return header; |
310 | } |
311 | |
312 | PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line) |
313 | { |
314 | int ofs; |
315 | mem_debug_header_t *header; |
316 | |
317 | header = malloc(sizeof(mem_debug_header_t) + size); |
318 | if (!header) |
319 | _tcc_error("memory full (malloc)" ); |
320 | |
321 | header->magic1 = MEM_DEBUG_MAGIC1; |
322 | header->magic2 = MEM_DEBUG_MAGIC2; |
323 | header->size = size; |
324 | MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3; |
325 | header->line_num = line; |
326 | ofs = strlen(file) - MEM_DEBUG_FILE_LEN; |
327 | strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN); |
328 | header->file_name[MEM_DEBUG_FILE_LEN] = 0; |
329 | |
330 | header->next = mem_debug_chain; |
331 | header->prev = NULL; |
332 | if (header->next) |
333 | header->next->prev = header; |
334 | mem_debug_chain = header; |
335 | |
336 | mem_cur_size += size; |
337 | if (mem_cur_size > mem_max_size) |
338 | mem_max_size = mem_cur_size; |
339 | |
340 | return MEM_USER_PTR(header); |
341 | } |
342 | |
343 | PUB_FUNC void tcc_free_debug(void *ptr) |
344 | { |
345 | mem_debug_header_t *header; |
346 | if (!ptr) |
347 | return; |
348 | header = malloc_check(ptr, "tcc_free" ); |
349 | mem_cur_size -= header->size; |
350 | header->size = (unsigned)-1; |
351 | if (header->next) |
352 | header->next->prev = header->prev; |
353 | if (header->prev) |
354 | header->prev->next = header->next; |
355 | if (header == mem_debug_chain) |
356 | mem_debug_chain = header->next; |
357 | free(header); |
358 | } |
359 | |
360 | PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line) |
361 | { |
362 | void *ptr; |
363 | ptr = tcc_malloc_debug(size,file,line); |
364 | memset(ptr, 0, size); |
365 | return ptr; |
366 | } |
367 | |
368 | PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line) |
369 | { |
370 | mem_debug_header_t *header; |
371 | int mem_debug_chain_update = 0; |
372 | if (!ptr) |
373 | return tcc_malloc_debug(size, file, line); |
374 | header = malloc_check(ptr, "tcc_realloc" ); |
375 | mem_cur_size -= header->size; |
376 | mem_debug_chain_update = (header == mem_debug_chain); |
377 | header = realloc(header, sizeof(mem_debug_header_t) + size); |
378 | if (!header) |
379 | _tcc_error("memory full (realloc)" ); |
380 | header->size = size; |
381 | MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3; |
382 | if (header->next) |
383 | header->next->prev = header; |
384 | if (header->prev) |
385 | header->prev->next = header; |
386 | if (mem_debug_chain_update) |
387 | mem_debug_chain = header; |
388 | mem_cur_size += size; |
389 | if (mem_cur_size > mem_max_size) |
390 | mem_max_size = mem_cur_size; |
391 | return MEM_USER_PTR(header); |
392 | } |
393 | |
394 | PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line) |
395 | { |
396 | char *ptr; |
397 | ptr = tcc_malloc_debug(strlen(str) + 1, file, line); |
398 | strcpy(ptr, str); |
399 | return ptr; |
400 | } |
401 | |
402 | PUB_FUNC void tcc_memcheck(void) |
403 | { |
404 | if (mem_cur_size) { |
405 | mem_debug_header_t *header = mem_debug_chain; |
406 | fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n" , |
407 | mem_cur_size, mem_max_size); |
408 | while (header) { |
409 | fprintf(stderr, "%s:%u: error: %u bytes leaked\n" , |
410 | header->file_name, header->line_num, header->size); |
411 | header = header->next; |
412 | } |
413 | #if MEM_DEBUG-0 == 2 |
414 | exit(2); |
415 | #endif |
416 | } |
417 | } |
418 | #endif /* MEM_DEBUG */ |
419 | |
420 | /********************************************************/ |
421 | /* dynarrays */ |
422 | |
423 | ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data) |
424 | { |
425 | int nb, nb_alloc; |
426 | void **pp; |
427 | |
428 | nb = *nb_ptr; |
429 | pp = *(void ***)ptab; |
430 | /* every power of two we double array size */ |
431 | if ((nb & (nb - 1)) == 0) { |
432 | if (!nb) |
433 | nb_alloc = 1; |
434 | else |
435 | nb_alloc = nb * 2; |
436 | pp = tcc_realloc(pp, nb_alloc * sizeof(void *)); |
437 | *(void***)ptab = pp; |
438 | } |
439 | pp[nb++] = data; |
440 | *nb_ptr = nb; |
441 | } |
442 | |
443 | ST_FUNC void dynarray_reset(void *pp, int *n) |
444 | { |
445 | void **p; |
446 | for (p = *(void***)pp; *n; ++p, --*n) |
447 | if (*p) |
448 | tcc_free(*p); |
449 | tcc_free(*(void**)pp); |
450 | *(void**)pp = NULL; |
451 | } |
452 | |
453 | static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in) |
454 | { |
455 | const char *p; |
456 | do { |
457 | int c; |
458 | CString str; |
459 | |
460 | cstr_new(&str); |
461 | for (p = in; c = *p, c != '\0' && c != PATHSEP[0]; ++p) { |
462 | if (c == '{' && p[1] && p[2] == '}') { |
463 | c = p[1], p += 2; |
464 | if (c == 'B') |
465 | cstr_cat(&str, s->tcc_lib_path, -1); |
466 | if (c == 'f' && file) { |
467 | /* substitute current file's dir */ |
468 | const char *f = file->true_filename; |
469 | const char *b = tcc_basename(f); |
470 | if (b > f) |
471 | cstr_cat(&str, f, b - f - 1); |
472 | else |
473 | cstr_cat(&str, "." , 1); |
474 | } |
475 | } else { |
476 | cstr_ccat(&str, c); |
477 | } |
478 | } |
479 | if (str.size) { |
480 | cstr_ccat(&str, '\0'); |
481 | dynarray_add(p_ary, p_nb_ary, tcc_strdup(str.data)); |
482 | } |
483 | cstr_free(&str); |
484 | in = p+1; |
485 | } while (*p); |
486 | } |
487 | |
488 | /********************************************************/ |
489 | |
490 | static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap) |
491 | { |
492 | int len; |
493 | len = strlen(buf); |
494 | vsnprintf(buf + len, buf_size - len, fmt, ap); |
495 | } |
496 | |
497 | static void strcat_printf(char *buf, int buf_size, const char *fmt, ...) |
498 | { |
499 | va_list ap; |
500 | va_start(ap, fmt); |
501 | strcat_vprintf(buf, buf_size, fmt, ap); |
502 | va_end(ap); |
503 | } |
504 | |
505 | #define ERROR_WARN 0 |
506 | #define ERROR_NOABORT 1 |
507 | #define ERROR_ERROR 2 |
508 | |
509 | PUB_FUNC void tcc_enter_state(TCCState *s1) |
510 | { |
511 | WAIT_SEM(); |
512 | tcc_state = s1; |
513 | } |
514 | |
515 | PUB_FUNC void tcc_exit_state(void) |
516 | { |
517 | tcc_state = NULL; |
518 | POST_SEM(); |
519 | } |
520 | |
521 | static void error1(int mode, const char *fmt, va_list ap) |
522 | { |
523 | char buf[2048]; |
524 | BufferedFile **pf, *f; |
525 | TCCState *s1 = tcc_state; |
526 | |
527 | buf[0] = '\0'; |
528 | if (s1 == NULL) |
529 | /* can happen only if called from tcc_malloc(): 'out of memory' */ |
530 | goto no_file; |
531 | |
532 | if (s1 && !s1->error_set_jmp_enabled) |
533 | /* tcc_state just was set by tcc_enter_state() */ |
534 | tcc_exit_state(); |
535 | |
536 | if (mode == ERROR_WARN) { |
537 | if (s1->warn_none) |
538 | return; |
539 | if (s1->warn_error) |
540 | mode = ERROR_ERROR; |
541 | } |
542 | |
543 | f = NULL; |
544 | if (s1->error_set_jmp_enabled) { /* we're called while parsing a file */ |
545 | /* use upper file if inline ":asm:" or token ":paste:" */ |
546 | for (f = file; f && f->filename[0] == ':'; f = f->prev) |
547 | ; |
548 | } |
549 | if (f) { |
550 | for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++) |
551 | strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n" , |
552 | (*pf)->filename, (*pf)->line_num); |
553 | strcat_printf(buf, sizeof(buf), "%s:%d: " , |
554 | f->filename, f->line_num - !!(tok_flags & TOK_FLAG_BOL)); |
555 | } else if (s1->current_filename) { |
556 | strcat_printf(buf, sizeof(buf), "%s: " , s1->current_filename); |
557 | } |
558 | |
559 | no_file: |
560 | if (0 == buf[0]) |
561 | strcat_printf(buf, sizeof(buf), "tcc: " ); |
562 | if (mode == ERROR_WARN) |
563 | strcat_printf(buf, sizeof(buf), "warning: " ); |
564 | else |
565 | strcat_printf(buf, sizeof(buf), "error: " ); |
566 | strcat_vprintf(buf, sizeof(buf), fmt, ap); |
567 | if (!s1 || !s1->error_func) { |
568 | /* default case: stderr */ |
569 | if (s1 && s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout) |
570 | /* print a newline during tcc -E */ |
571 | printf("\n" ), fflush(stdout); |
572 | fflush(stdout); /* flush -v output */ |
573 | fprintf(stderr, "%s\n" , buf); |
574 | fflush(stderr); /* print error/warning now (win32) */ |
575 | } else { |
576 | s1->error_func(s1->error_opaque, buf); |
577 | } |
578 | if (s1) { |
579 | if (mode != ERROR_WARN) |
580 | s1->nb_errors++; |
581 | if (mode != ERROR_ERROR) |
582 | return; |
583 | if (s1->error_set_jmp_enabled) |
584 | longjmp(s1->error_jmp_buf, 1); |
585 | } |
586 | exit(1); |
587 | } |
588 | |
589 | LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc error_func) |
590 | { |
591 | s->error_opaque = error_opaque; |
592 | s->error_func = error_func; |
593 | } |
594 | |
595 | LIBTCCAPI TCCErrorFunc tcc_get_error_func(TCCState *s) |
596 | { |
597 | return s->error_func; |
598 | } |
599 | |
600 | LIBTCCAPI void *tcc_get_error_opaque(TCCState *s) |
601 | { |
602 | return s->error_opaque; |
603 | } |
604 | |
605 | /* error without aborting current compilation */ |
606 | PUB_FUNC void _tcc_error_noabort(const char *fmt, ...) |
607 | { |
608 | va_list ap; |
609 | va_start(ap, fmt); |
610 | error1(ERROR_NOABORT, fmt, ap); |
611 | va_end(ap); |
612 | } |
613 | |
614 | PUB_FUNC void _tcc_error(const char *fmt, ...) |
615 | { |
616 | va_list ap; |
617 | va_start(ap, fmt); |
618 | for (;;) error1(ERROR_ERROR, fmt, ap); |
619 | } |
620 | |
621 | PUB_FUNC void _tcc_warning(const char *fmt, ...) |
622 | { |
623 | va_list ap; |
624 | va_start(ap, fmt); |
625 | error1(ERROR_WARN, fmt, ap); |
626 | va_end(ap); |
627 | } |
628 | |
629 | /********************************************************/ |
630 | /* I/O layer */ |
631 | |
632 | ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen) |
633 | { |
634 | BufferedFile *bf; |
635 | int buflen = initlen ? initlen : IO_BUF_SIZE; |
636 | |
637 | bf = tcc_mallocz(sizeof(BufferedFile) + buflen); |
638 | bf->buf_ptr = bf->buffer; |
639 | bf->buf_end = bf->buffer + initlen; |
640 | bf->buf_end[0] = CH_EOB; /* put eob symbol */ |
641 | pstrcpy(bf->filename, sizeof(bf->filename), filename); |
642 | #ifdef _WIN32 |
643 | normalize_slashes(bf->filename); |
644 | #endif |
645 | bf->true_filename = bf->filename; |
646 | bf->line_num = 1; |
647 | bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; |
648 | bf->fd = -1; |
649 | bf->prev = file; |
650 | file = bf; |
651 | tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; |
652 | } |
653 | |
654 | ST_FUNC void tcc_close(void) |
655 | { |
656 | TCCState *s1 = tcc_state; |
657 | BufferedFile *bf = file; |
658 | if (bf->fd > 0) { |
659 | close(bf->fd); |
660 | total_lines += bf->line_num; |
661 | } |
662 | if (bf->true_filename != bf->filename) |
663 | tcc_free(bf->true_filename); |
664 | file = bf->prev; |
665 | tcc_free(bf); |
666 | } |
667 | |
668 | static int _tcc_open(TCCState *s1, const char *filename) |
669 | { |
670 | int fd; |
671 | if (strcmp(filename, "-" ) == 0) |
672 | fd = 0, filename = "<stdin>" ; |
673 | else |
674 | fd = open(filename, O_RDONLY | O_BINARY); |
675 | if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3) |
676 | printf("%s %*s%s\n" , fd < 0 ? "nf" :"->" , |
677 | (int)(s1->include_stack_ptr - s1->include_stack), "" , filename); |
678 | return fd; |
679 | } |
680 | |
681 | ST_FUNC int tcc_open(TCCState *s1, const char *filename) |
682 | { |
683 | int fd = _tcc_open(s1, filename); |
684 | if (fd < 0) |
685 | return -1; |
686 | tcc_open_bf(s1, filename, 0); |
687 | file->fd = fd; |
688 | return 0; |
689 | } |
690 | |
691 | /* compile the file opened in 'file'. Return non zero if errors. */ |
692 | static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd) |
693 | { |
694 | /* Here we enter the code section where we use the global variables for |
695 | parsing and code generation (tccpp.c, tccgen.c, <target>-gen.c). |
696 | Other threads need to wait until we're done. |
697 | |
698 | Alternatively we could use thread local storage for those global |
699 | variables, which may or may not have advantages */ |
700 | |
701 | tcc_enter_state(s1); |
702 | |
703 | if (setjmp(s1->error_jmp_buf) == 0) { |
704 | s1->error_set_jmp_enabled = 1; |
705 | s1->nb_errors = 0; |
706 | |
707 | if (fd == -1) { |
708 | int len = strlen(str); |
709 | tcc_open_bf(s1, "<string>" , len); |
710 | memcpy(file->buffer, str, len); |
711 | } else { |
712 | tcc_open_bf(s1, str, 0); |
713 | file->fd = fd; |
714 | } |
715 | |
716 | tccelf_begin_file(s1); |
717 | preprocess_start(s1, filetype); |
718 | tccgen_init(s1); |
719 | if (s1->output_type == TCC_OUTPUT_PREPROCESS) { |
720 | tcc_preprocess(s1); |
721 | } else if (filetype & (AFF_TYPE_ASM | AFF_TYPE_ASMPP)) { |
722 | #ifdef CONFIG_TCC_ASM |
723 | tcc_assemble(s1, !!(filetype & AFF_TYPE_ASMPP)); |
724 | #else |
725 | tcc_error_noabort("asm not supported" ); |
726 | #endif |
727 | } else { |
728 | tccgen_compile(s1); |
729 | } |
730 | } |
731 | s1->error_set_jmp_enabled = 0; |
732 | tccgen_finish(s1); |
733 | preprocess_end(s1); |
734 | tcc_exit_state(); |
735 | |
736 | tccelf_end_file(s1); |
737 | return s1->nb_errors != 0 ? -1 : 0; |
738 | } |
739 | |
740 | LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str) |
741 | { |
742 | return tcc_compile(s, s->filetype, str, -1); |
743 | } |
744 | |
745 | /* define a preprocessor symbol. value can be NULL, sym can be "sym=val" */ |
746 | LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value) |
747 | { |
748 | const char *eq; |
749 | if (NULL == (eq = strchr(sym, '='))) |
750 | eq = strchr(sym, 0); |
751 | if (NULL == value) |
752 | value = *eq ? eq + 1 : "1" ; |
753 | cstr_printf(&s1->cmdline_defs, "#define %.*s %s\n" , (int)(eq-sym), sym, value); |
754 | } |
755 | |
756 | /* undefine a preprocessor symbol */ |
757 | LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym) |
758 | { |
759 | cstr_printf(&s1->cmdline_defs, "#undef %s\n" , sym); |
760 | } |
761 | |
762 | |
763 | LIBTCCAPI TCCState *tcc_new(void) |
764 | { |
765 | TCCState *s; |
766 | |
767 | s = tcc_mallocz(sizeof(TCCState)); |
768 | if (!s) |
769 | return NULL; |
770 | #ifdef MEM_DEBUG |
771 | ++nb_states; |
772 | #endif |
773 | |
774 | #undef gnu_ext |
775 | |
776 | s->gnu_ext = 1; |
777 | s->tcc_ext = 1; |
778 | s->nocommon = 1; |
779 | s->dollars_in_identifiers = 1; /*on by default like in gcc/clang*/ |
780 | s->cversion = 199901; /* default unless -std=c11 is supplied */ |
781 | s->warn_implicit_function_declaration = 1; |
782 | s->ms_extensions = 1; |
783 | |
784 | #ifdef CHAR_IS_UNSIGNED |
785 | s->char_is_unsigned = 1; |
786 | #endif |
787 | #ifdef TCC_TARGET_I386 |
788 | s->seg_size = 32; |
789 | #endif |
790 | /* enable this if you want symbols with leading underscore on windows: */ |
791 | #if defined TCC_TARGET_MACHO /* || defined TCC_TARGET_PE */ |
792 | s->leading_underscore = 1; |
793 | #endif |
794 | s->ppfp = stdout; |
795 | /* might be used in error() before preprocess_start() */ |
796 | s->include_stack_ptr = s->include_stack; |
797 | |
798 | tccelf_new(s); |
799 | |
800 | #ifdef _WIN32 |
801 | tcc_set_lib_path_w32(s); |
802 | #else |
803 | tcc_set_lib_path(s, CONFIG_TCCDIR); |
804 | #endif |
805 | |
806 | { |
807 | /* define __TINYC__ 92X */ |
808 | char buffer[32]; int a,b,c; |
809 | sscanf(TCC_VERSION, "%d.%d.%d" , &a, &b, &c); |
810 | sprintf(buffer, "%d" , a*10000 + b*100 + c); |
811 | tcc_define_symbol(s, "__TINYC__" , buffer); |
812 | } |
813 | |
814 | /* standard defines */ |
815 | tcc_define_symbol(s, "__STDC__" , NULL); |
816 | tcc_define_symbol(s, "__STDC_VERSION__" , "199901L" ); |
817 | tcc_define_symbol(s, "__STDC_HOSTED__" , NULL); |
818 | |
819 | /* target defines */ |
820 | #if defined(TCC_TARGET_I386) |
821 | tcc_define_symbol(s, "__i386__" , NULL); |
822 | tcc_define_symbol(s, "__i386" , NULL); |
823 | tcc_define_symbol(s, "i386" , NULL); |
824 | #elif defined(TCC_TARGET_X86_64) |
825 | tcc_define_symbol(s, "__x86_64__" , NULL); |
826 | #elif defined(TCC_TARGET_ARM) |
827 | tcc_define_symbol(s, "__ARM_ARCH_4__" , NULL); |
828 | tcc_define_symbol(s, "__arm_elf__" , NULL); |
829 | tcc_define_symbol(s, "__arm_elf" , NULL); |
830 | tcc_define_symbol(s, "arm_elf" , NULL); |
831 | tcc_define_symbol(s, "__arm__" , NULL); |
832 | tcc_define_symbol(s, "__arm" , NULL); |
833 | tcc_define_symbol(s, "arm" , NULL); |
834 | tcc_define_symbol(s, "__APCS_32__" , NULL); |
835 | tcc_define_symbol(s, "__ARMEL__" , NULL); |
836 | #if defined(TCC_ARM_EABI) |
837 | tcc_define_symbol(s, "__ARM_EABI__" , NULL); |
838 | #endif |
839 | #if defined(TCC_ARM_HARDFLOAT) |
840 | s->float_abi = ARM_HARD_FLOAT; |
841 | tcc_define_symbol(s, "__ARM_PCS_VFP" , NULL); |
842 | #else |
843 | s->float_abi = ARM_SOFTFP_FLOAT; |
844 | #endif |
845 | #elif defined(TCC_TARGET_ARM64) |
846 | tcc_define_symbol(s, "__aarch64__" , NULL); |
847 | #elif defined TCC_TARGET_C67 |
848 | tcc_define_symbol(s, "__C67__" , NULL); |
849 | #elif defined TCC_TARGET_RISCV64 |
850 | tcc_define_symbol(s, "__riscv" , NULL); |
851 | tcc_define_symbol(s, "__riscv_xlen" , "64" ); |
852 | tcc_define_symbol(s, "__riscv_flen" , "64" ); |
853 | tcc_define_symbol(s, "__riscv_div" , NULL); |
854 | tcc_define_symbol(s, "__riscv_mul" , NULL); |
855 | tcc_define_symbol(s, "__riscv_fdiv" , NULL); |
856 | tcc_define_symbol(s, "__riscv_fsqrt" , NULL); |
857 | tcc_define_symbol(s, "__riscv_float_abi_double" , NULL); |
858 | #endif |
859 | |
860 | #ifdef TCC_TARGET_PE |
861 | tcc_define_symbol(s, "_WIN32" , NULL); |
862 | tcc_define_symbol(s, "__declspec(x)" , "__attribute__((x))" ); |
863 | tcc_define_symbol(s, "__cdecl" , "" ); |
864 | # ifdef TCC_TARGET_X86_64 |
865 | tcc_define_symbol(s, "_WIN64" , NULL); |
866 | # endif |
867 | #else |
868 | tcc_define_symbol(s, "__unix__" , NULL); |
869 | tcc_define_symbol(s, "__unix" , NULL); |
870 | tcc_define_symbol(s, "unix" , NULL); |
871 | # if defined(__linux__) |
872 | tcc_define_symbol(s, "__linux__" , NULL); |
873 | tcc_define_symbol(s, "__linux" , NULL); |
874 | # endif |
875 | # if defined(__FreeBSD__) |
876 | tcc_define_symbol(s, "__FreeBSD__" , "__FreeBSD__" ); |
877 | /* No 'Thread Storage Local' on FreeBSD with tcc */ |
878 | tcc_define_symbol(s, "__NO_TLS" , NULL); |
879 | # endif |
880 | # if defined(__FreeBSD_kernel__) |
881 | tcc_define_symbol(s, "__FreeBSD_kernel__" , NULL); |
882 | # endif |
883 | # if defined(__NetBSD__) |
884 | tcc_define_symbol(s, "__NetBSD__" , "__NetBSD__" ); |
885 | # endif |
886 | # if defined(__OpenBSD__) |
887 | tcc_define_symbol(s, "__OpenBSD__" , "__OpenBSD__" ); |
888 | # endif |
889 | #endif |
890 | |
891 | /* TinyCC & gcc defines */ |
892 | #if PTR_SIZE == 4 |
893 | /* 32bit systems. */ |
894 | tcc_define_symbol(s, "__SIZE_TYPE__" , "unsigned int" ); |
895 | tcc_define_symbol(s, "__PTRDIFF_TYPE__" , "int" ); |
896 | tcc_define_symbol(s, "__ILP32__" , NULL); |
897 | #elif LONG_SIZE == 4 |
898 | /* 64bit Windows. */ |
899 | tcc_define_symbol(s, "__SIZE_TYPE__" , "unsigned long long" ); |
900 | tcc_define_symbol(s, "__PTRDIFF_TYPE__" , "long long" ); |
901 | tcc_define_symbol(s, "__LLP64__" , NULL); |
902 | #else |
903 | /* Other 64bit systems. */ |
904 | tcc_define_symbol(s, "__SIZE_TYPE__" , "unsigned long" ); |
905 | tcc_define_symbol(s, "__PTRDIFF_TYPE__" , "long" ); |
906 | tcc_define_symbol(s, "__LP64__" , NULL); |
907 | #endif |
908 | tcc_define_symbol(s, "__SIZEOF_POINTER__" , PTR_SIZE == 4 ? "4" : "8" ); |
909 | |
910 | #ifdef TCC_TARGET_PE |
911 | tcc_define_symbol(s, "__WCHAR_TYPE__" , "unsigned short" ); |
912 | tcc_define_symbol(s, "__WINT_TYPE__" , "unsigned short" ); |
913 | #else |
914 | tcc_define_symbol(s, "__WCHAR_TYPE__" , "int" ); |
915 | /* wint_t is unsigned int by default, but (signed) int on BSDs |
916 | and unsigned short on windows. Other OSes might have still |
917 | other conventions, sigh. */ |
918 | # if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) \ |
919 | || defined(__NetBSD__) || defined(__OpenBSD__) |
920 | tcc_define_symbol(s, "__WINT_TYPE__" , "int" ); |
921 | # ifdef __FreeBSD__ |
922 | /* define __GNUC__ to have some useful stuff from sys/cdefs.h |
923 | that are unconditionally used in FreeBSDs other system headers :/ */ |
924 | tcc_define_symbol(s, "__GNUC__" , "2" ); |
925 | tcc_define_symbol(s, "__GNUC_MINOR__" , "7" ); |
926 | tcc_define_symbol(s, "__builtin_alloca" , "alloca" ); |
927 | # endif |
928 | # else |
929 | tcc_define_symbol(s, "__WINT_TYPE__" , "unsigned int" ); |
930 | /* glibc defines */ |
931 | tcc_define_symbol(s, "__REDIRECT(name, proto, alias)" , |
932 | "name proto __asm__ (#alias)" ); |
933 | tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)" , |
934 | "name proto __asm__ (#alias) __THROW" ); |
935 | # endif |
936 | /* Some GCC builtins that are simple to express as macros. */ |
937 | tcc_define_symbol(s, "__builtin_extract_return_addr(x)" , "x" ); |
938 | #endif /* ndef TCC_TARGET_PE */ |
939 | #ifdef TCC_TARGET_MACHO |
940 | /* emulate APPLE-GCC to make libc's headerfiles compile: */ |
941 | tcc_define_symbol(s, "__APPLE__" , "1" ); |
942 | tcc_define_symbol(s, "__GNUC__" , "4" ); /* darwin emits warning on GCC<4 */ |
943 | tcc_define_symbol(s, "__APPLE_CC__" , "1" ); /* for <TargetConditionals.h> */ |
944 | tcc_define_symbol(s, "_DONT_USE_CTYPE_INLINE_" , "1" ); |
945 | tcc_define_symbol(s, "__builtin_alloca" , "alloca" ); /* as we claim GNUC */ |
946 | /* used by math.h */ |
947 | tcc_define_symbol(s, "__builtin_huge_val()" , "1e500" ); |
948 | tcc_define_symbol(s, "__builtin_huge_valf()" , "1e50f" ); |
949 | tcc_define_symbol(s, "__builtin_huge_vall()" , "1e5000L" ); |
950 | tcc_define_symbol(s, "__builtin_nanf(ignored_string)" , "__nan()" ); |
951 | /* used by _fd_def.h */ |
952 | tcc_define_symbol(s, "__builtin_bzero(p, ignored_size)" , "bzero(p, sizeof(*(p)))" ); |
953 | /* used by floats.h to implement FLT_ROUNDS C99 macro. 1 == to nearest */ |
954 | tcc_define_symbol(s, "__builtin_flt_rounds()" , "1" ); |
955 | |
956 | /* avoids usage of GCC/clang specific builtins in libc-headerfiles: */ |
957 | tcc_define_symbol(s, "__FINITE_MATH_ONLY__" , "1" ); |
958 | tcc_define_symbol(s, "_FORTIFY_SOURCE" , "0" ); |
959 | #endif /* ndef TCC_TARGET_MACHO */ |
960 | |
961 | #if LONG_SIZE == 4 |
962 | tcc_define_symbol(s, "__SIZEOF_LONG__" , "4" ); |
963 | tcc_define_symbol(s, "__LONG_MAX__" , "0x7fffffffL" ); |
964 | #else |
965 | tcc_define_symbol(s, "__SIZEOF_LONG__" , "8" ); |
966 | tcc_define_symbol(s, "__LONG_MAX__" , "0x7fffffffffffffffL" ); |
967 | #endif |
968 | tcc_define_symbol(s, "__SIZEOF_INT__" , "4" ); |
969 | tcc_define_symbol(s, "__SIZEOF_LONG_LONG__" , "8" ); |
970 | tcc_define_symbol(s, "__CHAR_BIT__" , "8" ); |
971 | tcc_define_symbol(s, "__ORDER_LITTLE_ENDIAN__" , "1234" ); |
972 | tcc_define_symbol(s, "__ORDER_BIG_ENDIAN__" , "4321" ); |
973 | tcc_define_symbol(s, "__BYTE_ORDER__" , "__ORDER_LITTLE_ENDIAN__" ); |
974 | tcc_define_symbol(s, "__INT_MAX__" , "0x7fffffff" ); |
975 | tcc_define_symbol(s, "__LONG_LONG_MAX__" , "0x7fffffffffffffffLL" ); |
976 | tcc_define_symbol(s, "__builtin_offsetof(type,field)" , "((__SIZE_TYPE__) &((type *)0)->field)" ); |
977 | return s; |
978 | } |
979 | |
980 | LIBTCCAPI void tcc_delete(TCCState *s1) |
981 | { |
982 | /* free sections */ |
983 | tccelf_delete(s1); |
984 | |
985 | /* free library paths */ |
986 | dynarray_reset(&s1->library_paths, &s1->nb_library_paths); |
987 | dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths); |
988 | |
989 | /* free include paths */ |
990 | dynarray_reset(&s1->include_paths, &s1->nb_include_paths); |
991 | dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths); |
992 | |
993 | tcc_free(s1->tcc_lib_path); |
994 | tcc_free(s1->soname); |
995 | tcc_free(s1->rpath); |
996 | tcc_free(s1->init_symbol); |
997 | tcc_free(s1->fini_symbol); |
998 | tcc_free(s1->outfile); |
999 | tcc_free(s1->deps_outfile); |
1000 | dynarray_reset(&s1->files, &s1->nb_files); |
1001 | dynarray_reset(&s1->target_deps, &s1->nb_target_deps); |
1002 | dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs); |
1003 | dynarray_reset(&s1->argv, &s1->argc); |
1004 | |
1005 | cstr_free(&s1->cmdline_defs); |
1006 | cstr_free(&s1->cmdline_incl); |
1007 | #ifdef TCC_IS_NATIVE |
1008 | /* free runtime memory */ |
1009 | tcc_run_free(s1); |
1010 | #endif |
1011 | |
1012 | tcc_free(s1); |
1013 | #ifdef MEM_DEBUG |
1014 | if (0 == --nb_states) |
1015 | tcc_memcheck(); |
1016 | #endif |
1017 | } |
1018 | |
1019 | LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type) |
1020 | { |
1021 | s->output_type = output_type; |
1022 | |
1023 | /* always elf for objects */ |
1024 | if (output_type == TCC_OUTPUT_OBJ) |
1025 | s->output_format = TCC_OUTPUT_FORMAT_ELF; |
1026 | |
1027 | if (s->char_is_unsigned) |
1028 | tcc_define_symbol(s, "__CHAR_UNSIGNED__" , NULL); |
1029 | |
1030 | if (s->cversion == 201112) { |
1031 | tcc_undefine_symbol(s, "__STDC_VERSION__" ); |
1032 | tcc_define_symbol(s, "__STDC_VERSION__" , "201112L" ); |
1033 | tcc_define_symbol(s, "__STDC_NO_ATOMICS__" , NULL); |
1034 | tcc_define_symbol(s, "__STDC_NO_COMPLEX__" , NULL); |
1035 | tcc_define_symbol(s, "__STDC_NO_THREADS__" , NULL); |
1036 | #ifndef TCC_TARGET_PE |
1037 | /* on Linux, this conflicts with a define introduced by |
1038 | /usr/include/stdc-predef.h included by glibc libs |
1039 | tcc_define_symbol(s, "__STDC_ISO_10646__", "201605L"); */ |
1040 | tcc_define_symbol(s, "__STDC_UTF_16__" , NULL); |
1041 | tcc_define_symbol(s, "__STDC_UTF_32__" , NULL); |
1042 | #endif |
1043 | } |
1044 | |
1045 | if (s->optimize > 0) |
1046 | tcc_define_symbol(s, "__OPTIMIZE__" , NULL); |
1047 | |
1048 | if (s->option_pthread) |
1049 | tcc_define_symbol(s, "_REENTRANT" , NULL); |
1050 | |
1051 | if (s->leading_underscore) |
1052 | tcc_define_symbol(s, "__leading_underscore" , NULL); |
1053 | |
1054 | if (!s->nostdinc) { |
1055 | /* default include paths */ |
1056 | /* -isystem paths have already been handled */ |
1057 | tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS); |
1058 | } |
1059 | |
1060 | #ifdef CONFIG_TCC_BCHECK |
1061 | if (s->do_bounds_check) { |
1062 | /* if bound checking, then add corresponding sections */ |
1063 | tccelf_bounds_new(s); |
1064 | /* define symbol */ |
1065 | tcc_define_symbol(s, "__BOUNDS_CHECKING_ON" , NULL); |
1066 | } |
1067 | #endif |
1068 | if (s->do_debug) { |
1069 | /* add debug sections */ |
1070 | tccelf_stab_new(s); |
1071 | } |
1072 | |
1073 | tcc_add_library_path(s, CONFIG_TCC_LIBPATHS); |
1074 | |
1075 | #ifdef TCC_TARGET_PE |
1076 | # ifdef _WIN32 |
1077 | if (!s->nostdlib && output_type != TCC_OUTPUT_OBJ) |
1078 | tcc_add_systemdir(s); |
1079 | # endif |
1080 | #else |
1081 | /* paths for crt objects */ |
1082 | tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX); |
1083 | /* add libc crt1/crti objects */ |
1084 | if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) && |
1085 | !s->nostdlib) { |
1086 | #ifndef TCC_TARGET_MACHO |
1087 | /* Mach-O with LC_MAIN doesn't need any crt startup code. */ |
1088 | if (output_type != TCC_OUTPUT_DLL) |
1089 | tcc_add_crt(s, "crt1.o" ); |
1090 | tcc_add_crt(s, "crti.o" ); |
1091 | #endif |
1092 | } |
1093 | #endif |
1094 | return 0; |
1095 | } |
1096 | |
1097 | LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname) |
1098 | { |
1099 | tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname); |
1100 | return 0; |
1101 | } |
1102 | |
1103 | LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname) |
1104 | { |
1105 | tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname); |
1106 | return 0; |
1107 | } |
1108 | |
1109 | ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) |
1110 | { |
1111 | int fd, ret; |
1112 | |
1113 | /* open the file */ |
1114 | fd = _tcc_open(s1, filename); |
1115 | if (fd < 0) { |
1116 | if (flags & AFF_PRINT_ERROR) |
1117 | tcc_error_noabort("file '%s' not found" , filename); |
1118 | return -1; |
1119 | } |
1120 | |
1121 | s1->current_filename = filename; |
1122 | if (flags & AFF_TYPE_BIN) { |
1123 | ElfW(Ehdr) ehdr; |
1124 | int obj_type; |
1125 | |
1126 | obj_type = tcc_object_type(fd, &ehdr); |
1127 | lseek(fd, 0, SEEK_SET); |
1128 | |
1129 | #ifdef TCC_TARGET_MACHO |
1130 | if (0 == obj_type && 0 == strcmp(tcc_fileextension(filename), ".dylib" )) |
1131 | obj_type = AFF_BINTYPE_DYN; |
1132 | #endif |
1133 | |
1134 | switch (obj_type) { |
1135 | case AFF_BINTYPE_REL: |
1136 | ret = tcc_load_object_file(s1, fd, 0); |
1137 | break; |
1138 | #ifndef TCC_TARGET_PE |
1139 | case AFF_BINTYPE_DYN: |
1140 | if (s1->output_type == TCC_OUTPUT_MEMORY) { |
1141 | ret = 0; |
1142 | #ifdef TCC_IS_NATIVE |
1143 | if (NULL == dlopen(filename, RTLD_GLOBAL | RTLD_LAZY)) |
1144 | ret = -1; |
1145 | #endif |
1146 | } else { |
1147 | #ifndef TCC_TARGET_MACHO |
1148 | ret = tcc_load_dll(s1, fd, filename, |
1149 | (flags & AFF_REFERENCED_DLL) != 0); |
1150 | #else |
1151 | ret = macho_load_dll(s1, fd, filename, |
1152 | (flags & AFF_REFERENCED_DLL) != 0); |
1153 | #endif |
1154 | } |
1155 | break; |
1156 | #endif |
1157 | case AFF_BINTYPE_AR: |
1158 | ret = tcc_load_archive(s1, fd, !(flags & AFF_WHOLE_ARCHIVE)); |
1159 | break; |
1160 | #ifdef TCC_TARGET_COFF |
1161 | case AFF_BINTYPE_C67: |
1162 | ret = tcc_load_coff(s1, fd); |
1163 | break; |
1164 | #endif |
1165 | default: |
1166 | #ifdef TCC_TARGET_PE |
1167 | ret = pe_load_file(s1, filename, fd); |
1168 | #elif defined(TCC_TARGET_MACHO) |
1169 | ret = -1; |
1170 | #else |
1171 | /* as GNU ld, consider it is an ld script if not recognized */ |
1172 | ret = tcc_load_ldscript(s1, fd); |
1173 | #endif |
1174 | if (ret < 0) |
1175 | tcc_error_noabort("%s: unrecognized file type %d" , filename, |
1176 | obj_type); |
1177 | break; |
1178 | } |
1179 | close(fd); |
1180 | } else { |
1181 | /* update target deps */ |
1182 | dynarray_add(&s1->target_deps, &s1->nb_target_deps, tcc_strdup(filename)); |
1183 | ret = tcc_compile(s1, flags, filename, fd); |
1184 | } |
1185 | s1->current_filename = NULL; |
1186 | return ret; |
1187 | } |
1188 | |
1189 | LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename) |
1190 | { |
1191 | int filetype = s->filetype; |
1192 | if (0 == (filetype & AFF_TYPE_MASK)) { |
1193 | /* use a file extension to detect a filetype */ |
1194 | const char *ext = tcc_fileextension(filename); |
1195 | if (ext[0]) { |
1196 | ext++; |
1197 | if (!strcmp(ext, "S" )) |
1198 | filetype = AFF_TYPE_ASMPP; |
1199 | else if (!strcmp(ext, "s" )) |
1200 | filetype = AFF_TYPE_ASM; |
1201 | else if (!PATHCMP(ext, "c" ) || !PATHCMP(ext, "i" )) |
1202 | filetype = AFF_TYPE_C; |
1203 | else |
1204 | filetype |= AFF_TYPE_BIN; |
1205 | } else { |
1206 | filetype = AFF_TYPE_C; |
1207 | } |
1208 | } |
1209 | return tcc_add_file_internal(s, filename, filetype | AFF_PRINT_ERROR); |
1210 | } |
1211 | |
1212 | LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname) |
1213 | { |
1214 | tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname); |
1215 | return 0; |
1216 | } |
1217 | |
1218 | static int tcc_add_library_internal(TCCState *s, const char *fmt, |
1219 | const char *filename, int flags, char **paths, int nb_paths) |
1220 | { |
1221 | char buf[1024]; |
1222 | int i; |
1223 | |
1224 | for(i = 0; i < nb_paths; i++) { |
1225 | snprintf(buf, sizeof(buf), fmt, paths[i], filename); |
1226 | if (tcc_add_file_internal(s, buf, flags | AFF_TYPE_BIN) == 0) |
1227 | return 0; |
1228 | } |
1229 | return -1; |
1230 | } |
1231 | |
1232 | #ifndef TCC_TARGET_MACHO |
1233 | /* find and load a dll. Return non zero if not found */ |
1234 | /* XXX: add '-rpath' option support ? */ |
1235 | ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags) |
1236 | { |
1237 | return tcc_add_library_internal(s, "%s/%s" , filename, flags, |
1238 | s->library_paths, s->nb_library_paths); |
1239 | } |
1240 | #endif |
1241 | |
1242 | #if !defined TCC_TARGET_PE && !defined TCC_TARGET_MACHO |
1243 | ST_FUNC int tcc_add_crt(TCCState *s1, const char *filename) |
1244 | { |
1245 | if (-1 == tcc_add_library_internal(s1, "%s/%s" , |
1246 | filename, 0, s1->crt_paths, s1->nb_crt_paths)) |
1247 | tcc_error_noabort("file '%s' not found" , filename); |
1248 | return 0; |
1249 | } |
1250 | #endif |
1251 | |
1252 | /* the library name is the same as the argument of the '-l' option */ |
1253 | LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname) |
1254 | { |
1255 | #if defined TCC_TARGET_PE |
1256 | const char *libs[] = { "%s/%s.def" , "%s/lib%s.def" , "%s/%s.dll" , "%s/lib%s.dll" , "%s/lib%s.a" , NULL }; |
1257 | const char **pp = s->static_link ? libs + 4 : libs; |
1258 | #elif defined TCC_TARGET_MACHO |
1259 | const char *libs[] = { "%s/lib%s.dylib" , "%s/lib%s.a" , NULL }; |
1260 | const char **pp = s->static_link ? libs + 1 : libs; |
1261 | #else |
1262 | const char *libs[] = { "%s/lib%s.so" , "%s/lib%s.a" , NULL }; |
1263 | const char **pp = s->static_link ? libs + 1 : libs; |
1264 | #endif |
1265 | int flags = s->filetype & AFF_WHOLE_ARCHIVE; |
1266 | while (*pp) { |
1267 | if (0 == tcc_add_library_internal(s, *pp, |
1268 | libraryname, flags, s->library_paths, s->nb_library_paths)) |
1269 | return 0; |
1270 | ++pp; |
1271 | } |
1272 | return -1; |
1273 | } |
1274 | |
1275 | PUB_FUNC int tcc_add_library_err(TCCState *s1, const char *libname) |
1276 | { |
1277 | int ret = tcc_add_library(s1, libname); |
1278 | if (ret < 0) |
1279 | tcc_error_noabort("library '%s' not found" , libname); |
1280 | return ret; |
1281 | } |
1282 | |
1283 | /* handle #pragma comment(lib,) */ |
1284 | ST_FUNC void tcc_add_pragma_libs(TCCState *s1) |
1285 | { |
1286 | int i; |
1287 | for (i = 0; i < s1->nb_pragma_libs; i++) |
1288 | tcc_add_library_err(s1, s1->pragma_libs[i]); |
1289 | } |
1290 | |
1291 | LIBTCCAPI int tcc_add_symbol(TCCState *s1, const char *name, const void *val) |
1292 | { |
1293 | #ifdef TCC_TARGET_PE |
1294 | /* On x86_64 'val' might not be reachable with a 32bit offset. |
1295 | So it is handled here as if it were in a DLL. */ |
1296 | pe_putimport(s1, 0, name, (uintptr_t)val); |
1297 | #else |
1298 | char buf[256]; |
1299 | if (s1->leading_underscore) { |
1300 | buf[0] = '_'; |
1301 | pstrcpy(buf + 1, sizeof(buf) - 1, name); |
1302 | name = buf; |
1303 | } |
1304 | set_global_sym(s1, name, NULL, (addr_t)(uintptr_t)val); /* NULL: SHN_ABS */ |
1305 | #endif |
1306 | return 0; |
1307 | } |
1308 | |
1309 | LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path) |
1310 | { |
1311 | tcc_free(s->tcc_lib_path); |
1312 | s->tcc_lib_path = tcc_strdup(path); |
1313 | } |
1314 | |
1315 | #define WD_ALL 0x0001 /* warning is activated when using -Wall */ |
1316 | #define FD_INVERT 0x0002 /* invert value before storing */ |
1317 | |
1318 | typedef struct FlagDef { |
1319 | uint16_t offset; |
1320 | uint16_t flags; |
1321 | const char *name; |
1322 | } FlagDef; |
1323 | |
1324 | static int no_flag(const char **pp) |
1325 | { |
1326 | const char *p = *pp; |
1327 | if (*p != 'n' || *++p != 'o' || *++p != '-') |
1328 | return 0; |
1329 | *pp = p + 1; |
1330 | return 1; |
1331 | } |
1332 | |
1333 | ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, const char *name) |
1334 | { |
1335 | int value, ret; |
1336 | const FlagDef *p; |
1337 | const char *r; |
1338 | |
1339 | value = 1; |
1340 | r = name; |
1341 | if (no_flag(&r)) |
1342 | value = 0; |
1343 | |
1344 | for (ret = -1, p = flags; p->name; ++p) { |
1345 | if (ret) { |
1346 | if (strcmp(r, p->name)) |
1347 | continue; |
1348 | } else { |
1349 | if (0 == (p->flags & WD_ALL)) |
1350 | continue; |
1351 | } |
1352 | if (p->offset) { |
1353 | *((unsigned char *)s + p->offset) = |
1354 | p->flags & FD_INVERT ? !value : value; |
1355 | if (ret) |
1356 | return 0; |
1357 | } else { |
1358 | ret = 0; |
1359 | } |
1360 | } |
1361 | return ret; |
1362 | } |
1363 | |
1364 | static int strstart(const char *val, const char **str) |
1365 | { |
1366 | const char *p, *q; |
1367 | p = *str; |
1368 | q = val; |
1369 | while (*q) { |
1370 | if (*p != *q) |
1371 | return 0; |
1372 | p++; |
1373 | q++; |
1374 | } |
1375 | *str = p; |
1376 | return 1; |
1377 | } |
1378 | |
1379 | /* Like strstart, but automatically takes into account that ld options can |
1380 | * |
1381 | * - start with double or single dash (e.g. '--soname' or '-soname') |
1382 | * - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so' |
1383 | * or '-Wl,-soname=x.so') |
1384 | * |
1385 | * you provide `val` always in 'option[=]' form (no leading -) |
1386 | */ |
1387 | static int link_option(const char *str, const char *val, const char **ptr) |
1388 | { |
1389 | const char *p, *q; |
1390 | int ret; |
1391 | |
1392 | /* there should be 1 or 2 dashes */ |
1393 | if (*str++ != '-') |
1394 | return 0; |
1395 | if (*str == '-') |
1396 | str++; |
1397 | |
1398 | /* then str & val should match (potentially up to '=') */ |
1399 | p = str; |
1400 | q = val; |
1401 | |
1402 | ret = 1; |
1403 | if (q[0] == '?') { |
1404 | ++q; |
1405 | if (no_flag(&p)) |
1406 | ret = -1; |
1407 | } |
1408 | |
1409 | while (*q != '\0' && *q != '=') { |
1410 | if (*p != *q) |
1411 | return 0; |
1412 | p++; |
1413 | q++; |
1414 | } |
1415 | |
1416 | /* '=' near eos means ',' or '=' is ok */ |
1417 | if (*q == '=') { |
1418 | if (*p == 0) |
1419 | *ptr = p; |
1420 | if (*p != ',' && *p != '=') |
1421 | return 0; |
1422 | p++; |
1423 | } else if (*p) { |
1424 | return 0; |
1425 | } |
1426 | *ptr = p; |
1427 | return ret; |
1428 | } |
1429 | |
1430 | static const char *skip_linker_arg(const char **str) |
1431 | { |
1432 | const char *s1 = *str; |
1433 | const char *s2 = strchr(s1, ','); |
1434 | *str = s2 ? s2++ : (s2 = s1 + strlen(s1)); |
1435 | return s2; |
1436 | } |
1437 | |
1438 | static void copy_linker_arg(char **pp, const char *s, int sep) |
1439 | { |
1440 | const char *q = s; |
1441 | char *p = *pp; |
1442 | int l = 0; |
1443 | if (p && sep) |
1444 | p[l = strlen(p)] = sep, ++l; |
1445 | skip_linker_arg(&q); |
1446 | pstrncpy(l + (*pp = tcc_realloc(p, q - s + l + 1)), s, q - s); |
1447 | } |
1448 | |
1449 | /* set linker options */ |
1450 | static int tcc_set_linker(TCCState *s, const char *option) |
1451 | { |
1452 | TCCState *s1 = s; |
1453 | while (*option) { |
1454 | |
1455 | const char *p = NULL; |
1456 | char *end = NULL; |
1457 | int ignoring = 0; |
1458 | int ret; |
1459 | |
1460 | if (link_option(option, "Bsymbolic" , &p)) { |
1461 | s->symbolic = 1; |
1462 | } else if (link_option(option, "nostdlib" , &p)) { |
1463 | s->nostdlib = 1; |
1464 | } else if (link_option(option, "fini=" , &p)) { |
1465 | copy_linker_arg(&s->fini_symbol, p, 0); |
1466 | ignoring = 1; |
1467 | } else if (link_option(option, "image-base=" , &p) |
1468 | || link_option(option, "Ttext=" , &p)) { |
1469 | s->text_addr = strtoull(p, &end, 16); |
1470 | s->has_text_addr = 1; |
1471 | } else if (link_option(option, "init=" , &p)) { |
1472 | copy_linker_arg(&s->init_symbol, p, 0); |
1473 | ignoring = 1; |
1474 | } else if (link_option(option, "oformat=" , &p)) { |
1475 | #if defined(TCC_TARGET_PE) |
1476 | if (strstart("pe-" , &p)) { |
1477 | #elif PTR_SIZE == 8 |
1478 | if (strstart("elf64-" , &p)) { |
1479 | #else |
1480 | if (strstart("elf32-" , &p)) { |
1481 | #endif |
1482 | s->output_format = TCC_OUTPUT_FORMAT_ELF; |
1483 | } else if (!strcmp(p, "binary" )) { |
1484 | s->output_format = TCC_OUTPUT_FORMAT_BINARY; |
1485 | #ifdef TCC_TARGET_COFF |
1486 | } else if (!strcmp(p, "coff" )) { |
1487 | s->output_format = TCC_OUTPUT_FORMAT_COFF; |
1488 | #endif |
1489 | } else |
1490 | goto err; |
1491 | |
1492 | } else if (link_option(option, "as-needed" , &p)) { |
1493 | ignoring = 1; |
1494 | } else if (link_option(option, "O" , &p)) { |
1495 | ignoring = 1; |
1496 | } else if (link_option(option, "export-all-symbols" , &p)) { |
1497 | s->rdynamic = 1; |
1498 | } else if (link_option(option, "export-dynamic" , &p)) { |
1499 | s->rdynamic = 1; |
1500 | } else if (link_option(option, "rpath=" , &p)) { |
1501 | copy_linker_arg(&s->rpath, p, ':'); |
1502 | } else if (link_option(option, "enable-new-dtags" , &p)) { |
1503 | s->enable_new_dtags = 1; |
1504 | } else if (link_option(option, "section-alignment=" , &p)) { |
1505 | s->section_align = strtoul(p, &end, 16); |
1506 | } else if (link_option(option, "soname=" , &p)) { |
1507 | copy_linker_arg(&s->soname, p, 0); |
1508 | #ifdef TCC_TARGET_PE |
1509 | } else if (link_option(option, "large-address-aware" , &p)) { |
1510 | s->pe_characteristics |= 0x20; |
1511 | } else if (link_option(option, "file-alignment=" , &p)) { |
1512 | s->pe_file_align = strtoul(p, &end, 16); |
1513 | } else if (link_option(option, "stack=" , &p)) { |
1514 | s->pe_stack_size = strtoul(p, &end, 10); |
1515 | } else if (link_option(option, "subsystem=" , &p)) { |
1516 | #if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) |
1517 | if (!strcmp(p, "native" )) { |
1518 | s->pe_subsystem = 1; |
1519 | } else if (!strcmp(p, "console" )) { |
1520 | s->pe_subsystem = 3; |
1521 | } else if (!strcmp(p, "gui" ) || !strcmp(p, "windows" )) { |
1522 | s->pe_subsystem = 2; |
1523 | } else if (!strcmp(p, "posix" )) { |
1524 | s->pe_subsystem = 7; |
1525 | } else if (!strcmp(p, "efiapp" )) { |
1526 | s->pe_subsystem = 10; |
1527 | } else if (!strcmp(p, "efiboot" )) { |
1528 | s->pe_subsystem = 11; |
1529 | } else if (!strcmp(p, "efiruntime" )) { |
1530 | s->pe_subsystem = 12; |
1531 | } else if (!strcmp(p, "efirom" )) { |
1532 | s->pe_subsystem = 13; |
1533 | #elif defined(TCC_TARGET_ARM) |
1534 | if (!strcmp(p, "wince" )) { |
1535 | s->pe_subsystem = 9; |
1536 | #endif |
1537 | } else |
1538 | goto err; |
1539 | #endif |
1540 | } else if (ret = link_option(option, "?whole-archive" , &p), ret) { |
1541 | if (ret > 0) |
1542 | s->filetype |= AFF_WHOLE_ARCHIVE; |
1543 | else |
1544 | s->filetype &= ~AFF_WHOLE_ARCHIVE; |
1545 | } else if (p) { |
1546 | return 0; |
1547 | } else { |
1548 | err: |
1549 | tcc_error("unsupported linker option '%s'" , option); |
1550 | } |
1551 | |
1552 | if (ignoring && s->warn_unsupported) |
1553 | tcc_warning("unsupported linker option '%s'" , option); |
1554 | |
1555 | option = skip_linker_arg(&p); |
1556 | } |
1557 | return 1; |
1558 | } |
1559 | |
1560 | typedef struct TCCOption { |
1561 | const char *name; |
1562 | uint16_t index; |
1563 | uint16_t flags; |
1564 | } TCCOption; |
1565 | |
1566 | enum { |
1567 | TCC_OPTION_HELP, |
1568 | TCC_OPTION_HELP2, |
1569 | TCC_OPTION_v, |
1570 | TCC_OPTION_I, |
1571 | TCC_OPTION_D, |
1572 | TCC_OPTION_U, |
1573 | TCC_OPTION_P, |
1574 | TCC_OPTION_L, |
1575 | TCC_OPTION_B, |
1576 | TCC_OPTION_l, |
1577 | TCC_OPTION_bench, |
1578 | TCC_OPTION_bt, |
1579 | TCC_OPTION_b, |
1580 | TCC_OPTION_ba, |
1581 | TCC_OPTION_g, |
1582 | TCC_OPTION_c, |
1583 | TCC_OPTION_dumpversion, |
1584 | TCC_OPTION_d, |
1585 | TCC_OPTION_static, |
1586 | TCC_OPTION_std, |
1587 | TCC_OPTION_shared, |
1588 | TCC_OPTION_soname, |
1589 | TCC_OPTION_o, |
1590 | TCC_OPTION_r, |
1591 | TCC_OPTION_s, |
1592 | TCC_OPTION_traditional, |
1593 | TCC_OPTION_Wl, |
1594 | TCC_OPTION_Wp, |
1595 | TCC_OPTION_W, |
1596 | TCC_OPTION_O, |
1597 | TCC_OPTION_mfloat_abi, |
1598 | TCC_OPTION_m, |
1599 | TCC_OPTION_f, |
1600 | TCC_OPTION_isystem, |
1601 | TCC_OPTION_iwithprefix, |
1602 | TCC_OPTION_include, |
1603 | TCC_OPTION_nostdinc, |
1604 | TCC_OPTION_nostdlib, |
1605 | TCC_OPTION_print_search_dirs, |
1606 | TCC_OPTION_rdynamic, |
1607 | TCC_OPTION_param, |
1608 | TCC_OPTION_pedantic, |
1609 | TCC_OPTION_pthread, |
1610 | TCC_OPTION_run, |
1611 | TCC_OPTION_w, |
1612 | TCC_OPTION_pipe, |
1613 | TCC_OPTION_E, |
1614 | TCC_OPTION_MD, |
1615 | TCC_OPTION_MF, |
1616 | TCC_OPTION_x, |
1617 | TCC_OPTION_ar, |
1618 | TCC_OPTION_impdef, |
1619 | TCC_OPTION_C |
1620 | }; |
1621 | |
1622 | #define TCC_OPTION_HAS_ARG 0x0001 |
1623 | #define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ |
1624 | |
1625 | static const TCCOption tcc_options[] = { |
1626 | { "h" , TCC_OPTION_HELP, 0 }, |
1627 | { "-help" , TCC_OPTION_HELP, 0 }, |
1628 | { "?" , TCC_OPTION_HELP, 0 }, |
1629 | { "hh" , TCC_OPTION_HELP2, 0 }, |
1630 | { "v" , TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1631 | { "-version" , TCC_OPTION_v, 0 }, /* handle as verbose, also prints version*/ |
1632 | { "I" , TCC_OPTION_I, TCC_OPTION_HAS_ARG }, |
1633 | { "D" , TCC_OPTION_D, TCC_OPTION_HAS_ARG }, |
1634 | { "U" , TCC_OPTION_U, TCC_OPTION_HAS_ARG }, |
1635 | { "P" , TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1636 | { "L" , TCC_OPTION_L, TCC_OPTION_HAS_ARG }, |
1637 | { "B" , TCC_OPTION_B, TCC_OPTION_HAS_ARG }, |
1638 | { "l" , TCC_OPTION_l, TCC_OPTION_HAS_ARG }, |
1639 | { "bench" , TCC_OPTION_bench, 0 }, |
1640 | #ifdef CONFIG_TCC_BACKTRACE |
1641 | { "bt" , TCC_OPTION_bt, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1642 | #endif |
1643 | #ifdef CONFIG_TCC_BCHECK |
1644 | { "b" , TCC_OPTION_b, 0 }, |
1645 | #endif |
1646 | { "g" , TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1647 | { "c" , TCC_OPTION_c, 0 }, |
1648 | { "dumpversion" , TCC_OPTION_dumpversion, 0}, |
1649 | { "d" , TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1650 | { "static" , TCC_OPTION_static, 0 }, |
1651 | { "std" , TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1652 | { "shared" , TCC_OPTION_shared, 0 }, |
1653 | { "soname" , TCC_OPTION_soname, TCC_OPTION_HAS_ARG }, |
1654 | { "o" , TCC_OPTION_o, TCC_OPTION_HAS_ARG }, |
1655 | { "-param" , TCC_OPTION_param, TCC_OPTION_HAS_ARG }, |
1656 | { "pedantic" , TCC_OPTION_pedantic, 0}, |
1657 | { "pthread" , TCC_OPTION_pthread, 0}, |
1658 | { "run" , TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1659 | { "rdynamic" , TCC_OPTION_rdynamic, 0 }, |
1660 | { "r" , TCC_OPTION_r, 0 }, |
1661 | { "s" , TCC_OPTION_s, 0 }, |
1662 | { "traditional" , TCC_OPTION_traditional, 0 }, |
1663 | { "Wl," , TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1664 | { "Wp," , TCC_OPTION_Wp, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1665 | { "W" , TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1666 | { "O" , TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1667 | #ifdef TCC_TARGET_ARM |
1668 | { "mfloat-abi" , TCC_OPTION_mfloat_abi, TCC_OPTION_HAS_ARG }, |
1669 | #endif |
1670 | { "m" , TCC_OPTION_m, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1671 | { "f" , TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
1672 | { "isystem" , TCC_OPTION_isystem, TCC_OPTION_HAS_ARG }, |
1673 | { "include" , TCC_OPTION_include, TCC_OPTION_HAS_ARG }, |
1674 | { "nostdinc" , TCC_OPTION_nostdinc, 0 }, |
1675 | { "nostdlib" , TCC_OPTION_nostdlib, 0 }, |
1676 | { "print-search-dirs" , TCC_OPTION_print_search_dirs, 0 }, |
1677 | { "w" , TCC_OPTION_w, 0 }, |
1678 | { "pipe" , TCC_OPTION_pipe, 0}, |
1679 | { "E" , TCC_OPTION_E, 0}, |
1680 | { "MD" , TCC_OPTION_MD, 0}, |
1681 | { "MF" , TCC_OPTION_MF, TCC_OPTION_HAS_ARG }, |
1682 | { "x" , TCC_OPTION_x, TCC_OPTION_HAS_ARG }, |
1683 | { "ar" , TCC_OPTION_ar, 0}, |
1684 | #ifdef TCC_TARGET_PE |
1685 | { "impdef" , TCC_OPTION_impdef, 0}, |
1686 | #endif |
1687 | { "C" , TCC_OPTION_C, 0}, |
1688 | { NULL, 0, 0 }, |
1689 | }; |
1690 | |
1691 | static const FlagDef options_W[] = { |
1692 | { 0, 0, "all" }, |
1693 | { offsetof(TCCState, warn_unsupported), 0, "unsupported" }, |
1694 | { offsetof(TCCState, warn_write_strings), 0, "write-strings" }, |
1695 | { offsetof(TCCState, warn_error), 0, "error" }, |
1696 | { offsetof(TCCState, warn_gcc_compat), 0, "gcc-compat" }, |
1697 | { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL, |
1698 | "implicit-function-declaration" }, |
1699 | { 0, 0, NULL } |
1700 | }; |
1701 | |
1702 | static const FlagDef options_f[] = { |
1703 | { offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" }, |
1704 | { offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" }, |
1705 | { offsetof(TCCState, nocommon), FD_INVERT, "common" }, |
1706 | { offsetof(TCCState, leading_underscore), 0, "leading-underscore" }, |
1707 | { offsetof(TCCState, ms_extensions), 0, "ms-extensions" }, |
1708 | { offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" }, |
1709 | { 0, 0, NULL } |
1710 | }; |
1711 | |
1712 | static const FlagDef options_m[] = { |
1713 | { offsetof(TCCState, ms_bitfields), 0, "ms-bitfields" }, |
1714 | #ifdef TCC_TARGET_X86_64 |
1715 | { offsetof(TCCState, nosse), FD_INVERT, "sse" }, |
1716 | #endif |
1717 | { 0, 0, NULL } |
1718 | }; |
1719 | |
1720 | static void args_parser_add_file(TCCState *s, const char* filename, int filetype) |
1721 | { |
1722 | struct filespec *f = tcc_malloc(sizeof *f + strlen(filename)); |
1723 | f->type = filetype; |
1724 | strcpy(f->name, filename); |
1725 | dynarray_add(&s->files, &s->nb_files, f); |
1726 | } |
1727 | |
1728 | static int args_parser_make_argv(const char *r, int *argc, char ***argv) |
1729 | { |
1730 | int ret = 0, q, c; |
1731 | CString str; |
1732 | for(;;) { |
1733 | while (c = (unsigned char)*r, c && c <= ' ') |
1734 | ++r; |
1735 | if (c == 0) |
1736 | break; |
1737 | q = 0; |
1738 | cstr_new(&str); |
1739 | while (c = (unsigned char)*r, c) { |
1740 | ++r; |
1741 | if (c == '\\' && (*r == '"' || *r == '\\')) { |
1742 | c = *r++; |
1743 | } else if (c == '"') { |
1744 | q = !q; |
1745 | continue; |
1746 | } else if (q == 0 && c <= ' ') { |
1747 | break; |
1748 | } |
1749 | cstr_ccat(&str, c); |
1750 | } |
1751 | cstr_ccat(&str, 0); |
1752 | //printf("<%s>\n", str.data), fflush(stdout); |
1753 | dynarray_add(argv, argc, tcc_strdup(str.data)); |
1754 | cstr_free(&str); |
1755 | ++ret; |
1756 | } |
1757 | return ret; |
1758 | } |
1759 | |
1760 | /* read list file */ |
1761 | static void args_parser_listfile(TCCState *s, |
1762 | const char *filename, int optind, int *pargc, char ***pargv) |
1763 | { |
1764 | TCCState *s1 = s; |
1765 | int fd, i; |
1766 | size_t len; |
1767 | char *p; |
1768 | int argc = 0; |
1769 | char **argv = NULL; |
1770 | |
1771 | fd = open(filename, O_RDONLY | O_BINARY); |
1772 | if (fd < 0) |
1773 | tcc_error("listfile '%s' not found" , filename); |
1774 | |
1775 | len = lseek(fd, 0, SEEK_END); |
1776 | p = tcc_malloc(len + 1), p[len] = 0; |
1777 | lseek(fd, 0, SEEK_SET), read(fd, p, len), close(fd); |
1778 | |
1779 | for (i = 0; i < *pargc; ++i) |
1780 | if (i == optind) |
1781 | args_parser_make_argv(p, &argc, &argv); |
1782 | else |
1783 | dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i])); |
1784 | |
1785 | tcc_free(p); |
1786 | dynarray_reset(&s->argv, &s->argc); |
1787 | *pargc = s->argc = argc, *pargv = s->argv = argv; |
1788 | } |
1789 | |
1790 | PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind) |
1791 | { |
1792 | TCCState *s1 = s; |
1793 | const TCCOption *popt; |
1794 | const char *optarg, *r; |
1795 | const char *run = NULL; |
1796 | int x; |
1797 | CString linker_arg; /* collect -Wl options */ |
1798 | int tool = 0, arg_start = 0, noaction = optind; |
1799 | char **argv = *pargv; |
1800 | int argc = *pargc; |
1801 | |
1802 | cstr_new(&linker_arg); |
1803 | |
1804 | while (optind < argc) { |
1805 | r = argv[optind]; |
1806 | if (r[0] == '@' && r[1] != '\0') { |
1807 | args_parser_listfile(s, r + 1, optind, &argc, &argv); |
1808 | continue; |
1809 | } |
1810 | optind++; |
1811 | if (tool) { |
1812 | if (r[0] == '-' && r[1] == 'v' && r[2] == 0) |
1813 | ++s->verbose; |
1814 | continue; |
1815 | } |
1816 | reparse: |
1817 | if (r[0] != '-' || r[1] == '\0') { |
1818 | if (r[0] != '@') /* allow "tcc file(s) -run @ args ..." */ |
1819 | args_parser_add_file(s, r, s->filetype); |
1820 | if (run) { |
1821 | tcc_set_options(s, run); |
1822 | arg_start = optind - 1; |
1823 | break; |
1824 | } |
1825 | continue; |
1826 | } |
1827 | |
1828 | /* find option in table */ |
1829 | for(popt = tcc_options; ; ++popt) { |
1830 | const char *p1 = popt->name; |
1831 | const char *r1 = r + 1; |
1832 | if (p1 == NULL) |
1833 | tcc_error("invalid option -- '%s'" , r); |
1834 | if (!strstart(p1, &r1)) |
1835 | continue; |
1836 | optarg = r1; |
1837 | if (popt->flags & TCC_OPTION_HAS_ARG) { |
1838 | if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) { |
1839 | if (optind >= argc) |
1840 | arg_err: |
1841 | tcc_error("argument to '%s' is missing" , r); |
1842 | optarg = argv[optind++]; |
1843 | } |
1844 | } else if (*r1 != '\0') |
1845 | continue; |
1846 | break; |
1847 | } |
1848 | |
1849 | switch(popt->index) { |
1850 | case TCC_OPTION_HELP: |
1851 | x = OPT_HELP; |
1852 | goto extra_action; |
1853 | case TCC_OPTION_HELP2: |
1854 | x = OPT_HELP2; |
1855 | goto extra_action; |
1856 | case TCC_OPTION_I: |
1857 | tcc_add_include_path(s, optarg); |
1858 | break; |
1859 | case TCC_OPTION_D: |
1860 | tcc_define_symbol(s, optarg, NULL); |
1861 | break; |
1862 | case TCC_OPTION_U: |
1863 | tcc_undefine_symbol(s, optarg); |
1864 | break; |
1865 | case TCC_OPTION_L: |
1866 | tcc_add_library_path(s, optarg); |
1867 | break; |
1868 | case TCC_OPTION_B: |
1869 | /* set tcc utilities path (mainly for tcc development) */ |
1870 | tcc_set_lib_path(s, optarg); |
1871 | break; |
1872 | case TCC_OPTION_l: |
1873 | args_parser_add_file(s, optarg, AFF_TYPE_LIB | (s->filetype & ~AFF_TYPE_MASK)); |
1874 | s->nb_libraries++; |
1875 | break; |
1876 | case TCC_OPTION_pthread: |
1877 | s->option_pthread = 1; |
1878 | break; |
1879 | case TCC_OPTION_bench: |
1880 | s->do_bench = 1; |
1881 | break; |
1882 | #ifdef CONFIG_TCC_BACKTRACE |
1883 | case TCC_OPTION_bt: |
1884 | s->rt_num_callers = atoi(optarg); |
1885 | s->do_backtrace = 1; |
1886 | s->do_debug = 1; |
1887 | break; |
1888 | #endif |
1889 | #ifdef CONFIG_TCC_BCHECK |
1890 | case TCC_OPTION_b: |
1891 | s->do_bounds_check = 1; |
1892 | s->do_backtrace = 1; |
1893 | s->do_debug = 1; |
1894 | break; |
1895 | #endif |
1896 | case TCC_OPTION_g: |
1897 | s->do_debug = 1; |
1898 | break; |
1899 | case TCC_OPTION_c: |
1900 | x = TCC_OUTPUT_OBJ; |
1901 | set_output_type: |
1902 | if (s->output_type) |
1903 | tcc_warning("-%s: overriding compiler action already specified" , popt->name); |
1904 | s->output_type = x; |
1905 | break; |
1906 | case TCC_OPTION_d: |
1907 | if (*optarg == 'D') |
1908 | s->dflag = 3; |
1909 | else if (*optarg == 'M') |
1910 | s->dflag = 7; |
1911 | else if (*optarg == 't') |
1912 | s->dflag = 16; |
1913 | else if (isnum(*optarg)) |
1914 | s->g_debug |= atoi(optarg); |
1915 | else |
1916 | goto unsupported_option; |
1917 | break; |
1918 | case TCC_OPTION_static: |
1919 | s->static_link = 1; |
1920 | break; |
1921 | case TCC_OPTION_std: |
1922 | if (strcmp(optarg, "=c11" ) == 0) |
1923 | s->cversion = 201112; |
1924 | break; |
1925 | case TCC_OPTION_shared: |
1926 | x = TCC_OUTPUT_DLL; |
1927 | goto set_output_type; |
1928 | case TCC_OPTION_soname: |
1929 | s->soname = tcc_strdup(optarg); |
1930 | break; |
1931 | case TCC_OPTION_o: |
1932 | if (s->outfile) { |
1933 | tcc_warning("multiple -o option" ); |
1934 | tcc_free(s->outfile); |
1935 | } |
1936 | s->outfile = tcc_strdup(optarg); |
1937 | break; |
1938 | case TCC_OPTION_r: |
1939 | /* generate a .o merging several output files */ |
1940 | s->option_r = 1; |
1941 | x = TCC_OUTPUT_OBJ; |
1942 | goto set_output_type; |
1943 | case TCC_OPTION_isystem: |
1944 | tcc_add_sysinclude_path(s, optarg); |
1945 | break; |
1946 | case TCC_OPTION_include: |
1947 | cstr_printf(&s->cmdline_incl, "#include \"%s\"\n" , optarg); |
1948 | break; |
1949 | case TCC_OPTION_nostdinc: |
1950 | s->nostdinc = 1; |
1951 | break; |
1952 | case TCC_OPTION_nostdlib: |
1953 | s->nostdlib = 1; |
1954 | break; |
1955 | case TCC_OPTION_run: |
1956 | #ifndef TCC_IS_NATIVE |
1957 | tcc_error("-run is not available in a cross compiler" ); |
1958 | #endif |
1959 | run = optarg; |
1960 | x = TCC_OUTPUT_MEMORY; |
1961 | goto set_output_type; |
1962 | case TCC_OPTION_v: |
1963 | do ++s->verbose; while (*optarg++ == 'v'); |
1964 | ++noaction; |
1965 | break; |
1966 | case TCC_OPTION_f: |
1967 | if (set_flag(s, options_f, optarg) < 0) |
1968 | goto unsupported_option; |
1969 | break; |
1970 | #ifdef TCC_TARGET_ARM |
1971 | case TCC_OPTION_mfloat_abi: |
1972 | /* tcc doesn't support soft float yet */ |
1973 | if (!strcmp(optarg, "softfp" )) { |
1974 | s->float_abi = ARM_SOFTFP_FLOAT; |
1975 | tcc_undefine_symbol(s, "__ARM_PCS_VFP" ); |
1976 | } else if (!strcmp(optarg, "hard" )) |
1977 | s->float_abi = ARM_HARD_FLOAT; |
1978 | else |
1979 | tcc_error("unsupported float abi '%s'" , optarg); |
1980 | break; |
1981 | #endif |
1982 | case TCC_OPTION_m: |
1983 | if (set_flag(s, options_m, optarg) < 0) { |
1984 | if (x = atoi(optarg), x != 32 && x != 64) |
1985 | goto unsupported_option; |
1986 | if (PTR_SIZE != x/8) |
1987 | return x; |
1988 | ++noaction; |
1989 | } |
1990 | break; |
1991 | case TCC_OPTION_W: |
1992 | s->warn_none = 0; |
1993 | if (optarg[0] && set_flag(s, options_W, optarg) < 0) |
1994 | goto unsupported_option; |
1995 | break; |
1996 | case TCC_OPTION_w: |
1997 | s->warn_none = 1; |
1998 | break; |
1999 | case TCC_OPTION_rdynamic: |
2000 | s->rdynamic = 1; |
2001 | break; |
2002 | case TCC_OPTION_Wl: |
2003 | if (linker_arg.size) |
2004 | --linker_arg.size, cstr_ccat(&linker_arg, ','); |
2005 | cstr_cat(&linker_arg, optarg, 0); |
2006 | if (tcc_set_linker(s, linker_arg.data)) |
2007 | cstr_free(&linker_arg); |
2008 | break; |
2009 | case TCC_OPTION_Wp: |
2010 | r = optarg; |
2011 | goto reparse; |
2012 | case TCC_OPTION_E: |
2013 | x = TCC_OUTPUT_PREPROCESS; |
2014 | goto set_output_type; |
2015 | case TCC_OPTION_P: |
2016 | s->Pflag = atoi(optarg) + 1; |
2017 | break; |
2018 | case TCC_OPTION_MD: |
2019 | s->gen_deps = 1; |
2020 | break; |
2021 | case TCC_OPTION_MF: |
2022 | s->deps_outfile = tcc_strdup(optarg); |
2023 | break; |
2024 | case TCC_OPTION_dumpversion: |
2025 | printf ("%s\n" , TCC_VERSION); |
2026 | exit(0); |
2027 | break; |
2028 | case TCC_OPTION_x: |
2029 | x = 0; |
2030 | if (*optarg == 'c') |
2031 | x = AFF_TYPE_C; |
2032 | else if (*optarg == 'a') |
2033 | x = AFF_TYPE_ASMPP; |
2034 | else if (*optarg == 'b') |
2035 | x = AFF_TYPE_BIN; |
2036 | else if (*optarg == 'n') |
2037 | x = AFF_TYPE_NONE; |
2038 | else |
2039 | tcc_warning("unsupported language '%s'" , optarg); |
2040 | s->filetype = x | (s->filetype & ~AFF_TYPE_MASK); |
2041 | break; |
2042 | case TCC_OPTION_O: |
2043 | s->optimize = atoi(optarg); |
2044 | break; |
2045 | case TCC_OPTION_print_search_dirs: |
2046 | x = OPT_PRINT_DIRS; |
2047 | goto extra_action; |
2048 | case TCC_OPTION_impdef: |
2049 | x = OPT_IMPDEF; |
2050 | goto extra_action; |
2051 | case TCC_OPTION_ar: |
2052 | x = OPT_AR; |
2053 | : |
2054 | arg_start = optind - 1; |
2055 | if (arg_start != noaction) |
2056 | tcc_error("cannot parse %s here" , r); |
2057 | tool = x; |
2058 | break; |
2059 | case TCC_OPTION_traditional: |
2060 | case TCC_OPTION_pedantic: |
2061 | case TCC_OPTION_pipe: |
2062 | case TCC_OPTION_s: |
2063 | case TCC_OPTION_C: |
2064 | /* ignored */ |
2065 | break; |
2066 | default: |
2067 | unsupported_option: |
2068 | if (s->warn_unsupported) |
2069 | tcc_warning("unsupported option '%s'" , r); |
2070 | break; |
2071 | } |
2072 | } |
2073 | if (linker_arg.size) { |
2074 | r = linker_arg.data; |
2075 | goto arg_err; |
2076 | } |
2077 | *pargc = argc - arg_start; |
2078 | *pargv = argv + arg_start; |
2079 | if (tool) |
2080 | return tool; |
2081 | if (optind != noaction) |
2082 | return 0; |
2083 | if (s->verbose == 2) |
2084 | return OPT_PRINT_DIRS; |
2085 | if (s->verbose) |
2086 | return OPT_V; |
2087 | return OPT_HELP; |
2088 | } |
2089 | |
2090 | LIBTCCAPI void tcc_set_options(TCCState *s, const char *r) |
2091 | { |
2092 | char **argv = NULL; |
2093 | int argc = 0; |
2094 | args_parser_make_argv(r, &argc, &argv); |
2095 | tcc_parse_args(s, &argc, &argv, 0); |
2096 | dynarray_reset(&argv, &argc); |
2097 | } |
2098 | |
2099 | PUB_FUNC void tcc_print_stats(TCCState *s1, unsigned total_time) |
2100 | { |
2101 | if (total_time < 1) |
2102 | total_time = 1; |
2103 | if (total_bytes < 1) |
2104 | total_bytes = 1; |
2105 | fprintf(stderr, "* %d idents, %d lines, %d bytes\n" |
2106 | "* %0.3f s, %u lines/s, %0.1f MB/s\n" , |
2107 | total_idents, total_lines, total_bytes, |
2108 | (double)total_time/1000, |
2109 | (unsigned)total_lines*1000/total_time, |
2110 | (double)total_bytes/1000/total_time); |
2111 | fprintf(stderr, "* text %d, data %d, bss %d bytes\n" , |
2112 | s1->total_output[0], s1->total_output[1], s1->total_output[2]); |
2113 | #ifdef MEM_DEBUG |
2114 | fprintf(stderr, "* %d bytes memory used\n" , mem_max_size); |
2115 | #endif |
2116 | } |
2117 | |