2 * GAS like assembler for TCC
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
24 ST_FUNC
int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
29 snprintf(buf
, sizeof(buf
), "L..%u", n
);
30 ts
= tok_alloc(buf
, strlen(buf
));
34 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
);
35 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
);
38 /* We do not use the C expression parser to handle symbols. Maybe the
39 C expression parser could be tweaked to do so. */
41 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
51 n
= strtoul(p
, (char **)&p
, 0);
52 if (*p
== 'b' || *p
== 'f') {
53 /* backward or forward label */
54 label
= asm_get_local_label_name(s1
, n
);
55 sym
= label_find(label
);
57 /* backward : find the last corresponding defined label */
58 if (sym
&& sym
->r
== 0)
61 tcc_error("local label '%d' not found backward", n
);
65 /* if the last label is defined, then define a new one */
66 sym
= label_push(&s1
->asm_labels
, label
, 0);
67 sym
->type
.t
= VT_STATIC
| VT_VOID
;
72 } else if (*p
== '\0') {
76 tcc_error("invalid number syntax");
82 asm_expr_unary(s1
, pe
);
88 asm_expr_unary(s1
, pe
);
90 tcc_error("invalid operation with label");
110 sym_dot
.type
.t
= VT_VOID
| VT_STATIC
;
111 sym_dot
.r
= cur_text_section
->sh_num
;
116 if (tok
>= TOK_IDENT
) {
117 /* label case : if the label was not found, add one */
118 sym
= label_find(tok
);
120 sym
= label_push(&s1
->asm_labels
, tok
, 0);
121 /* NOTE: by default, the symbol is global */
122 sym
->type
.t
= VT_VOID
;
124 if (sym
->r
== SHN_ABS
) {
125 /* if absolute symbol, no need to put a symbol value */
134 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
140 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
145 asm_expr_unary(s1
, pe
);
148 if (op
!= '*' && op
!= '/' && op
!= '%' &&
149 op
!= TOK_SHL
&& op
!= TOK_SAR
)
152 asm_expr_unary(s1
, &e2
);
153 if (pe
->sym
|| e2
.sym
)
154 tcc_error("invalid operation with label");
162 tcc_error("division by zero");
182 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
187 asm_expr_prod(s1
, pe
);
190 if (op
!= '&' && op
!= '|' && op
!= '^')
193 asm_expr_prod(s1
, &e2
);
194 if (pe
->sym
|| e2
.sym
)
195 tcc_error("invalid operation with label");
211 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
216 asm_expr_logic(s1
, pe
);
219 if (op
!= '+' && op
!= '-')
222 asm_expr_logic(s1
, &e2
);
224 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
225 goto cannot_relocate
;
227 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
231 /* NOTE: we are less powerful than gas in that case
232 because we store only one symbol in the expression */
233 if (!pe
->sym
&& !e2
.sym
) {
235 } else if (pe
->sym
&& !e2
.sym
) {
237 } else if (pe
->sym
&& e2
.sym
) {
238 if (pe
->sym
== e2
.sym
) {
240 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
241 /* we also accept defined symbols in the same section */
242 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
244 goto cannot_relocate
;
246 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
249 tcc_error("invalid operation with label");
255 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
257 asm_expr_sum(s1
, pe
);
260 ST_FUNC
int asm_int_expr(TCCState
*s1
)
269 /* NOTE: the same name space as C labels is used to avoid using too
270 much memory when storing labels in TokenStrings */
271 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
272 int sh_num
, int value
)
276 sym
= label_find(label
);
279 /* the label is already defined */
281 tcc_error("assembler label '%s' already defined",
282 get_tok_str(label
, NULL
));
284 /* redefinition of local labels is possible */
290 sym
= label_push(&s1
->asm_labels
, label
, 0);
291 sym
->type
.t
= VT_STATIC
| VT_VOID
;
297 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
299 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
302 static void asm_free_labels(TCCState
*st
)
307 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
309 /* define symbol value in object file */
314 sec
= st
->sections
[s
->r
];
315 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
318 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
321 st
->asm_labels
= NULL
;
324 static void use_section1(TCCState
*s1
, Section
*sec
)
326 cur_text_section
->data_offset
= ind
;
327 cur_text_section
= sec
;
328 ind
= cur_text_section
->data_offset
;
331 static void use_section(TCCState
*s1
, const char *name
)
334 sec
= find_section(s1
, name
);
335 use_section1(s1
, sec
);
338 static void asm_parse_directive(TCCState
*s1
)
340 int n
, offset
, v
, size
, tok1
;
344 /* assembler directive */
345 sec
= cur_text_section
;
347 case TOK_ASMDIR_align
:
348 case TOK_ASMDIR_p2align
:
349 case TOK_ASMDIR_skip
:
350 case TOK_ASMDIR_space
:
353 n
= asm_int_expr(s1
);
354 if (tok1
== TOK_ASMDIR_p2align
)
357 tcc_error("invalid p2align, must be between 0 and 30");
359 tok1
= TOK_ASMDIR_align
;
361 if (tok1
== TOK_ASMDIR_align
) {
362 if (n
< 0 || (n
& (n
-1)) != 0)
363 tcc_error("alignment must be a positive power of two");
364 offset
= (ind
+ n
- 1) & -n
;
366 /* the section must have a compatible alignment */
367 if (sec
->sh_addralign
< n
)
368 sec
->sh_addralign
= n
;
375 v
= asm_int_expr(s1
);
378 if (sec
->sh_type
!= SHT_NOBITS
) {
379 sec
->data_offset
= ind
;
380 ptr
= section_ptr_add(sec
, size
);
381 memset(ptr
, v
, size
);
385 case TOK_ASMDIR_quad
:
392 if (tok
!= TOK_PPNUM
) {
394 tcc_error("64 bit constant");
396 vl
= strtoll(p
, (char **)&p
, 0);
400 if (sec
->sh_type
!= SHT_NOBITS
) {
401 /* XXX: endianness */
412 case TOK_ASMDIR_byte
:
415 case TOK_ASMDIR_word
:
416 case TOK_ASMDIR_short
:
419 case TOK_ASMDIR_long
:
427 if (sec
->sh_type
!= SHT_NOBITS
) {
446 case TOK_ASMDIR_fill
:
448 int repeat
, size
, val
, i
, j
;
449 uint8_t repeat_buf
[8];
451 repeat
= asm_int_expr(s1
);
453 tcc_error("repeat < 0; .fill ignored");
460 size
= asm_int_expr(s1
);
462 tcc_error("size < 0; .fill ignored");
469 val
= asm_int_expr(s1
);
472 /* XXX: endianness */
474 repeat_buf
[1] = val
>> 8;
475 repeat_buf
[2] = val
>> 16;
476 repeat_buf
[3] = val
>> 24;
481 for(i
= 0; i
< repeat
; i
++) {
482 for(j
= 0; j
< size
; j
++) {
488 case TOK_ASMDIR_rept
:
491 TokenString
*init_str
;
492 ParseState saved_parse_state
= {0};
494 repeat
= asm_int_expr(s1
);
495 init_str
= tok_str_alloc();
497 while ((tok
!= TOK_ASMDIR_endr
) && (tok
!= CH_EOF
)) {
498 tok_str_add_tok(init_str
);
501 if (tok
== CH_EOF
) tcc_error("we at end of file, .endr not found");
503 tok_str_add(init_str
, -1);
504 tok_str_add(init_str
, 0);
505 save_parse_state(&saved_parse_state
);
506 begin_macro(init_str
, 1);
507 while (repeat
-- > 0) {
508 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
));
509 macro_ptr
= init_str
->str
;
512 restore_parse_state(&saved_parse_state
);
519 /* XXX: handle section symbols too */
520 n
= asm_int_expr(s1
);
522 tcc_error("attempt to .org backwards");
528 case TOK_ASMDIR_globl
:
529 case TOK_ASMDIR_global
:
530 case TOK_ASMDIR_weak
:
531 case TOK_ASMDIR_hidden
:
537 sym
= label_find(tok
);
539 sym
= label_push(&s1
->asm_labels
, tok
, 0);
540 sym
->type
.t
= VT_VOID
;
542 if (tok1
!= TOK_ASMDIR_hidden
)
543 sym
->type
.t
&= ~VT_STATIC
;
544 if (tok1
== TOK_ASMDIR_weak
)
545 sym
->type
.t
|= VT_WEAK
;
546 else if (tok1
== TOK_ASMDIR_hidden
)
547 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
549 } while (tok
== ',');
551 case TOK_ASMDIR_string
:
552 case TOK_ASMDIR_ascii
:
553 case TOK_ASMDIR_asciz
:
562 expect("string constant");
564 size
= tokc
.str
.size
;
565 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
567 for(i
= 0; i
< size
; i
++)
572 } else if (tok
!= TOK_STR
) {
578 case TOK_ASMDIR_text
:
579 case TOK_ASMDIR_data
:
586 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
587 n
= asm_int_expr(s1
);
591 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
593 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
594 use_section(s1
, sname
);
597 case TOK_ASMDIR_file
:
605 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
607 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
609 if (s1
->warn_unsupported
)
610 tcc_warning("ignoring .file %s", filename
);
615 case TOK_ASMDIR_ident
:
623 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
625 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
627 if (s1
->warn_unsupported
)
628 tcc_warning("ignoring .ident %s", ident
);
633 case TOK_ASMDIR_size
:
638 sym
= label_find(tok
);
640 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
643 /* XXX .size name,label2-label1 */
644 if (s1
->warn_unsupported
)
645 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
649 while (tok
!= '\n' && tok
!= CH_EOF
) {
654 case TOK_ASMDIR_type
:
660 sym
= label_find(tok
);
662 sym
= label_push(&s1
->asm_labels
, tok
, 0);
663 sym
->type
.t
= VT_VOID
;
668 if (tok
== TOK_STR
) {
669 newtype
= tokc
.str
.data
;
671 if (tok
== '@' || tok
== '%')
673 newtype
= get_tok_str(tok
, NULL
);
676 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
677 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
679 else if (s1
->warn_unsupported
)
680 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
681 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
686 case TOK_ASMDIR_section
:
690 /* XXX: support more options */
693 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
695 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
697 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
701 /* skip section options */
704 expect("string constant");
708 if (tok
== '@' || tok
== '%')
713 last_text_section
= cur_text_section
;
714 use_section(s1
, sname
);
717 case TOK_ASMDIR_previous
:
721 if (!last_text_section
)
722 tcc_error("no previous section referenced");
723 sec
= cur_text_section
;
724 use_section1(s1
, last_text_section
);
725 last_text_section
= sec
;
728 #ifdef TCC_TARGET_I386
729 case TOK_ASMDIR_code16
:
735 case TOK_ASMDIR_code32
:
742 #ifdef TCC_TARGET_X86_64
743 /* added for compatibility with GAS */
744 case TOK_ASMDIR_code64
:
749 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
755 /* assemble a file */
756 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
760 /* XXX: undefine C labels */
762 ch
= file
->buf_ptr
[0];
763 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
764 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
766 parse_flags
|= PARSE_FLAG_PREPROCESS
;
771 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
774 /* horrible gas comment */
775 while (tok
!= TOK_LINEFEED
)
777 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
778 asm_parse_directive(s1
);
779 } else if (tok
== TOK_PPNUM
) {
783 n
= strtoul(p
, (char **)&p
, 10);
786 /* new local label */
787 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
791 } else if (tok
>= TOK_IDENT
) {
792 /* instruction or label */
796 /* handle "extern void vide(void); __asm__("vide: ret");" as
797 "__asm__("globl vide\nvide: ret");" */
798 Sym
*sym
= sym_find(opcode
);
799 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && nocode_wanted
) {
800 sym
= label_find(opcode
);
802 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
803 sym
->type
.t
= VT_VOID
;
807 asm_new_label(s1
, opcode
, 0);
810 } else if (tok
== '=') {
813 n
= asm_int_expr(s1
);
814 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
817 asm_opcode(s1
, opcode
);
821 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
822 expect("end of line");
824 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
833 /* Assemble the current file */
834 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
839 preprocess_start(s1
);
841 /* default section is text */
842 cur_text_section
= text_section
;
843 ind
= cur_text_section
->data_offset
;
845 define_start
= define_stack
;
847 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
848 symbols can be safely used */
849 put_elf_sym(symtab_section
, 0, 0,
850 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
851 SHN_ABS
, file
->filename
);
853 ret
= tcc_assemble_internal(s1
, do_preprocess
);
855 cur_text_section
->data_offset
= ind
;
857 free_defines(define_start
);
862 /********************************************************************/
863 /* GCC inline asm support */
865 /* assemble the string 'str' in the current C compilation unit without
866 C preprocessing. NOTE: str is modified by modifying the '\0' at the
868 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
870 int saved_parse_flags
;
871 const int *saved_macro_ptr
;
873 saved_parse_flags
= parse_flags
;
874 saved_macro_ptr
= macro_ptr
;
876 tcc_open_bf(s1
, ":asm:", len
);
877 memcpy(file
->buffer
, str
, len
);
880 tcc_assemble_internal(s1
, 0);
883 parse_flags
= saved_parse_flags
;
884 macro_ptr
= saved_macro_ptr
;
887 /* find a constraint by its number or id (gcc 3 extended
888 syntax). return -1 if not found. Return in *pp in char after the
890 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
891 const char *name
, const char **pp
)
899 while (isnum(*name
)) {
900 index
= (index
* 10) + (*name
) - '0';
903 if ((unsigned)index
>= nb_operands
)
905 } else if (*name
== '[') {
907 p
= strchr(name
, ']');
909 ts
= tok_alloc(name
, p
- name
);
910 for(index
= 0; index
< nb_operands
; index
++) {
911 if (operands
[index
].id
== ts
->tok
)
928 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
930 CString
*out_str
, CString
*in_str
)
932 int c
, index
, modifier
;
947 if (*str
== 'c' || *str
== 'n' ||
948 *str
== 'b' || *str
== 'w' ||
949 *str
== 'h' || *str
== 'k')
951 index
= find_constraint(operands
, nb_operands
, str
, &str
);
953 tcc_error("invalid operand reference after %%");
954 op
= &operands
[index
];
958 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
961 subst_asm_operand(out_str
, &sv
, modifier
);
964 cstr_ccat(out_str
, c
);
972 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
979 nb_operands
= *nb_operands_ptr
;
981 if (nb_operands
>= MAX_ASM_OPERANDS
)
982 tcc_error("too many asm operands");
983 op
= &operands
[nb_operands
++];
988 expect("identifier");
994 expect("string constant");
995 op
->constraint
= tcc_malloc(tokc
.str
.size
);
996 strcpy(op
->constraint
, tokc
.str
.data
);
1001 if (!(vtop
->type
.t
& VT_ARRAY
))
1004 /* we want to avoid LLOCAL case, except when the 'm'
1005 constraint is used. Note that it may come from
1006 register storage, so we need to convert (reg)
1008 if ((vtop
->r
& VT_LVAL
) &&
1009 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1010 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1011 !strchr(op
->constraint
, 'm')) {
1023 *nb_operands_ptr
= nb_operands
;
1027 /* parse the GCC asm() instruction */
1028 ST_FUNC
void asm_instr(void)
1030 CString astr
, astr1
;
1031 ASMOperand operands
[MAX_ASM_OPERANDS
];
1032 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1033 uint8_t clobber_regs
[NB_ASM_REGS
];
1036 /* since we always generate the asm() instruction, we can ignore
1038 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1041 parse_asm_str(&astr
);
1045 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1050 parse_asm_operands(operands
, &nb_operands
, 1);
1051 nb_outputs
= nb_operands
;
1056 parse_asm_operands(operands
, &nb_operands
, 0);
1059 /* XXX: handle registers */
1063 expect("string constant");
1064 asm_clobber(clobber_regs
, tokc
.str
.data
);
1077 /* NOTE: we do not eat the ';' so that we can restore the current
1078 token after the assembler parsing */
1082 /* save all values in the memory */
1085 /* compute constraints */
1086 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1087 clobber_regs
, &out_reg
);
1089 /* substitute the operands in the asm string. No substitution is
1090 done if no operands (GCC behaviour) */
1092 printf("asm: \"%s\"\n", (char *)astr
.data
);
1095 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1101 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1104 /* generate loads */
1105 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1106 clobber_regs
, out_reg
);
1108 /* assemble the string with tcc internal assembler */
1109 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1111 /* restore the current C token */
1114 /* store the output values if needed */
1115 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1116 clobber_regs
, out_reg
);
1118 /* free everything */
1119 for(i
=0;i
<nb_operands
;i
++) {
1122 tcc_free(op
->constraint
);
1128 ST_FUNC
void asm_global_instr(void)
1133 parse_asm_str(&astr
);
1135 /* NOTE: we do not eat the ';' so that we can restore the current
1136 token after the assembler parsing */
1141 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1143 cur_text_section
= text_section
;
1144 ind
= cur_text_section
->data_offset
;
1146 /* assemble the string with tcc internal assembler */
1147 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1149 cur_text_section
->data_offset
= ind
;
1151 /* restore the current C token */
1156 #endif /* CONFIG_TCC_ASM */