commit d59a75252c48f351272b3e61368818584582118b
parent f8164e2795d7e8f74d270efd23968f1dace98db8
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 9 Jun 2026 12:15:42 -0700
cg: report non-local-exec TLS models as unsupported
kit_cg_target_supports_symbol_feature reported initial-exec / local-dynamic
/ general-dynamic as supported on any TLS-capable format (ELF/Mach-O), but
kit emits local-exec only -- so a caller could request a dynamic model and
silently receive local-exec.
Report the truth: local-exec stays format-gated; the three dynamic models
return 0 (no codegen, no __tls_get_addr runtime). The linker enforces the
same contract at link time (link_require_local_tls).
obj_format_supports_symbol_feature is left unchanged: its contract is format
representability (ELF can represent IE relocs), which stays honest; it is now
only consulted for local-exec.
Extend 64_target_feature_queries.toy to assert the three dynamic models
report false on every target.
Diffstat:
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/src/cg/type.c b/src/cg/type.c
@@ -916,10 +916,19 @@ int kit_cg_target_supports_symbol_feature(KitCompiler* c,
case KIT_CG_SYMFEAT_COMMON:
return 1;
case KIT_CG_SYMFEAT_TLS_LOCAL_EXEC:
+ /* Local-exec is the only TLS model kit emits. Whether it is actually
+ * available still depends on the object format being able to represent a
+ * TLS access at all (ELF/Mach-O yes; COFF/Wasm no). */
+ return obj_format_supports_symbol_feature(c, (int)feat);
case KIT_CG_SYMFEAT_TLS_INITIAL_EXEC:
case KIT_CG_SYMFEAT_TLS_LOCAL_DYNAMIC:
case KIT_CG_SYMFEAT_TLS_GENERAL_DYNAMIC:
- return obj_format_supports_symbol_feature(c, (int)feat);
+ /* kit has no initial-exec / local-dynamic / general-dynamic codegen and
+ * no dynamic-TLS runtime (__tls_get_addr), so it never emits these even
+ * where the object format could represent them. Report the truth so a
+ * caller can't request a dynamic model and silently get local-exec; the
+ * linker enforces the same contract (see link_require_local_tls). */
+ return 0;
case KIT_CG_SYMFEAT_DLLIMPORT:
case KIT_CG_SYMFEAT_DLLEXPORT:
case KIT_CG_SYMFEAT_MERGE_SECTIONS:
diff --git a/test/toy/cases/64_target_feature_queries.toy b/test/toy/cases/64_target_feature_queries.toy
@@ -3,8 +3,16 @@ fn __user_main(): i64 {
let common_ok: bool = @supports_symbol_feature(.common);
let strict: bool = @has_backend_feature(.strict_alignment);
let red_zone: bool = @has_backend_feature(.red_zone);
+ // kit emits local-exec TLS only. The capability query must be honest about
+ // that on every target: initial-exec / local-dynamic / general-dynamic are
+ // never emitted, so they must never report as supported (regardless of
+ // whether the object format could otherwise represent them).
+ let ie: bool = @supports_symbol_feature(.tls_initial_exec);
+ let ld: bool = @supports_symbol_feature(.tls_local_dynamic);
+ let gd: bool = @supports_symbol_feature(.tls_general_dynamic);
if (weak_ok or !weak_ok) and (common_ok or !common_ok) and
- (strict or !strict) and (red_zone or !red_zone) {
+ (strict or !strict) and (red_zone or !red_zone) and
+ !ie and !ld and !gd {
return 42;
}
return 1;