https://gcc.gnu.org/bugzilla/show_bug.cgi?id=34165
--- Comment #2 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The master branch has been updated by Matt Turner <[email protected]>: https://gcc.gnu.org/g:2104f1a937eb8c7a701fb7338bee9bfb5f72f902 commit r17-6145-g2104f1a937eb8c7a701fb7338bee9bfb5f72f902 Author: Matt Turner <[email protected]> Date: Wed Oct 28 11:55:37 2026 -0400 c-family: Recompute TLS model of weak variables [PR c/125418] Since r16-3673 (C) and r16-3637 (C++) the front ends upgrade a TLS variable's access model to decl_default_tls_model as soon as its declaration has been processed. If the variable becomes weak only afterwards, the model is chosen while the symbol still appears to bind locally. A hidden extern TLS variable that is then made weak keeps a model that assumes it is defined in the same module, which is wrong for an undefined weak symbol: with local-exec the linker has no TLS segment offset to use. This breaks static links of glibc on ia64, and on alpha when the linker lacks the fix for PR ld/34165. glibc declares its _nl_current_LC_* variables as extern __thread struct __locale_data *const *_nl_current_LC_CTYPE attribute_hidden attribute_tls_model_ie; #pragma weak _nl_current_LC_CTYPE There are two ways the variable becomes weak too late. #pragma weak is applied after the declaration has been processed. And a redeclaration has its model chosen on its own, before duplicate_decls merges it with an earlier weak declaration and merge_weak makes it weak, and that model then replaces the earlier declaration's. The second happens with __attribute__((weak)) on the earlier declaration as well as with #pragma weak. Add weak_decl_tls_model, which recomputes the default model of a weak TLS variable and lowers the model if the new default is weaker. An explicit tls_model attribute remains a lower bound, as it is when the declaration is first processed, so the example above ends up initial-exec rather than local-exec, and an explicit local-exec is left alone. Call it from apply_pragma_weak, and from duplicate_decls in both front ends once a merged variable is weak. To read the attribute, split the string matching out of handle_tls_model_attribute, and make that function drop an attribute whose argument is not a string once it has diagnosed it, so a tls_model attribute left on a declaration always has a string argument. apply_pragma_weak recomputes nothing for "#pragma weak sym = target". That makes the symbol an alias defined in this translation unit, so it binds no less locally than before, but decl_default_tls_model cannot see that yet and would treat it as weak undefined. By the time a redeclaration is merged it can, so the alias keeps its model. The test for this scans the early visibility dump, because the whole-program visibility pass upgrades the model of an attribute-free variable again and would hide the difference. gcc/c-family/ChangeLog: PR c/125418 * c-attribs.cc (parse_tls_model): New function, split out of... (handle_tls_model_attribute): ...here. Use it. Do not add the attribute if its argument is not a string. (weak_decl_tls_model): New function. * c-common.h (parse_tls_model, weak_decl_tls_model): Declare. * c-pragma.cc (apply_pragma_weak): Call weak_decl_tls_model after declaring a symbol weak. gcc/c/ChangeLog: PR c/125418 * c-decl.cc (merge_decls): Call weak_decl_tls_model for a weak thread-local variable. gcc/cp/ChangeLog: PR c/125418 * decl.cc (duplicate_decls): Call weak_decl_tls_model for a weak thread-local variable. gcc/testsuite/ChangeLog: PR c/125418 * c-c++-common/tls-pragma-weak-1.c: New test. * c-c++-common/tls-pragma-weak-2.c: New test. * c-c++-common/tls-pragma-weak-3.c: New test. * c-c++-common/tls-pragma-weak-4.c: New test. * gcc.dg/tls/pragma-weak-alias.c: New test.
