From 352e1d0fc4fba2f121c63adb36788d5a3e542176 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Wed, 30 Sep 2020 17:46:01 +0200 Subject: [PATCH] Reinstate attribute alias handling commit 2a0167a merged alias and asm symbol renaming, but broke semantics of aliases, see testcase. Basically the difference between the two is that an asm rename doesn't generate a new symbol, i.e. with int foo __asm__("bar"); all source reference to 'foo' will be to 'bar', nothing of the name 'foo' will remain in the object file, and for instance reference to 'foo' from other compilation units won't be resolved to this one. Aliases OTOH create an additional symbol. With: void target (void) { return; } void afunc (void) __attribute__((alias("target"))); reference to 'afunc' will remain 'afunc' in the object file. It will generate two symbols, 'afunc' and 'target' referring to the same entity. This difference matters if other compilation units make references to 'afunc'. A side requirement of this is that for alias to work that the target symbol needs to be defined in the same unit. For TCC we even require a stricter variant: it must be defined before the alias is created. Now, with this I merely re-instated the old flow of events before above commit. It didn't seem useful anymore to place both names in the asm_label member of attributes, and the asm_label member of Sym now again only needs the hold the __asm__ rename. It also follows that tcc_predefs.h can't make use of attribute alias to e.g. map __builtin_memcpy to __bound_memcpy (simply because the latter isn't defined in all units), but rather must use __asm__ renaming, which in turn means that the underscore handling needs to be done by hand. --- tcc.h | 1 + tccgen.c | 26 ++++++++++++++++++++------ tccpp.c | 13 +++++++++---- tests/tests2/120+_alias.c | 15 +++++++++++++++ tests/tests2/120_alias.c | 25 +++++++++++++++++++++++++ tests/tests2/120_alias.expect | 5 +++++ tests/tests2/Makefile | 5 +++++ 7 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 tests/tests2/120+_alias.c create mode 100644 tests/tests2/120_alias.c create mode 100644 tests/tests2/120_alias.expect diff --git a/tcc.h b/tcc.h index 494460b2..ee919efc 100644 --- a/tcc.h +++ b/tcc.h @@ -643,6 +643,7 @@ typedef struct AttributeDef { struct FuncAttr f; struct Section *section; Sym *cleanup_func; + int alias_target; /* token */ int asm_label; /* associated asm label */ char attr_mode; /* __attribute__((__mode__(...))) */ } AttributeDef; diff --git a/tccgen.c b/tccgen.c index 5cecc19f..efdb8117 100644 --- a/tccgen.c +++ b/tccgen.c @@ -939,10 +939,8 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num, #endif if (sym->asm_label) { - name = get_tok_str(sym->asm_label & ~SYM_FIELD, NULL); - /* with SYM_FIELD it was __attribute__((alias("..."))) actually */ - if (!(sym->asm_label & SYM_FIELD)) - can_add_underscore = 0; + name = get_tok_str(sym->asm_label, NULL); + can_add_underscore = 0; } if (tcc_state->leading_underscore && can_add_underscore) { @@ -1524,6 +1522,8 @@ static void merge_attr(AttributeDef *ad, AttributeDef *ad1) if (ad1->section) ad->section = ad1->section; + if (ad1->alias_target) + ad->alias_target = ad1->alias_target; if (ad1->asm_label) ad->asm_label = ad1->asm_label; if (ad1->attr_mode) @@ -4147,8 +4147,8 @@ redo: case TOK_ALIAS2: skip('('); parse_mult_str(&astr, "alias(\"target\")"); - ad->asm_label = /* save string as token, for later */ - tok_alloc((char*)astr.data, astr.size-1)->tok | SYM_FIELD; + ad->alias_target = /* save string as token, for later */ + tok_alloc((char*)astr.data, astr.size-1)->tok; skip(')'); cstr_free(&astr); break; @@ -8459,6 +8459,20 @@ found: /* external variable or function */ type.t |= VT_EXTERN; sym = external_sym(v, &type, r, &ad); + if (ad.alias_target) { + /* Aliases need to be emitted when their target + symbol is emitted, even if perhaps unreferenced. + We only support the case where the base is + already defined, otherwise we would need + deferring to emit the aliases until the end of + the compile unit. */ + Sym *alias_target = sym_find(ad.alias_target); + ElfSym *esym = elfsym(alias_target); + if (!esym) + tcc_error("unsupported forward __alias__ attribute"); + put_extern_sym2(sym, esym->st_shndx, + esym->st_value, esym->st_size, 0); + } } else { if (type.t & VT_STATIC) r |= VT_CONST; diff --git a/tccpp.c b/tccpp.c index daf5d92e..74ce9954 100644 --- a/tccpp.c +++ b/tccpp.c @@ -3673,16 +3673,21 @@ static void tcc_predefs(CString *cstr) "#ifndef __builtin_va_copy\n" "#define __builtin_va_copy(dest,src) (dest)=(src)\n" "#endif\n" + "#ifdef __leading_underscore\n" + "#define RENAME(X) __asm__(\"_\"X)\n" + "#else\n" + "#define RENAME(X) __asm__(X)\n" + "#endif\n" /* TCC BBUILTIN AND BOUNDS ALIASES */ "#ifdef __BOUNDS_CHECKING_ON\n" - "#define __BUILTINBC(ret,name,params) ret __builtin_##name params __attribute__((alias(\"__bound_\"#name)));\n" - "#define __BOUND(ret,name,params) ret name params __attribute__((alias(\"__bound_\"#name)));\n" + "#define __BUILTINBC(ret,name,params) ret __builtin_##name params RENAME(\"__bound_\"#name);\n" + "#define __BOUND(ret,name,params) ret name params RENAME(\"__bound_\"#name);\n" "#else\n" - "#define __BUILTINBC(ret,name,params) ret __builtin_##name params __attribute__((alias(#name)));\n" + "#define __BUILTINBC(ret,name,params) ret __builtin_##name params RENAME(#name);\n" "#define __BOUND(ret,name,params)\n" "#endif\n" "#define __BOTH(ret,name,params) __BUILTINBC(ret,name,params)__BOUND(ret,name,params)\n" - "#define __BUILTIN(ret,name,params) ret __builtin_##name params __attribute__((alias(\"\"#name)));\n" + "#define __BUILTIN(ret,name,params) ret __builtin_##name params RENAME(#name);\n" "__BOTH(void*,memcpy,(void*,const void*,__SIZE_TYPE__))\n" "__BOTH(void*,memmove,(void*,const void*,__SIZE_TYPE__))\n" "__BOTH(void*,memset,(void*,int,__SIZE_TYPE__))\n" diff --git a/tests/tests2/120+_alias.c b/tests/tests2/120+_alias.c new file mode 100644 index 00000000..8f29d8d9 --- /dev/null +++ b/tests/tests2/120+_alias.c @@ -0,0 +1,15 @@ +extern int printf (const char *, ...); +extern void target(void); +extern void alias_for_target(void); +extern void asm_for_target(void); + +void inunit2(void); + +void inunit2(void) +{ + target(); + alias_for_target(); + /* This symbol is not supposed to be available in this unit: + asm_for_target(); + */ +} diff --git a/tests/tests2/120_alias.c b/tests/tests2/120_alias.c new file mode 100644 index 00000000..c9eb2985 --- /dev/null +++ b/tests/tests2/120_alias.c @@ -0,0 +1,25 @@ +/* Check semantics of various constructs to generate renamed symbols. */ +extern int printf (const char *, ...); +void target(void); +void target(void) { + printf("in target function\n"); +} + +void alias_for_target(void) __attribute__((alias("target"))); +void asm_for_target(void) __asm__("target"); + +/* This is not supposed to compile, alias targets must be defined in the + same unit. In TCC they even must be defined before the reference +void alias_for_undef(void) __attribute__((alias("undefined"))); +*/ + +extern void inunit2(void); + +int main(void) +{ + target(); + alias_for_target(); + asm_for_target(); + inunit2(); + return 0; +} diff --git a/tests/tests2/120_alias.expect b/tests/tests2/120_alias.expect new file mode 100644 index 00000000..021e3f06 --- /dev/null +++ b/tests/tests2/120_alias.expect @@ -0,0 +1,5 @@ +in target function +in target function +in target function +in target function +in target function diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index f7f47910..2403a121 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -74,6 +74,11 @@ GEN-ALWAYS = 104_inline.test : FLAGS += $(subst 104,104+,$1) 104_inline.test : GEN = $(GEN-TCC) +# this test needs two files, and we want to invoke the linker +120_alias.test : FLAGS += $(subst 120,120+,$1) +120_alias.test : GEN = $(GEN-TCC) +120_alias.test : NORUN = true + # this test needs pthread 106_pthread.test: FLAGS += -pthread 106_pthread.test: NORUN = true