Adjust testcase for PIE compilers
[tinycc.git] / tccasm.c
blob848f976a3f243cadad1de7c4287971bab72a12ee
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 #include "tcc.h"
22 #ifdef CONFIG_TCC_ASM
24 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
26 char buf[64];
27 TokenSym *ts;
29 snprintf(buf, sizeof(buf), "L..%u", n);
30 ts = tok_alloc(buf, strlen(buf));
31 return ts->tok;
34 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
35 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
36 static Sym sym_dot;
38 /* Return a symbol we can use inside the assembler, having name NAME.
39 The assembler symbol table is different from the C symbol table
40 (and the Sym members are used differently). But we must be able
41 to look up file-global C symbols from inside the assembler, e.g.
42 for global asm blocks to be able to refer to defined C symbols.
44 This routine gives back either an existing asm-internal
45 symbol, or a new one. In the latter case the new asm-internal
46 symbol is initialized with info from the C symbol table.
48 If CSYM is non-null we take symbol info from it, otherwise
49 we look up NAME in the C symbol table and use that. */
50 ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
52 Sym *sym = label_find(name);
53 if (!sym) {
54 sym = label_push(&tcc_state->asm_labels, name, 0);
55 sym->type.t = VT_VOID | VT_STATIC | VT_EXTERN;
56 if (!csym) {
57 csym = sym_find(name);
58 /* We might be called for an asm block from inside a C routine
59 and so might have local decls on the identifier stack. Search
60 for the first global one. */
61 while (csym && csym->sym_scope)
62 csym = csym->prev_tok;
64 /* Now, if we have a defined global symbol copy over
65 section and offset. */
66 if (csym &&
67 ((csym->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST)) &&
68 csym->c) {
69 ElfW(Sym) *esym;
70 esym = &((ElfW(Sym) *)symtab_section->data)[csym->c];
71 sym->c = csym->c;
72 sym->r = esym->st_shndx;
73 sym->jnext = esym->st_value;
74 /* XXX can't yet store st_size anywhere. */
75 sym->type.t = VT_VOID | (csym->type.t & VT_STATIC);
76 /* Mark that this asm symbol doesn't need to be fed back. */
77 sym->a.dllimport = 1;
80 return sym;
83 /* We do not use the C expression parser to handle symbols. Maybe the
84 C expression parser could be tweaked to do so. */
86 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
88 Sym *sym;
89 int op, label;
90 uint64_t n;
91 const char *p;
93 switch(tok) {
94 case TOK_PPNUM:
95 p = tokc.str.data;
96 n = strtoull(p, (char **)&p, 0);
97 if (*p == 'b' || *p == 'f') {
98 /* backward or forward label */
99 label = asm_get_local_label_name(s1, n);
100 sym = label_find(label);
101 if (*p == 'b') {
102 /* backward : find the last corresponding defined label */
103 if (sym && sym->r == 0)
104 sym = sym->prev_tok;
105 if (!sym)
106 tcc_error("local label '%d' not found backward", n);
107 } else {
108 /* forward */
109 if (!sym || sym->r) {
110 /* if the last label is defined, then define a new one */
111 sym = label_push(&s1->asm_labels, label, 0);
112 sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
115 pe->v = 0;
116 pe->sym = sym;
117 pe->pcrel = 0;
118 } else if (*p == '\0') {
119 pe->v = n;
120 pe->sym = NULL;
121 pe->pcrel = 0;
122 } else {
123 tcc_error("invalid number syntax");
125 next();
126 break;
127 case '+':
128 next();
129 asm_expr_unary(s1, pe);
130 break;
131 case '-':
132 case '~':
133 op = tok;
134 next();
135 asm_expr_unary(s1, pe);
136 if (pe->sym)
137 tcc_error("invalid operation with label");
138 if (op == '-')
139 pe->v = -pe->v;
140 else
141 pe->v = ~pe->v;
142 break;
143 case TOK_CCHAR:
144 case TOK_LCHAR:
145 pe->v = tokc.i;
146 pe->sym = NULL;
147 pe->pcrel = 0;
148 next();
149 break;
150 case '(':
151 next();
152 asm_expr(s1, pe);
153 skip(')');
154 break;
155 case '.':
156 pe->v = 0;
157 pe->sym = &sym_dot;
158 pe->pcrel = 0;
159 sym_dot.type.t = VT_VOID | VT_STATIC;
160 sym_dot.r = cur_text_section->sh_num;
161 sym_dot.jnext = ind;
162 next();
163 break;
164 default:
165 if (tok >= TOK_IDENT) {
166 /* label case : if the label was not found, add one */
167 sym = get_asm_sym(tok, NULL);
168 if (sym->r == SHN_ABS) {
169 /* if absolute symbol, no need to put a symbol value */
170 pe->v = sym->jnext;
171 pe->sym = NULL;
172 pe->pcrel = 0;
173 } else {
174 pe->v = 0;
175 pe->sym = sym;
176 pe->pcrel = 0;
178 next();
179 } else {
180 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
182 break;
186 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
188 int op;
189 ExprValue e2;
191 asm_expr_unary(s1, pe);
192 for(;;) {
193 op = tok;
194 if (op != '*' && op != '/' && op != '%' &&
195 op != TOK_SHL && op != TOK_SAR)
196 break;
197 next();
198 asm_expr_unary(s1, &e2);
199 if (pe->sym || e2.sym)
200 tcc_error("invalid operation with label");
201 switch(op) {
202 case '*':
203 pe->v *= e2.v;
204 break;
205 case '/':
206 if (e2.v == 0) {
207 div_error:
208 tcc_error("division by zero");
210 pe->v /= e2.v;
211 break;
212 case '%':
213 if (e2.v == 0)
214 goto div_error;
215 pe->v %= e2.v;
216 break;
217 case TOK_SHL:
218 pe->v <<= e2.v;
219 break;
220 default:
221 case TOK_SAR:
222 pe->v >>= e2.v;
223 break;
228 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
230 int op;
231 ExprValue e2;
233 asm_expr_prod(s1, pe);
234 for(;;) {
235 op = tok;
236 if (op != '&' && op != '|' && op != '^')
237 break;
238 next();
239 asm_expr_prod(s1, &e2);
240 if (pe->sym || e2.sym)
241 tcc_error("invalid operation with label");
242 switch(op) {
243 case '&':
244 pe->v &= e2.v;
245 break;
246 case '|':
247 pe->v |= e2.v;
248 break;
249 default:
250 case '^':
251 pe->v ^= e2.v;
252 break;
257 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
259 int op;
260 ExprValue e2;
262 asm_expr_logic(s1, pe);
263 for(;;) {
264 op = tok;
265 if (op != '+' && op != '-')
266 break;
267 next();
268 asm_expr_logic(s1, &e2);
269 if (op == '+') {
270 if (pe->sym != NULL && e2.sym != NULL)
271 goto cannot_relocate;
272 pe->v += e2.v;
273 if (pe->sym == NULL && e2.sym != NULL)
274 pe->sym = e2.sym;
275 } else {
276 pe->v -= e2.v;
277 /* NOTE: we are less powerful than gas in that case
278 because we store only one symbol in the expression */
279 if (!e2.sym) {
280 /* OK */
281 } else if (pe->sym == e2.sym) {
282 /* OK */
283 pe->sym = NULL; /* same symbols can be subtracted to NULL */
284 } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) {
285 /* we also accept defined symbols in the same section */
286 pe->v += pe->sym->jnext - e2.sym->jnext;
287 pe->sym = NULL;
288 } else if (e2.sym->r == cur_text_section->sh_num) {
289 /* When subtracting a defined symbol in current section
290 this actually makes the value PC-relative. */
291 pe->v -= e2.sym->jnext - ind - 4;
292 pe->pcrel = 1;
293 e2.sym = NULL;
294 } else {
295 cannot_relocate:
296 tcc_error("invalid operation with label");
302 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
304 int op;
305 ExprValue e2;
307 asm_expr_sum(s1, pe);
308 for(;;) {
309 op = tok;
310 if (op != TOK_EQ && op != TOK_NE
311 && (op > TOK_GT || op < TOK_ULE))
312 break;
313 next();
314 asm_expr_sum(s1, &e2);
315 if (pe->sym || e2.sym)
316 tcc_error("invalid operation with label");
317 switch(op) {
318 case TOK_EQ:
319 pe->v = pe->v == e2.v;
320 break;
321 case TOK_NE:
322 pe->v = pe->v != e2.v;
323 break;
324 case TOK_LT:
325 pe->v = (int64_t)pe->v < (int64_t)e2.v;
326 break;
327 case TOK_GE:
328 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
329 break;
330 case TOK_LE:
331 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
332 break;
333 case TOK_GT:
334 pe->v = (int64_t)pe->v > (int64_t)e2.v;
335 break;
336 default:
337 break;
339 /* GAS compare results are -1/0 not 1/0. */
340 pe->v = -(int64_t)pe->v;
344 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
346 asm_expr_cmp(s1, pe);
349 ST_FUNC int asm_int_expr(TCCState *s1)
351 ExprValue e;
352 asm_expr(s1, &e);
353 if (e.sym)
354 expect("constant");
355 return e.v;
358 /* NOTE: the same name space as C labels is used to avoid using too
359 much memory when storing labels in TokenStrings */
360 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
361 int sh_num, int value)
363 Sym *sym;
365 sym = label_find(label);
366 if (sym) {
367 /* A VT_EXTERN symbol, even if it has a section is considered
368 overridable. This is how we "define" .set targets. Real
369 definitions won't have VT_EXTERN set. */
370 if (sym->r && !(sym->type.t & VT_EXTERN)) {
371 /* the label is already defined */
372 if (!is_local) {
373 tcc_error("assembler label '%s' already defined",
374 get_tok_str(label, NULL));
375 } else {
376 /* redefinition of local labels is possible */
377 goto new_label;
380 } else {
381 new_label:
382 sym = label_push(&s1->asm_labels, label, 0);
383 /* If we need a symbol to hold a value, mark it as
384 tentative only (for .set). If this is for a real label
385 we'll remove VT_EXTERN. */
386 sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
388 sym->r = sh_num;
389 sym->jnext = value;
390 return sym;
393 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
395 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
398 /* Set the value of LABEL to that of some expression (possibly
399 involving other symbols). LABEL can be overwritten later still. */
400 static Sym* set_symbol(TCCState *s1, int label)
402 long n;
403 ExprValue e;
404 next();
405 asm_expr(s1, &e);
406 n = e.v;
407 if (e.sym)
408 n += e.sym->jnext;
409 return asm_new_label1(s1, label, 0, e.sym ? e.sym->r : SHN_ABS, n);
412 /* Patch ELF symbol associated with SYM based on the assemblers
413 understanding. */
414 static void patch_binding(Sym *sym)
416 ElfW(Sym) *esym;
417 if (0 == sym->c)
418 return;
419 esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
420 if (sym->a.visibility)
421 esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
422 | sym->a.visibility;
424 esym->st_info = ELFW(ST_INFO)(sym->a.weak ? STB_WEAK
425 : (sym->type.t & VT_STATIC) ? STB_LOCAL
426 : STB_GLOBAL,
427 ELFW(ST_TYPE)(esym->st_info));
430 static void asm_free_labels(TCCState *st)
432 Sym *s, *s1;
433 Section *sec;
435 for(s = st->asm_labels; s != NULL; s = s1) {
436 s1 = s->prev;
437 /* define symbol value in object file */
438 s->type.t &= ~VT_EXTERN;
439 if (!s->a.dllimport) {
440 if (s->r) {
441 if (s->r == SHN_ABS)
442 sec = SECTION_ABS;
443 else
444 sec = st->sections[s->r];
445 put_extern_sym2(s, sec, s->jnext, 0, 0);
446 } else /* undefined symbols are global */
447 s->type.t &= ~VT_STATIC;
449 patch_binding(s);
450 /* remove label */
451 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
452 sym_free(s);
454 st->asm_labels = NULL;
457 static void use_section1(TCCState *s1, Section *sec)
459 cur_text_section->data_offset = ind;
460 cur_text_section = sec;
461 ind = cur_text_section->data_offset;
464 static void use_section(TCCState *s1, const char *name)
466 Section *sec;
467 sec = find_section(s1, name);
468 use_section1(s1, sec);
471 static void push_section(TCCState *s1, const char *name)
473 Section *sec = find_section(s1, name);
474 sec->prev = cur_text_section;
475 use_section1(s1, sec);
478 static void pop_section(TCCState *s1)
480 Section *prev = cur_text_section->prev;
481 if (!prev)
482 tcc_error(".popsection without .pushsection");
483 cur_text_section->prev = NULL;
484 use_section1(s1, prev);
487 static void asm_parse_directive(TCCState *s1, int global)
489 int n, offset, v, size, tok1;
490 Section *sec;
491 uint8_t *ptr;
493 /* assembler directive */
494 sec = cur_text_section;
495 switch(tok) {
496 case TOK_ASMDIR_align:
497 case TOK_ASMDIR_balign:
498 case TOK_ASMDIR_p2align:
499 case TOK_ASMDIR_skip:
500 case TOK_ASMDIR_space:
501 tok1 = tok;
502 next();
503 n = asm_int_expr(s1);
504 if (tok1 == TOK_ASMDIR_p2align)
506 if (n < 0 || n > 30)
507 tcc_error("invalid p2align, must be between 0 and 30");
508 n = 1 << n;
509 tok1 = TOK_ASMDIR_align;
511 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
512 if (n < 0 || (n & (n-1)) != 0)
513 tcc_error("alignment must be a positive power of two");
514 offset = (ind + n - 1) & -n;
515 size = offset - ind;
516 /* the section must have a compatible alignment */
517 if (sec->sh_addralign < n)
518 sec->sh_addralign = n;
519 } else {
520 if (n < 0)
521 n = 0;
522 size = n;
524 v = 0;
525 if (tok == ',') {
526 next();
527 v = asm_int_expr(s1);
529 zero_pad:
530 if (sec->sh_type != SHT_NOBITS) {
531 sec->data_offset = ind;
532 ptr = section_ptr_add(sec, size);
533 memset(ptr, v, size);
535 ind += size;
536 break;
537 case TOK_ASMDIR_quad:
538 #ifdef TCC_TARGET_X86_64
539 size = 8;
540 goto asm_data;
541 #else
542 next();
543 for(;;) {
544 uint64_t vl;
545 const char *p;
547 p = tokc.str.data;
548 if (tok != TOK_PPNUM) {
549 error_constant:
550 tcc_error("64 bit constant");
552 vl = strtoll(p, (char **)&p, 0);
553 if (*p != '\0')
554 goto error_constant;
555 next();
556 if (sec->sh_type != SHT_NOBITS) {
557 /* XXX: endianness */
558 gen_le32(vl);
559 gen_le32(vl >> 32);
560 } else {
561 ind += 8;
563 if (tok != ',')
564 break;
565 next();
567 break;
568 #endif
569 case TOK_ASMDIR_byte:
570 size = 1;
571 goto asm_data;
572 case TOK_ASMDIR_word:
573 case TOK_ASMDIR_short:
574 size = 2;
575 goto asm_data;
576 case TOK_ASMDIR_long:
577 case TOK_ASMDIR_int:
578 size = 4;
579 asm_data:
580 next();
581 for(;;) {
582 ExprValue e;
583 asm_expr(s1, &e);
584 if (sec->sh_type != SHT_NOBITS) {
585 if (size == 4) {
586 gen_expr32(&e);
587 #ifdef TCC_TARGET_X86_64
588 } else if (size == 8) {
589 gen_expr64(&e);
590 #endif
591 } else {
592 if (e.sym)
593 expect("constant");
594 if (size == 1)
595 g(e.v);
596 else
597 gen_le16(e.v);
599 } else {
600 ind += size;
602 if (tok != ',')
603 break;
604 next();
606 break;
607 case TOK_ASMDIR_fill:
609 int repeat, size, val, i, j;
610 uint8_t repeat_buf[8];
611 next();
612 repeat = asm_int_expr(s1);
613 if (repeat < 0) {
614 tcc_error("repeat < 0; .fill ignored");
615 break;
617 size = 1;
618 val = 0;
619 if (tok == ',') {
620 next();
621 size = asm_int_expr(s1);
622 if (size < 0) {
623 tcc_error("size < 0; .fill ignored");
624 break;
626 if (size > 8)
627 size = 8;
628 if (tok == ',') {
629 next();
630 val = asm_int_expr(s1);
633 /* XXX: endianness */
634 repeat_buf[0] = val;
635 repeat_buf[1] = val >> 8;
636 repeat_buf[2] = val >> 16;
637 repeat_buf[3] = val >> 24;
638 repeat_buf[4] = 0;
639 repeat_buf[5] = 0;
640 repeat_buf[6] = 0;
641 repeat_buf[7] = 0;
642 for(i = 0; i < repeat; i++) {
643 for(j = 0; j < size; j++) {
644 g(repeat_buf[j]);
648 break;
649 case TOK_ASMDIR_rept:
651 int repeat;
652 TokenString *init_str;
653 next();
654 repeat = asm_int_expr(s1);
655 init_str = tok_str_alloc();
656 while (next(), tok != TOK_ASMDIR_endr) {
657 if (tok == CH_EOF)
658 tcc_error("we at end of file, .endr not found");
659 tok_str_add_tok(init_str);
661 tok_str_add(init_str, -1);
662 tok_str_add(init_str, 0);
663 begin_macro(init_str, 1);
664 while (repeat-- > 0) {
665 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
666 global);
667 macro_ptr = init_str->str;
669 end_macro();
670 next();
671 break;
673 case TOK_ASMDIR_org:
675 unsigned long n;
676 ExprValue e;
677 next();
678 asm_expr(s1, &e);
679 n = e.v;
680 if (e.sym) {
681 if (e.sym->r != cur_text_section->sh_num)
682 expect("constant or same-section symbol");
683 n += e.sym->jnext;
685 if (n < ind)
686 tcc_error("attempt to .org backwards");
687 v = 0;
688 size = n - ind;
689 goto zero_pad;
691 break;
692 case TOK_ASMDIR_set:
693 next();
694 tok1 = tok;
695 next();
696 /* Also accept '.set stuff', but don't do anything with this.
697 It's used in GAS to set various features like '.set mips16'. */
698 if (tok == ',')
699 set_symbol(s1, tok1);
700 break;
701 case TOK_ASMDIR_globl:
702 case TOK_ASMDIR_global:
703 case TOK_ASMDIR_weak:
704 case TOK_ASMDIR_hidden:
705 tok1 = tok;
706 do {
707 Sym *sym;
709 next();
710 sym = get_asm_sym(tok, NULL);
711 if (tok1 != TOK_ASMDIR_hidden)
712 sym->type.t &= ~VT_STATIC;
713 if (tok1 == TOK_ASMDIR_weak)
714 sym->a.weak = 1;
715 else if (tok1 == TOK_ASMDIR_hidden)
716 sym->a.visibility = STV_HIDDEN;
717 next();
718 } while (tok == ',');
719 break;
720 case TOK_ASMDIR_string:
721 case TOK_ASMDIR_ascii:
722 case TOK_ASMDIR_asciz:
724 const uint8_t *p;
725 int i, size, t;
727 t = tok;
728 next();
729 for(;;) {
730 if (tok != TOK_STR)
731 expect("string constant");
732 p = tokc.str.data;
733 size = tokc.str.size;
734 if (t == TOK_ASMDIR_ascii && size > 0)
735 size--;
736 for(i = 0; i < size; i++)
737 g(p[i]);
738 next();
739 if (tok == ',') {
740 next();
741 } else if (tok != TOK_STR) {
742 break;
746 break;
747 case TOK_ASMDIR_text:
748 case TOK_ASMDIR_data:
749 case TOK_ASMDIR_bss:
751 char sname[64];
752 tok1 = tok;
753 n = 0;
754 next();
755 if (tok != ';' && tok != TOK_LINEFEED) {
756 n = asm_int_expr(s1);
757 next();
759 if (n)
760 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
761 else
762 sprintf(sname, "%s", get_tok_str(tok1, NULL));
763 use_section(s1, sname);
765 break;
766 case TOK_ASMDIR_file:
768 char filename[512];
770 filename[0] = '\0';
771 next();
773 if (tok == TOK_STR)
774 pstrcat(filename, sizeof(filename), tokc.str.data);
775 else
776 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
778 if (s1->warn_unsupported)
779 tcc_warning("ignoring .file %s", filename);
781 next();
783 break;
784 case TOK_ASMDIR_ident:
786 char ident[256];
788 ident[0] = '\0';
789 next();
791 if (tok == TOK_STR)
792 pstrcat(ident, sizeof(ident), tokc.str.data);
793 else
794 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
796 if (s1->warn_unsupported)
797 tcc_warning("ignoring .ident %s", ident);
799 next();
801 break;
802 case TOK_ASMDIR_size:
804 Sym *sym;
806 next();
807 sym = label_find(tok);
808 if (!sym) {
809 tcc_error("label not found: %s", get_tok_str(tok, NULL));
812 /* XXX .size name,label2-label1 */
813 if (s1->warn_unsupported)
814 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
816 next();
817 skip(',');
818 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
819 next();
822 break;
823 case TOK_ASMDIR_type:
825 Sym *sym;
826 const char *newtype;
828 next();
829 sym = get_asm_sym(tok, NULL);
830 next();
831 skip(',');
832 if (tok == TOK_STR) {
833 newtype = tokc.str.data;
834 } else {
835 if (tok == '@' || tok == '%')
836 next();
837 newtype = get_tok_str(tok, NULL);
840 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
841 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
843 else if (s1->warn_unsupported)
844 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
845 get_tok_str(sym->v, NULL), sym->type.t, newtype);
847 next();
849 break;
850 case TOK_ASMDIR_pushsection:
851 case TOK_ASMDIR_section:
853 char sname[256];
854 int old_nb_section = s1->nb_sections;
856 tok1 = tok;
857 /* XXX: support more options */
858 next();
859 sname[0] = '\0';
860 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
861 if (tok == TOK_STR)
862 pstrcat(sname, sizeof(sname), tokc.str.data);
863 else
864 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
865 next();
867 if (tok == ',') {
868 /* skip section options */
869 next();
870 if (tok != TOK_STR)
871 expect("string constant");
872 next();
873 if (tok == ',') {
874 next();
875 if (tok == '@' || tok == '%')
876 next();
877 next();
880 last_text_section = cur_text_section;
881 if (tok1 == TOK_ASMDIR_section)
882 use_section(s1, sname);
883 else
884 push_section(s1, sname);
885 /* If we just allocated a new section reset its alignment to
886 1. new_section normally acts for GCC compatibility and
887 sets alignment to PTR_SIZE. The assembler behaves different. */
888 if (old_nb_section != s1->nb_sections)
889 cur_text_section->sh_addralign = 1;
891 break;
892 case TOK_ASMDIR_previous:
894 Section *sec;
895 next();
896 if (!last_text_section)
897 tcc_error("no previous section referenced");
898 sec = cur_text_section;
899 use_section1(s1, last_text_section);
900 last_text_section = sec;
902 break;
903 case TOK_ASMDIR_popsection:
904 next();
905 pop_section(s1);
906 break;
907 #ifdef TCC_TARGET_I386
908 case TOK_ASMDIR_code16:
910 next();
911 s1->seg_size = 16;
913 break;
914 case TOK_ASMDIR_code32:
916 next();
917 s1->seg_size = 32;
919 break;
920 #endif
921 #ifdef TCC_TARGET_X86_64
922 /* added for compatibility with GAS */
923 case TOK_ASMDIR_code64:
924 next();
925 break;
926 #endif
927 default:
928 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
929 break;
934 /* assemble a file */
935 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
937 int opcode;
938 int saved_parse_flags = parse_flags;
940 /* XXX: undefine C labels */
941 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
942 if (do_preprocess)
943 parse_flags |= PARSE_FLAG_PREPROCESS;
944 for(;;) {
945 next();
946 if (tok == TOK_EOF)
947 break;
948 /* generate line number info */
949 if (global && s1->do_debug)
950 tcc_debug_line(s1);
951 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
952 redo:
953 if (tok == '#') {
954 /* horrible gas comment */
955 while (tok != TOK_LINEFEED)
956 next();
957 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
958 asm_parse_directive(s1, global);
959 } else if (tok == TOK_PPNUM) {
960 Sym *sym;
961 const char *p;
962 int n;
963 p = tokc.str.data;
964 n = strtoul(p, (char **)&p, 10);
965 if (*p != '\0')
966 expect("':'");
967 /* new local label */
968 sym = asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
969 /* Remove the marker for tentative definitions. */
970 sym->type.t &= ~VT_EXTERN;
971 next();
972 skip(':');
973 goto redo;
974 } else if (tok >= TOK_IDENT) {
975 /* instruction or label */
976 opcode = tok;
977 next();
978 if (tok == ':') {
979 /* handle "extern void vide(void); __asm__("vide: ret");" as
980 "__asm__("globl vide\nvide: ret");" */
981 Sym *sym = sym_find(opcode);
982 if (sym && (sym->type.t & VT_EXTERN) && global) {
983 sym = label_find(opcode);
984 if (!sym) {
985 sym = label_push(&s1->asm_labels, opcode, 0);
986 sym->type.t = VT_VOID | VT_EXTERN;
989 /* new label */
990 sym = asm_new_label(s1, opcode, 0);
991 sym->type.t &= ~VT_EXTERN;
992 next();
993 goto redo;
994 } else if (tok == '=') {
995 set_symbol(s1, opcode);
996 goto redo;
997 } else {
998 asm_opcode(s1, opcode);
1001 /* end of line */
1002 if (tok != ';' && tok != TOK_LINEFEED)
1003 expect("end of line");
1004 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
1007 asm_free_labels(s1);
1008 parse_flags = saved_parse_flags;
1009 return 0;
1012 /* Assemble the current file */
1013 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
1015 int ret;
1016 tcc_debug_start(s1);
1017 /* default section is text */
1018 cur_text_section = text_section;
1019 ind = cur_text_section->data_offset;
1020 nocode_wanted = 0;
1021 ret = tcc_assemble_internal(s1, do_preprocess, 1);
1022 cur_text_section->data_offset = ind;
1023 tcc_debug_end(s1);
1024 return ret;
1027 /********************************************************************/
1028 /* GCC inline asm support */
1030 /* assemble the string 'str' in the current C compilation unit without
1031 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1032 end */
1033 static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
1035 const int *saved_macro_ptr = macro_ptr;
1036 int dotid = set_idnum('.', IS_ID);
1038 tcc_open_bf(s1, ":asm:", len);
1039 memcpy(file->buffer, str, len);
1040 macro_ptr = NULL;
1041 tcc_assemble_internal(s1, 0, global);
1042 tcc_close();
1044 set_idnum('.', dotid);
1045 macro_ptr = saved_macro_ptr;
1048 /* find a constraint by its number or id (gcc 3 extended
1049 syntax). return -1 if not found. Return in *pp in char after the
1050 constraint */
1051 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1052 const char *name, const char **pp)
1054 int index;
1055 TokenSym *ts;
1056 const char *p;
1058 if (isnum(*name)) {
1059 index = 0;
1060 while (isnum(*name)) {
1061 index = (index * 10) + (*name) - '0';
1062 name++;
1064 if ((unsigned)index >= nb_operands)
1065 index = -1;
1066 } else if (*name == '[') {
1067 name++;
1068 p = strchr(name, ']');
1069 if (p) {
1070 ts = tok_alloc(name, p - name);
1071 for(index = 0; index < nb_operands; index++) {
1072 if (operands[index].id == ts->tok)
1073 goto found;
1075 index = -1;
1076 found:
1077 name = p + 1;
1078 } else {
1079 index = -1;
1081 } else {
1082 index = -1;
1084 if (pp)
1085 *pp = name;
1086 return index;
1089 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1090 CString *out_str, CString *in_str)
1092 int c, index, modifier;
1093 const char *str;
1094 ASMOperand *op;
1095 SValue sv;
1097 cstr_new(out_str);
1098 str = in_str->data;
1099 for(;;) {
1100 c = *str++;
1101 if (c == '%') {
1102 if (*str == '%') {
1103 str++;
1104 goto add_char;
1106 modifier = 0;
1107 if (*str == 'c' || *str == 'n' ||
1108 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1109 *str == 'q' ||
1110 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1111 and make literal operands not be decorated with '$'. */
1112 *str == 'P')
1113 modifier = *str++;
1114 index = find_constraint(operands, nb_operands, str, &str);
1115 if (index < 0)
1116 tcc_error("invalid operand reference after %%");
1117 op = &operands[index];
1118 sv = *op->vt;
1119 if (op->reg >= 0) {
1120 sv.r = op->reg;
1121 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1122 sv.r |= VT_LVAL;
1124 subst_asm_operand(out_str, &sv, modifier);
1125 } else {
1126 add_char:
1127 cstr_ccat(out_str, c);
1128 if (c == '\0')
1129 break;
1135 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1136 int is_output)
1138 ASMOperand *op;
1139 int nb_operands;
1141 if (tok != ':') {
1142 nb_operands = *nb_operands_ptr;
1143 for(;;) {
1144 CString astr;
1145 if (nb_operands >= MAX_ASM_OPERANDS)
1146 tcc_error("too many asm operands");
1147 op = &operands[nb_operands++];
1148 op->id = 0;
1149 if (tok == '[') {
1150 next();
1151 if (tok < TOK_IDENT)
1152 expect("identifier");
1153 op->id = tok;
1154 next();
1155 skip(']');
1157 parse_mult_str(&astr, "string constant");
1158 op->constraint = tcc_malloc(astr.size);
1159 strcpy(op->constraint, astr.data);
1160 cstr_free(&astr);
1161 skip('(');
1162 gexpr();
1163 if (is_output) {
1164 if (!(vtop->type.t & VT_ARRAY))
1165 test_lvalue();
1166 } else {
1167 /* we want to avoid LLOCAL case, except when the 'm'
1168 constraint is used. Note that it may come from
1169 register storage, so we need to convert (reg)
1170 case */
1171 if ((vtop->r & VT_LVAL) &&
1172 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1173 (vtop->r & VT_VALMASK) < VT_CONST) &&
1174 !strchr(op->constraint, 'm')) {
1175 gv(RC_INT);
1178 op->vt = vtop;
1179 skip(')');
1180 if (tok == ',') {
1181 next();
1182 } else {
1183 break;
1186 *nb_operands_ptr = nb_operands;
1190 /* parse the GCC asm() instruction */
1191 ST_FUNC void asm_instr(void)
1193 CString astr, astr1;
1194 ASMOperand operands[MAX_ASM_OPERANDS];
1195 int nb_outputs, nb_operands, i, must_subst, out_reg;
1196 uint8_t clobber_regs[NB_ASM_REGS];
1198 next();
1199 /* since we always generate the asm() instruction, we can ignore
1200 volatile */
1201 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1202 next();
1204 parse_asm_str(&astr);
1205 nb_operands = 0;
1206 nb_outputs = 0;
1207 must_subst = 0;
1208 memset(clobber_regs, 0, sizeof(clobber_regs));
1209 if (tok == ':') {
1210 next();
1211 must_subst = 1;
1212 /* output args */
1213 parse_asm_operands(operands, &nb_operands, 1);
1214 nb_outputs = nb_operands;
1215 if (tok == ':') {
1216 next();
1217 if (tok != ')') {
1218 /* input args */
1219 parse_asm_operands(operands, &nb_operands, 0);
1220 if (tok == ':') {
1221 /* clobber list */
1222 /* XXX: handle registers */
1223 next();
1224 for(;;) {
1225 if (tok != TOK_STR)
1226 expect("string constant");
1227 asm_clobber(clobber_regs, tokc.str.data);
1228 next();
1229 if (tok == ',') {
1230 next();
1231 } else {
1232 break;
1239 skip(')');
1240 /* NOTE: we do not eat the ';' so that we can restore the current
1241 token after the assembler parsing */
1242 if (tok != ';')
1243 expect("';'");
1245 /* save all values in the memory */
1246 save_regs(0);
1248 /* compute constraints */
1249 asm_compute_constraints(operands, nb_operands, nb_outputs,
1250 clobber_regs, &out_reg);
1252 /* substitute the operands in the asm string. No substitution is
1253 done if no operands (GCC behaviour) */
1254 #ifdef ASM_DEBUG
1255 printf("asm: \"%s\"\n", (char *)astr.data);
1256 #endif
1257 if (must_subst) {
1258 subst_asm_operands(operands, nb_operands, &astr1, &astr);
1259 cstr_free(&astr);
1260 } else {
1261 astr1 = astr;
1263 #ifdef ASM_DEBUG
1264 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1265 #endif
1267 /* generate loads */
1268 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1269 clobber_regs, out_reg);
1271 /* assemble the string with tcc internal assembler */
1272 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1274 /* restore the current C token */
1275 next();
1277 /* store the output values if needed */
1278 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1279 clobber_regs, out_reg);
1281 /* free everything */
1282 for(i=0;i<nb_operands;i++) {
1283 ASMOperand *op;
1284 op = &operands[i];
1285 tcc_free(op->constraint);
1286 vpop();
1288 cstr_free(&astr1);
1291 ST_FUNC void asm_global_instr(void)
1293 CString astr;
1294 int saved_nocode_wanted = nocode_wanted;
1296 /* Global asm blocks are always emitted. */
1297 nocode_wanted = 0;
1298 next();
1299 parse_asm_str(&astr);
1300 skip(')');
1301 /* NOTE: we do not eat the ';' so that we can restore the current
1302 token after the assembler parsing */
1303 if (tok != ';')
1304 expect("';'");
1306 #ifdef ASM_DEBUG
1307 printf("asm_global: \"%s\"\n", (char *)astr.data);
1308 #endif
1309 cur_text_section = text_section;
1310 ind = cur_text_section->data_offset;
1312 /* assemble the string with tcc internal assembler */
1313 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1315 cur_text_section->data_offset = ind;
1317 /* restore the current C token */
1318 next();
1320 cstr_free(&astr);
1321 nocode_wanted = saved_nocode_wanted;
1323 #endif /* CONFIG_TCC_ASM */