kit

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

commit f73b0ab8419d3e2781fc654d0e107a2206e719d8
parent f962942019b876ed343d1ba705c0c6fa96bd22bf
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 16 Jul 2026 11:24:06 -0700

link: validate legacy TAPI targets

Diffstat:
Mdoc/RELEASE_AUDIT_2026_6_0.md | 2+-
Msrc/obj/macho/tbd_read.c | 116++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mtest/link/link_compat_test.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 152 insertions(+), 32 deletions(-)

diff --git a/doc/RELEASE_AUDIT_2026_6_0.md b/doc/RELEASE_AUDIT_2026_6_0.md @@ -491,7 +491,7 @@ Implementation checkpoint (updated 2026-07-16): | KIT-P2-007 | **Complete** | `test-selfdist`: 107 pass, 0 fail; release/dist gates reject the test signer ID after copy, comment changes, or valid reserialization | | KIT-P1-012 | **Complete** | authenticated updater result cases included in `test-selfdist`: 107 pass, 0 fail | | KIT-P1-001 | **Complete** | `target_test`: 177 checks, 0 failures; `test-driver-targets`: 61 pass, 0 fail | -| KIT-P0-004 | **Complete** | public ELF/Mach-O/COFF matrix 106/0, including dylib/TBD device/simulator/arm64e, import-library, pointer-width/endian boundaries; driver authority/inference/archive suite 15/0 | +| KIT-P0-004 | **Complete** | public ELF/Mach-O/COFF matrix 114/0, including TAPI v2-v4 device/simulator/arm64e, import-library, pointer-width/endian boundaries; driver authority/inference/archive suite 15/0 | | KIT-P1-002 | **Complete** | NDK r27d NativeActivity preprocess/compile/shared-link at API 21 and 35; independent `llvm-readelf` confirms AArch64 `ET_DYN` plus exported `ANativeActivity_onCreate`: 3 pass, 0 fail | | KIT-P1-003 | **Complete** | `test-smoke-rv32`: 20 pass, 0 fail, including concurrent empty-cache/read-only/QEMU cases | | KIT-P1-004 | **Complete** | independent `llvm-readelf` rv32/rv64 soft/hard, mixed-ABI, and RVC merge oracle: 36 pass, 0 fail | diff --git a/src/obj/macho/tbd_read.c b/src/obj/macho/tbd_read.c @@ -54,18 +54,65 @@ static int is_target_cont(u8 c) { return is_id_cont(c) || c == '-'; } -/* TAPI v4 declares the supported architecture/platform pairs in a top-level - * `targets:` list. Validate that list against the already-resolved link target - * before treating the text stub as a DSO. Older TAPI documents that have no - * top-level targets field retain the reader's legacy behavior. */ +static int tbd_line_has_token(const u8* data, size_t start, size_t end, + const char* token, size_t token_len) { + size_t i; + for (i = start; i + token_len <= end; ++i) { + size_t after = i + token_len; + if ((i == start || !is_target_cont(data[i - 1u])) && + memcmp(data + i, token, token_len) == 0 && + (after == end || !is_target_cont(data[after]))) + return 1; + } + return 0; +} + +static int tbd_line_has_target(const u8* data, size_t start, size_t end, + const char* arch, size_t arch_len, + const char* platform, size_t platform_len, + int allow_arm64e) { + size_t i; + for (i = start; i + arch_len + platform_len <= end; ++i) { + size_t after = i + arch_len + platform_len; + if ((i == start || !is_target_cont(data[i - 1u])) && + memcmp(data + i, arch, arch_len) == 0 && + memcmp(data + i + arch_len, platform, platform_len) == 0 && + (after == end || !is_target_cont(data[after]))) + return 1; + /* Current macOS SDK stubs advertise arm64e for system dylibs even when an + * ordinary arm64 consumer is linked. ld64 accepts that ABI-compatible + * provider, so Kit must accept the SDK's only ARM slice as well. */ + if (allow_arm64e && i + 6u + platform_len <= end && + (i == start || !is_target_cont(data[i - 1u])) && + memcmp(data + i, "arm64e", 6u) == 0 && + memcmp(data + i + 6u, platform, platform_len) == 0 && + (i + 6u + platform_len == end || + !is_target_cont(data[i + 6u + platform_len]))) + return 1; + } + return 0; +} + +/* TAPI v4 declares architecture/platform pairs in top-level `targets:`. v2/v3 + * split the same authority across top-level `archs:` and `platform:` fields. + * Validate either schema before treating the text stub as a DSO; only truly + * target-less legacy documents retain the permissive scanner behavior. */ static int tbd_supports_target(Compiler* c, const u8* data, size_t len, int* declared_out) { - static const char KEY[] = "targets:"; + static const char TARGETS_KEY[] = "targets:"; + static const char ARCHS_KEY[] = "archs:"; + static const char PLATFORM_KEY[] = "platform:"; const char* arch; const char* platform; + const char* legacy_platform; size_t arch_len; size_t platform_len; + size_t legacy_platform_len; size_t line = 0; + int saw_archs = 0; + int saw_platform = 0; + int arch_matches = 0; + int platform_matches = 0; *declared_out = 0; switch (c->target.arch) { @@ -76,58 +123,65 @@ static int tbd_supports_target(Compiler* c, const u8* data, size_t len, arch = "x86_64"; break; default: + *declared_out = 1; return 0; } switch (c->target.os) { case KIT_OS_MACOS: platform = "-macos"; + legacy_platform = "macosx"; break; case KIT_OS_IOS: platform = "-ios"; + legacy_platform = "ios"; break; case KIT_OS_IOS_SIMULATOR: platform = "-ios-simulator"; + legacy_platform = "ios-simulator"; break; default: + *declared_out = 1; return 0; } arch_len = strlen(arch); platform_len = strlen(platform); + legacy_platform_len = strlen(legacy_platform); while (line < len) { size_t end = line; - size_t i; while (end < len && data[end] != '\n' && data[end] != '\r') ++end; - if (end - line >= sizeof(KEY) - 1u && - memcmp(data + line, KEY, sizeof(KEY) - 1u) == 0) { + if (end - line >= sizeof(TARGETS_KEY) - 1u && + memcmp(data + line, TARGETS_KEY, sizeof(TARGETS_KEY) - 1u) == 0) { *declared_out = 1; - for (i = line + sizeof(KEY) - 1u; - i + arch_len + platform_len <= end; ++i) { - size_t after = i + arch_len + platform_len; - if ((i == line || !is_target_cont(data[i - 1u])) && - memcmp(data + i, arch, arch_len) == 0 && - memcmp(data + i + arch_len, platform, platform_len) == 0 && - (after == end || !is_target_cont(data[after]))) - return 1; - /* Current macOS SDK stubs advertise arm64e for system dylibs even - * when an ordinary arm64 consumer is being linked. ld64 accepts that - * ABI-compatible provider, so Kit must not reject the SDK's only ARM - * slice before symbol ingestion. */ - if (c->target.arch == KIT_ARCH_ARM_64 && - i + 6u + platform_len <= end && - (i == line || !is_target_cont(data[i - 1u])) && - memcmp(data + i, "arm64e", 6u) == 0 && - memcmp(data + i + 6u, platform, platform_len) == 0 && - (i + 6u + platform_len == end || - !is_target_cont(data[i + 6u + platform_len]))) - return 1; - } - return 0; + return tbd_line_has_target( + data, line + sizeof(TARGETS_KEY) - 1u, end, arch, arch_len, platform, + platform_len, c->target.arch == KIT_ARCH_ARM_64); + } + if (end - line >= sizeof(ARCHS_KEY) - 1u && + memcmp(data + line, ARCHS_KEY, sizeof(ARCHS_KEY) - 1u) == 0) { + size_t value = line + sizeof(ARCHS_KEY) - 1u; + saw_archs = 1; + arch_matches = + tbd_line_has_token(data, value, end, arch, arch_len) || + (c->target.arch == KIT_ARCH_ARM_64 && + tbd_line_has_token(data, value, end, "arm64e", 6u)); + } + if (end - line >= sizeof(PLATFORM_KEY) - 1u && + memcmp(data + line, PLATFORM_KEY, sizeof(PLATFORM_KEY) - 1u) == 0) { + size_t value = line + sizeof(PLATFORM_KEY) - 1u; + saw_platform = 1; + platform_matches = tbd_line_has_token( + data, value, end, legacy_platform, legacy_platform_len); + if (!platform_matches && c->target.os == KIT_OS_MACOS) + platform_matches = + tbd_line_has_token(data, value, end, "macos", 5u); } while (end < len && (data[end] == '\n' || data[end] == '\r')) ++end; line = end; } - return 1; + *declared_out = saw_archs || saw_platform; + return (!saw_archs || arch_matches) && + (!saw_platform || platform_matches); } /* Extract the install-name from the first document. We look for a diff --git a/test/link/link_compat_test.c b/test/link/link_compat_test.c @@ -458,6 +458,27 @@ static void check_macho_dso_boundary(void) { " - targets: [ arm64e-macos ]\n" " symbols: [ _audit_import ]\n" "...\n"; + static const uint8_t tbd_v3_x64[] = + "--- !tapi-tbd-v3\n" + "archs: [ x86_64 ]\n" + "platform: macosx\n" + "install-name: '/usr/lib/libAudit.dylib'\n" + "exports:\n" + " - archs: [ x86_64 ]\n" + " symbols: [ _audit_import ]\n" + "...\n"; + static const uint8_t tbd_v3_arm64[] = + "--- !tapi-tbd-v3\n" + "archs: [ arm64 ]\n" + "platform: macosx\n" + "install-name: '/usr/lib/libAudit.dylib'\n" + "...\n"; + static const uint8_t tbd_v3_ios[] = + "--- !tapi-tbd-v3\n" + "archs: [ x86_64 ]\n" + "platform: ios\n" + "install-name: '/usr/lib/libAudit.dylib'\n" + "...\n"; KitCompiler* expected = NULL; KitCompiler* elf_expected = NULL; KitCompiler* simulator_expected = NULL; @@ -476,6 +497,12 @@ static void check_macho_dso_boundary(void) { .len = sizeof(tbd_ios_simulator) - 1u}; KitSlice arm64e_tbd = {.data = tbd_arm64e, .len = sizeof(tbd_arm64e) - 1u}; + KitSlice v3_native_tbd = {.data = tbd_v3_x64, + .len = sizeof(tbd_v3_x64) - 1u}; + KitSlice v3_arch_tbd = {.data = tbd_v3_arm64, + .len = sizeof(tbd_v3_arm64) - 1u}; + KitSlice v3_platform_tbd = {.data = tbd_v3_ios, + .len = sizeof(tbd_v3_ios) - 1u}; EXPECT(kit_unit_compiler_new( &g_u, @@ -593,6 +620,45 @@ static void check_macho_dso_boundary(void) { } s = new_reloc_session(expected); + EXPECT(s != NULL, "matching TAPI v3 session"); + if (s) { + EXPECT(kit_link_session_add_dso_bytes( + s, KIT_SLICE_LIT("libnative-v3.tbd"), &v3_native_tbd) == + KIT_OK, + "matching TAPI v3 archs/platform accepted"); + kit_link_session_free(s); + s = NULL; + } + + s = new_reloc_session(expected); + EXPECT(s != NULL, "architecture-mismatched TAPI v3 session"); + if (s) { + g_u.suppress_fatal = 1; + st = kit_link_session_add_dso_bytes( + s, KIT_SLICE_LIT("libforeign-arch-v3.tbd"), &v3_arch_tbd); + g_u.suppress_fatal = 0; + EXPECT(st == KIT_ERR, "foreign-architecture TAPI v3 rejected"); + EXPECT(strstr(g_u.last_diag, "libforeign-arch-v3.tbd") != NULL, + "TAPI v3 architecture mismatch is named: %s", g_u.last_diag); + kit_link_session_free(s); + s = NULL; + } + + s = new_reloc_session(expected); + EXPECT(s != NULL, "platform-mismatched TAPI v3 session"); + if (s) { + g_u.suppress_fatal = 1; + st = kit_link_session_add_dso_bytes( + s, KIT_SLICE_LIT("libforeign-platform-v3.tbd"), &v3_platform_tbd); + g_u.suppress_fatal = 0; + EXPECT(st == KIT_ERR, "foreign-platform TAPI v3 rejected"); + EXPECT(strstr(g_u.last_diag, "libforeign-platform-v3.tbd") != NULL, + "TAPI v3 platform mismatch is named: %s", g_u.last_diag); + kit_link_session_free(s); + s = NULL; + } + + s = new_reloc_session(expected); EXPECT(s != NULL, "architecture-mismatched TBD session"); if (s) { g_u.suppress_fatal = 1;