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
;
73 } else if (*p
== '\0') {
78 tcc_error("invalid number syntax");
84 asm_expr_unary(s1
, pe
);
90 asm_expr_unary(s1
, pe
);
92 tcc_error("invalid operation with label");
114 sym_dot
.type
.t
= VT_VOID
| VT_STATIC
;
115 sym_dot
.r
= cur_text_section
->sh_num
;
120 if (tok
>= TOK_IDENT
) {
121 /* label case : if the label was not found, add one */
122 sym
= label_find(tok
);
124 sym
= label_push(&s1
->asm_labels
, tok
, 0);
125 /* NOTE: by default, the symbol is global */
126 sym
->type
.t
= VT_VOID
;
128 if (sym
->r
== SHN_ABS
) {
129 /* if absolute symbol, no need to put a symbol value */
140 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
146 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
151 asm_expr_unary(s1
, pe
);
154 if (op
!= '*' && op
!= '/' && op
!= '%' &&
155 op
!= TOK_SHL
&& op
!= TOK_SAR
)
158 asm_expr_unary(s1
, &e2
);
159 if (pe
->sym
|| e2
.sym
)
160 tcc_error("invalid operation with label");
168 tcc_error("division by zero");
188 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
193 asm_expr_prod(s1
, pe
);
196 if (op
!= '&' && op
!= '|' && op
!= '^')
199 asm_expr_prod(s1
, &e2
);
200 if (pe
->sym
|| e2
.sym
)
201 tcc_error("invalid operation with label");
217 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
222 asm_expr_logic(s1
, pe
);
225 if (op
!= '+' && op
!= '-')
228 asm_expr_logic(s1
, &e2
);
230 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
231 goto cannot_relocate
;
233 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
237 /* NOTE: we are less powerful than gas in that case
238 because we store only one symbol in the expression */
241 } else if (pe
->sym
== e2
.sym
) {
243 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
244 } else if (pe
->sym
&& pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
245 /* we also accept defined symbols in the same section */
246 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
248 } else if (e2
.sym
->r
== cur_text_section
->sh_num
) {
249 /* When subtracting a defined symbol in current section
250 this actually makes the value PC-relative. */
251 pe
->v
-= e2
.sym
->jnext
- ind
- 4;
256 tcc_error("invalid operation with label");
262 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
264 asm_expr_sum(s1
, pe
);
267 ST_FUNC
int asm_int_expr(TCCState
*s1
)
276 /* NOTE: the same name space as C labels is used to avoid using too
277 much memory when storing labels in TokenStrings */
278 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
279 int sh_num
, int value
)
283 sym
= label_find(label
);
286 /* the label is already defined */
288 tcc_error("assembler label '%s' already defined",
289 get_tok_str(label
, NULL
));
291 /* redefinition of local labels is possible */
297 sym
= label_push(&s1
->asm_labels
, label
, 0);
298 sym
->type
.t
= VT_STATIC
| VT_VOID
;
304 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
306 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
309 static void asm_free_labels(TCCState
*st
)
314 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
316 /* define symbol value in object file */
321 sec
= st
->sections
[s
->r
];
322 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
325 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
328 st
->asm_labels
= NULL
;
331 static void use_section1(TCCState
*s1
, Section
*sec
)
333 cur_text_section
->data_offset
= ind
;
334 cur_text_section
= sec
;
335 ind
= cur_text_section
->data_offset
;
338 static void use_section(TCCState
*s1
, const char *name
)
341 sec
= find_section(s1
, name
);
342 use_section1(s1
, sec
);
345 static void push_section(TCCState
*s1
, const char *name
)
347 Section
*sec
= find_section(s1
, name
);
348 sec
->prev
= cur_text_section
;
349 use_section1(s1
, sec
);
352 static void pop_section(TCCState
*s1
)
354 Section
*prev
= cur_text_section
->prev
;
356 tcc_error(".popsection without .pushsection");
357 cur_text_section
->prev
= NULL
;
358 use_section1(s1
, prev
);
361 static void asm_parse_directive(TCCState
*s1
)
363 int n
, offset
, v
, size
, tok1
;
367 /* assembler directive */
368 sec
= cur_text_section
;
370 case TOK_ASMDIR_align
:
371 case TOK_ASMDIR_balign
:
372 case TOK_ASMDIR_p2align
:
373 case TOK_ASMDIR_skip
:
374 case TOK_ASMDIR_space
:
377 n
= asm_int_expr(s1
);
378 if (tok1
== TOK_ASMDIR_p2align
)
381 tcc_error("invalid p2align, must be between 0 and 30");
383 tok1
= TOK_ASMDIR_align
;
385 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
386 if (n
< 0 || (n
& (n
-1)) != 0)
387 tcc_error("alignment must be a positive power of two");
388 offset
= (ind
+ n
- 1) & -n
;
390 /* the section must have a compatible alignment */
391 if (sec
->sh_addralign
< n
)
392 sec
->sh_addralign
= n
;
399 v
= asm_int_expr(s1
);
402 if (sec
->sh_type
!= SHT_NOBITS
) {
403 sec
->data_offset
= ind
;
404 ptr
= section_ptr_add(sec
, size
);
405 memset(ptr
, v
, size
);
409 case TOK_ASMDIR_quad
:
416 if (tok
!= TOK_PPNUM
) {
418 tcc_error("64 bit constant");
420 vl
= strtoll(p
, (char **)&p
, 0);
424 if (sec
->sh_type
!= SHT_NOBITS
) {
425 /* XXX: endianness */
436 case TOK_ASMDIR_byte
:
439 case TOK_ASMDIR_word
:
440 case TOK_ASMDIR_short
:
443 case TOK_ASMDIR_long
:
451 if (sec
->sh_type
!= SHT_NOBITS
) {
470 case TOK_ASMDIR_fill
:
472 int repeat
, size
, val
, i
, j
;
473 uint8_t repeat_buf
[8];
475 repeat
= asm_int_expr(s1
);
477 tcc_error("repeat < 0; .fill ignored");
484 size
= asm_int_expr(s1
);
486 tcc_error("size < 0; .fill ignored");
493 val
= asm_int_expr(s1
);
496 /* XXX: endianness */
498 repeat_buf
[1] = val
>> 8;
499 repeat_buf
[2] = val
>> 16;
500 repeat_buf
[3] = val
>> 24;
505 for(i
= 0; i
< repeat
; i
++) {
506 for(j
= 0; j
< size
; j
++) {
512 case TOK_ASMDIR_rept
:
515 TokenString
*init_str
;
516 ParseState saved_parse_state
= {0};
518 repeat
= asm_int_expr(s1
);
519 init_str
= tok_str_alloc();
521 while ((tok
!= TOK_ASMDIR_endr
) && (tok
!= CH_EOF
)) {
522 tok_str_add_tok(init_str
);
525 if (tok
== CH_EOF
) tcc_error("we at end of file, .endr not found");
527 tok_str_add(init_str
, -1);
528 tok_str_add(init_str
, 0);
529 save_parse_state(&saved_parse_state
);
530 begin_macro(init_str
, 1);
531 while (repeat
-- > 0) {
532 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
));
533 macro_ptr
= init_str
->str
;
536 restore_parse_state(&saved_parse_state
);
547 if (e
.sym
->r
!= cur_text_section
->sh_num
)
548 expect("constant or same-section symbol");
552 tcc_error("attempt to .org backwards");
558 case TOK_ASMDIR_globl
:
559 case TOK_ASMDIR_global
:
560 case TOK_ASMDIR_weak
:
561 case TOK_ASMDIR_hidden
:
567 sym
= label_find(tok
);
569 sym
= label_push(&s1
->asm_labels
, tok
, 0);
570 sym
->type
.t
= VT_VOID
;
572 if (tok1
!= TOK_ASMDIR_hidden
)
573 sym
->type
.t
&= ~VT_STATIC
;
574 if (tok1
== TOK_ASMDIR_weak
)
575 sym
->type
.t
|= VT_WEAK
;
576 else if (tok1
== TOK_ASMDIR_hidden
)
577 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
579 } while (tok
== ',');
581 case TOK_ASMDIR_string
:
582 case TOK_ASMDIR_ascii
:
583 case TOK_ASMDIR_asciz
:
592 expect("string constant");
594 size
= tokc
.str
.size
;
595 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
597 for(i
= 0; i
< size
; i
++)
602 } else if (tok
!= TOK_STR
) {
608 case TOK_ASMDIR_text
:
609 case TOK_ASMDIR_data
:
616 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
617 n
= asm_int_expr(s1
);
621 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
623 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
624 use_section(s1
, sname
);
627 case TOK_ASMDIR_file
:
635 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
637 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
639 if (s1
->warn_unsupported
)
640 tcc_warning("ignoring .file %s", filename
);
645 case TOK_ASMDIR_ident
:
653 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
655 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
657 if (s1
->warn_unsupported
)
658 tcc_warning("ignoring .ident %s", ident
);
663 case TOK_ASMDIR_size
:
668 sym
= label_find(tok
);
670 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
673 /* XXX .size name,label2-label1 */
674 if (s1
->warn_unsupported
)
675 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
679 while (tok
!= '\n' && tok
!= CH_EOF
) {
684 case TOK_ASMDIR_type
:
690 sym
= label_find(tok
);
692 sym
= label_push(&s1
->asm_labels
, tok
, 0);
693 sym
->type
.t
= VT_VOID
;
698 if (tok
== TOK_STR
) {
699 newtype
= tokc
.str
.data
;
701 if (tok
== '@' || tok
== '%')
703 newtype
= get_tok_str(tok
, NULL
);
706 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
707 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
709 else if (s1
->warn_unsupported
)
710 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
711 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
716 case TOK_ASMDIR_pushsection
:
717 case TOK_ASMDIR_section
:
720 int old_nb_section
= s1
->nb_sections
;
723 /* XXX: support more options */
726 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
728 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
730 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
734 /* skip section options */
737 expect("string constant");
741 if (tok
== '@' || tok
== '%')
746 last_text_section
= cur_text_section
;
747 if (tok1
== TOK_ASMDIR_section
)
748 use_section(s1
, sname
);
750 push_section(s1
, sname
);
751 /* If we just allocated a new section reset its alignment to
752 1. new_section normally acts for GCC compatibility and
753 sets alignment to PTR_SIZE. The assembler behaves different. */
754 if (old_nb_section
!= s1
->nb_sections
)
755 cur_text_section
->sh_addralign
= 1;
758 case TOK_ASMDIR_previous
:
762 if (!last_text_section
)
763 tcc_error("no previous section referenced");
764 sec
= cur_text_section
;
765 use_section1(s1
, last_text_section
);
766 last_text_section
= sec
;
769 case TOK_ASMDIR_popsection
:
773 #ifdef TCC_TARGET_I386
774 case TOK_ASMDIR_code16
:
780 case TOK_ASMDIR_code32
:
787 #ifdef TCC_TARGET_X86_64
788 /* added for compatibility with GAS */
789 case TOK_ASMDIR_code64
:
794 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
800 /* assemble a file */
801 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
805 /* XXX: undefine C labels */
807 ch
= file
->buf_ptr
[0];
808 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
809 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
811 parse_flags
|= PARSE_FLAG_PREPROCESS
;
816 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
819 /* horrible gas comment */
820 while (tok
!= TOK_LINEFEED
)
822 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
823 asm_parse_directive(s1
);
824 } else if (tok
== TOK_PPNUM
) {
828 n
= strtoul(p
, (char **)&p
, 10);
831 /* new local label */
832 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
836 } else if (tok
>= TOK_IDENT
) {
837 /* instruction or label */
841 /* handle "extern void vide(void); __asm__("vide: ret");" as
842 "__asm__("globl vide\nvide: ret");" */
843 Sym
*sym
= sym_find(opcode
);
844 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && nocode_wanted
) {
845 sym
= label_find(opcode
);
847 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
848 sym
->type
.t
= VT_VOID
;
852 asm_new_label(s1
, opcode
, 0);
855 } else if (tok
== '=') {
858 n
= asm_int_expr(s1
);
859 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
862 asm_opcode(s1
, opcode
);
866 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
867 expect("end of line");
869 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
878 /* Assemble the current file */
879 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
884 preprocess_start(s1
);
886 /* default section is text */
887 cur_text_section
= text_section
;
888 ind
= cur_text_section
->data_offset
;
890 define_start
= define_stack
;
892 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
893 symbols can be safely used */
894 put_elf_sym(symtab_section
, 0, 0,
895 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
896 SHN_ABS
, file
->filename
);
898 ret
= tcc_assemble_internal(s1
, do_preprocess
);
900 cur_text_section
->data_offset
= ind
;
902 free_defines(define_start
);
907 /********************************************************************/
908 /* GCC inline asm support */
910 /* assemble the string 'str' in the current C compilation unit without
911 C preprocessing. NOTE: str is modified by modifying the '\0' at the
913 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
915 int saved_parse_flags
;
916 const int *saved_macro_ptr
;
918 saved_parse_flags
= parse_flags
;
919 saved_macro_ptr
= macro_ptr
;
921 tcc_open_bf(s1
, ":asm:", len
);
922 memcpy(file
->buffer
, str
, len
);
925 tcc_assemble_internal(s1
, 0);
928 parse_flags
= saved_parse_flags
;
929 macro_ptr
= saved_macro_ptr
;
932 /* find a constraint by its number or id (gcc 3 extended
933 syntax). return -1 if not found. Return in *pp in char after the
935 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
936 const char *name
, const char **pp
)
944 while (isnum(*name
)) {
945 index
= (index
* 10) + (*name
) - '0';
948 if ((unsigned)index
>= nb_operands
)
950 } else if (*name
== '[') {
952 p
= strchr(name
, ']');
954 ts
= tok_alloc(name
, p
- name
);
955 for(index
= 0; index
< nb_operands
; index
++) {
956 if (operands
[index
].id
== ts
->tok
)
973 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
975 CString
*out_str
, CString
*in_str
)
977 int c
, index
, modifier
;
992 if (*str
== 'c' || *str
== 'n' ||
993 *str
== 'b' || *str
== 'w' ||
994 *str
== 'h' || *str
== 'k' || *str
== 'q' ||
995 /* P in GCC would add "@PLT" to symbol refs in PIC mode
996 Ignore this in TCC. */
999 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1001 tcc_error("invalid operand reference after %%");
1002 op
= &operands
[index
];
1006 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1009 subst_asm_operand(out_str
, &sv
, modifier
);
1012 cstr_ccat(out_str
, c
);
1020 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1027 nb_operands
= *nb_operands_ptr
;
1029 if (nb_operands
>= MAX_ASM_OPERANDS
)
1030 tcc_error("too many asm operands");
1031 op
= &operands
[nb_operands
++];
1035 if (tok
< TOK_IDENT
)
1036 expect("identifier");
1042 expect("string constant");
1043 op
->constraint
= tcc_malloc(tokc
.str
.size
);
1044 strcpy(op
->constraint
, tokc
.str
.data
);
1049 if (!(vtop
->type
.t
& VT_ARRAY
))
1052 /* we want to avoid LLOCAL case, except when the 'm'
1053 constraint is used. Note that it may come from
1054 register storage, so we need to convert (reg)
1056 if ((vtop
->r
& VT_LVAL
) &&
1057 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1058 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1059 !strchr(op
->constraint
, 'm')) {
1071 *nb_operands_ptr
= nb_operands
;
1075 /* parse the GCC asm() instruction */
1076 ST_FUNC
void asm_instr(void)
1078 CString astr
, astr1
;
1079 ASMOperand operands
[MAX_ASM_OPERANDS
];
1080 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1081 uint8_t clobber_regs
[NB_ASM_REGS
];
1084 /* since we always generate the asm() instruction, we can ignore
1086 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1089 parse_asm_str(&astr
);
1093 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1098 parse_asm_operands(operands
, &nb_operands
, 1);
1099 nb_outputs
= nb_operands
;
1104 parse_asm_operands(operands
, &nb_operands
, 0);
1107 /* XXX: handle registers */
1111 expect("string constant");
1112 asm_clobber(clobber_regs
, tokc
.str
.data
);
1125 /* NOTE: we do not eat the ';' so that we can restore the current
1126 token after the assembler parsing */
1130 /* save all values in the memory */
1133 /* compute constraints */
1134 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1135 clobber_regs
, &out_reg
);
1137 /* substitute the operands in the asm string. No substitution is
1138 done if no operands (GCC behaviour) */
1140 printf("asm: \"%s\"\n", (char *)astr
.data
);
1143 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1149 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1152 /* generate loads */
1153 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1154 clobber_regs
, out_reg
);
1156 /* assemble the string with tcc internal assembler */
1157 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1159 /* restore the current C token */
1162 /* store the output values if needed */
1163 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1164 clobber_regs
, out_reg
);
1166 /* free everything */
1167 for(i
=0;i
<nb_operands
;i
++) {
1170 tcc_free(op
->constraint
);
1176 ST_FUNC
void asm_global_instr(void)
1181 parse_asm_str(&astr
);
1183 /* NOTE: we do not eat the ';' so that we can restore the current
1184 token after the assembler parsing */
1189 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1191 cur_text_section
= text_section
;
1192 ind
= cur_text_section
->data_offset
;
1194 /* assemble the string with tcc internal assembler */
1195 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1197 cur_text_section
->data_offset
= ind
;
1199 /* restore the current C token */
1204 #endif /* CONFIG_TCC_ASM */