kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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:
Msrc/cg/type.c | 11++++++++++-
Mtest/toy/cases/64_target_feature_queries.toy | 10+++++++++-
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;