1318 lines · 36.0 KB · C
RawDownload
/* *  GAS like assembler for TCC *  *  Copyright (c) 2001-2004 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 */ #define USING_GLOBALS#include "tcc.h"#ifdef CONFIG_TCC_ASM static Section *last_text_section; /* to handle .previous asm directive */ ST_FUNC 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;} static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);static Sym* asm_new_label(TCCState *s1, int label, int is_local);static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value); /* If a C name has an _ prepended then only asm labels that start   with _ are representable in C, by removing the first _.  ASM names   without _ at the beginning don't correspond to C names, but we use   the global C symbol table to track ASM names as well, so we need to   transform those into ones that don't conflict with a C name,   so prepend a '.' for them, but force the ELF asm name to be set.  */static int asm2cname(int v, int *addeddot){    const char *name;    *addeddot = 0;    if (!tcc_state->leading_underscore)      return v;    name = get_tok_str(v, NULL);    if (!name)      return v;    if (name[0] == '_') {        v = tok_alloc(name + 1, strlen(name) - 1)->tok;    } else if (!strchr(name, '.')) {        int n = strlen(name) + 2;        char newname[256];        snprintf(newname, sizeof newname, ".%s", name);        v = tok_alloc(newname, n - 1)->tok;        *addeddot = 1;    }    return v;} static Sym *asm_label_find(int v){    Sym *sym;    int addeddot;    v = asm2cname(v, &addeddot);    sym = sym_find(v);    while (sym && sym->sym_scope && !(sym->type.t & VT_STATIC))        sym = sym->prev_tok;    return sym;} static Sym *asm_label_push(int v){    int addeddot, v2 = asm2cname(v, &addeddot);    /* We always add VT_EXTERN, for sym definition that's tentative       (for .set, removed for real defs), for mere references it's correct       as is.  */    Sym *sym = global_identifier_push(v2, VT_ASM | VT_EXTERN | VT_STATIC, 0);    if (addeddot)        sym->asm_label = v;    return sym;} /* Return a symbol we can use inside the assembler, having name NAME.   Symbols from asm and C source share a namespace.  If we generate   an asm symbol it's also a (file-global) C symbol, but it's   either not accessible by name (like "L.123"), or its type information   is such that it's not usable without a proper C declaration.    Sometimes we need symbols accessible by name from asm, which   are anonymous in C, in this case CSYM can be used to transfer   all information from that symbol to the (possibly newly created)   asm symbol.  */ST_FUNC Sym* get_asm_sym(int name, Sym *csym){    Sym *sym = asm_label_find(name);    if (!sym) {	sym = asm_label_push(name);	if (csym)	  sym->c = csym->c;    }    return sym;} static Sym* asm_section_sym(TCCState *s1, Section *sec){    char buf[100];    int label = tok_alloc(buf,        snprintf(buf, sizeof buf, "L.%s", sec->name)        )->tok;    Sym *sym = asm_label_find(label);    return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);} /* 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, label;    uint64_t n;    const char *p;     switch(tok) {    case TOK_PPNUM:        p = tokc.str.data;        n = strtoull(p, (char **)&p, 0);        if (*p == 'b' || *p == 'f') {            /* backward or forward label */            label = asm_get_local_label_name(s1, n);            sym = asm_label_find(label);            if (*p == 'b') {                /* backward : find the last corresponding defined label */                if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))                    sym = sym->prev_tok;                if (!sym)                    tcc_error("local label '%d' not found backward", (int)n);            } else {                /* forward */                if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {                    /* if the last label is defined, then define a new one */		    sym = asm_label_push(label);                }            }	    pe->v = 0;	    pe->sym = sym;	    pe->pcrel = 0;        } else if (*p == '\0') {            pe->v = n;            pe->sym = NULL;	    pe->pcrel = 0;        } else {            tcc_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)            tcc_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;	pe->pcrel = 0;	next();	break;    case '(':        next();        asm_expr(s1, pe);        skip(')');        break;    case '.':        pe->v = ind;        pe->sym = asm_section_sym(s1, cur_text_section);        pe->pcrel = 0;        next();        break;    default:        if (tok >= TOK_IDENT) {	    ElfSym *esym;            /* label case : if the label was not found, add one */	    sym = get_asm_sym(tok, NULL);	    esym = elfsym(sym);            if (esym && esym->st_shndx == SHN_ABS) {                /* if absolute symbol, no need to put a symbol value */                pe->v = esym->st_value;                pe->sym = NULL;		pe->pcrel = 0;            } else {                pe->v = 0;                pe->sym = sym;		pe->pcrel = 0;            }            next();        } else {            tcc_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)            tcc_error("invalid operation with label");        switch(op) {        case '*':            pe->v *= e2.v;            break;        case '/':              if (e2.v == 0) {            div_error:                tcc_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)            tcc_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 (!e2.sym) {		/* OK */	    } else if (pe->sym == e2.sym) { 		/* OK */		pe->sym = NULL; /* same symbols can be subtracted to NULL */	    } else {		ElfSym *esym1, *esym2;		esym1 = elfsym(pe->sym);		esym2 = elfsym(e2.sym);		if (esym1 && esym1->st_shndx == esym2->st_shndx		    && esym1->st_shndx != SHN_UNDEF) {		    /* we also accept defined symbols in the same section */		    pe->v += esym1->st_value - esym2->st_value;		    pe->sym = NULL;		} else if (esym2->st_shndx == cur_text_section->sh_num) {		    /* When subtracting a defined symbol in current section		       this actually makes the value PC-relative.  */		    pe->v -= esym2->st_value - ind - 4;		    pe->pcrel = 1;		    e2.sym = NULL;		} else {cannot_relocate:		    tcc_error("invalid operation with label");		}	    }        }    }} static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe){    int op;    ExprValue e2;     asm_expr_sum(s1, pe);    for(;;) {        op = tok;	if (op != TOK_EQ && op != TOK_NE	    && (op > TOK_GT || op < TOK_ULE))            break;        next();        asm_expr_sum(s1, &e2);        if (pe->sym || e2.sym)            tcc_error("invalid operation with label");        switch(op) {	case TOK_EQ:	    pe->v = pe->v == e2.v;	    break;	case TOK_NE:	    pe->v = pe->v != e2.v;	    break;	case TOK_LT:	    pe->v = (int64_t)pe->v < (int64_t)e2.v;	    break;	case TOK_GE:	    pe->v = (int64_t)pe->v >= (int64_t)e2.v;	    break;	case TOK_LE:	    pe->v = (int64_t)pe->v <= (int64_t)e2.v;	    break;	case TOK_GT:	    pe->v = (int64_t)pe->v > (int64_t)e2.v;	    break;        default:            break;        }	/* GAS compare results are -1/0 not 1/0.  */	pe->v = -(int64_t)pe->v;    }} ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe){    asm_expr_cmp(s1, pe);} ST_FUNC int asm_int_expr(TCCState *s1){    ExprValue e;    asm_expr(s1, &e);    if (e.sym)        expect("constant");    return e.v;} static Sym* asm_new_label1(TCCState *s1, int label, int is_local,                           int sh_num, int value){    Sym *sym;    ElfSym *esym;     sym = asm_label_find(label);    if (sym) {	esym = elfsym(sym);	/* A VT_EXTERN symbol, even if it has a section is considered	   overridable.  This is how we "define" .set targets.  Real	   definitions won't have VT_EXTERN set.  */        if (esym && esym->st_shndx != SHN_UNDEF) {            /* the label is already defined */            if (IS_ASM_SYM(sym)                && (is_local == 1 || (sym->type.t & VT_EXTERN)))                goto new_label;            if (!(sym->type.t & VT_EXTERN))                tcc_error("assembler label '%s' already defined",                          get_tok_str(label, NULL));        }    } else {    new_label:        sym = asm_label_push(label);    }    if (!sym->c)      put_extern_sym2(sym, SHN_UNDEF, 0, 0, 1);    esym = elfsym(sym);    esym->st_shndx = sh_num;    esym->st_value = value;    if (is_local != 2)        sym->type.t &= ~VT_EXTERN;    return sym;} static Sym* asm_new_label(TCCState *s1, int label, int is_local){    return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);} /* Set the value of LABEL to that of some expression (possibly   involving other symbols).  LABEL can be overwritten later still.  */static Sym* set_symbol(TCCState *s1, int label){    long n;    ExprValue e;    Sym *sym;    ElfSym *esym;    next();    asm_expr(s1, &e);    n = e.v;    esym = elfsym(e.sym);    if (esym)	n += esym->st_value;    sym = asm_new_label1(s1, label, 2, esym ? esym->st_shndx : SHN_ABS, n);    elfsym(sym)->st_other |= ST_ASM_SET;    return sym;} static void use_section1(TCCState *s1, Section *sec){    cur_text_section->data_offset = ind;    cur_text_section = sec;    ind = cur_text_section->data_offset;} static void use_section(TCCState *s1, const char *name){    Section *sec;    sec = find_section(s1, name);    use_section1(s1, sec);} static void push_section(TCCState *s1, const char *name){    Section *sec = find_section(s1, name);    sec->prev = cur_text_section;    use_section1(s1, sec);} static void pop_section(TCCState *s1){    Section *prev = cur_text_section->prev;    if (!prev)        tcc_error(".popsection without .pushsection");    cur_text_section->prev = NULL;    use_section1(s1, prev);} static void asm_parse_directive(TCCState *s1, int global){    int n, offset, v, size, tok1;    Section *sec;    uint8_t *ptr;     /* assembler directive */    sec = cur_text_section;    switch(tok) {    case TOK_ASMDIR_align:    case TOK_ASMDIR_balign:

Lines 1–500 of 1318.