Browse Source

tccasm: Unify C and asm symbol table

This makes the asm symbols use the same members as the C symbols
for global decls, e.g. using the ELF symbol to hold offset and
section.  That allows us to use only one symbol table for C and
asm symbols and to get rid of hacks to synch between them.

We still need some special handling for symbols that come purely
from asm sources.
Michael Matz 1 year ago
parent
commit
9e0d23cc47
8 changed files with 165 additions and 189 deletions
  1. 7 8
      i386-asm.c
  2. 7 4
      tcc.h
  3. 90 152
      tccasm.c
  4. 9 3
      tccelf.c
  5. 42 21
      tccgen.c
  6. 0 1
      tccpp.c
  7. 7 0
      tests/asm-c-connect-1.c
  8. 3 0
      tests/asm-c-connect-2.c

+ 7 - 8
i386-asm.c

@@ -500,12 +500,13 @@ ST_FUNC void gen_expr64(ExprValue *pe)
 static void gen_disp32(ExprValue *pe)
 {
     Sym *sym = pe->sym;
-    if (sym && sym->r == cur_text_section->sh_num) {
+    ElfSym *esym = elfsym(sym);
+    if (esym && esym->st_shndx == cur_text_section->sh_num) {
         /* same section: we can output an absolute value. Note
            that the TCC compiler behaves differently here because
            it always outputs a relocation to ease (future) code
            elimination in the linker */
-        gen_le32(pe->v + sym->jnext - ind - 4);
+        gen_le32(pe->v + esym->st_value - ind - 4);
     } else {
         if (sym && sym->type.t == VT_VOID) {
             sym->type.t = VT_FUNC;
@@ -1017,16 +1018,14 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
     if (pa->instr_type & OPC_B)
         v += s >= 1;
     if (nb_ops == 1 && pa->op_type[0] == OPT_DISP8) {
-        Sym *sym;
+	ElfSym *esym;
         int jmp_disp;
 
         /* see if we can really generate the jump with a byte offset */
-        sym = ops[0].e.sym;
-        if (!sym)
+	esym = elfsym(ops[0].e.sym);
+        if (!esym || esym->st_shndx != cur_text_section->sh_num)
             goto no_short_jump;
-        if (sym->r != cur_text_section->sh_num)
-            goto no_short_jump;
-        jmp_disp = ops[0].e.v + sym->jnext - ind - 2 - (v >= 0xff);
+        jmp_disp = ops[0].e.v + esym->st_value - ind - 2 - (v >= 0xff);
         if (jmp_disp == (int8_t)jmp_disp) {
             /* OK to generate jump */
 	    ops[0].e.sym = 0;

+ 7 - 4
tcc.h

@@ -356,6 +356,7 @@ extern long double strtold (const char *__nptr, char **__endptr);
 #endif
 /* target address type */
 #define addr_t ElfW(Addr)
+#define ElfSym ElfW(Sym)
 
 #if PTR_SIZE == 8 && !defined TCC_TARGET_PE
 # define LONG_SIZE 8
@@ -383,7 +384,6 @@ typedef struct TokenSym {
     struct Sym *sym_label; /* direct pointer to label */
     struct Sym *sym_struct; /* direct pointer to structure */
     struct Sym *sym_identifier; /* direct pointer to identifier */
-    struct Sym *sym_asm_label; /* direct pointer to asm label */
     int tok; /* token number */
     int len;
     char str[1];
@@ -440,9 +440,8 @@ struct SymAttr {
     visibility  : 2,
     dllexport   : 1,
     dllimport   : 1,
-    asmcsym     : 1,
     asmexport   : 1,
-    unused      : 3;
+    unused      : 4;
 };
 
 /* function attributes or temporary attributes for parsing */
@@ -799,6 +798,7 @@ struct TCCState {
     int nb_sym_attrs;
     /* tiny assembler state */
     Sym *asm_labels;
+    ElfSym esym_dot;
 
 #ifdef TCC_TARGET_PE
     /* PE info */
@@ -1147,7 +1147,7 @@ ST_FUNC Sym *sym_push(int v, CType *type, int r, int c);
 ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep);
 ST_INLN Sym *struct_find(int v);
 ST_INLN Sym *sym_find(int v);
-ST_FUNC Sym *global_identifier_push(int v, int t, int c);
+ST_FUNC Sym *global_identifier_push_1(Sym **, int v, int t, int c);
 
 ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen);
 ST_FUNC int tcc_open(TCCState *s1, const char *filename);
@@ -1316,6 +1316,7 @@ ST_INLN int is_float(int t);
 ST_FUNC int ieee_finite(double d);
 ST_FUNC void test_lvalue(void);
 ST_FUNC void vpushi(int v);
+ST_FUNC ElfSym *elfsym(Sym *);
 ST_FUNC Sym *external_global_sym(int v, CType *type, int r);
 ST_FUNC void vset(CType *type, int r, int v);
 ST_FUNC void vswap(void);
@@ -1618,6 +1619,8 @@ PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp);
 # define ST_PE_IMPORT 0x20
 # define ST_PE_STDCALL 0x40
 #endif
+#define ST_ASM_SET 0x04
+
 /* ------------ tccrun.c ----------------- */
 #ifdef TCC_IS_NATIVE
 #ifdef CONFIG_TCC_STATIC

+ 90 - 152
tccasm.c

@@ -36,64 +36,40 @@ static Sym sym_dot;
 
 static Sym *asm_label_find(int v)
 {
-    v -= TOK_IDENT;
-    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
-        return NULL;
-    return table_ident[v]->sym_asm_label;
+    Sym *sym = sym_find(v);
+    while (sym && sym->sym_scope)
+        sym = sym->prev_tok;
+    return sym;
 }
 
-static Sym *asm_label_push(Sym **ptop, int v)
+static Sym *asm_label_push(int v, int t)
 {
-    Sym *s, **ps;
-    s = sym_push2(ptop, v, 0, 0);
-    ps = &table_ident[v - TOK_IDENT]->sym_asm_label;
-    s->prev_tok = *ps;
-    *ps = s;
-    return s;
+    Sym *sym = global_identifier_push_1(&tcc_state->asm_labels, v, t, 0);
+    /* 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->type.t |= VT_VOID | VT_EXTERN;
+    sym->r = VT_CONST | VT_SYM;
+    return sym;
 }
 
 /* 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.
-   
-   If CSYM is non-null we take symbol info from it, otherwise
-   we look up NAME in the C symbol table and use that.  */
+   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(&tcc_state->asm_labels, name);
-	sym->type.t = VT_VOID | VT_EXTERN;
-	if (!csym) {
-	    csym = sym_find(name);
-	    /* 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->sym_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->c = csym->c;
-	    sym->r = esym->st_shndx;
-	    sym->jnext = esym->st_value;
-	    /* XXX can't yet store st_size anywhere.  */
-	    sym->type.t = VT_VOID | (csym->type.t & VT_STATIC);
-	    /* Mark that this asm symbol doesn't need to be fed back.  */
-	    sym->a.asmcsym = 1;
-	    sym->a.asmexport = !(csym->type.t & VT_STATIC);
-	}
+	sym = asm_label_push(name, 0);
+	if (csym)
+	  sym->c = csym->c;
     }
     return sym;
 }
@@ -118,16 +94,15 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe)
             sym = asm_label_find(label);
             if (*p == 'b') {
                 /* backward : find the last corresponding defined label */
-                if (sym && sym->r == 0)
+                if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
                     sym = sym->prev_tok;
                 if (!sym)
                     tcc_error("local label '%d' not found backward", n);
             } else {
                 /* forward */
-                if (!sym || sym->r) {
+                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(&s1->asm_labels, label);
-                    sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
+		    sym = asm_label_push(label, VT_STATIC);
                 }
             }
 	    pe->v = 0;
@@ -175,17 +150,20 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe)
         pe->sym = &sym_dot;
 	pe->pcrel = 0;
         sym_dot.type.t = VT_VOID | VT_STATIC;
-        sym_dot.r = cur_text_section->sh_num;
-        sym_dot.jnext = ind;
+	sym_dot.c = -1;
+        tcc_state->esym_dot.st_shndx = cur_text_section->sh_num;
+        tcc_state->esym_dot.st_value = ind;
         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);
-            if (sym->r == SHN_ABS) {
+	    esym = elfsym(sym);
+            if (esym && esym->st_shndx == SHN_ABS) {
                 /* if absolute symbol, no need to put a symbol value */
-                pe->v = sym->jnext;
+                pe->v = esym->st_value;
                 pe->sym = NULL;
 		pe->pcrel = 0;
             } else {
@@ -299,20 +277,26 @@ static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
 	    } else if (pe->sym == e2.sym) { 
 		/* OK */
 		pe->sym = NULL; /* same symbols can be subtracted to NULL */
-	    } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) {
-		/* we also accept defined symbols in the same section */
-		pe->v += pe->sym->jnext - e2.sym->jnext;
-		pe->sym = NULL;
-	    } else if (e2.sym->r == cur_text_section->sh_num) {
-		/* When subtracting a defined symbol in current section
-		   this actually makes the value PC-relative.  */
-		pe->v -= e2.sym->jnext - ind - 4;
-		pe->pcrel = 1;
-		e2.sym = NULL;
-            } else {
-            cannot_relocate:
-                tcc_error("invalid operation with label");
-            }
+	    } 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");
+		}
+	    }
         }
     }
 }
@@ -377,13 +361,15 @@ 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 (sym->r && !(sym->type.t & VT_EXTERN)) {
+        if (esym && esym->st_shndx != SHN_UNDEF && !(sym->type.t & VT_EXTERN)) {
             /* the label is already defined */
             if (!is_local) {
                 tcc_error("assembler label '%s' already defined", 
@@ -395,14 +381,13 @@ static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
         }
     } else {
     new_label:
-        sym = asm_label_push(&s1->asm_labels, label);
-	/* If we need a symbol to hold a value, mark it as
-	   tentative only (for .set).  If this is for a real label
-	   we'll remove VT_EXTERN.  */
-        sym->type.t = VT_VOID | (is_local ? VT_STATIC : 0) | VT_EXTERN;
+        sym = asm_label_push(label, is_local ? VT_STATIC : 0);
     }
-    sym->r = sh_num;
-    sym->jnext = value;
+    if (!sym->c)
+      put_extern_sym2(sym, NULL, 0, 0, 0);
+    esym = elfsym(sym);
+    esym->st_shndx = sh_num;
+    esym->st_value = value;
     return sym;
 }
 
@@ -417,79 +402,41 @@ static Sym* set_symbol(TCCState *s1, int label)
 {
     long n;
     ExprValue e;
+    Sym *sym;
+    ElfSym *esym;
     next();
     asm_expr(s1, &e);
     n = e.v;
-    if (e.sym)
-	n += e.sym->jnext;
-    return asm_new_label1(s1, label, 0, e.sym ? e.sym->r : SHN_ABS, n);
-}
-
-/* Patch ELF symbol associated with SYM based on the assemblers
-   understanding.  */
-static void patch_binding(Sym *sym)
-{
-    ElfW(Sym) *esym;
-    if (0 == sym->c)
-        return;
-    esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
-    if (sym->a.visibility)
-        esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
-            | sym->a.visibility;
-
-    esym->st_info = ELFW(ST_INFO)(sym->a.weak ? STB_WEAK
-				  : (sym->type.t & VT_STATIC) ? STB_LOCAL
-				  : STB_GLOBAL,
-				  ELFW(ST_TYPE)(esym->st_info));
+    esym = elfsym(e.sym);
+    if (esym)
+	n += esym->st_value;
+    sym = asm_new_label1(s1, label, 0, esym ? esym->st_shndx : SHN_ABS, n);
+    elfsym(sym)->st_other |= ST_ASM_SET;
+    return sym;
 }
 
 ST_FUNC void asm_free_labels(TCCState *st)
 {
     Sym *s, *s1;
-    Section *sec;
 
     for(s = st->asm_labels; s != NULL; s = s1) {
-	int was_ext = s->type.t & VT_EXTERN;
+	ElfSym *esym = elfsym(s);
         s1 = s->prev;
-        /* define symbol value in object file and care for updating
-	   the C and asm symbols */
+        /* Possibly update binding and visibility from asm directives.  */
 	s->type.t &= ~VT_EXTERN;
-	if (!s->a.asmcsym) {
-	    Sym *csym = sym_find(s->v);
-	    ElfW(Sym) *esym = NULL;
-	    if (csym) {
-		s->a.asmexport |= !(csym->type.t & VT_STATIC);
-		if (csym->c) {
-		    esym = &((ElfW(Sym) *)symtab_section->data)[csym->c];
-		    if (s->c) {
-			/* We have generated code and possibly relocs
-			   referencing the symtab entry s->c (the asm
-			   ELF symbol).  If that's undefined but the C
-			   symbol is defined, copy over the info.  */
-			ElfW(Sym) *asm_esym = &((ElfW(Sym) *)symtab_section->data)[s->c];
-			if (asm_esym->st_shndx == SHN_UNDEF) {
-			    *asm_esym = *esym;
-			}
-		    }
-		}
-	    }
-	    if (!s->a.asmexport)
-	      s->type.t |= VT_STATIC;
-	    if (s->r) {
-		if (s->r == SHN_ABS)
-		  sec = SECTION_ABS;
-		else
-		  sec = st->sections[s->r];
-		/* !was_ext so that we run into an multi-def error if
-		   we defined it in C and in asm (non-tentatively) */
-		if (!esym || esym->st_shndx == SHN_UNDEF || !was_ext)
-		    put_extern_sym2(s, sec, s->jnext, 0, 0);
-	    } else /* undefined symbols are global */
-	        s->type.t &= ~VT_STATIC, s->a.asmexport = 1;
-	    patch_binding(s);
+	if (esym) {
+	    if (!s->a.asmexport && esym->st_shndx != SHN_UNDEF)
+	        s->type.t |= VT_STATIC;
+	    if (s->a.visibility)
+	        esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
+		                 | s->a.visibility;
+	    esym->st_info = ELFW(ST_INFO)(s->a.weak ? STB_WEAK
+					  : (s->type.t & VT_STATIC) ? STB_LOCAL
+					  : STB_GLOBAL,
+					  ELFW(ST_TYPE)(esym->st_info));
 	}
         /* remove label */
-        table_ident[s->v - TOK_IDENT]->sym_asm_label = NULL;
+        table_ident[s->v - TOK_IDENT]->sym_identifier = s->prev_tok;
         sym_free(s);
     }
     st->asm_labels = NULL;
@@ -715,13 +662,15 @@ static void asm_parse_directive(TCCState *s1, int global)
         {
             unsigned long n;
 	    ExprValue e;
+	    ElfSym *esym;
             next();
 	    asm_expr(s1, &e);
 	    n = e.v;
-	    if (e.sym) {
-		if (e.sym->r != cur_text_section->sh_num)
+	    esym = elfsym(e.sym);
+	    if (esym) {
+		if (esym->st_shndx != cur_text_section->sh_num)
 		  expect("constant or same-section symbol");
-		n += e.sym->jnext;
+		n += esym->st_value;
 	    }
             if (n < ind)
                 tcc_error("attempt to .org backwards");
@@ -978,7 +927,6 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
     int opcode;
     int saved_parse_flags = parse_flags;
 
-    /* XXX: undefine C labels */
     parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
     if (do_preprocess)
         parse_flags |= PARSE_FLAG_PREPROCESS;
@@ -1017,18 +965,8 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
             opcode = tok;
             next();
             if (tok == ':') {
-                /* handle "extern void vide(void); __asm__("vide: ret");" as
-                "__asm__("globl vide\nvide: ret");" */
-                Sym *sym = sym_find(opcode);
-                if (sym && (sym->type.t & VT_EXTERN) && global) {
-                    sym = asm_label_find(opcode);
-                    if (!sym) {
-                        sym = asm_label_push(&s1->asm_labels, opcode);
-                        sym->type.t = VT_VOID | VT_EXTERN;
-                    }
-                }
                 /* new label */
-                sym = asm_new_label(s1, opcode, 0);
+                Sym *sym = asm_new_label(s1, opcode, 0);
 		sym->type.t &= ~VT_EXTERN;
                 next();
                 goto redo;

+ 9 - 3
tccelf.c

@@ -310,7 +310,8 @@ static void rebuild_hash(Section *s, unsigned int nb_buckets)
 
     sym = (ElfW(Sym) *)s->data + 1;
     for(sym_index = 1; sym_index < nb_syms; sym_index++) {
-        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
+        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL
+	    || sym->st_shndx == SHN_UNDEF) {
             h = elf_hash(strtab + sym->st_name) % nb_buckets;
             *ptr = hash[h];
             hash[h] = sym_index;
@@ -349,8 +350,9 @@ ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size,
         int *ptr, *base;
         ptr = section_ptr_add(hs, sizeof(int));
         base = (int *)hs->data;
-        /* only add global or weak symbols */
-        if (ELFW(ST_BIND)(info) != STB_LOCAL) {
+        /* only add global, weak or undef symbols.  The latter might
+	   become global late (from asm references).  */
+        if (ELFW(ST_BIND)(info) != STB_LOCAL || shndx == SHN_UNDEF) {
             /* add another hashing entry */
             nbuckets = base[0];
             h = elf_hash((unsigned char *) name) % nbuckets;
@@ -486,6 +488,10 @@ ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size,
                 /* data symbol keeps precedence over common/bss */
             } else if (s == tcc_state->dynsymtab_section) {
                 /* we accept that two DLL define the same symbol */
+	    } else if (esym->st_other & ST_ASM_SET) {
+		/* If the existing symbol came from an asm .set
+		   we can override.  */
+		goto do_patch;
             } else {
 #if 0
                 printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",

+ 42 - 21
tccgen.c

@@ -286,14 +286,23 @@ ST_FUNC int tccgen_compile(TCCState *s1)
 }
 
 /* ------------------------------------------------------------------------- */
+ST_FUNC ElfSym *elfsym(Sym *s)
+{
+  if (!s || !s->c)
+    return NULL;
+  if (s->c == -1)
+    return &tcc_state->esym_dot;
+  else
+    return &((ElfSym *)symtab_section->data)[s->c];
+}
+
 /* apply storage attributes to Elf symbol */
 
 static void update_storage(Sym *sym)
 {
-    ElfW(Sym) *esym;
-    if (0 == sym->c)
+    ElfSym *esym = elfsym(sym);
+    if (!esym)
         return;
-    esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
     if (sym->a.visibility)
         esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
             | sym->a.visibility;
@@ -325,7 +334,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, Section *section,
                             int can_add_underscore)
 {
     int sym_type, sym_bind, sh_num, info, other, t;
-    ElfW(Sym) *esym;
+    ElfSym *esym;
     const char *name;
     char buf1[256];
 #ifdef CONFIG_TCC_BCHECK
@@ -402,7 +411,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, Section *section,
         info = ELFW(ST_INFO)(sym_bind, sym_type);
         sym->c = set_elf_sym(symtab_section, value, size, info, other, sh_num, name);
     } else {
-        esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
+        esym = elfsym(sym);
         esym->st_value = value;
         esym->st_size = size;
         esym->st_shndx = sh_num;
@@ -569,23 +578,28 @@ ST_FUNC Sym *sym_push(int v, CType *type, int r, int c)
 }
 
 /* push a global identifier */
-ST_FUNC Sym *global_identifier_push(int v, int t, int c)
+ST_FUNC Sym *global_identifier_push_1(Sym **ptop, int v, int t, int c)
 {
     Sym *s, **ps;
-    s = sym_push2(&global_stack, v, t, c);
+    s = sym_push2(ptop, v, t, c);
     /* don't record anonymous symbol */
     if (v < SYM_FIRST_ANOM) {
         ps = &table_ident[v - TOK_IDENT]->sym_identifier;
         /* modify the top most local identifier, so that
            sym_identifier will point to 's' when popped */
-        while (*ps != NULL)
+        while (*ps != NULL && (*ps)->sym_scope)
             ps = &(*ps)->prev_tok;
-        s->prev_tok = NULL;
+        s->prev_tok = *ps;
         *ps = s;
     }
     return s;
 }
 
+static Sym *global_identifier_push(int v, int t, int c)
+{
+    return global_identifier_push_1(&global_stack, v, t, c);
+}
+
 /* pop symbols until top reaches 'b'.  If KEEP is non-zero don't really
    pop them yet from the list, but do remove them from the token array.  */
 ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep)
@@ -834,9 +848,13 @@ ST_FUNC Sym *external_global_sym(int v, CType *type, int r)
 /* Merge some storage attributes.  */
 static void patch_storage(Sym *sym, AttributeDef *ad, CType *type)
 {
-    if (type && !is_compatible_types(&sym->type, type))
-        tcc_error("incompatible types for redefinition of '%s'",
-            get_tok_str(sym->v, NULL));
+    if (type) {
+	if ((sym->type.t & VT_BTYPE) == VT_VOID) /* from asm */
+	    sym->type = *type;
+	else if (!is_compatible_types(&sym->type, type))
+	    tcc_error("incompatible types for redefinition of '%s'",
+                      get_tok_str(sym->v, NULL));
+    }
 #ifdef TCC_TARGET_PE
     if (sym->a.dllimport != ad->a.dllimport)
         tcc_error("incompatible dll linkage for redefinition of '%s'",
@@ -6426,9 +6444,9 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
 	    (vtop->type.t & VT_BTYPE) != VT_PTR) {
 	    /* These come from compound literals, memcpy stuff over.  */
 	    Section *ssec;
-	    ElfW(Sym) *esym;
+	    ElfSym *esym;
 	    ElfW_Rel *rel;
-	    esym = &((ElfW(Sym) *)symtab_section->data)[vtop->sym->c];
+	    esym = elfsym(vtop->sym);
 	    ssec = tcc_state->sections[esym->st_shndx];
 	    memmove (ptr, ssec->data + esym->st_value, size);
 	    if (ssec->reloc) {
@@ -6895,8 +6913,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
                     /* no init data, we won't add more to the symbol */
                     goto no_alloc;
                 } else if (sym->c) {
-                    ElfW(Sym) *esym;
-                    esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
+                    ElfSym *esym = elfsym(sym);
                     if (esym->st_shndx == data_section->sh_num)
                         tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
                 }
@@ -7030,8 +7047,7 @@ static void gen_function(Sym *sym)
     sym_pop(&local_stack, NULL, 0);
     /* end of function */
     /* patch symbol size */
-    ((ElfW(Sym) *)symtab_section->data)[sym->c].st_size = 
-        ind - func_ind;
+    elfsym(sym)->st_size = ind - func_ind;
     tcc_debug_funcend(tcc_state, ind - func_ind);
     /* It's better to crash than to generate wrong code */
     cur_text_section = NULL;
@@ -7209,6 +7225,11 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
                 sym = sym_find(v);
                 if (sym) {
                     Sym *ref;
+		    /* If type is VT_VOID the symbol was created by tccasm
+		       first, and we see the first reference from C now. */
+		    if ((sym->type.t & VT_BTYPE) == VT_VOID)
+		        sym->type = type;
+
                     if ((sym->type.t & VT_BTYPE) != VT_FUNC)
                         goto func_error1;
 
@@ -7330,12 +7351,12 @@ found:
                         sym = external_sym(v, &type, r, &ad);
                         if (ad.alias_target) {
                             Section tsec;
-                            ElfW(Sym) *esym;
+                            ElfSym *esym;
                             Sym *alias_target;
                             alias_target = sym_find(ad.alias_target);
-                            if (!alias_target || !alias_target->c)
+                            esym = elfsym(alias_target);
+                            if (!esym)
                                 tcc_error("unsupported forward __alias__ attribute");
-                            esym = &((ElfW(Sym) *)symtab_section->data)[alias_target->c];
                             tsec.sh_num = esym->st_shndx;
                             /* Local statics have a scope until now (for
                                warnings), remove it here.  */

+ 0 - 1
tccpp.c

@@ -432,7 +432,6 @@ static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
     ts->sym_label = NULL;
     ts->sym_struct = NULL;
     ts->sym_identifier = NULL;
-    ts->sym_asm_label = NULL;
     ts->len = len;
     ts->hash_next = NULL;
     memcpy(ts->str, str, len);

+ 7 - 0
tests/asm-c-connect-1.c

@@ -14,11 +14,13 @@ static int x1_c(void)
 
 asm(".text;"_"x1: call "_"x1_c; ret");
 
+void callx4(void);
 int main(int argc, char *argv[])
 {
     asm("call "_"x1");
     asm("call "_"x2");
     asm("call "_"x3");
+    callx4();
     return 0;
 }
 
@@ -30,3 +32,8 @@ int x2(void)
 }
 
 extern int x3(void);
+
+void x4(void)
+{
+    printf("x4\n");
+}

+ 3 - 0
tests/asm-c-connect-2.c

@@ -5,3 +5,6 @@ int x3(void)
     printf("x3\n");
     return 3;
 }
+
+void callx4(void);
+__asm__("callx4: call x4; ret");