📄 tccasm.c
字号:
/* * GAS like assembler for TCC * * Copyright (c) 2001, 2002 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */static int asm_get_local_label_name(TCCState *s1, unsigned int n){ char buf[64]; TokenSym *ts; snprintf(buf, sizeof(buf), "L..%u", n); ts = tok_alloc(buf, strlen(buf)); return ts->tok;}/* We do not use the C expression parser to handle symbols. Maybe the C expression parser could be tweaked to do so. */static void asm_expr_unary(TCCState *s1, ExprValue *pe){ Sym *sym; int op, n, label; const char *p; switch(tok) { case TOK_PPNUM: p = tokc.cstr->data; n = strtoul(p, (char **)&p, 0); if (*p == 'b' || *p == 'f') { /* backward or forward label */ label = asm_get_local_label_name(s1, n); sym = label_find(label); if (*p == 'b') { /* backward : find the last corresponding defined label */ if (sym && sym->r == 0) sym = sym->prev_tok; if (!sym) error("local label '%d' not found backward", n); } else { /* forward */ if (!sym || sym->r) { /* if the last label is defined, then define a new one */ sym = label_push(&s1->asm_labels, label, 0); sym->type.t = VT_STATIC | VT_VOID; } } pe->v = 0; pe->sym = sym; } else if (*p == '\0') { pe->v = n; pe->sym = NULL; } else { error("invalid number syntax"); } next(); break; case '+': next(); asm_expr_unary(s1, pe); break; case '-': case '~': op = tok; next(); asm_expr_unary(s1, pe); if (pe->sym) error("invalid operation with label"); if (op == '-') pe->v = -pe->v; else pe->v = ~pe->v; break; case TOK_CCHAR: case TOK_LCHAR: pe->v = tokc.i; pe->sym = NULL; next(); break; default: if (tok >= TOK_IDENT) { /* label case : if the label was not found, add one */ sym = label_find(tok); if (!sym) { sym = label_push(&s1->asm_labels, tok, 0); /* NOTE: by default, the symbol is global */ sym->type.t = VT_VOID; } pe->v = 0; pe->sym = sym; next(); } else { error("bad expression syntax [%s]", get_tok_str(tok, &tokc)); } break; }} static void asm_expr_prod(TCCState *s1, ExprValue *pe){ int op; ExprValue e2; asm_expr_unary(s1, pe); for(;;) { op = tok; if (op != '*' && op != '/' && op != '%' && op != TOK_SHL && op != TOK_SAR) break; next(); asm_expr_unary(s1, &e2); if (pe->sym || e2.sym) error("invalid operation with label"); switch(op) { case '*': pe->v *= e2.v; break; case '/': if (e2.v == 0) { div_error: error("division by zero"); } pe->v /= e2.v; break; case '%': if (e2.v == 0) goto div_error; pe->v %= e2.v; break; case TOK_SHL: pe->v <<= e2.v; break; default: case TOK_SAR: pe->v >>= e2.v; break; } }}static void asm_expr_logic(TCCState *s1, ExprValue *pe){ int op; ExprValue e2; asm_expr_prod(s1, pe); for(;;) { op = tok; if (op != '&' && op != '|' && op != '^') break; next(); asm_expr_prod(s1, &e2); if (pe->sym || e2.sym) error("invalid operation with label"); switch(op) { case '&': pe->v &= e2.v; break; case '|': pe->v |= e2.v; break; default: case '^': pe->v ^= e2.v; break; } }}static inline void asm_expr_sum(TCCState *s1, ExprValue *pe){ int op; ExprValue e2; asm_expr_logic(s1, pe); for(;;) { op = tok; if (op != '+' && op != '-') break; next(); asm_expr_logic(s1, &e2); if (op == '+') { if (pe->sym != NULL && e2.sym != NULL) goto cannot_relocate; pe->v += e2.v; if (pe->sym == NULL && e2.sym != NULL) pe->sym = e2.sym; } else { pe->v -= e2.v; /* NOTE: we are less powerful than gas in that case because we store only one symbol in the expression */ if (!pe->sym && !e2.sym) { /* OK */ } else if (pe->sym && !e2.sym) { /* OK */ } else if (pe->sym && e2.sym) { if (pe->sym == e2.sym) { /* OK */ } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) { /* we also accept defined symbols in the same section */ pe->v += (long)pe->sym->next - (long)e2.sym->next; } else { goto cannot_relocate; } pe->sym = NULL; /* same symbols can be substracted to NULL */ } else { cannot_relocate: error("invalid operation with label"); } } }}static void asm_expr(TCCState *s1, ExprValue *pe){ asm_expr_sum(s1, pe);}static int asm_int_expr(TCCState *s1){ ExprValue e; asm_expr(s1, &e); if (e.sym) expect("constant"); return e.v;}/* NOTE: the same name space as C labels is used to avoid using too much memory when storing labels in TokenStrings */static void asm_new_label(TCCState *s1, int label, int is_local){ Sym *sym; sym = label_find(label); if (sym) { if (sym->r) { /* the label is already defined */ if (!is_local) { error("assembler label '%s' already defined", get_tok_str(label, NULL)); } else { /* redefinition of local labels is possible */ goto new_label; } } } else { new_label: sym = label_push(&s1->asm_labels, label, 0); sym->type.t = VT_STATIC | VT_VOID; } sym->r = cur_text_section->sh_num; sym->next = (void *)ind;}static void asm_free_labels(TCCState *st){ Sym *s, *s1; for(s = st->asm_labels; s != NULL; s = s1) { s1 = s->prev; /* define symbol value in object file */ if (s->r) { put_extern_sym(s, st->sections[s->r], (long)s->next, 0); } /* remove label */ table_ident[s->v - TOK_IDENT]->sym_label = NULL; tcc_free(s); } st->asm_labels = NULL;}static void use_section(TCCState *s1, const char *name){ Section *sec; sec = find_section(s1, name); cur_text_section->data_offset = ind; cur_text_section = sec; ind = cur_text_section->data_offset;}static void asm_parse_directive(TCCState *s1){ int n, offset, v, size, tok1; Section *sec; uint8_t *ptr; /* assembler directive */ next(); sec = cur_text_section; switch(tok) { case TOK_ASM_align: case TOK_ASM_skip: case TOK_ASM_space: tok1 = tok; next(); n = asm_int_expr(s1); if (tok1 == TOK_ASM_align) { if (n < 0 || (n & (n-1)) != 0) error("alignment must be a positive power of two"); offset = (ind + n - 1) & -n; size = offset - ind; } else { size = n; } v = 0; if (tok == ',') { next(); v = asm_int_expr(s1); } if (sec->sh_type != SHT_NOBITS) { sec->data_offset = ind; ptr = section_ptr_add(sec, size); memset(ptr, v, size); } ind += size; break; case TOK_ASM_byte: size = 1; goto asm_data; case TOK_ASM_word: case TOK_SHORT: size = 2; goto asm_data; case TOK_LONG: case TOK_INT: size = 4; asm_data: next(); for(;;) { ExprValue e; asm_expr(s1, &e); if (sec->sh_type != SHT_NOBITS) { if (size == 4) { gen_expr32(&e); } else { if (e.sym) expect("constant"); if (size == 1) g(e.v); else gen_le16(e.v); } } else { ind += size; } if (tok != ',') break; next(); } break; case TOK_ASM_globl: { Sym *sym; next(); sym = label_find(tok); if (!sym) { sym = label_push(&s1->asm_labels, tok, 0); sym->type.t = VT_VOID; } sym->type.t &= ~VT_STATIC; next(); } break; case TOK_ASM_string: { const uint8_t *p; int i; next(); if (tok != TOK_STR) expect("string constant"); p = tokc.cstr->data; for(i = 0; i < tokc.cstr->size; i++) g(p[i]); next(); } break; case TOK_ASM_text: case TOK_ASM_data: case TOK_ASM_bss: { char sname[64]; tok1 = tok; n = 0; next(); if (tok != ';' && tok != TOK_LINEFEED) { n = asm_int_expr(s1); next(); } sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n); use_section(s1, sname); } break; case TOK_SECTION1: { char sname[256]; /* XXX: support more options */ next();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -