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.
This commit is contained in:
Michael Matz 2016-08-07 05:37:43 +02:00
parent c4edfb4e08
commit 9ae10cad1f
2 changed files with 71 additions and 19 deletions

View File

@ -35,6 +35,45 @@ ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
static int tcc_assemble_internal(TCCState *s1, int do_preprocess); static int tcc_assemble_internal(TCCState *s1, int do_preprocess);
static Sym sym_dot; 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 /* We do not use the C expression parser to handle symbols. Maybe the
C expression parser could be tweaked to do so. */ C expression parser could be tweaked to do so. */
@ -119,12 +158,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe)
default: default:
if (tok >= TOK_IDENT) { if (tok >= TOK_IDENT) {
/* label case : if the label was not found, add one */ /* label case : if the label was not found, add one */
sym = label_find(tok); sym = get_asm_sym(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;
}
if (sym->r == SHN_ABS) { if (sym->r == SHN_ABS) {
/* if absolute symbol, no need to put a symbol value */ /* if absolute symbol, no need to put a symbol value */
pe->v = sym->jnext; 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. */ involving other symbols). LABEL can be overwritten later still. */
static Sym* set_symbol(TCCState *s1, int label) static Sym* set_symbol(TCCState *s1, int label)
{ {
Sym *sym;
long n; long n;
ExprValue e; ExprValue e;
next(); next();
@ -379,7 +412,7 @@ static void asm_free_labels(TCCState *st)
s1 = s->prev; s1 = s->prev;
/* define symbol value in object file */ /* define symbol value in object file */
s->type.t &= ~VT_EXTERN; s->type.t &= ~VT_EXTERN;
if (s->r) { if (s->r && !(s->type.t & VT_IMPORT)) {
if (s->r == SHN_ABS) if (s->r == SHN_ABS)
sec = SECTION_ABS; sec = SECTION_ABS;
else else
@ -649,11 +682,7 @@ static void asm_parse_directive(TCCState *s1)
Sym *sym; Sym *sym;
next(); next();
sym = label_find(tok); sym = get_asm_sym(tok);
if (!sym) {
sym = label_push(&s1->asm_labels, tok, 0);
sym->type.t = VT_VOID | VT_EXTERN;
}
if (tok1 != TOK_ASMDIR_hidden) if (tok1 != TOK_ASMDIR_hidden)
sym->type.t &= ~VT_STATIC; sym->type.t &= ~VT_STATIC;
if (tok1 == TOK_ASMDIR_weak) if (tok1 == TOK_ASMDIR_weak)
@ -772,12 +801,7 @@ static void asm_parse_directive(TCCState *s1)
const char *newtype; const char *newtype;
next(); next();
sym = label_find(tok); sym = get_asm_sym(tok);
if (!sym) {
sym = label_push(&s1->asm_labels, tok, 0);
sym->type.t = VT_VOID | VT_EXTERN;
}
next(); next();
skip(','); skip(',');
if (tok == TOK_STR) { if (tok == TOK_STR) {

View File

@ -2647,6 +2647,24 @@ void other_constraints_test(void)
printf ("oc1: %d\n", ret == (unsigned long)&var); 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; unsigned int set;
void asm_test(void) void asm_test(void)
@ -2655,6 +2673,10 @@ void asm_test(void)
unsigned int val; unsigned int val;
struct struct123 s1; struct struct123 s1;
struct struct1231 s2 = { (unsigned long)&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"); printf("inline asm:\n");
@ -2692,6 +2714,12 @@ void asm_test(void)
printf("set=0x%x\n", set); printf("set=0x%x\n", set);
val = 0x01020304; val = 0x01020304;
printf("swab32(0x%08x) = 0x%0x\n", val, swab32(val)); 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; return;
label1: label1:
goto label2; goto label2;