diff --git a/tcc.h b/tcc.h index da26298..1efd046 100644 --- a/tcc.h +++ b/tcc.h @@ -383,6 +383,7 @@ 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]; @@ -1584,6 +1585,7 @@ ST_FUNC Sym* get_asm_sym(int name, Sym *csym); ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe); ST_FUNC int asm_int_expr(TCCState *s1); ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess); +ST_FUNC void asm_free_labels(TCCState *st); /* ------------ i386-asm.c ------------ */ ST_FUNC void gen_expr32(ExprValue *pe); #ifdef TCC_TARGET_X86_64 diff --git a/tccasm.c b/tccasm.c index 848f976..7761c39 100644 --- a/tccasm.c +++ b/tccasm.c @@ -31,10 +31,27 @@ ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n) return ts->tok; } -ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe); static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global); 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; +} + +static Sym *asm_label_push(Sym **ptop, int v) +{ + 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; +} + /* 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 @@ -49,10 +66,10 @@ static Sym sym_dot; we look up NAME in the C symbol table and use that. */ ST_FUNC Sym* get_asm_sym(int name, Sym *csym) { - Sym *sym = label_find(name); + Sym *sym = asm_label_find(name); if (!sym) { - sym = label_push(&tcc_state->asm_labels, name, 0); - sym->type.t = VT_VOID | VT_STATIC | VT_EXTERN; + 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 @@ -75,6 +92,7 @@ ST_FUNC Sym* get_asm_sym(int name, Sym *csym) sym->type.t = VT_VOID | (csym->type.t & VT_STATIC); /* Mark that this asm symbol doesn't need to be fed back. */ sym->a.dllimport = 1; + sym->a.dllexport = !(csym->type.t & VT_STATIC); } } return sym; @@ -97,7 +115,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) if (*p == 'b' || *p == 'f') { /* backward or forward label */ label = asm_get_local_label_name(s1, n); - sym = label_find(label); + sym = asm_label_find(label); if (*p == 'b') { /* backward : find the last corresponding defined label */ if (sym && sym->r == 0) @@ -108,7 +126,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) /* 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 = asm_label_push(&s1->asm_labels, label); sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN; } } @@ -355,14 +373,12 @@ ST_FUNC int asm_int_expr(TCCState *s1) 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 Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value) { Sym *sym; - sym = label_find(label); + sym = asm_label_find(label); if (sym) { /* A VT_EXTERN symbol, even if it has a section is considered overridable. This is how we "define" .set targets. Real @@ -379,11 +395,11 @@ static Sym* asm_new_label1(TCCState *s1, int label, int is_local, } } else { new_label: - sym = label_push(&s1->asm_labels, label, 0); + 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_STATIC | VT_VOID | VT_EXTERN; + sym->type.t = VT_VOID | (is_local ? VT_STATIC : 0) | VT_EXTERN; } sym->r = sh_num; sym->jnext = value; @@ -427,28 +443,53 @@ static void patch_binding(Sym *sym) ELFW(ST_TYPE)(esym->st_info)); } -static void asm_free_labels(TCCState *st) +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; s1 = s->prev; - /* define symbol value in object file */ + /* define symbol value in object file and care for updating + the C and asm symbols */ s->type.t &= ~VT_EXTERN; if (!s->a.dllimport) { + Sym *csym = sym_find(s->v); + ElfW(Sym) *esym = NULL; + if (csym) { + s->a.dllexport |= !(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.dllexport) + s->type.t |= VT_STATIC; if (s->r) { if (s->r == SHN_ABS) sec = SECTION_ABS; else sec = st->sections[s->r]; - put_extern_sym2(s, sec, s->jnext, 0, 0); + /* !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->type.t &= ~VT_STATIC, s->a.dllexport = 1; + patch_binding(s); } - patch_binding(s); /* remove label */ - table_ident[s->v - TOK_IDENT]->sym_label = NULL; + table_ident[s->v - TOK_IDENT]->sym_asm_label = NULL; sym_free(s); } st->asm_labels = NULL; @@ -709,7 +750,7 @@ static void asm_parse_directive(TCCState *s1, int global) next(); sym = get_asm_sym(tok, NULL); if (tok1 != TOK_ASMDIR_hidden) - sym->type.t &= ~VT_STATIC; + sym->type.t &= ~VT_STATIC, sym->a.dllexport = 1; if (tok1 == TOK_ASMDIR_weak) sym->a.weak = 1; else if (tok1 == TOK_ASMDIR_hidden) @@ -804,7 +845,7 @@ static void asm_parse_directive(TCCState *s1, int global) Sym *sym; next(); - sym = label_find(tok); + sym = asm_label_find(tok); if (!sym) { tcc_error("label not found: %s", get_tok_str(tok, NULL)); } @@ -980,9 +1021,9 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global) "__asm__("globl vide\nvide: ret");" */ Sym *sym = sym_find(opcode); if (sym && (sym->type.t & VT_EXTERN) && global) { - sym = label_find(opcode); + sym = asm_label_find(opcode); if (!sym) { - sym = label_push(&s1->asm_labels, opcode, 0); + sym = asm_label_push(&s1->asm_labels, opcode); sym->type.t = VT_VOID | VT_EXTERN; } } @@ -1004,7 +1045,6 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global) parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ } - asm_free_labels(s1); parse_flags = saved_parse_flags; return 0; } @@ -1019,6 +1059,7 @@ ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess) ind = cur_text_section->data_offset; nocode_wanted = 0; ret = tcc_assemble_internal(s1, do_preprocess, 1); + asm_free_labels(s1); cur_text_section->data_offset = ind; tcc_debug_end(s1); return ret; diff --git a/tccgen.c b/tccgen.c index 7d554b5..a86103c 100644 --- a/tccgen.c +++ b/tccgen.c @@ -275,6 +275,11 @@ ST_FUNC int tccgen_compile(TCCState *s1) decl(VT_CONST); gen_inline_functions(s1); check_vstack(); + +#ifdef CONFIG_TCC_ASM + asm_free_labels(s1); +#endif + /* end of translation unit info */ tcc_debug_end(s1); return 0; diff --git a/tccpp.c b/tccpp.c index 76f9e42..d8e7f53 100644 --- a/tccpp.c +++ b/tccpp.c @@ -432,6 +432,7 @@ 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); diff --git a/tests/Makefile b/tests/Makefile index 6a61717..61b585e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -16,6 +16,7 @@ TESTS = \ memtest \ dlltest \ abitest \ + asm-c-connect-test \ vla_test-run \ cross-test \ tests2-dir \ @@ -44,7 +45,7 @@ ifeq ($(CONFIG_arm_eabi),yes) TESTS := $(filter-out test3,$(TESTS)) endif ifeq (,$(filter i386 x86_64,$(ARCH))) - TESTS := $(filter-out dlltest,$(TESTS)) + TESTS := $(filter-out dlltest asm-c-connect-test,$(TESTS)) endif ifndef CONFIG_cross TESTS := $(filter-out cross-%,$(TESTS)) @@ -231,6 +232,13 @@ vla_test-run: vla_test$(EXESUF) @echo ------------ $@ ------------ ./vla_test$(EXESUF) +asm-c-connect$(EXESUF): asm-c-connect-1.c asm-c-connect-2.c + $(TCC) -o $@ $^ + +asm-c-connect-test: asm-c-connect$(EXESUF) + @echo ------------ $@ ------------ + ./asm-c-connect$(EXESUF) + cross-test : @echo ------------ $@ ------------ $(TOP)/i386-tcc$(EXESUF) $(TCCFLAGS-unx) -c $(TOPSRC)/examples/ex3.c && echo "ok" @@ -264,6 +272,7 @@ cache: tcc_g clean: rm -f *~ *.o *.a *.bin *.i *.ref *.out *.out? *.out?b *.cc *.gcc rm -f *-cc *-gcc *-tcc *.exe hello libtcc_test vla_test tcctest[1234] + rm -f asm-c-connect$(EXESUF) rm -f ex? tcc_g weaktest.*.txt *.def @$(MAKE) -C tests2 $@ @$(MAKE) -C pp $@ diff --git a/tests/asm-c-connect-1.c b/tests/asm-c-connect-1.c new file mode 100644 index 0000000..3f7b010 --- /dev/null +++ b/tests/asm-c-connect-1.c @@ -0,0 +1,31 @@ +#include + +#if defined _WIN32 && !defined __TINYC__ +# define U "_" +#else +# define U +#endif + +const char str[] = "x1\n"; +#ifdef __x86_64__ +asm(U"x1: push %rbp; mov $"U"str, %rdi; call "U"printf; pop %rbp; ret"); +#elif defined (__i386__) +asm(U"x1: push $"U"str; call "U"printf; pop %eax; ret"); +#endif + +int main(int argc, char *argv[]) +{ + asm("call "U"x1"); + asm("call "U"x2"); + asm("call "U"x3"); + return 0; +} + +static +int x2(void) +{ + printf("x2\n"); + return 2; +} + +extern int x3(void); diff --git a/tests/asm-c-connect-2.c b/tests/asm-c-connect-2.c new file mode 100644 index 0000000..3a8cff2 --- /dev/null +++ b/tests/asm-c-connect-2.c @@ -0,0 +1,7 @@ +#include + +int x3(void) +{ + printf("x3\n"); + return 3; +}