From 9ae10cad1fecc2be27691de0a37a1ed25abc9639 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Sun, 7 Aug 2016 05:37:43 +0200 Subject: [PATCH] tccasm: Lookup C symbols from ASM blocks It's now possible to use symbols defined in C code to be used from later inline asm blocks. See testcase. --- tccasm.c | 62 ++++++++++++++++++++++++++++++++++--------------- tests/tcctest.c | 28 ++++++++++++++++++++++ 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/tccasm.c b/tccasm.c index 0bf6456..192e485 100644 --- a/tccasm.c +++ b/tccasm.c @@ -35,6 +35,45 @@ ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe); static int tcc_assemble_internal(TCCState *s1, int do_preprocess); static Sym sym_dot; +/* Return a symbol we can use inside the assembler, having name NAME. + The assembler symbol table is different from the C symbol table + (and the Sym members are used differently). But we must be able + to look up file-global C symbols from inside the assembler, e.g. + for global asm blocks to be able to refer to defined C symbols. + + This routine gives back either an existing asm-internal + symbol, or a new one. In the latter case the new asm-internal + symbol is initialized with info from the C symbol table. */ +static Sym* get_asm_sym(int name) +{ + Sym *sym = label_find(name); + if (!sym) { + Sym *csym = sym_find(name); + sym = label_push(&tcc_state->asm_labels, name, 0); + sym->type.t = VT_VOID | VT_EXTERN; + /* We might be called for an asm block from inside a C routine + and so might have local decls on the identifier stack. Search + for the first global one. */ + while (csym && csym->scope) + csym = csym->prev_tok; + /* Now, if we have a defined global symbol copy over + section and offset. */ + if (csym && + ((csym->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST)) && + csym->c) { + ElfW(Sym) *esym; + esym = &((ElfW(Sym) *)symtab_section->data)[csym->c]; + sym->r = esym->st_shndx; + sym->jnext = esym->st_value; + /* XXX can't yet store st_size anywhere. */ + sym->type.t &= ~VT_EXTERN; + /* Mark that this asm symbol doesn't need to be fed back. */ + sym->type.t |= VT_IMPORT; + } + } + return sym; +} + /* We do not use the C expression parser to handle symbols. Maybe the C expression parser could be tweaked to do so. */ @@ -119,12 +158,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) 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 | VT_EXTERN; - } + sym = get_asm_sym(tok); if (sym->r == SHN_ABS) { /* if absolute symbol, no need to put a symbol value */ pe->v = sym->jnext; @@ -359,7 +393,6 @@ static Sym* asm_new_label(TCCState *s1, int label, int is_local) involving other symbols). LABEL can be overwritten later still. */ static Sym* set_symbol(TCCState *s1, int label) { - Sym *sym; long n; ExprValue e; next(); @@ -379,7 +412,7 @@ static void asm_free_labels(TCCState *st) s1 = s->prev; /* define symbol value in object file */ s->type.t &= ~VT_EXTERN; - if (s->r) { + if (s->r && !(s->type.t & VT_IMPORT)) { if (s->r == SHN_ABS) sec = SECTION_ABS; else @@ -649,11 +682,7 @@ static void asm_parse_directive(TCCState *s1) Sym *sym; next(); - sym = label_find(tok); - if (!sym) { - sym = label_push(&s1->asm_labels, tok, 0); - sym->type.t = VT_VOID | VT_EXTERN; - } + sym = get_asm_sym(tok); if (tok1 != TOK_ASMDIR_hidden) sym->type.t &= ~VT_STATIC; if (tok1 == TOK_ASMDIR_weak) @@ -772,12 +801,7 @@ static void asm_parse_directive(TCCState *s1) const char *newtype; next(); - sym = label_find(tok); - if (!sym) { - sym = label_push(&s1->asm_labels, tok, 0); - sym->type.t = VT_VOID | VT_EXTERN; - } - + sym = get_asm_sym(tok); next(); skip(','); if (tok == TOK_STR) { diff --git a/tests/tcctest.c b/tests/tcctest.c index d8c8bcf..30d78b8 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -2647,6 +2647,24 @@ void other_constraints_test(void) printf ("oc1: %d\n", ret == (unsigned long)&var); } +/* Test global asm blocks playing with aliases. */ +void base_func(void) +{ + printf ("asmc: base\n"); +} + +extern void override_func1 (void); +extern void override_func2 (void); + +asm(".weak override_func1\n.set override_func1, base_func"); +asm(".set override_func1, base_func"); +asm(".set override_func2, base_func"); + +void override_func2 (void) +{ + printf ("asmc: override2\n"); +} + unsigned int set; void asm_test(void) @@ -2655,6 +2673,10 @@ void asm_test(void) unsigned int val; struct struct123 s1; struct struct1231 s2 = { (unsigned long)&s1 }; + /* Hide the outer base_func, but check later that the inline + asm block gets the outer one. */ + int base_func = 42; + void override_func3 (void); printf("inline asm:\n"); @@ -2692,6 +2714,12 @@ void asm_test(void) printf("set=0x%x\n", set); val = 0x01020304; printf("swab32(0x%08x) = 0x%0x\n", val, swab32(val)); + override_func1(); + override_func2(); + /* The base_func ref from the following inline asm should find + the global one, not the local decl from this function. */ + asm volatile(".weak override_func3\n.set override_func3, base_func"); + override_func3(); return; label1: goto label2;