tccelf.c: write section headers before sections
[tinycc.git] / tccasm.c
blob766e1f24f265b05d052927c46493030d5b4916b0
1 /*
2 * GAS like assembler for TCC
3 *
4 * Copyright (c) 2001-2004 Fabrice Bellard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #define USING_GLOBALS
22 #include "tcc.h"
23 #ifdef CONFIG_TCC_ASM
25 static Section *last_text_section; /* to handle .previous asm directive */
26 static int asmgoto_n;
28 static int asm_get_prefix_name(TCCState *s1, const char *prefix, unsigned int n)
30 char buf[64];
31 snprintf(buf, sizeof(buf), "%s%u", prefix, n);
32 return tok_alloc_const(buf);
35 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
37 return asm_get_prefix_name(s1, "L..", n);
40 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
41 static Sym* asm_new_label(TCCState *s1, int label, int is_local);
42 static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value);
44 /* If a C name has an _ prepended then only asm labels that start
45 with _ are representable in C, by removing the first _. ASM names
46 without _ at the beginning don't correspond to C names, but we use
47 the global C symbol table to track ASM names as well, so we need to
48 transform those into ones that don't conflict with a C name,
49 so prepend a '.' for them, but force the ELF asm name to be set. */
50 static int asm2cname(int v, int *addeddot)
52 const char *name;
53 *addeddot = 0;
54 if (!tcc_state->leading_underscore)
55 return v;
56 name = get_tok_str(v, NULL);
57 if (!name)
58 return v;
59 if (name[0] == '_') {
60 v = tok_alloc_const(name + 1);
61 } else if (!strchr(name, '.')) {
62 char newname[256];
63 snprintf(newname, sizeof newname, ".%s", name);
64 v = tok_alloc_const(newname);
65 *addeddot = 1;
67 return v;
70 static Sym *asm_label_find(int v)
72 Sym *sym;
73 int addeddot;
74 v = asm2cname(v, &addeddot);
75 sym = sym_find(v);
76 while (sym && sym->sym_scope && !(sym->type.t & VT_STATIC))
77 sym = sym->prev_tok;
78 return sym;
81 static Sym *asm_label_push(int v)
83 int addeddot, v2 = asm2cname(v, &addeddot);
84 /* We always add VT_EXTERN, for sym definition that's tentative
85 (for .set, removed for real defs), for mere references it's correct
86 as is. */
87 Sym *sym = global_identifier_push(v2, VT_ASM | VT_EXTERN | VT_STATIC, 0);
88 if (addeddot)
89 sym->asm_label = v;
90 return sym;
93 /* Return a symbol we can use inside the assembler, having name NAME.
94 Symbols from asm and C source share a namespace. If we generate
95 an asm symbol it's also a (file-global) C symbol, but it's
96 either not accessible by name (like "L.123"), or its type information
97 is such that it's not usable without a proper C declaration.
99 Sometimes we need symbols accessible by name from asm, which
100 are anonymous in C, in this case CSYM can be used to transfer
101 all information from that symbol to the (possibly newly created)
102 asm symbol. */
103 ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
105 Sym *sym = asm_label_find(name);
106 if (!sym) {
107 sym = asm_label_push(name);
108 if (csym)
109 sym->c = csym->c;
111 return sym;
114 static Sym* asm_section_sym(TCCState *s1, Section *sec)
116 char buf[100]; int label; Sym *sym;
117 snprintf(buf, sizeof buf, "L.%s", sec->name);
118 label = tok_alloc_const(buf);
119 sym = asm_label_find(label);
120 return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);
123 /* We do not use the C expression parser to handle symbols. Maybe the
124 C expression parser could be tweaked to do so. */
126 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
128 Sym *sym;
129 int op, label;
130 uint64_t n;
131 const char *p;
133 switch(tok) {
134 case TOK_PPNUM:
135 p = tokc.str.data;
136 n = strtoull(p, (char **)&p, 0);
137 if (*p == 'b' || *p == 'f') {
138 /* backward or forward label */
139 label = asm_get_local_label_name(s1, n);
140 sym = asm_label_find(label);
141 if (*p == 'b') {
142 /* backward : find the last corresponding defined label */
143 if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
144 sym = sym->prev_tok;
145 if (!sym)
146 tcc_error("local label '%d' not found backward", (int)n);
147 } else {
148 /* forward */
149 if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {
150 /* if the last label is defined, then define a new one */
151 sym = asm_label_push(label);
154 pe->v = 0;
155 pe->sym = sym;
156 pe->pcrel = 0;
157 } else if (*p == '\0') {
158 pe->v = n;
159 pe->sym = NULL;
160 pe->pcrel = 0;
161 } else {
162 tcc_error("invalid number syntax");
164 next();
165 break;
166 case '+':
167 next();
168 asm_expr_unary(s1, pe);
169 break;
170 case '-':
171 case '~':
172 op = tok;
173 next();
174 asm_expr_unary(s1, pe);
175 if (pe->sym)
176 tcc_error("invalid operation with label");
177 if (op == '-')
178 pe->v = -pe->v;
179 else
180 pe->v = ~pe->v;
181 break;
182 case TOK_CCHAR:
183 case TOK_LCHAR:
184 pe->v = tokc.i;
185 pe->sym = NULL;
186 pe->pcrel = 0;
187 next();
188 break;
189 case '(':
190 next();
191 asm_expr(s1, pe);
192 skip(')');
193 break;
194 case '.':
195 pe->v = ind;
196 pe->sym = asm_section_sym(s1, cur_text_section);
197 pe->pcrel = 0;
198 next();
199 break;
200 default:
201 if (tok >= TOK_IDENT) {
202 ElfSym *esym;
203 /* label case : if the label was not found, add one */
204 sym = get_asm_sym(tok, NULL);
205 esym = elfsym(sym);
206 if (esym && esym->st_shndx == SHN_ABS) {
207 /* if absolute symbol, no need to put a symbol value */
208 pe->v = esym->st_value;
209 pe->sym = NULL;
210 pe->pcrel = 0;
211 } else {
212 pe->v = 0;
213 pe->sym = sym;
214 pe->pcrel = 0;
216 next();
217 } else {
218 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
220 break;
224 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
226 int op;
227 ExprValue e2;
229 asm_expr_unary(s1, pe);
230 for(;;) {
231 op = tok;
232 if (op != '*' && op != '/' && op != '%' &&
233 op != TOK_SHL && op != TOK_SAR)
234 break;
235 next();
236 asm_expr_unary(s1, &e2);
237 if (pe->sym || e2.sym)
238 tcc_error("invalid operation with label");
239 switch(op) {
240 case '*':
241 pe->v *= e2.v;
242 break;
243 case '/':
244 if (e2.v == 0) {
245 div_error:
246 tcc_error("division by zero");
248 pe->v /= e2.v;
249 break;
250 case '%':
251 if (e2.v == 0)
252 goto div_error;
253 pe->v %= e2.v;
254 break;
255 case TOK_SHL:
256 pe->v <<= e2.v;
257 break;
258 default:
259 case TOK_SAR:
260 pe->v >>= e2.v;
261 break;
266 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
268 int op;
269 ExprValue e2;
271 asm_expr_prod(s1, pe);
272 for(;;) {
273 op = tok;
274 if (op != '&' && op != '|' && op != '^')
275 break;
276 next();
277 asm_expr_prod(s1, &e2);
278 if (pe->sym || e2.sym)
279 tcc_error("invalid operation with label");
280 switch(op) {
281 case '&':
282 pe->v &= e2.v;
283 break;
284 case '|':
285 pe->v |= e2.v;
286 break;
287 default:
288 case '^':
289 pe->v ^= e2.v;
290 break;
295 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
297 int op;
298 ExprValue e2;
300 asm_expr_logic(s1, pe);
301 for(;;) {
302 op = tok;
303 if (op != '+' && op != '-')
304 break;
305 next();
306 asm_expr_logic(s1, &e2);
307 if (op == '+') {
308 if (pe->sym != NULL && e2.sym != NULL)
309 goto cannot_relocate;
310 pe->v += e2.v;
311 if (pe->sym == NULL && e2.sym != NULL)
312 pe->sym = e2.sym;
313 } else {
314 pe->v -= e2.v;
315 /* NOTE: we are less powerful than gas in that case
316 because we store only one symbol in the expression */
317 if (!e2.sym) {
318 /* OK */
319 } else if (pe->sym == e2.sym) {
320 /* OK */
321 pe->sym = NULL; /* same symbols can be subtracted to NULL */
322 } else {
323 ElfSym *esym1, *esym2;
324 esym1 = elfsym(pe->sym);
325 esym2 = elfsym(e2.sym);
326 if (!esym2)
327 goto cannot_relocate;
328 if (esym1 && esym1->st_shndx == esym2->st_shndx
329 && esym1->st_shndx != SHN_UNDEF) {
330 /* we also accept defined symbols in the same section */
331 pe->v += esym1->st_value - esym2->st_value;
332 pe->sym = NULL;
333 } else if (esym2->st_shndx == cur_text_section->sh_num) {
334 /* When subtracting a defined symbol in current section
335 this actually makes the value PC-relative. */
336 pe->v += 0 - esym2->st_value;
337 pe->pcrel = 1;
338 e2.sym = NULL;
339 } else {
340 cannot_relocate:
341 tcc_error("invalid operation with label");
348 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
350 int op;
351 ExprValue e2;
353 asm_expr_sum(s1, pe);
354 for(;;) {
355 op = tok;
356 if (op != TOK_EQ && op != TOK_NE
357 && (op > TOK_GT || op < TOK_ULE))
358 break;
359 next();
360 asm_expr_sum(s1, &e2);
361 if (pe->sym || e2.sym)
362 tcc_error("invalid operation with label");
363 switch(op) {
364 case TOK_EQ:
365 pe->v = pe->v == e2.v;
366 break;
367 case TOK_NE:
368 pe->v = pe->v != e2.v;
369 break;
370 case TOK_LT:
371 pe->v = (int64_t)pe->v < (int64_t)e2.v;
372 break;
373 case TOK_GE:
374 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
375 break;
376 case TOK_LE:
377 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
378 break;
379 case TOK_GT:
380 pe->v = (int64_t)pe->v > (int64_t)e2.v;
381 break;
382 default:
383 break;
385 /* GAS compare results are -1/0 not 1/0. */
386 pe->v = -(int64_t)pe->v;
390 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
392 asm_expr_cmp(s1, pe);
395 ST_FUNC int asm_int_expr(TCCState *s1)
397 ExprValue e;
398 asm_expr(s1, &e);
399 if (e.sym)
400 expect("constant");
401 return e.v;
404 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
405 int sh_num, int value)
407 Sym *sym;
408 ElfSym *esym;
410 sym = asm_label_find(label);
411 if (sym) {
412 esym = elfsym(sym);
413 /* A VT_EXTERN symbol, even if it has a section is considered
414 overridable. This is how we "define" .set targets. Real
415 definitions won't have VT_EXTERN set. */
416 if (esym && esym->st_shndx != SHN_UNDEF) {
417 /* the label is already defined */
418 if (IS_ASM_SYM(sym)
419 && (is_local == 1 || (sym->type.t & VT_EXTERN)))
420 goto new_label;
421 if (!(sym->type.t & VT_EXTERN))
422 tcc_error("assembler label '%s' already defined",
423 get_tok_str(label, NULL));
425 } else {
426 new_label:
427 sym = asm_label_push(label);
429 if (!sym->c)
430 put_extern_sym2(sym, SHN_UNDEF, 0, 0, 1);
431 esym = elfsym(sym);
432 esym->st_shndx = sh_num;
433 esym->st_value = value;
434 if (is_local != 2)
435 sym->type.t &= ~VT_EXTERN;
436 return sym;
439 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
441 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
444 /* Set the value of LABEL to that of some expression (possibly
445 involving other symbols). LABEL can be overwritten later still. */
446 static Sym* set_symbol(TCCState *s1, int label)
448 long n;
449 ExprValue e;
450 Sym *sym;
451 ElfSym *esym;
452 next();
453 asm_expr(s1, &e);
454 n = e.v;
455 esym = elfsym(e.sym);
456 if (esym)
457 n += esym->st_value;
458 sym = asm_new_label1(s1, label, 2, esym ? esym->st_shndx : SHN_ABS, n);
459 elfsym(sym)->st_other |= ST_ASM_SET;
460 return sym;
463 static void use_section1(TCCState *s1, Section *sec)
465 cur_text_section->data_offset = ind;
466 cur_text_section = sec;
467 ind = cur_text_section->data_offset;
470 static void use_section(TCCState *s1, const char *name)
472 Section *sec;
473 sec = find_section(s1, name);
474 use_section1(s1, sec);
477 static void push_section(TCCState *s1, const char *name)
479 Section *sec = find_section(s1, name);
480 sec->prev = cur_text_section;
481 use_section1(s1, sec);
484 static void pop_section(TCCState *s1)
486 Section *prev = cur_text_section->prev;
487 if (!prev)
488 tcc_error(".popsection without .pushsection");
489 cur_text_section->prev = NULL;
490 use_section1(s1, prev);
493 static void asm_parse_directive(TCCState *s1, int global)
495 int n, offset, v, size, tok1;
496 Section *sec;
497 uint8_t *ptr;
499 /* assembler directive */
500 sec = cur_text_section;
501 switch(tok) {
502 case TOK_ASMDIR_align:
503 case TOK_ASMDIR_balign:
504 case TOK_ASMDIR_p2align:
505 case TOK_ASMDIR_skip:
506 case TOK_ASMDIR_space:
507 tok1 = tok;
508 next();
509 n = asm_int_expr(s1);
510 if (tok1 == TOK_ASMDIR_p2align)
512 if (n < 0 || n > 30)
513 tcc_error("invalid p2align, must be between 0 and 30");
514 n = 1 << n;
515 tok1 = TOK_ASMDIR_align;
517 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
518 if (n < 0 || (n & (n-1)) != 0)
519 tcc_error("alignment must be a positive power of two");
520 offset = (ind + n - 1) & -n;
521 size = offset - ind;
522 /* the section must have a compatible alignment */
523 if (sec->sh_addralign < n)
524 sec->sh_addralign = n;
525 } else {
526 if (n < 0)
527 n = 0;
528 size = n;
530 v = 0;
531 if (tok == ',') {
532 next();
533 v = asm_int_expr(s1);
535 zero_pad:
536 if (sec->sh_type != SHT_NOBITS) {
537 sec->data_offset = ind;
538 ptr = section_ptr_add(sec, size);
539 memset(ptr, v, size);
541 ind += size;
542 break;
543 case TOK_ASMDIR_quad:
544 #ifdef TCC_TARGET_X86_64
545 size = 8;
546 goto asm_data;
547 #else
548 next();
549 for(;;) {
550 uint64_t vl;
551 const char *p;
553 p = tokc.str.data;
554 if (tok != TOK_PPNUM) {
555 error_constant:
556 tcc_error("64 bit constant");
558 vl = strtoll(p, (char **)&p, 0);
559 if (*p != '\0')
560 goto error_constant;
561 next();
562 if (sec->sh_type != SHT_NOBITS) {
563 /* XXX: endianness */
564 gen_le32(vl);
565 gen_le32(vl >> 32);
566 } else {
567 ind += 8;
569 if (tok != ',')
570 break;
571 next();
573 break;
574 #endif
575 case TOK_ASMDIR_byte:
576 size = 1;
577 goto asm_data;
578 case TOK_ASMDIR_word:
579 case TOK_ASMDIR_short:
580 size = 2;
581 goto asm_data;
582 case TOK_ASMDIR_long:
583 case TOK_ASMDIR_int:
584 size = 4;
585 asm_data:
586 next();
587 for(;;) {
588 ExprValue e;
589 asm_expr(s1, &e);
590 if (sec->sh_type != SHT_NOBITS) {
591 if (size == 4) {
592 gen_expr32(&e);
593 #ifdef TCC_TARGET_X86_64
594 } else if (size == 8) {
595 gen_expr64(&e);
596 #endif
597 } else {
598 if (e.sym)
599 expect("constant");
600 if (size == 1)
601 g(e.v);
602 else
603 gen_le16(e.v);
605 } else {
606 ind += size;
608 if (tok != ',')
609 break;
610 next();
612 break;
613 case TOK_ASMDIR_fill:
615 int repeat, size, val, i, j;
616 uint8_t repeat_buf[8];
617 next();
618 repeat = asm_int_expr(s1);
619 if (repeat < 0) {
620 tcc_error("repeat < 0; .fill ignored");
621 break;
623 size = 1;
624 val = 0;
625 if (tok == ',') {
626 next();
627 size = asm_int_expr(s1);
628 if (size < 0) {
629 tcc_error("size < 0; .fill ignored");
630 break;
632 if (size > 8)
633 size = 8;
634 if (tok == ',') {
635 next();
636 val = asm_int_expr(s1);
639 /* XXX: endianness */
640 repeat_buf[0] = val;
641 repeat_buf[1] = val >> 8;
642 repeat_buf[2] = val >> 16;
643 repeat_buf[3] = val >> 24;
644 repeat_buf[4] = 0;
645 repeat_buf[5] = 0;
646 repeat_buf[6] = 0;
647 repeat_buf[7] = 0;
648 for(i = 0; i < repeat; i++) {
649 for(j = 0; j < size; j++) {
650 g(repeat_buf[j]);
654 break;
655 case TOK_ASMDIR_rept:
657 int repeat;
658 TokenString *init_str;
659 next();
660 repeat = asm_int_expr(s1);
661 init_str = tok_str_alloc();
662 while (next(), tok != TOK_ASMDIR_endr) {
663 if (tok == CH_EOF)
664 tcc_error("we at end of file, .endr not found");
665 tok_str_add_tok(init_str);
667 tok_str_add(init_str, TOK_EOF);
668 begin_macro(init_str, 1);
669 while (repeat-- > 0) {
670 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
671 global);
672 macro_ptr = init_str->str;
674 end_macro();
675 next();
676 break;
678 case TOK_ASMDIR_org:
680 unsigned long n;
681 ExprValue e;
682 ElfSym *esym;
683 next();
684 asm_expr(s1, &e);
685 n = e.v;
686 esym = elfsym(e.sym);
687 if (esym) {
688 if (esym->st_shndx != cur_text_section->sh_num)
689 expect("constant or same-section symbol");
690 n += esym->st_value;
692 if (n < ind)
693 tcc_error("attempt to .org backwards");
694 v = 0;
695 size = n - ind;
696 goto zero_pad;
698 break;
699 case TOK_ASMDIR_set:
700 next();
701 tok1 = tok;
702 next();
703 /* Also accept '.set stuff', but don't do anything with this.
704 It's used in GAS to set various features like '.set mips16'. */
705 if (tok == ',')
706 set_symbol(s1, tok1);
707 break;
708 case TOK_ASMDIR_globl:
709 case TOK_ASMDIR_global:
710 case TOK_ASMDIR_weak:
711 case TOK_ASMDIR_hidden:
712 tok1 = tok;
713 do {
714 Sym *sym;
715 next();
716 sym = get_asm_sym(tok, NULL);
717 if (tok1 != TOK_ASMDIR_hidden)
718 sym->type.t &= ~VT_STATIC;
719 if (tok1 == TOK_ASMDIR_weak)
720 sym->a.weak = 1;
721 else if (tok1 == TOK_ASMDIR_hidden)
722 sym->a.visibility = STV_HIDDEN;
723 update_storage(sym);
724 next();
725 } while (tok == ',');
726 break;
727 case TOK_ASMDIR_string:
728 case TOK_ASMDIR_ascii:
729 case TOK_ASMDIR_asciz:
731 const char *p;
732 int i, size, t;
734 t = tok;
735 next();
736 for(;;) {
737 if (tok != TOK_STR)
738 expect("string constant");
739 p = tokc.str.data;
740 size = tokc.str.size;
741 if (t == TOK_ASMDIR_ascii && size > 0)
742 size--;
743 for(i = 0; i < size; i++)
744 g(p[i]);
745 next();
746 if (tok == ',') {
747 next();
748 } else if (tok != TOK_STR) {
749 break;
753 break;
754 case TOK_ASMDIR_text:
755 case TOK_ASMDIR_data:
756 case TOK_ASMDIR_bss:
758 char sname[64];
759 tok1 = tok;
760 n = 0;
761 next();
762 if (tok != ';' && tok != TOK_LINEFEED) {
763 n = asm_int_expr(s1);
764 next();
766 if (n)
767 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
768 else
769 sprintf(sname, "%s", get_tok_str(tok1, NULL));
770 use_section(s1, sname);
772 break;
773 case TOK_ASMDIR_file:
775 const char *p;
776 parse_flags &= ~PARSE_FLAG_TOK_STR;
777 next();
778 if (tok == TOK_PPNUM)
779 next();
780 if (tok == TOK_PPSTR && tokc.str.data[0] == '"') {
781 tokc.str.data[tokc.str.size - 2] = 0;
782 p = tokc.str.data + 1;
783 } else if (tok >= TOK_IDENT) {
784 p = get_tok_str(tok, &tokc);
785 } else {
786 skip_to_eol(0);
787 break;
789 tccpp_putfile(p);
790 next();
792 break;
793 case TOK_ASMDIR_ident:
795 char ident[256];
797 ident[0] = '\0';
798 next();
799 if (tok == TOK_STR)
800 pstrcat(ident, sizeof(ident), tokc.str.data);
801 else
802 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
803 tcc_warning_c(warn_unsupported)("ignoring .ident %s", ident);
804 next();
806 break;
807 case TOK_ASMDIR_size:
809 Sym *sym;
811 next();
812 sym = asm_label_find(tok);
813 if (!sym) {
814 tcc_error("label not found: %s", get_tok_str(tok, NULL));
816 /* XXX .size name,label2-label1 */
817 tcc_warning_c(warn_unsupported)("ignoring .size %s,*", get_tok_str(tok, NULL));
818 next();
819 skip(',');
820 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
821 next();
824 break;
825 case TOK_ASMDIR_type:
827 Sym *sym;
828 const char *newtype;
830 next();
831 sym = get_asm_sym(tok, NULL);
832 next();
833 skip(',');
834 if (tok == TOK_STR) {
835 newtype = tokc.str.data;
836 } else {
837 if (tok == '@' || tok == '%')
838 next();
839 newtype = get_tok_str(tok, NULL);
842 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
843 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
844 if (sym->c) {
845 ElfSym *esym = elfsym(sym);
846 esym->st_info = ELFW(ST_INFO)(ELFW(ST_BIND)(esym->st_info), STT_FUNC);
848 } else
849 tcc_warning_c(warn_unsupported)("change type of '%s' from 0x%x to '%s' ignored",
850 get_tok_str(sym->v, NULL), sym->type.t, newtype);
852 next();
854 break;
855 case TOK_ASMDIR_pushsection:
856 case TOK_ASMDIR_section:
858 char sname[256];
859 int old_nb_section = s1->nb_sections;
860 int flags = SHF_ALLOC;
862 tok1 = tok;
863 /* XXX: support more options */
864 next();
865 sname[0] = '\0';
866 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
867 if (tok == TOK_STR)
868 pstrcat(sname, sizeof(sname), tokc.str.data);
869 else
870 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
871 next();
873 if (tok == ',') {
874 const char *p;
875 /* skip section options */
876 next();
877 if (tok != TOK_STR)
878 expect("string constant");
879 for (p = tokc.str.data; *p; ++p) {
880 if (*p == 'w')
881 flags |= SHF_WRITE;
882 if (*p == 'x')
883 flags |= SHF_EXECINSTR;
885 next();
886 if (tok == ',') {
887 next();
888 if (tok == '@' || tok == '%')
889 next();
890 next();
893 last_text_section = cur_text_section;
894 if (tok1 == TOK_ASMDIR_section)
895 use_section(s1, sname);
896 else
897 push_section(s1, sname);
898 /* If we just allocated a new section reset its alignment to
899 1. new_section normally acts for GCC compatibility and
900 sets alignment to PTR_SIZE. The assembler behaves different. */
901 if (old_nb_section != s1->nb_sections) {
902 cur_text_section->sh_addralign = 1;
903 cur_text_section->sh_flags = flags;
906 break;
907 case TOK_ASMDIR_previous:
909 Section *sec;
910 next();
911 if (!last_text_section)
912 tcc_error("no previous section referenced");
913 sec = cur_text_section;
914 use_section1(s1, last_text_section);
915 last_text_section = sec;
917 break;
918 case TOK_ASMDIR_popsection:
919 next();
920 pop_section(s1);
921 break;
922 #ifdef TCC_TARGET_I386
923 case TOK_ASMDIR_code16:
925 next();
926 s1->seg_size = 16;
928 break;
929 case TOK_ASMDIR_code32:
931 next();
932 s1->seg_size = 32;
934 break;
935 #endif
936 #ifdef TCC_TARGET_X86_64
937 /* added for compatibility with GAS */
938 case TOK_ASMDIR_code64:
939 next();
940 break;
941 #endif
942 #ifdef TCC_TARGET_RISCV64
943 case TOK_ASMDIR_option:
944 next();
945 switch(tok){
946 case TOK_ASM_rvc: /* Will be deprecated soon in favor of arch */
947 case TOK_ASM_norvc: /* Will be deprecated soon in favor of arch */
948 case TOK_ASM_pic:
949 case TOK_ASM_nopic:
950 case TOK_ASM_relax:
951 case TOK_ASM_norelax:
952 case TOK_ASM_push:
953 case TOK_ASM_pop:
954 /* TODO: unimplemented */
955 next();
956 break;
957 case TOK_ASM_arch:
958 /* TODO: unimplemented, requires extra parsing */
959 tcc_error("unimp .option '.%s'", get_tok_str(tok, NULL));
960 break;
961 default:
962 tcc_error("unknown .option '.%s'", get_tok_str(tok, NULL));
963 break;
965 break;
966 #endif
967 default:
968 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
969 break;
974 /* assemble a file */
975 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
977 int opcode;
978 int saved_parse_flags = parse_flags;
980 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
981 if (do_preprocess)
982 parse_flags |= PARSE_FLAG_PREPROCESS;
983 for(;;) {
984 next();
985 if (tok == TOK_EOF)
986 break;
987 tcc_debug_line(s1);
988 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
989 redo:
990 if (tok == '#') {
991 /* horrible gas comment */
992 while (tok != TOK_LINEFEED)
993 next();
994 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
995 asm_parse_directive(s1, global);
996 } else if (tok == TOK_PPNUM) {
997 const char *p;
998 int n;
999 p = tokc.str.data;
1000 n = strtoul(p, (char **)&p, 10);
1001 if (*p != '\0')
1002 expect("':'");
1003 /* new local label */
1004 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
1005 next();
1006 skip(':');
1007 goto redo;
1008 } else if (tok >= TOK_IDENT) {
1009 /* instruction or label */
1010 opcode = tok;
1011 next();
1012 if (tok == ':') {
1013 /* new label */
1014 asm_new_label(s1, opcode, 0);
1015 next();
1016 goto redo;
1017 } else if (tok == '=') {
1018 set_symbol(s1, opcode);
1019 goto redo;
1020 } else {
1021 asm_opcode(s1, opcode);
1024 /* end of line */
1025 if (tok != ';' && tok != TOK_LINEFEED)
1026 expect("end of line");
1027 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
1030 parse_flags = saved_parse_flags;
1031 return 0;
1034 /* Assemble the current file */
1035 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
1037 int ret;
1038 tcc_debug_start(s1);
1039 /* default section is text */
1040 cur_text_section = text_section;
1041 ind = cur_text_section->data_offset;
1042 nocode_wanted = 0;
1043 ret = tcc_assemble_internal(s1, do_preprocess, 1);
1044 cur_text_section->data_offset = ind;
1045 tcc_debug_end(s1);
1046 return ret;
1049 /********************************************************************/
1050 /* GCC inline asm support */
1052 /* assemble the string 'str' in the current C compilation unit without
1053 C preprocessing. */
1054 static void tcc_assemble_inline(TCCState *s1, const char *str, int len, int global)
1056 const int *saved_macro_ptr = macro_ptr;
1057 int dotid = set_idnum('.', IS_ID);
1058 #ifndef TCC_TARGET_RISCV64
1059 int dolid = set_idnum('$', 0);
1060 #endif
1062 tcc_open_bf(s1, ":asm:", len);
1063 memcpy(file->buffer, str, len);
1064 macro_ptr = NULL;
1065 tcc_assemble_internal(s1, 0, global);
1066 tcc_close();
1068 #ifndef TCC_TARGET_RISCV64
1069 set_idnum('$', dolid);
1070 #endif
1071 set_idnum('.', dotid);
1072 macro_ptr = saved_macro_ptr;
1075 /* find a constraint by its number or id (gcc 3 extended
1076 syntax). return -1 if not found. Return in *pp in char after the
1077 constraint */
1078 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1079 const char *name, const char **pp)
1081 int index;
1082 TokenSym *ts;
1083 const char *p;
1085 if (isnum(*name)) {
1086 index = 0;
1087 while (isnum(*name)) {
1088 index = (index * 10) + (*name) - '0';
1089 name++;
1091 if ((unsigned)index >= nb_operands)
1092 index = -1;
1093 } else if (*name == '[') {
1094 name++;
1095 p = strchr(name, ']');
1096 if (p) {
1097 ts = tok_alloc(name, p - name);
1098 for(index = 0; index < nb_operands; index++) {
1099 if (operands[index].id == ts->tok)
1100 goto found;
1102 index = -1;
1103 found:
1104 name = p + 1;
1105 } else {
1106 index = -1;
1108 } else {
1109 index = -1;
1111 if (pp)
1112 *pp = name;
1113 return index;
1116 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1117 CString *out_str, const char *str)
1119 int c, index, modifier;
1120 ASMOperand *op;
1121 SValue sv;
1123 for(;;) {
1124 c = *str++;
1125 if (c == '%') {
1126 if (*str == '%') {
1127 str++;
1128 goto add_char;
1130 modifier = 0;
1131 if (*str == 'c' || *str == 'n' ||
1132 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1133 *str == 'q' || *str == 'l' ||
1134 #ifdef TCC_TARGET_RISCV64
1135 *str == 'z' ||
1136 #endif
1137 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1138 and make literal operands not be decorated with '$'. */
1139 *str == 'P')
1140 modifier = *str++;
1141 index = find_constraint(operands, nb_operands, str, &str);
1142 if (index < 0)
1143 tcc_error("invalid operand reference after %%");
1144 op = &operands[index];
1145 if (modifier == 'l') {
1146 cstr_cat(out_str, get_tok_str(op->is_label, NULL), -1);
1147 } else {
1148 sv = *op->vt;
1149 if (op->reg >= 0) {
1150 sv.r = op->reg;
1151 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1152 sv.r |= VT_LVAL;
1154 subst_asm_operand(out_str, &sv, modifier);
1156 } else {
1157 add_char:
1158 cstr_ccat(out_str, c);
1159 if (c == '\0')
1160 break;
1166 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1167 int is_output)
1169 ASMOperand *op;
1170 int nb_operands;
1171 char* astr;
1173 if (tok != ':') {
1174 nb_operands = *nb_operands_ptr;
1175 for(;;) {
1176 if (nb_operands >= MAX_ASM_OPERANDS)
1177 tcc_error("too many asm operands");
1178 op = &operands[nb_operands++];
1179 op->id = 0;
1180 if (tok == '[') {
1181 next();
1182 if (tok < TOK_IDENT)
1183 expect("identifier");
1184 op->id = tok;
1185 next();
1186 skip(']');
1188 astr = parse_mult_str("string constant")->data;
1189 pstrcpy(op->constraint, sizeof op->constraint, astr);
1190 skip('(');
1191 gexpr();
1192 if (is_output) {
1193 if (!(vtop->type.t & VT_ARRAY))
1194 test_lvalue();
1195 } else {
1196 /* we want to avoid LLOCAL case, except when the 'm'
1197 constraint is used. Note that it may come from
1198 register storage, so we need to convert (reg)
1199 case */
1200 if ((vtop->r & VT_LVAL) &&
1201 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1202 (vtop->r & VT_VALMASK) < VT_CONST) &&
1203 !strchr(op->constraint, 'm')) {
1204 gv(RC_INT);
1207 op->vt = vtop;
1208 skip(')');
1209 if (tok == ',') {
1210 next();
1211 } else {
1212 break;
1215 *nb_operands_ptr = nb_operands;
1219 /* parse the GCC asm() instruction */
1220 ST_FUNC void asm_instr(void)
1222 CString astr, *astr1;
1224 ASMOperand operands[MAX_ASM_OPERANDS];
1225 int nb_outputs, nb_operands, i, must_subst, out_reg, nb_labels;
1226 uint8_t clobber_regs[NB_ASM_REGS];
1227 Section *sec;
1229 /* since we always generate the asm() instruction, we can ignore
1230 volatile */
1231 while (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3
1232 || tok == TOK_GOTO) {
1233 next();
1236 astr1 = parse_asm_str();
1237 cstr_new_s(&astr);
1238 cstr_cat(&astr, astr1->data, astr1->size);
1240 nb_operands = 0;
1241 nb_outputs = 0;
1242 nb_labels = 0;
1243 must_subst = 0;
1244 memset(clobber_regs, 0, sizeof(clobber_regs));
1245 if (tok == ':') {
1246 next();
1247 must_subst = 1;
1248 /* output args */
1249 parse_asm_operands(operands, &nb_operands, 1);
1250 nb_outputs = nb_operands;
1251 if (tok == ':') {
1252 next();
1253 if (tok != ')') {
1254 /* input args */
1255 parse_asm_operands(operands, &nb_operands, 0);
1256 if (tok == ':') {
1257 /* clobber list */
1258 /* XXX: handle registers */
1259 next();
1260 for(;;) {
1261 if (tok == ':')
1262 break;
1263 if (tok != TOK_STR)
1264 expect("string constant");
1265 asm_clobber(clobber_regs, tokc.str.data);
1266 next();
1267 if (tok == ',') {
1268 next();
1269 } else {
1270 break;
1274 if (tok == ':') {
1275 /* goto labels */
1276 next();
1277 for (;;) {
1278 Sym *csym;
1279 int asmname;
1280 if (nb_operands + nb_labels >= MAX_ASM_OPERANDS)
1281 tcc_error("too many asm operands");
1282 if (tok < TOK_UIDENT)
1283 expect("label identifier");
1284 operands[nb_operands + nb_labels++].id = tok;
1286 csym = label_find(tok);
1287 if (!csym) {
1288 csym = label_push(&global_label_stack, tok,
1289 LABEL_FORWARD);
1290 } else {
1291 if (csym->r == LABEL_DECLARED)
1292 csym->r = LABEL_FORWARD;
1294 next();
1295 asmname = asm_get_prefix_name(tcc_state, "LG.",
1296 ++asmgoto_n);
1297 if (!csym->c)
1298 put_extern_sym2(csym, SHN_UNDEF, 0, 0, 1);
1299 get_asm_sym(asmname, csym);
1300 operands[nb_operands + nb_labels - 1].is_label = asmname;
1302 if (tok != ',')
1303 break;
1304 next();
1310 skip(')');
1311 /* NOTE: we do not eat the ';' so that we can restore the current
1312 token after the assembler parsing */
1313 if (tok != ';')
1314 expect("';'");
1316 /* save all values in the memory */
1317 save_regs(0);
1319 /* compute constraints */
1320 asm_compute_constraints(operands, nb_operands, nb_outputs,
1321 clobber_regs, &out_reg);
1323 /* substitute the operands in the asm string. No substitution is
1324 done if no operands (GCC behaviour) */
1325 #ifdef ASM_DEBUG
1326 printf("asm: \"%s\"\n", (char *)astr.data);
1327 #endif
1328 if (must_subst) {
1329 cstr_reset(astr1);
1330 cstr_cat(astr1, astr.data, astr.size);
1331 cstr_reset(&astr);
1332 subst_asm_operands(operands, nb_operands + nb_labels, &astr, astr1->data);
1335 #ifdef ASM_DEBUG
1336 printf("subst_asm: \"%s\"\n", (char *)astr.data);
1337 #endif
1339 /* generate loads */
1340 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1341 clobber_regs, out_reg);
1343 /* We don't allow switching section within inline asm to
1344 bleed out to surrounding code. */
1345 sec = cur_text_section;
1346 /* assemble the string with tcc internal assembler */
1347 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 0);
1348 cstr_free_s(&astr);
1349 if (sec != cur_text_section) {
1350 tcc_warning("inline asm tries to change current section");
1351 use_section1(tcc_state, sec);
1354 /* restore the current C token */
1355 next();
1357 /* store the output values if needed */
1358 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1359 clobber_regs, out_reg);
1361 /* free everything */
1362 for(i=0;i<nb_operands;i++) {
1363 vpop();
1368 ST_FUNC void asm_global_instr(void)
1370 CString *astr;
1371 int saved_nocode_wanted = nocode_wanted;
1373 /* Global asm blocks are always emitted. */
1374 nocode_wanted = 0;
1375 next();
1376 astr = parse_asm_str();
1377 skip(')');
1378 /* NOTE: we do not eat the ';' so that we can restore the current
1379 token after the assembler parsing */
1380 if (tok != ';')
1381 expect("';'");
1383 #ifdef ASM_DEBUG
1384 printf("asm_global: \"%s\"\n", (char *)astr.data);
1385 #endif
1386 cur_text_section = text_section;
1387 ind = cur_text_section->data_offset;
1389 /* assemble the string with tcc internal assembler */
1390 tcc_assemble_inline(tcc_state, astr->data, astr->size - 1, 1);
1392 cur_text_section->data_offset = ind;
1394 /* restore the current C token */
1395 next();
1397 nocode_wanted = saved_nocode_wanted;
1400 /********************************************************/
1401 #else
1402 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
1404 tcc_error("asm not supported");
1407 ST_FUNC void asm_instr(void)
1409 tcc_error("inline asm() not supported");
1412 ST_FUNC void asm_global_instr(void)
1414 tcc_error("inline asm() not supported");
1416 #endif /* CONFIG_TCC_ASM */