1/*
2 * GAS like assembler for TCC
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#define USING_GLOBALS
22#include "tcc.h"
23#ifdef CONFIG_TCC_ASM
24
25static Section *last_text_section; /* to handle .previous asm directive */
26
27ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
28{
29 char buf[64];
30 TokenSym *ts;
31
32 snprintf(buf, sizeof(buf), "L..%u", n);
33 ts = tok_alloc(buf, strlen(buf));
34 return ts->tok;
35}
36
37static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
38static Sym* asm_new_label(TCCState *s1, int label, int is_local);
39static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value);
40
41/* If a C name has an _ prepended then only asm labels that start
42 with _ are representable in C, by removing the first _. ASM names
43 without _ at the beginning don't correspond to C names, but we use
44 the global C symbol table to track ASM names as well, so we need to
45 transform those into ones that don't conflict with a C name,
46 so prepend a '.' for them, but force the ELF asm name to be set. */
47static int asm2cname(int v, int *addeddot)
48{
49 const char *name;
50 *addeddot = 0;
51 if (!tcc_state->leading_underscore)
52 return v;
53 name = get_tok_str(v, NULL);
54 if (!name)
55 return v;
56 if (name[0] == '_') {
57 v = tok_alloc(name + 1, strlen(name) - 1)->tok;
58 } else if (!strchr(name, '.')) {
59 int n = strlen(name) + 2;
60 char newname[256];
61 snprintf(newname, sizeof newname, ".%s", name);
62 v = tok_alloc(newname, n - 1)->tok;
63 *addeddot = 1;
64 }
65 return v;
66}
67
68static Sym *asm_label_find(int v)
69{
70 Sym *sym;
71 int addeddot;
72 v = asm2cname(v, &addeddot);
73 sym = sym_find(v);
74 while (sym && sym->sym_scope && !(sym->type.t & VT_STATIC))
75 sym = sym->prev_tok;
76 return sym;
77}
78
79static Sym *asm_label_push(int v)
80{
81 int addeddot, v2 = asm2cname(v, &addeddot);
82 /* We always add VT_EXTERN, for sym definition that's tentative
83 (for .set, removed for real defs), for mere references it's correct
84 as is. */
85 Sym *sym = global_identifier_push(v2, VT_ASM | VT_EXTERN | VT_STATIC, 0);
86 if (addeddot)
87 sym->asm_label = v;
88 return sym;
89}
90
91/* Return a symbol we can use inside the assembler, having name NAME.
92 Symbols from asm and C source share a namespace. If we generate
93 an asm symbol it's also a (file-global) C symbol, but it's
94 either not accessible by name (like "L.123"), or its type information
95 is such that it's not usable without a proper C declaration.
96
97 Sometimes we need symbols accessible by name from asm, which
98 are anonymous in C, in this case CSYM can be used to transfer
99 all information from that symbol to the (possibly newly created)
100 asm symbol. */
101ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
102{
103 Sym *sym = asm_label_find(name);
104 if (!sym) {
105 sym = asm_label_push(name);
106 if (csym)
107 sym->c = csym->c;
108 }
109 return sym;
110}
111
112static Sym* asm_section_sym(TCCState *s1, Section *sec)
113{
114 char buf[100];
115 int label = tok_alloc(buf,
116 snprintf(buf, sizeof buf, "L.%s", sec->name)
117 )->tok;
118 Sym *sym = asm_label_find(label);
119 return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);
120}
121
122/* We do not use the C expression parser to handle symbols. Maybe the
123 C expression parser could be tweaked to do so. */
124
125static void asm_expr_unary(TCCState *s1, ExprValue *pe)
126{
127 Sym *sym;
128 int op, label;
129 uint64_t n;
130 const char *p;
131
132 switch(tok) {
133 case TOK_PPNUM:
134 p = tokc.str.data;
135 n = strtoull(p, (char **)&p, 0);
136 if (*p == 'b' || *p == 'f') {
137 /* backward or forward label */
138 label = asm_get_local_label_name(s1, n);
139 sym = asm_label_find(label);
140 if (*p == 'b') {
141 /* backward : find the last corresponding defined label */
142 if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
143 sym = sym->prev_tok;
144 if (!sym)
145 tcc_error("local label '%d' not found backward", (int)n);
146 } else {
147 /* forward */
148 if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {
149 /* if the last label is defined, then define a new one */
150 sym = asm_label_push(label);
151 }
152 }
153 pe->v = 0;
154 pe->sym = sym;
155 pe->pcrel = 0;
156 } else if (*p == '\0') {
157 pe->v = n;
158 pe->sym = NULL;
159 pe->pcrel = 0;
160 } else {
161 tcc_error("invalid number syntax");
162 }
163 next();
164 break;
165 case '+':
166 next();
167 asm_expr_unary(s1, pe);
168 break;
169 case '-':
170 case '~':
171 op = tok;
172 next();
173 asm_expr_unary(s1, pe);
174 if (pe->sym)
175 tcc_error("invalid operation with label");
176 if (op == '-')
177 pe->v = -pe->v;
178 else
179 pe->v = ~pe->v;
180 break;
181 case TOK_CCHAR:
182 case TOK_LCHAR:
183 pe->v = tokc.i;
184 pe->sym = NULL;
185 pe->pcrel = 0;
186 next();
187 break;
188 case '(':
189 next();
190 asm_expr(s1, pe);
191 skip(')');
192 break;
193 case '.':
194 pe->v = ind;
195 pe->sym = asm_section_sym(s1, cur_text_section);
196 pe->pcrel = 0;
197 next();
198 break;
199 default:
200 if (tok >= TOK_IDENT) {
201 ElfSym *esym;
202 /* label case : if the label was not found, add one */
203 sym = get_asm_sym(tok, NULL);
204 esym = elfsym(sym);
205 if (esym && esym->st_shndx == SHN_ABS) {
206 /* if absolute symbol, no need to put a symbol value */
207 pe->v = esym->st_value;
208 pe->sym = NULL;
209 pe->pcrel = 0;
210 } else {
211 pe->v = 0;
212 pe->sym = sym;
213 pe->pcrel = 0;
214 }
215 next();
216 } else {
217 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
218 }
219 break;
220 }
221}
222
223static void asm_expr_prod(TCCState *s1, ExprValue *pe)
224{
225 int op;
226 ExprValue e2;
227
228 asm_expr_unary(s1, pe);
229 for(;;) {
230 op = tok;
231 if (op != '*' && op != '/' && op != '%' &&
232 op != TOK_SHL && op != TOK_SAR)
233 break;
234 next();
235 asm_expr_unary(s1, &e2);
236 if (pe->sym || e2.sym)
237 tcc_error("invalid operation with label");
238 switch(op) {
239 case '*':
240 pe->v *= e2.v;
241 break;
242 case '/':
243 if (e2.v == 0) {
244 div_error:
245 tcc_error("division by zero");
246 }
247 pe->v /= e2.v;
248 break;
249 case '%':
250 if (e2.v == 0)
251 goto div_error;
252 pe->v %= e2.v;
253 break;
254 case TOK_SHL:
255 pe->v <<= e2.v;
256 break;
257 default:
258 case TOK_SAR:
259 pe->v >>= e2.v;
260 break;
261 }
262 }
263}
264
265static void asm_expr_logic(TCCState *s1, ExprValue *pe)
266{
267 int op;
268 ExprValue e2;
269
270 asm_expr_prod(s1, pe);
271 for(;;) {
272 op = tok;
273 if (op != '&' && op != '|' && op != '^')
274 break;
275 next();
276 asm_expr_prod(s1, &e2);
277 if (pe->sym || e2.sym)
278 tcc_error("invalid operation with label");
279 switch(op) {
280 case '&':
281 pe->v &= e2.v;
282 break;
283 case '|':
284 pe->v |= e2.v;
285 break;
286 default:
287 case '^':
288 pe->v ^= e2.v;
289 break;
290 }
291 }
292}
293
294static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
295{
296 int op;
297 ExprValue e2;
298
299 asm_expr_logic(s1, pe);
300 for(;;) {
301 op = tok;
302 if (op != '+' && op != '-')
303 break;
304 next();
305 asm_expr_logic(s1, &e2);
306 if (op == '+') {
307 if (pe->sym != NULL && e2.sym != NULL)
308 goto cannot_relocate;
309 pe->v += e2.v;
310 if (pe->sym == NULL && e2.sym != NULL)
311 pe->sym = e2.sym;
312 } else {
313 pe->v -= e2.v;
314 /* NOTE: we are less powerful than gas in that case
315 because we store only one symbol in the expression */
316 if (!e2.sym) {
317 /* OK */
318 } else if (pe->sym == e2.sym) {
319 /* OK */
320 pe->sym = NULL; /* same symbols can be subtracted to NULL */
321 } else {
322 ElfSym *esym1, *esym2;
323 esym1 = elfsym(pe->sym);
324 esym2 = elfsym(e2.sym);
325 if (esym1 && esym1->st_shndx == esym2->st_shndx
326 && esym1->st_shndx != SHN_UNDEF) {
327 /* we also accept defined symbols in the same section */
328 pe->v += esym1->st_value - esym2->st_value;
329 pe->sym = NULL;
330 } else if (esym2->st_shndx == cur_text_section->sh_num) {
331 /* When subtracting a defined symbol in current section
332 this actually makes the value PC-relative. */
333 pe->v -= esym2->st_value - ind - 4;
334 pe->pcrel = 1;
335 e2.sym = NULL;
336 } else {
337cannot_relocate:
338 tcc_error("invalid operation with label");
339 }
340 }
341 }
342 }
343}
344
345static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
346{
347 int op;
348 ExprValue e2;
349
350 asm_expr_sum(s1, pe);
351 for(;;) {
352 op = tok;
353 if (op != TOK_EQ && op != TOK_NE
354 && (op > TOK_GT || op < TOK_ULE))
355 break;
356 next();
357 asm_expr_sum(s1, &e2);
358 if (pe->sym || e2.sym)
359 tcc_error("invalid operation with label");
360 switch(op) {
361 case TOK_EQ:
362 pe->v = pe->v == e2.v;
363 break;
364 case TOK_NE:
365 pe->v = pe->v != e2.v;
366 break;
367 case TOK_LT:
368 pe->v = (int64_t)pe->v < (int64_t)e2.v;
369 break;
370 case TOK_GE:
371 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
372 break;
373 case TOK_LE:
374 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
375 break;
376 case TOK_GT:
377 pe->v = (int64_t)pe->v > (int64_t)e2.v;
378 break;
379 default:
380 break;
381 }
382 /* GAS compare results are -1/0 not 1/0. */
383 pe->v = -(int64_t)pe->v;
384 }
385}
386
387ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
388{
389 asm_expr_cmp(s1, pe);
390}
391
392ST_FUNC int asm_int_expr(TCCState *s1)
393{
394 ExprValue e;
395 asm_expr(s1, &e);
396 if (e.sym)
397 expect("constant");
398 return e.v;
399}
400
401static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
402 int sh_num, int value)
403{
404 Sym *sym;
405 ElfSym *esym;
406
407 sym = asm_label_find(label);
408 if (sym) {
409 esym = elfsym(sym);
410 /* A VT_EXTERN symbol, even if it has a section is considered
411 overridable. This is how we "define" .set targets. Real
412 definitions won't have VT_EXTERN set. */
413 if (esym && esym->st_shndx != SHN_UNDEF) {
414 /* the label is already defined */
415 if (IS_ASM_SYM(sym)
416 && (is_local == 1 || (sym->type.t & VT_EXTERN)))
417 goto new_label;
418 if (!(sym->type.t & VT_EXTERN))
419 tcc_error("assembler label '%s' already defined",
420 get_tok_str(label, NULL));
421 }
422 } else {
423 new_label:
424 sym = asm_label_push(label);
425 }
426 if (!sym->c)
427 put_extern_sym2(sym, SHN_UNDEF, 0, 0, 1);
428 esym = elfsym(sym);
429 esym->st_shndx = sh_num;
430 esym->st_value = value;
431 if (is_local != 2)
432 sym->type.t &= ~VT_EXTERN;
433 return sym;
434}
435
436static Sym* asm_new_label(TCCState *s1, int label, int is_local)
437{
438 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
439}
440
441/* Set the value of LABEL to that of some expression (possibly
442 involving other symbols). LABEL can be overwritten later still. */
443static Sym* set_symbol(TCCState *s1, int label)
444{
445 long n;
446 ExprValue e;
447 Sym *sym;
448 ElfSym *esym;
449 next();
450 asm_expr(s1, &e);
451 n = e.v;
452 esym = elfsym(e.sym);
453 if (esym)
454 n += esym->st_value;
455 sym = asm_new_label1(s1, label, 2, esym ? esym->st_shndx : SHN_ABS, n);
456 elfsym(sym)->st_other |= ST_ASM_SET;
457 return sym;
458}
459
460static void use_section1(TCCState *s1, Section *sec)
461{
462 cur_text_section->data_offset = ind;
463 cur_text_section = sec;
464 ind = cur_text_section->data_offset;
465}
466
467static void use_section(TCCState *s1, const char *name)
468{
469 Section *sec;
470 sec = find_section(s1, name);
471 use_section1(s1, sec);
472}
473
474static void push_section(TCCState *s1, const char *name)
475{
476 Section *sec = find_section(s1, name);
477 sec->prev = cur_text_section;
478 use_section1(s1, sec);
479}
480
481static void pop_section(TCCState *s1)
482{
483 Section *prev = cur_text_section->prev;
484 if (!prev)
485 tcc_error(".popsection without .pushsection");
486 cur_text_section->prev = NULL;
487 use_section1(s1, prev);
488}
489
490static void asm_parse_directive(TCCState *s1, int global)
491{
492 int n, offset, v, size, tok1;
493 Section *sec;
494 uint8_t *ptr;
495
496 /* assembler directive */
497 sec = cur_text_section;
498 switch(tok) {
499 case TOK_ASMDIR_align:
500 case TOK_ASMDIR_balign:
501 case TOK_ASMDIR_p2align:
502 case TOK_ASMDIR_skip:
503 case TOK_ASMDIR_space:
504 tok1 = tok;
505 next();
506 n = asm_int_expr(s1);
507 if (tok1 == TOK_ASMDIR_p2align)
508 {
509 if (n < 0 || n > 30)
510 tcc_error("invalid p2align, must be between 0 and 30");
511 n = 1 << n;
512 tok1 = TOK_ASMDIR_align;
513 }
514 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
515 if (n < 0 || (n & (n-1)) != 0)
516 tcc_error("alignment must be a positive power of two");
517 offset = (ind + n - 1) & -n;
518 size = offset - ind;
519 /* the section must have a compatible alignment */
520 if (sec->sh_addralign < n)
521 sec->sh_addralign = n;
522 } else {
523 if (n < 0)
524 n = 0;
525 size = n;
526 }
527 v = 0;
528 if (tok == ',') {
529 next();
530 v = asm_int_expr(s1);
531 }
532 zero_pad:
533 if (sec->sh_type != SHT_NOBITS) {
534 sec->data_offset = ind;
535 ptr = section_ptr_add(sec, size);
536 memset(ptr, v, size);
537 }
538 ind += size;
539 break;
540 case TOK_ASMDIR_quad:
541#ifdef TCC_TARGET_X86_64
542 size = 8;
543 goto asm_data;
544#else
545 next();
546 for(;;) {
547 uint64_t vl;
548 const char *p;
549
550 p = tokc.str.data;
551 if (tok != TOK_PPNUM) {
552 error_constant:
553 tcc_error("64 bit constant");
554 }
555 vl = strtoll(p, (char **)&p, 0);
556 if (*p != '\0')
557 goto error_constant;
558 next();
559 if (sec->sh_type != SHT_NOBITS) {
560 /* XXX: endianness */
561 gen_le32(vl);
562 gen_le32(vl >> 32);
563 } else {
564 ind += 8;
565 }
566 if (tok != ',')
567 break;
568 next();
569 }
570 break;
571#endif
572 case TOK_ASMDIR_byte:
573 size = 1;
574 goto asm_data;
575 case TOK_ASMDIR_word:
576 case TOK_ASMDIR_short:
577 size = 2;
578 goto asm_data;
579 case TOK_ASMDIR_long:
580 case TOK_ASMDIR_int:
581 size = 4;
582 asm_data:
583 next();
584 for(;;) {
585 ExprValue e;
586 asm_expr(s1, &e);
587 if (sec->sh_type != SHT_NOBITS) {
588 if (size == 4) {
589 gen_expr32(&e);
590#ifdef TCC_TARGET_X86_64
591 } else if (size == 8) {
592 gen_expr64(&e);
593#endif
594 } else {
595 if (e.sym)
596 expect("constant");
597 if (size == 1)
598 g(e.v);
599 else
600 gen_le16(e.v);
601 }
602 } else {
603 ind += size;
604 }
605 if (tok != ',')
606 break;
607 next();
608 }
609 break;
610 case TOK_ASMDIR_fill:
611 {
612 int repeat, size, val, i, j;
613 uint8_t repeat_buf[8];
614 next();
615 repeat = asm_int_expr(s1);
616 if (repeat < 0) {
617 tcc_error("repeat < 0; .fill ignored");
618 break;
619 }
620 size = 1;
621 val = 0;
622 if (tok == ',') {
623 next();
624 size = asm_int_expr(s1);
625 if (size < 0) {
626 tcc_error("size < 0; .fill ignored");
627 break;
628 }
629 if (size > 8)
630 size = 8;
631 if (tok == ',') {
632 next();
633 val = asm_int_expr(s1);
634 }
635 }
636 /* XXX: endianness */
637 repeat_buf[0] = val;
638 repeat_buf[1] = val >> 8;
639 repeat_buf[2] = val >> 16;
640 repeat_buf[3] = val >> 24;
641 repeat_buf[4] = 0;
642 repeat_buf[5] = 0;
643 repeat_buf[6] = 0;
644 repeat_buf[7] = 0;
645 for(i = 0; i < repeat; i++) {
646 for(j = 0; j < size; j++) {
647 g(repeat_buf[j]);
648 }
649 }
650 }
651 break;
652 case TOK_ASMDIR_rept:
653 {
654 int repeat;
655 TokenString *init_str;
656 next();
657 repeat = asm_int_expr(s1);
658 init_str = tok_str_alloc();
659 while (next(), tok != TOK_ASMDIR_endr) {
660 if (tok == CH_EOF)
661 tcc_error("we at end of file, .endr not found");
662 tok_str_add_tok(init_str);
663 }
664 tok_str_add(init_str, -1);
665 tok_str_add(init_str, 0);
666 begin_macro(init_str, 1);
667 while (repeat-- > 0) {
668 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
669 global);
670 macro_ptr = init_str->str;
671 }
672 end_macro();
673 next();
674 break;
675 }
676 case TOK_ASMDIR_org:
677 {
678 unsigned long n;
679 ExprValue e;
680 ElfSym *esym;
681 next();
682 asm_expr(s1, &e);
683 n = e.v;
684 esym = elfsym(e.sym);
685 if (esym) {
686 if (esym->st_shndx != cur_text_section->sh_num)
687 expect("constant or same-section symbol");
688 n += esym->st_value;
689 }
690 if (n < ind)
691 tcc_error("attempt to .org backwards");
692 v = 0;
693 size = n - ind;
694 goto zero_pad;
695 }
696 break;
697 case TOK_ASMDIR_set:
698 next();
699 tok1 = tok;
700 next();
701 /* Also accept '.set stuff', but don't do anything with this.
702 It's used in GAS to set various features like '.set mips16'. */
703 if (tok == ',')
704 set_symbol(s1, tok1);
705 break;
706 case TOK_ASMDIR_globl:
707 case TOK_ASMDIR_global:
708 case TOK_ASMDIR_weak:
709 case TOK_ASMDIR_hidden:
710 tok1 = tok;
711 do {
712 Sym *sym;
713 next();
714 sym = get_asm_sym(tok, NULL);
715 if (tok1 != TOK_ASMDIR_hidden)
716 sym->type.t &= ~VT_STATIC;
717 if (tok1 == TOK_ASMDIR_weak)
718 sym->a.weak = 1;
719 else if (tok1 == TOK_ASMDIR_hidden)
720 sym->a.visibility = STV_HIDDEN;
721 update_storage(sym);
722 next();
723 } while (tok == ',');
724 break;
725 case TOK_ASMDIR_string:
726 case TOK_ASMDIR_ascii:
727 case TOK_ASMDIR_asciz:
728 {
729 const uint8_t *p;
730 int i, size, t;
731
732 t = tok;
733 next();
734 for(;;) {
735 if (tok != TOK_STR)
736 expect("string constant");
737 p = tokc.str.data;
738 size = tokc.str.size;
739 if (t == TOK_ASMDIR_ascii && size > 0)
740 size--;
741 for(i = 0; i < size; i++)
742 g(p[i]);
743 next();
744 if (tok == ',') {
745 next();
746 } else if (tok != TOK_STR) {
747 break;
748 }
749 }
750 }
751 break;
752 case TOK_ASMDIR_text:
753 case TOK_ASMDIR_data:
754 case TOK_ASMDIR_bss:
755 {
756 char sname[64];
757 tok1 = tok;
758 n = 0;
759 next();
760 if (tok != ';' && tok != TOK_LINEFEED) {
761 n = asm_int_expr(s1);
762 next();
763 }
764 if (n)
765 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
766 else
767 sprintf(sname, "%s", get_tok_str(tok1, NULL));
768 use_section(s1, sname);
769 }
770 break;
771 case TOK_ASMDIR_file:
772 {
773 char filename[512];
774
775 filename[0] = '\0';
776 next();
777
778 if (tok == TOK_STR)
779 pstrcat(filename, sizeof(filename), tokc.str.data);
780 else
781 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
782
783 if (s1->warn_unsupported)
784 tcc_warning("ignoring .file %s", filename);
785
786 next();
787 }
788 break;
789 case TOK_ASMDIR_ident:
790 {
791 char ident[256];
792
793 ident[0] = '\0';
794 next();
795
796 if (tok == TOK_STR)
797 pstrcat(ident, sizeof(ident), tokc.str.data);
798 else
799 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
800
801 if (s1->warn_unsupported)
802 tcc_warning("ignoring .ident %s", ident);
803
804 next();
805 }
806 break;
807 case TOK_ASMDIR_size:
808 {
809 Sym *sym;
810
811 next();
812 sym = asm_label_find(tok);
813 if (!sym) {
814 tcc_error("label not found: %s", get_tok_str(tok, NULL));
815 }
816
817 /* XXX .size name,label2-label1 */
818 if (s1->warn_unsupported)
819 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
820
821 next();
822 skip(',');
823 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
824 next();
825 }
826 }
827 break;
828 case TOK_ASMDIR_type:
829 {
830 Sym *sym;
831 const char *newtype;
832
833 next();
834 sym = get_asm_sym(tok, NULL);
835 next();
836 skip(',');
837 if (tok == TOK_STR) {
838 newtype = tokc.str.data;
839 } else {
840 if (tok == '@' || tok == '%')
841 next();
842 newtype = get_tok_str(tok, NULL);
843 }
844
845 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
846 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
847 }
848 else if (s1->warn_unsupported)
849 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
850 get_tok_str(sym->v, NULL), sym->type.t, newtype);
851
852 next();
853 }
854 break;
855 case TOK_ASMDIR_pushsection:
856 case TOK_ASMDIR_section:
857 {
858 char sname[256];
859 int old_nb_section = s1->nb_sections;
860
861 tok1 = tok;
862 /* XXX: support more options */
863 next();
864 sname[0] = '\0';
865 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
866 if (tok == TOK_STR)
867 pstrcat(sname, sizeof(sname), tokc.str.data);
868 else
869 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
870 next();
871 }
872 if (tok == ',') {
873 /* skip section options */
874 next();
875 if (tok != TOK_STR)
876 expect("string constant");
877 next();
878 if (tok == ',') {
879 next();
880 if (tok == '@' || tok == '%')
881 next();
882 next();
883 }
884 }
885 last_text_section = cur_text_section;
886 if (tok1 == TOK_ASMDIR_section)
887 use_section(s1, sname);
888 else
889 push_section(s1, sname);
890 /* If we just allocated a new section reset its alignment to
891 1. new_section normally acts for GCC compatibility and
892 sets alignment to PTR_SIZE. The assembler behaves different. */
893 if (old_nb_section != s1->nb_sections)
894 cur_text_section->sh_addralign = 1;
895 }
896 break;
897 case TOK_ASMDIR_previous:
898 {
899 Section *sec;
900 next();
901 if (!last_text_section)
902 tcc_error("no previous section referenced");
903 sec = cur_text_section;
904 use_section1(s1, last_text_section);
905 last_text_section = sec;
906 }
907 break;
908 case TOK_ASMDIR_popsection:
909 next();
910 pop_section(s1);
911 break;
912#ifdef TCC_TARGET_I386
913 case TOK_ASMDIR_code16:
914 {
915 next();
916 s1->seg_size = 16;
917 }
918 break;
919 case TOK_ASMDIR_code32:
920 {
921 next();
922 s1->seg_size = 32;
923 }
924 break;
925#endif
926#ifdef TCC_TARGET_X86_64
927 /* added for compatibility with GAS */
928 case TOK_ASMDIR_code64:
929 next();
930 break;
931#endif
932 default:
933 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
934 break;
935 }
936}
937
938
939/* assemble a file */
940static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
941{
942 int opcode;
943 int saved_parse_flags = parse_flags;
944
945 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
946 if (do_preprocess)
947 parse_flags |= PARSE_FLAG_PREPROCESS;
948 for(;;) {
949 next();
950 if (tok == TOK_EOF)
951 break;
952 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
953 redo:
954 if (tok == '#') {
955 /* horrible gas comment */
956 while (tok != TOK_LINEFEED)
957 next();
958 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
959 asm_parse_directive(s1, global);
960 } else if (tok == TOK_PPNUM) {
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 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
969 next();
970 skip(':');
971 goto redo;
972 } else if (tok >= TOK_IDENT) {
973 /* instruction or label */
974 opcode = tok;
975 next();
976 if (tok == ':') {
977 /* new label */
978 asm_new_label(s1, opcode, 0);
979 next();
980 goto redo;
981 } else if (tok == '=') {
982 set_symbol(s1, opcode);
983 goto redo;
984 } else {
985 asm_opcode(s1, opcode);
986 }
987 }
988 /* end of line */
989 if (tok != ';' && tok != TOK_LINEFEED)
990 expect("end of line");
991 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
992 }
993
994 parse_flags = saved_parse_flags;
995 return 0;
996}
997
998/* Assemble the current file */
999ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
1000{
1001 int ret;
1002 tcc_debug_start(s1);
1003 /* default section is text */
1004 cur_text_section = text_section;
1005 ind = cur_text_section->data_offset;
1006 nocode_wanted = 0;
1007 ret = tcc_assemble_internal(s1, do_preprocess, 1);
1008 cur_text_section->data_offset = ind;
1009 tcc_debug_end(s1);
1010 return ret;
1011}
1012
1013/********************************************************************/
1014/* GCC inline asm support */
1015
1016/* assemble the string 'str' in the current C compilation unit without
1017 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1018 end */
1019static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
1020{
1021 const int *saved_macro_ptr = macro_ptr;
1022 int dotid = set_idnum('.', IS_ID);
1023 int dolid = set_idnum('$', 0);
1024
1025 tcc_open_bf(s1, ":asm:", len);
1026 memcpy(file->buffer, str, len);
1027 macro_ptr = NULL;
1028 tcc_assemble_internal(s1, 0, global);
1029 tcc_close();
1030
1031 set_idnum('$', dolid);
1032 set_idnum('.', dotid);
1033 macro_ptr = saved_macro_ptr;
1034}
1035
1036/* find a constraint by its number or id (gcc 3 extended
1037 syntax). return -1 if not found. Return in *pp in char after the
1038 constraint */
1039ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1040 const char *name, const char **pp)
1041{
1042 int index;
1043 TokenSym *ts;
1044 const char *p;
1045
1046 if (isnum(*name)) {
1047 index = 0;
1048 while (isnum(*name)) {
1049 index = (index * 10) + (*name) - '0';
1050 name++;
1051 }
1052 if ((unsigned)index >= nb_operands)
1053 index = -1;
1054 } else if (*name == '[') {
1055 name++;
1056 p = strchr(name, ']');
1057 if (p) {
1058 ts = tok_alloc(name, p - name);
1059 for(index = 0; index < nb_operands; index++) {
1060 if (operands[index].id == ts->tok)
1061 goto found;
1062 }
1063 index = -1;
1064 found:
1065 name = p + 1;
1066 } else {
1067 index = -1;
1068 }
1069 } else {
1070 index = -1;
1071 }
1072 if (pp)
1073 *pp = name;
1074 return index;
1075}
1076
1077static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1078 CString *out_str, CString *in_str)
1079{
1080 int c, index, modifier;
1081 const char *str;
1082 ASMOperand *op;
1083 SValue sv;
1084
1085 cstr_new(out_str);
1086 str = in_str->data;
1087 for(;;) {
1088 c = *str++;
1089 if (c == '%') {
1090 if (*str == '%') {
1091 str++;
1092 goto add_char;
1093 }
1094 modifier = 0;
1095 if (*str == 'c' || *str == 'n' ||
1096 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1097 *str == 'q' ||
1098 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1099 and make literal operands not be decorated with '$'. */
1100 *str == 'P')
1101 modifier = *str++;
1102 index = find_constraint(operands, nb_operands, str, &str);
1103 if (index < 0)
1104 tcc_error("invalid operand reference after %%");
1105 op = &operands[index];
1106 sv = *op->vt;
1107 if (op->reg >= 0) {
1108 sv.r = op->reg;
1109 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1110 sv.r |= VT_LVAL;
1111 }
1112 subst_asm_operand(out_str, &sv, modifier);
1113 } else {
1114 add_char:
1115 cstr_ccat(out_str, c);
1116 if (c == '\0')
1117 break;
1118 }
1119 }
1120}
1121
1122
1123static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1124 int is_output)
1125{
1126 ASMOperand *op;
1127 int nb_operands;
1128
1129 if (tok != ':') {
1130 nb_operands = *nb_operands_ptr;
1131 for(;;) {
1132 CString astr;
1133 if (nb_operands >= MAX_ASM_OPERANDS)
1134 tcc_error("too many asm operands");
1135 op = &operands[nb_operands++];
1136 op->id = 0;
1137 if (tok == '[') {
1138 next();
1139 if (tok < TOK_IDENT)
1140 expect("identifier");
1141 op->id = tok;
1142 next();
1143 skip(']');
1144 }
1145 parse_mult_str(&astr, "string constant");
1146 op->constraint = tcc_malloc(astr.size);
1147 strcpy(op->constraint, astr.data);
1148 cstr_free(&astr);
1149 skip('(');
1150 gexpr();
1151 if (is_output) {
1152 if (!(vtop->type.t & VT_ARRAY))
1153 test_lvalue();
1154 } else {
1155 /* we want to avoid LLOCAL case, except when the 'm'
1156 constraint is used. Note that it may come from
1157 register storage, so we need to convert (reg)
1158 case */
1159 if ((vtop->r & VT_LVAL) &&
1160 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1161 (vtop->r & VT_VALMASK) < VT_CONST) &&
1162 !strchr(op->constraint, 'm')) {
1163 gv(RC_INT);
1164 }
1165 }
1166 op->vt = vtop;
1167 skip(')');
1168 if (tok == ',') {
1169 next();
1170 } else {
1171 break;
1172 }
1173 }
1174 *nb_operands_ptr = nb_operands;
1175 }
1176}
1177
1178/* parse the GCC asm() instruction */
1179ST_FUNC void asm_instr(void)
1180{
1181 CString astr, astr1;
1182 ASMOperand operands[MAX_ASM_OPERANDS];
1183 int nb_outputs, nb_operands, i, must_subst, out_reg;
1184 uint8_t clobber_regs[NB_ASM_REGS];
1185 Section *sec;
1186
1187 /* since we always generate the asm() instruction, we can ignore
1188 volatile */
1189 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1190 next();
1191 }
1192 parse_asm_str(&astr);
1193 nb_operands = 0;
1194 nb_outputs = 0;
1195 must_subst = 0;
1196 memset(clobber_regs, 0, sizeof(clobber_regs));
1197 if (tok == ':') {
1198 next();
1199 must_subst = 1;
1200 /* output args */
1201 parse_asm_operands(operands, &nb_operands, 1);
1202 nb_outputs = nb_operands;
1203 if (tok == ':') {
1204 next();
1205 if (tok != ')') {
1206 /* input args */
1207 parse_asm_operands(operands, &nb_operands, 0);
1208 if (tok == ':') {
1209 /* clobber list */
1210 /* XXX: handle registers */
1211 next();
1212 for(;;) {
1213 if (tok != TOK_STR)
1214 expect("string constant");
1215 asm_clobber(clobber_regs, tokc.str.data);
1216 next();
1217 if (tok == ',') {
1218 next();
1219 } else {
1220 break;
1221 }
1222 }
1223 }
1224 }
1225 }
1226 }
1227 skip(')');
1228 /* NOTE: we do not eat the ';' so that we can restore the current
1229 token after the assembler parsing */
1230 if (tok != ';')
1231 expect("';'");
1232
1233 /* save all values in the memory */
1234 save_regs(0);
1235
1236 /* compute constraints */
1237 asm_compute_constraints(operands, nb_operands, nb_outputs,
1238 clobber_regs, &out_reg);
1239
1240 /* substitute the operands in the asm string. No substitution is
1241 done if no operands (GCC behaviour) */
1242#ifdef ASM_DEBUG
1243 printf("asm: \"%s\"\n", (char *)astr.data);
1244#endif
1245 if (must_subst) {
1246 subst_asm_operands(operands, nb_operands, &astr1, &astr);
1247 cstr_free(&astr);
1248 } else {
1249 astr1 = astr;
1250 }
1251#ifdef ASM_DEBUG
1252 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1253#endif
1254
1255 /* generate loads */
1256 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1257 clobber_regs, out_reg);
1258
1259 /* We don't allow switching section within inline asm to
1260 bleed out to surrounding code. */
1261 sec = cur_text_section;
1262 /* assemble the string with tcc internal assembler */
1263 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1264 if (sec != cur_text_section) {
1265 tcc_warning("inline asm tries to change current section");
1266 use_section1(tcc_state, sec);
1267 }
1268
1269 /* restore the current C token */
1270 next();
1271
1272 /* store the output values if needed */
1273 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1274 clobber_regs, out_reg);
1275
1276 /* free everything */
1277 for(i=0;i<nb_operands;i++) {
1278 ASMOperand *op;
1279 op = &operands[i];
1280 tcc_free(op->constraint);
1281 vpop();
1282 }
1283 cstr_free(&astr1);
1284}
1285
1286ST_FUNC void asm_global_instr(void)
1287{
1288 CString astr;
1289 int saved_nocode_wanted = nocode_wanted;
1290
1291 /* Global asm blocks are always emitted. */
1292 nocode_wanted = 0;
1293 next();
1294 parse_asm_str(&astr);
1295 skip(')');
1296 /* NOTE: we do not eat the ';' so that we can restore the current
1297 token after the assembler parsing */
1298 if (tok != ';')
1299 expect("';'");
1300
1301#ifdef ASM_DEBUG
1302 printf("asm_global: \"%s\"\n", (char *)astr.data);
1303#endif
1304 cur_text_section = text_section;
1305 ind = cur_text_section->data_offset;
1306
1307 /* assemble the string with tcc internal assembler */
1308 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1309
1310 cur_text_section->data_offset = ind;
1311
1312 /* restore the current C token */
1313 next();
1314
1315 cstr_free(&astr);
1316 nocode_wanted = saved_nocode_wanted;
1317}
1318#endif /* CONFIG_TCC_ASM */
1319