kit

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

link_compat_test.c (30187B)


      1 /* Public LinkSession input-target compatibility coverage. */
      2 
      3 #include <kit/core.h>
      4 #include <kit/link.h>
      5 #include <kit/object.h>
      6 
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #include "lib/kit_unit.h"
     12 
     13 static KitUnit g_u;
     14 #define EXPECT(c, ...) CU_EXPECT(&g_u, c, __VA_ARGS__)
     15 
     16 typedef struct EmittedObject {
     17   KitObjBuilder* ob;
     18   KitWriter* writer;
     19   KitSlice bytes;
     20 } EmittedObject;
     21 
     22 static int emit_empty_object(KitCompiler* c, EmittedObject* out) {
     23   size_t len = 0;
     24   memset(out, 0, sizeof(*out));
     25   if (kit_obj_builder_new(c, &out->ob) != KIT_OK || !out->ob) return 0;
     26   if (kit_obj_builder_finalize(out->ob) != KIT_OK) return 0;
     27   if (kit_writer_mem(&g_u.heap, &out->writer) != KIT_OK || !out->writer)
     28     return 0;
     29   if (kit_obj_builder_emit(out->ob, out->writer) != KIT_OK) return 0;
     30   out->bytes.data = kit_writer_mem_bytes(out->writer, &len);
     31   out->bytes.len = len;
     32   return out->bytes.data != NULL && out->bytes.len != 0;
     33 }
     34 
     35 static void emitted_object_fini(EmittedObject* o) {
     36   if (o->writer) kit_writer_close(o->writer);
     37   if (o->ob) kit_obj_builder_free(o->ob);
     38   memset(o, 0, sizeof(*o));
     39 }
     40 
     41 static void ar_field(char* dst, size_t width, const char* value) {
     42   size_t n = strlen(value);
     43   memset(dst, ' ', width);
     44   if (n > width) n = width;
     45   memcpy(dst, value, n);
     46 }
     47 
     48 static uint8_t* make_archive(KitSlice member, size_t* len_out) {
     49   static const char magic[] = "!<arch>\n";
     50   size_t padded = member.len + (member.len & 1u);
     51   size_t total = 8u + 60u + padded;
     52   uint8_t* bytes = (uint8_t*)malloc(total);
     53   char size_buf[32];
     54   char* hdr;
     55   if (!bytes) return NULL;
     56   memset(bytes, 0, total);
     57   memcpy(bytes, magic, 8u);
     58   hdr = (char*)bytes + 8u;
     59   ar_field(hdr + 0u, 16u, "foreign.o/");
     60   ar_field(hdr + 16u, 12u, "0");
     61   ar_field(hdr + 28u, 6u, "0");
     62   ar_field(hdr + 34u, 6u, "0");
     63   ar_field(hdr + 40u, 8u, "100644");
     64   snprintf(size_buf, sizeof(size_buf), "%lu", (unsigned long)member.len);
     65   ar_field(hdr + 48u, 10u, size_buf);
     66   hdr[58] = '`';
     67   hdr[59] = '\n';
     68   memcpy(bytes + 68u, member.data, member.len);
     69   if (member.len & 1u) bytes[68u + member.len] = '\n';
     70   *len_out = total;
     71   return bytes;
     72 }
     73 
     74 static void put_u16le(uint8_t* p, uint16_t v) {
     75   p[0] = (uint8_t)v;
     76   p[1] = (uint8_t)(v >> 8);
     77 }
     78 
     79 static void put_u32le(uint8_t* p, uint32_t v) {
     80   p[0] = (uint8_t)v;
     81   p[1] = (uint8_t)(v >> 8);
     82   p[2] = (uint8_t)(v >> 16);
     83   p[3] = (uint8_t)(v >> 24);
     84 }
     85 
     86 static KitSlice make_macho_dylib(uint8_t bytes[32], uint32_t cpu_type) {
     87   memset(bytes, 0, 32u);
     88   put_u32le(bytes + 0u, 0xfeedfacfu); /* MH_MAGIC_64 */
     89   put_u32le(bytes + 4u, cpu_type);
     90   put_u32le(bytes + 8u, 3u); /* CPU_SUBTYPE_*_ALL */
     91   put_u32le(bytes + 12u, 6u); /* MH_DYLIB */
     92   return (KitSlice){.data = bytes, .len = 32u};
     93 }
     94 
     95 static uint8_t* make_coff_short_import(uint16_t machine, size_t* len_out) {
     96   static const char body[] = "audit_import\0audit.dll\0";
     97   size_t body_len = sizeof(body) - 1u;
     98   size_t total = 20u + body_len;
     99   uint8_t* bytes = (uint8_t*)malloc(total);
    100   if (!bytes) return NULL;
    101   memset(bytes, 0, total);
    102   put_u16le(bytes + 0u, 0u);      /* IMPORT_OBJECT_HDR_SIG1 */
    103   put_u16le(bytes + 2u, 0xffffu); /* IMPORT_OBJECT_HDR_SIG2 */
    104   put_u16le(bytes + 6u, machine);
    105   put_u32le(bytes + 12u, (uint32_t)body_len);
    106   put_u16le(bytes + 18u, 4u); /* CODE | IMPORT_OBJECT_NAME << 2 */
    107   memcpy(bytes + 20u, body, body_len);
    108   *len_out = total;
    109   return bytes;
    110 }
    111 
    112 static KitLinkSession* new_reloc_session(KitCompiler* c) {
    113   KitLinkSessionOptions opts;
    114   KitLinkSession* s = NULL;
    115   memset(&opts, 0, sizeof(opts));
    116   opts.output_kind = KIT_LINK_OUTPUT_RELOCATABLE;
    117   if (kit_link_session_new(c, &opts, &s) != KIT_OK) return NULL;
    118   return s;
    119 }
    120 
    121 static void check_input_target_boundary(void) {
    122   KitCompiler* expected = NULL;
    123   KitCompiler* foreign = NULL;
    124   EmittedObject x64;
    125   EmittedObject aa64;
    126   KitLinkSession* s = NULL;
    127   KitStatus st;
    128   uint8_t* archive = NULL;
    129   size_t archive_len = 0;
    130 
    131   memset(&x64, 0, sizeof(x64));
    132   memset(&aa64, 0, sizeof(aa64));
    133   EXPECT(kit_unit_compiler_new(
    134              &g_u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF),
    135              &expected) == KIT_OK &&
    136              expected,
    137          "x64 compiler");
    138   EXPECT(kit_unit_compiler_new(
    139              &g_u, kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF),
    140              &foreign) == KIT_OK &&
    141              foreign,
    142          "aarch64 compiler");
    143   if (!expected || !foreign) goto done;
    144   EXPECT(emit_empty_object(expected, &x64), "emit x64 object");
    145   EXPECT(emit_empty_object(foreign, &aa64), "emit aarch64 object");
    146   if (!x64.bytes.data || !aa64.bytes.data) goto done;
    147 
    148   s = new_reloc_session(expected);
    149   EXPECT(s != NULL, "valid link session");
    150   if (s) {
    151     EXPECT(kit_link_session_add_obj_bytes(s, KIT_SLICE_LIT("native-x64.o"),
    152                                           &x64.bytes) == KIT_OK,
    153            "matching object accepted");
    154     kit_link_session_free(s);
    155     s = NULL;
    156   }
    157 
    158   s = new_reloc_session(expected);
    159   EXPECT(s != NULL, "mismatch link session");
    160   if (s) {
    161     g_u.suppress_fatal = 1;
    162     st = kit_link_session_add_obj_bytes(s, KIT_SLICE_LIT("foreign-aa64.o"),
    163                                         &aa64.bytes);
    164     g_u.suppress_fatal = 0;
    165     EXPECT(st == KIT_ERR, "foreign object rejected at add boundary");
    166     EXPECT(strstr(g_u.last_diag, "foreign-aa64.o") != NULL &&
    167                strstr(g_u.last_diag, "architecture mismatch") != NULL &&
    168                strstr(g_u.last_diag, "expected") != NULL &&
    169                strstr(g_u.last_diag, "got") != NULL,
    170            "foreign object diagnostic is named and actionable: %s",
    171            g_u.last_diag);
    172     kit_link_session_free(s);
    173     s = NULL;
    174   }
    175 
    176   s = new_reloc_session(expected);
    177   EXPECT(s != NULL, "DSO mismatch link session");
    178   if (s) {
    179     g_u.suppress_fatal = 1;
    180     st = kit_link_session_add_dso_bytes(s, KIT_SLICE_LIT("foreign-aa64.so"),
    181                                         &aa64.bytes);
    182     g_u.suppress_fatal = 0;
    183     EXPECT(st == KIT_ERR, "foreign DSO/import input rejected at add boundary");
    184     EXPECT(strstr(g_u.last_diag, "foreign-aa64.so") != NULL &&
    185                strstr(g_u.last_diag, "architecture mismatch") != NULL,
    186            "DSO diagnostic names the incompatible input: %s", g_u.last_diag);
    187     kit_link_session_free(s);
    188     s = NULL;
    189   }
    190 
    191   archive = make_archive(aa64.bytes, &archive_len);
    192   EXPECT(archive != NULL, "foreign archive fixture");
    193   s = new_reloc_session(expected);
    194   EXPECT(s != NULL, "archive link session");
    195   if (archive && s) {
    196     KitLinkArchiveInput input;
    197     memset(&input, 0, sizeof(input));
    198     input.name = KIT_SLICE_LIT("libforeign.a");
    199     input.bytes.data = archive;
    200     input.bytes.len = archive_len;
    201     g_u.suppress_fatal = 1;
    202     st = kit_link_session_add_archive_bytes(s, &input);
    203     g_u.suppress_fatal = 0;
    204     EXPECT(st == KIT_ERR, "foreign archive member rejected before selection");
    205     EXPECT(strstr(g_u.last_diag, "libforeign.a(foreign.o)") != NULL &&
    206                strstr(g_u.last_diag, "architecture mismatch") != NULL,
    207            "archive diagnostic names container and member: %s", g_u.last_diag);
    208   }
    209   if (s) {
    210     kit_link_session_free(s);
    211     s = NULL;
    212   }
    213 
    214   s = new_reloc_session(expected);
    215   EXPECT(s != NULL, "builder link session");
    216   if (s) {
    217     g_u.suppress_fatal = 1;
    218     st = kit_link_session_add_obj(s, aa64.ob);
    219     g_u.suppress_fatal = 0;
    220     EXPECT(st == KIT_ERR, "foreign in-memory builder rejected");
    221     EXPECT(strstr(g_u.last_diag, "<in-memory object>") != NULL &&
    222                strstr(g_u.last_diag, "architecture mismatch") != NULL,
    223            "in-memory diagnostic names input: %s", g_u.last_diag);
    224   }
    225 
    226 done:
    227   if (s) kit_link_session_free(s);
    228   free(archive);
    229   emitted_object_fini(&aa64);
    230   emitted_object_fini(&x64);
    231   if (foreign) kit_compiler_free(foreign);
    232   if (expected) kit_compiler_free(expected);
    233 }
    234 
    235 static void check_format_boundary(KitOSKind os, KitObjFmt obj,
    236                                   const char* label) {
    237   KitCompiler* expected = NULL;
    238   KitCompiler* foreign = NULL;
    239   EmittedObject native;
    240   EmittedObject other;
    241   KitLinkSession* s = NULL;
    242   KitStatus st;
    243   uint8_t* archive = NULL;
    244   size_t archive_len = 0;
    245 
    246   memset(&native, 0, sizeof(native));
    247   memset(&other, 0, sizeof(other));
    248   EXPECT(kit_unit_compiler_new(
    249              &g_u, kit_unit_target(KIT_ARCH_X86_64, os, obj), &expected) ==
    250                  KIT_OK &&
    251              expected,
    252          "%s x64 compiler", label);
    253   EXPECT(kit_unit_compiler_new(
    254              &g_u, kit_unit_target(KIT_ARCH_ARM_64, os, obj), &foreign) ==
    255                  KIT_OK &&
    256              foreign,
    257          "%s aarch64 compiler", label);
    258   if (!expected || !foreign) goto done;
    259   EXPECT(emit_empty_object(expected, &native), "%s emit x64 object", label);
    260   EXPECT(emit_empty_object(foreign, &other), "%s emit aarch64 object", label);
    261   if (!native.bytes.data || !other.bytes.data) goto done;
    262 
    263   s = new_reloc_session(expected);
    264   EXPECT(s != NULL, "%s raw mismatch session", label);
    265   if (s) {
    266     g_u.suppress_fatal = 1;
    267     st = kit_link_session_add_obj_bytes(s, kit_slice_cstr(label), &other.bytes);
    268     g_u.suppress_fatal = 0;
    269     EXPECT(st == KIT_ERR, "%s foreign raw object rejected", label);
    270     EXPECT(strstr(g_u.last_diag, label) != NULL &&
    271                strstr(g_u.last_diag, "architecture mismatch") != NULL,
    272            "%s raw diagnostic names expected/actual input: %s", label,
    273            g_u.last_diag);
    274     kit_link_session_free(s);
    275     s = NULL;
    276   }
    277 
    278   archive = make_archive(other.bytes, &archive_len);
    279   EXPECT(archive != NULL, "%s archive fixture", label);
    280   s = new_reloc_session(expected);
    281   EXPECT(s != NULL, "%s archive mismatch session", label);
    282   if (archive && s) {
    283     KitLinkArchiveInput input;
    284     memset(&input, 0, sizeof(input));
    285     input.name = kit_slice_cstr(label);
    286     input.bytes.data = archive;
    287     input.bytes.len = archive_len;
    288     g_u.suppress_fatal = 1;
    289     st = kit_link_session_add_archive_bytes(s, &input);
    290     g_u.suppress_fatal = 0;
    291     EXPECT(st == KIT_ERR, "%s foreign archive member rejected", label);
    292     EXPECT(strstr(g_u.last_diag, label) != NULL &&
    293                strstr(g_u.last_diag, "foreign.o") != NULL,
    294            "%s archive diagnostic names container/member: %s", label,
    295            g_u.last_diag);
    296   }
    297   if (s) {
    298     kit_link_session_free(s);
    299     s = NULL;
    300   }
    301 
    302   s = new_reloc_session(expected);
    303   EXPECT(s != NULL, "%s builder mismatch session", label);
    304   if (s) {
    305     g_u.suppress_fatal = 1;
    306     st = kit_link_session_add_obj(s, other.ob);
    307     g_u.suppress_fatal = 0;
    308     EXPECT(st == KIT_ERR, "%s foreign in-memory builder rejected", label);
    309   }
    310 
    311 done:
    312   if (s) kit_link_session_free(s);
    313   free(archive);
    314   emitted_object_fini(&other);
    315   emitted_object_fini(&native);
    316   if (foreign) kit_compiler_free(foreign);
    317   if (expected) kit_compiler_free(expected);
    318 }
    319 
    320 static void check_platform_and_format_rules(void) {
    321   KitCompiler* linux = NULL;
    322   KitCompiler* freebsd = NULL;
    323   KitCompiler* macho = NULL;
    324   KitCompiler* android = NULL;
    325   EmittedObject linux_obj;
    326   EmittedObject freebsd_obj;
    327   EmittedObject macho_obj;
    328   KitLinkSession* s = NULL;
    329   KitStatus st;
    330 
    331   memset(&linux_obj, 0, sizeof(linux_obj));
    332   memset(&freebsd_obj, 0, sizeof(freebsd_obj));
    333   memset(&macho_obj, 0, sizeof(macho_obj));
    334   EXPECT(kit_unit_compiler_new(
    335              &g_u,
    336              kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF),
    337              &linux) == KIT_OK &&
    338              linux,
    339          "platform-rule Linux compiler");
    340   EXPECT(kit_unit_compiler_new(
    341              &g_u,
    342              kit_unit_target(KIT_ARCH_X86_64, KIT_OS_FREEBSD, KIT_OBJ_ELF),
    343              &freebsd) == KIT_OK &&
    344              freebsd,
    345          "platform-rule FreeBSD compiler");
    346   EXPECT(kit_unit_compiler_new(
    347              &g_u,
    348              kit_unit_target(KIT_ARCH_X86_64, KIT_OS_MACOS, KIT_OBJ_MACHO),
    349              &macho) == KIT_OK &&
    350              macho,
    351          "format-rule Mach-O compiler");
    352   EXPECT(kit_unit_compiler_new(
    353              &g_u,
    354              kit_unit_target(KIT_ARCH_X86_64, KIT_OS_ANDROID, KIT_OBJ_ELF),
    355              &android) == KIT_OK &&
    356              android,
    357          "platform-rule Android compiler");
    358   if (!linux || !freebsd || !macho || !android) goto done;
    359   EXPECT(emit_empty_object(linux, &linux_obj), "emit generic Linux ELF");
    360   EXPECT(emit_empty_object(freebsd, &freebsd_obj), "emit FreeBSD ELF");
    361   EXPECT(emit_empty_object(macho, &macho_obj), "emit x64 Mach-O");
    362   if (!linux_obj.bytes.data || !freebsd_obj.bytes.data ||
    363       !macho_obj.bytes.data)
    364     goto done;
    365 
    366   s = new_reloc_session(linux);
    367   EXPECT(s != NULL, "platform mismatch session");
    368   if (s) {
    369     g_u.suppress_fatal = 1;
    370     st = kit_link_session_add_obj_bytes(s, KIT_SLICE_LIT("freebsd-x64.o"),
    371                                         &freebsd_obj.bytes);
    372     g_u.suppress_fatal = 0;
    373     EXPECT(st == KIT_ERR, "foreign operating-system ELF rejected");
    374     EXPECT(strstr(g_u.last_diag, "operating system mismatch") != NULL,
    375            "platform mismatch diagnostic: %s", g_u.last_diag);
    376     kit_link_session_free(s);
    377     s = NULL;
    378   }
    379 
    380   s = new_reloc_session(linux);
    381   EXPECT(s != NULL, "format mismatch session");
    382   if (s) {
    383     g_u.suppress_fatal = 1;
    384     st = kit_link_session_add_obj_bytes(s, KIT_SLICE_LIT("foreign-x64.o"),
    385                                         &macho_obj.bytes);
    386     g_u.suppress_fatal = 0;
    387     EXPECT(st == KIT_ERR, "foreign object format rejected");
    388     EXPECT(strstr(g_u.last_diag, "object format mismatch") != NULL,
    389            "format mismatch diagnostic: %s", g_u.last_diag);
    390     kit_link_session_free(s);
    391     s = NULL;
    392   }
    393 
    394   /* Generic EI_OSABI_NONE/Linux relocatables are valid inputs to Android;
    395    * this is an explicit directional rule, not general OS borrowing. */
    396   s = new_reloc_session(android);
    397   EXPECT(s != NULL, "Android generic-ELF session");
    398   if (s) {
    399     EXPECT(kit_link_session_add_obj_bytes(s, KIT_SLICE_LIT("generic-linux.o"),
    400                                           &linux_obj.bytes) == KIT_OK,
    401            "Android accepts a generic/Linux ELF relocatable");
    402   }
    403 
    404 done:
    405   if (s) kit_link_session_free(s);
    406   emitted_object_fini(&macho_obj);
    407   emitted_object_fini(&freebsd_obj);
    408   emitted_object_fini(&linux_obj);
    409   if (android) kit_compiler_free(android);
    410   if (macho) kit_compiler_free(macho);
    411   if (freebsd) kit_compiler_free(freebsd);
    412   if (linux) kit_compiler_free(linux);
    413 }
    414 
    415 static void check_macho_dso_boundary(void) {
    416   static const uint8_t tbd_x64[] =
    417       "--- !tapi-tbd\n"
    418       "tbd-version: 4\n"
    419       "targets: [ x86_64-macos ]\n"
    420       "install-name: '/usr/lib/libAudit.dylib'\n"
    421       "exports:\n"
    422       "  - targets: [ x86_64-macos ]\n"
    423       "    symbols: [ _audit_import ]\n"
    424       "...\n";
    425   static const uint8_t tbd_arm64[] =
    426       "--- !tapi-tbd\n"
    427       "tbd-version: 4\n"
    428       "targets: [ arm64-macos ]\n"
    429       "install-name: '/usr/lib/libAudit.dylib'\n"
    430       "exports:\n"
    431       "  - targets: [ arm64-macos ]\n"
    432       "    symbols: [ _audit_import ]\n"
    433       "...\n";
    434   static const uint8_t tbd_ios[] =
    435       "--- !tapi-tbd\n"
    436       "tbd-version: 4\n"
    437       "targets: [ x86_64-ios ]\n"
    438       "install-name: '/usr/lib/libAudit.dylib'\n"
    439       "exports:\n"
    440       "  - targets: [ x86_64-ios ]\n"
    441       "    symbols: [ _audit_import ]\n"
    442       "...\n";
    443   static const uint8_t tbd_ios_simulator[] =
    444       "--- !tapi-tbd\n"
    445       "tbd-version: 4\n"
    446       "targets: [ x86_64-ios-simulator ]\n"
    447       "install-name: '/usr/lib/libAudit.dylib'\n"
    448       "exports:\n"
    449       "  - targets: [ x86_64-ios-simulator ]\n"
    450       "    symbols: [ _audit_import ]\n"
    451       "...\n";
    452   static const uint8_t tbd_arm64e[] =
    453       "--- !tapi-tbd\n"
    454       "tbd-version: 4\n"
    455       "targets: [ arm64e-macos ]\n"
    456       "install-name: '/usr/lib/libAudit.dylib'\n"
    457       "exports:\n"
    458       "  - targets: [ arm64e-macos ]\n"
    459       "    symbols: [ _audit_import ]\n"
    460       "...\n";
    461   static const uint8_t tbd_v3_x64[] =
    462       "--- !tapi-tbd-v3\n"
    463       "archs: [ x86_64 ]\n"
    464       "platform: macosx\n"
    465       "install-name: '/usr/lib/libAudit.dylib'\n"
    466       "exports:\n"
    467       "  - archs: [ x86_64 ]\n"
    468       "    symbols: [ _audit_import ]\n"
    469       "...\n";
    470   static const uint8_t tbd_v3_arm64[] =
    471       "--- !tapi-tbd-v3\n"
    472       "archs: [ arm64 ]\n"
    473       "platform: macosx\n"
    474       "install-name: '/usr/lib/libAudit.dylib'\n"
    475       "...\n";
    476   static const uint8_t tbd_v3_ios[] =
    477       "--- !tapi-tbd-v3\n"
    478       "archs: [ x86_64 ]\n"
    479       "platform: ios\n"
    480       "install-name: '/usr/lib/libAudit.dylib'\n"
    481       "...\n";
    482   KitCompiler* expected = NULL;
    483   KitCompiler* elf_expected = NULL;
    484   KitCompiler* simulator_expected = NULL;
    485   KitCompiler* arm_expected = NULL;
    486   KitLinkSession* s = NULL;
    487   KitStatus st;
    488   uint8_t native_bytes[32];
    489   uint8_t foreign_bytes[32];
    490   KitSlice native = make_macho_dylib(native_bytes, 0x01000007u);
    491   KitSlice foreign = make_macho_dylib(foreign_bytes, 0x0100000cu);
    492   KitSlice native_tbd = {.data = tbd_x64, .len = sizeof(tbd_x64) - 1u};
    493   KitSlice foreign_tbd = {.data = tbd_arm64,
    494                           .len = sizeof(tbd_arm64) - 1u};
    495   KitSlice platform_tbd = {.data = tbd_ios, .len = sizeof(tbd_ios) - 1u};
    496   KitSlice simulator_tbd = {.data = tbd_ios_simulator,
    497                             .len = sizeof(tbd_ios_simulator) - 1u};
    498   KitSlice arm64e_tbd = {.data = tbd_arm64e,
    499                          .len = sizeof(tbd_arm64e) - 1u};
    500   KitSlice v3_native_tbd = {.data = tbd_v3_x64,
    501                             .len = sizeof(tbd_v3_x64) - 1u};
    502   KitSlice v3_arch_tbd = {.data = tbd_v3_arm64,
    503                           .len = sizeof(tbd_v3_arm64) - 1u};
    504   KitSlice v3_platform_tbd = {.data = tbd_v3_ios,
    505                               .len = sizeof(tbd_v3_ios) - 1u};
    506 
    507   EXPECT(kit_unit_compiler_new(
    508              &g_u,
    509              kit_unit_target(KIT_ARCH_X86_64, KIT_OS_MACOS, KIT_OBJ_MACHO),
    510              &expected) == KIT_OK &&
    511              expected,
    512          "Mach-O DSO boundary compiler");
    513   EXPECT(kit_unit_compiler_new(
    514              &g_u,
    515              kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF),
    516              &elf_expected) == KIT_OK &&
    517              elf_expected,
    518          "TBD format-boundary compiler");
    519   EXPECT(kit_unit_compiler_new(
    520              &g_u,
    521              kit_unit_target(KIT_ARCH_X86_64, KIT_OS_IOS_SIMULATOR,
    522                              KIT_OBJ_MACHO),
    523              &simulator_expected) == KIT_OK &&
    524              simulator_expected,
    525          "iOS-simulator TBD boundary compiler");
    526   EXPECT(kit_unit_compiler_new(
    527              &g_u,
    528              kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO),
    529              &arm_expected) == KIT_OK &&
    530              arm_expected,
    531          "arm64e-compatible TBD boundary compiler");
    532   if (!expected || !elf_expected || !simulator_expected || !arm_expected)
    533     goto done;
    534 
    535   s = new_reloc_session(expected);
    536   EXPECT(s != NULL, "Mach-O DSO matching session");
    537   if (s) {
    538     EXPECT(kit_link_session_add_dso_bytes(
    539                s, KIT_SLICE_LIT("libnative-x64.dylib"), &native) == KIT_OK,
    540            "matching Mach-O dylib accepted");
    541     kit_link_session_free(s);
    542     s = NULL;
    543   }
    544 
    545   s = new_reloc_session(simulator_expected);
    546   EXPECT(s != NULL, "matching iOS-simulator TBD session");
    547   if (s) {
    548     EXPECT(kit_link_session_add_dso_bytes(
    549                s, KIT_SLICE_LIT("libnative-simulator.tbd"), &simulator_tbd) ==
    550                KIT_OK,
    551            "matching iOS-simulator TBD accepted");
    552     kit_link_session_free(s);
    553     s = NULL;
    554   }
    555 
    556   s = new_reloc_session(arm_expected);
    557   EXPECT(s != NULL, "arm64e-compatible TBD session");
    558   if (s) {
    559     EXPECT(kit_link_session_add_dso_bytes(
    560                s, KIT_SLICE_LIT("libnative-arm64e.tbd"), &arm64e_tbd) ==
    561                KIT_OK,
    562            "arm64 consumer accepts arm64e SDK stub");
    563     kit_link_session_free(s);
    564     s = NULL;
    565   }
    566 
    567   s = new_reloc_session(simulator_expected);
    568   EXPECT(s != NULL, "device-TBD simulator mismatch session");
    569   if (s) {
    570     g_u.suppress_fatal = 1;
    571     st = kit_link_session_add_dso_bytes(
    572         s, KIT_SLICE_LIT("libdevice-only.tbd"), &platform_tbd);
    573     g_u.suppress_fatal = 0;
    574     EXPECT(st == KIT_ERR, "device-only TBD rejected by simulator link");
    575     EXPECT(strstr(g_u.last_diag, "libdevice-only.tbd") != NULL &&
    576                strstr(g_u.last_diag, "ios-simulator") != NULL,
    577            "iOS-simulator TBD mismatch is named: %s", g_u.last_diag);
    578     kit_link_session_free(s);
    579     s = NULL;
    580   }
    581 
    582   s = new_reloc_session(expected);
    583   EXPECT(s != NULL, "Mach-O DSO mismatch session");
    584   if (s) {
    585     g_u.suppress_fatal = 1;
    586     st = kit_link_session_add_dso_bytes(
    587         s, KIT_SLICE_LIT("libforeign-arm64.dylib"), &foreign);
    588     g_u.suppress_fatal = 0;
    589     EXPECT(st == KIT_ERR, "foreign Mach-O dylib rejected");
    590     EXPECT(strstr(g_u.last_diag, "libforeign-arm64.dylib") != NULL &&
    591                strstr(g_u.last_diag, "architecture mismatch") != NULL,
    592            "Mach-O dylib mismatch is named: %s", g_u.last_diag);
    593     kit_link_session_free(s);
    594     s = NULL;
    595   }
    596 
    597   s = new_reloc_session(elf_expected);
    598   EXPECT(s != NULL, "TBD format-mismatched session");
    599   if (s) {
    600     g_u.suppress_fatal = 1;
    601     st = kit_link_session_add_dso_bytes(
    602         s, KIT_SLICE_LIT("libforeign-format.tbd"), &native_tbd);
    603     g_u.suppress_fatal = 0;
    604     EXPECT(st == KIT_ERR, "Mach-O TBD rejected by ELF link session");
    605     EXPECT(strstr(g_u.last_diag, "libforeign-format.tbd") != NULL &&
    606                strstr(g_u.last_diag, "object format mismatch") != NULL,
    607            "TBD format mismatch is named: %s", g_u.last_diag);
    608     kit_link_session_free(s);
    609     s = NULL;
    610   }
    611 
    612   s = new_reloc_session(expected);
    613   EXPECT(s != NULL, "matching TBD session");
    614   if (s) {
    615     EXPECT(kit_link_session_add_dso_bytes(
    616                s, KIT_SLICE_LIT("libnative-x64.tbd"), &native_tbd) == KIT_OK,
    617            "matching Mach-O TBD accepted");
    618     kit_link_session_free(s);
    619     s = NULL;
    620   }
    621 
    622   s = new_reloc_session(expected);
    623   EXPECT(s != NULL, "matching TAPI v3 session");
    624   if (s) {
    625     EXPECT(kit_link_session_add_dso_bytes(
    626                s, KIT_SLICE_LIT("libnative-v3.tbd"), &v3_native_tbd) ==
    627                KIT_OK,
    628            "matching TAPI v3 archs/platform accepted");
    629     kit_link_session_free(s);
    630     s = NULL;
    631   }
    632 
    633   s = new_reloc_session(expected);
    634   EXPECT(s != NULL, "architecture-mismatched TAPI v3 session");
    635   if (s) {
    636     g_u.suppress_fatal = 1;
    637     st = kit_link_session_add_dso_bytes(
    638         s, KIT_SLICE_LIT("libforeign-arch-v3.tbd"), &v3_arch_tbd);
    639     g_u.suppress_fatal = 0;
    640     EXPECT(st == KIT_ERR, "foreign-architecture TAPI v3 rejected");
    641     EXPECT(strstr(g_u.last_diag, "libforeign-arch-v3.tbd") != NULL,
    642            "TAPI v3 architecture mismatch is named: %s", g_u.last_diag);
    643     kit_link_session_free(s);
    644     s = NULL;
    645   }
    646 
    647   s = new_reloc_session(expected);
    648   EXPECT(s != NULL, "platform-mismatched TAPI v3 session");
    649   if (s) {
    650     g_u.suppress_fatal = 1;
    651     st = kit_link_session_add_dso_bytes(
    652         s, KIT_SLICE_LIT("libforeign-platform-v3.tbd"), &v3_platform_tbd);
    653     g_u.suppress_fatal = 0;
    654     EXPECT(st == KIT_ERR, "foreign-platform TAPI v3 rejected");
    655     EXPECT(strstr(g_u.last_diag, "libforeign-platform-v3.tbd") != NULL,
    656            "TAPI v3 platform mismatch is named: %s", g_u.last_diag);
    657     kit_link_session_free(s);
    658     s = NULL;
    659   }
    660 
    661   s = new_reloc_session(expected);
    662   EXPECT(s != NULL, "architecture-mismatched TBD session");
    663   if (s) {
    664     g_u.suppress_fatal = 1;
    665     st = kit_link_session_add_dso_bytes(
    666         s, KIT_SLICE_LIT("libforeign-arm64.tbd"), &foreign_tbd);
    667     g_u.suppress_fatal = 0;
    668     EXPECT(st == KIT_ERR, "foreign-architecture TBD rejected");
    669     EXPECT(strstr(g_u.last_diag, "libforeign-arm64.tbd") != NULL,
    670            "TBD architecture mismatch is named: %s", g_u.last_diag);
    671     kit_link_session_free(s);
    672     s = NULL;
    673   }
    674 
    675   s = new_reloc_session(expected);
    676   EXPECT(s != NULL, "platform-mismatched TBD session");
    677   if (s) {
    678     g_u.suppress_fatal = 1;
    679     st = kit_link_session_add_dso_bytes(
    680         s, KIT_SLICE_LIT("libforeign-ios.tbd"), &platform_tbd);
    681     g_u.suppress_fatal = 0;
    682     EXPECT(st == KIT_ERR, "foreign-platform TBD rejected");
    683     EXPECT(strstr(g_u.last_diag, "libforeign-ios.tbd") != NULL,
    684            "TBD platform mismatch is named: %s", g_u.last_diag);
    685   }
    686 
    687 done:
    688   if (s) kit_link_session_free(s);
    689   if (arm_expected) kit_compiler_free(arm_expected);
    690   if (simulator_expected) kit_compiler_free(simulator_expected);
    691   if (elf_expected) kit_compiler_free(elf_expected);
    692   if (expected) kit_compiler_free(expected);
    693 }
    694 
    695 static void check_coff_import_boundary(void) {
    696   KitCompiler* expected = NULL;
    697   KitLinkSession* s = NULL;
    698   KitStatus st;
    699   uint8_t* native_import = NULL;
    700   uint8_t* foreign_import = NULL;
    701   uint8_t* native_archive = NULL;
    702   uint8_t* foreign_archive = NULL;
    703   size_t native_import_len = 0, foreign_import_len = 0;
    704   size_t native_archive_len = 0, foreign_archive_len = 0;
    705 
    706   EXPECT(kit_unit_compiler_new(
    707              &g_u,
    708              kit_unit_target(KIT_ARCH_X86_64, KIT_OS_WINDOWS, KIT_OBJ_COFF),
    709              &expected) == KIT_OK &&
    710              expected,
    711          "COFF import-library boundary compiler");
    712   native_import = make_coff_short_import(0x8664u, &native_import_len);
    713   foreign_import = make_coff_short_import(0xaa64u, &foreign_import_len);
    714   EXPECT(native_import && foreign_import, "COFF short-import fixtures");
    715   if (!expected || !native_import || !foreign_import) goto done;
    716   native_archive = make_archive(
    717       (KitSlice){.data = native_import, .len = native_import_len},
    718       &native_archive_len);
    719   foreign_archive = make_archive(
    720       (KitSlice){.data = foreign_import, .len = foreign_import_len},
    721       &foreign_archive_len);
    722   EXPECT(native_archive && foreign_archive, "COFF import-library archives");
    723   if (!native_archive || !foreign_archive) goto done;
    724 
    725   s = new_reloc_session(expected);
    726   EXPECT(s != NULL, "matching COFF import-library session");
    727   if (s) {
    728     KitLinkArchiveInput input = {0};
    729     input.name = KIT_SLICE_LIT("native-x64.lib");
    730     input.bytes.data = native_archive;
    731     input.bytes.len = native_archive_len;
    732     EXPECT(kit_link_session_add_archive_bytes(s, &input) == KIT_OK,
    733            "matching COFF import library accepted");
    734     kit_link_session_free(s);
    735     s = NULL;
    736   }
    737 
    738   s = new_reloc_session(expected);
    739   EXPECT(s != NULL, "mismatched COFF import-library session");
    740   if (s) {
    741     KitLinkArchiveInput input = {0};
    742     input.name = KIT_SLICE_LIT("foreign-arm64.lib");
    743     input.bytes.data = foreign_archive;
    744     input.bytes.len = foreign_archive_len;
    745     g_u.suppress_fatal = 1;
    746     st = kit_link_session_add_archive_bytes(s, &input);
    747     g_u.suppress_fatal = 0;
    748     EXPECT(st == KIT_ERR, "foreign COFF import library rejected");
    749     EXPECT(strstr(g_u.last_diag, "foreign-arm64.lib(foreign.o)") != NULL &&
    750                strstr(g_u.last_diag, "architecture mismatch") != NULL,
    751            "COFF import mismatch names archive/member: %s", g_u.last_diag);
    752   }
    753 
    754 done:
    755   if (s) kit_link_session_free(s);
    756   free(foreign_archive);
    757   free(native_archive);
    758   free(foreign_import);
    759   free(native_import);
    760   if (expected) kit_compiler_free(expected);
    761 }
    762 
    763 static void check_data_model_boundary(void) {
    764   KitTargetSpec expected_spec =
    765       kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF);
    766   KitTargetSpec narrow_spec = expected_spec;
    767   KitTargetSpec endian_spec = expected_spec;
    768   KitCompiler* expected = NULL;
    769   KitCompiler* narrow = NULL;
    770   KitCompiler* endian = NULL;
    771   KitObjBuilder* narrow_obj = NULL;
    772   KitObjBuilder* endian_obj = NULL;
    773   KitLinkSession* s = NULL;
    774   KitStatus st;
    775 
    776   narrow_spec.ptr_size = 4u;
    777   narrow_spec.ptr_align = 4u;
    778   endian_spec.big_endian = 1u;
    779   EXPECT(kit_unit_compiler_new(&g_u, expected_spec, &expected) == KIT_OK &&
    780              expected,
    781          "data-model expected compiler");
    782   EXPECT(kit_unit_compiler_new(&g_u, narrow_spec, &narrow) == KIT_OK && narrow,
    783          "data-model narrow compiler");
    784   EXPECT(kit_unit_compiler_new(&g_u, endian_spec, &endian) == KIT_OK && endian,
    785          "data-model big-endian compiler");
    786   if (!expected || !narrow || !endian) goto done;
    787   EXPECT(kit_obj_builder_new(narrow, &narrow_obj) == KIT_OK && narrow_obj,
    788          "data-model narrow builder");
    789   EXPECT(kit_obj_builder_new(endian, &endian_obj) == KIT_OK && endian_obj,
    790          "data-model big-endian builder");
    791   if (!narrow_obj || !endian_obj) goto done;
    792 
    793   s = new_reloc_session(expected);
    794   EXPECT(s != NULL, "pointer-width mismatch session");
    795   if (s) {
    796     g_u.suppress_fatal = 1;
    797     st = kit_link_session_add_obj(s, narrow_obj);
    798     g_u.suppress_fatal = 0;
    799     EXPECT(st == KIT_ERR, "pointer-width mismatch rejected");
    800     EXPECT(strstr(g_u.last_diag, "pointer size mismatch") != NULL &&
    801                strstr(g_u.last_diag, "ptr=8") != NULL &&
    802                strstr(g_u.last_diag, "ptr=4") != NULL,
    803            "pointer-width diagnostic has expected/actual: %s", g_u.last_diag);
    804     kit_link_session_free(s);
    805     s = NULL;
    806   }
    807 
    808   s = new_reloc_session(expected);
    809   EXPECT(s != NULL, "endianness mismatch session");
    810   if (s) {
    811     g_u.suppress_fatal = 1;
    812     st = kit_link_session_add_obj(s, endian_obj);
    813     g_u.suppress_fatal = 0;
    814     EXPECT(st == KIT_ERR, "endianness mismatch rejected");
    815     EXPECT(strstr(g_u.last_diag, "endianness mismatch") != NULL &&
    816                strstr(g_u.last_diag, "endian=little") != NULL &&
    817                strstr(g_u.last_diag, "endian=big") != NULL,
    818            "endianness diagnostic has expected/actual: %s", g_u.last_diag);
    819   }
    820 
    821 done:
    822   if (s) kit_link_session_free(s);
    823   if (endian_obj) kit_obj_builder_free(endian_obj);
    824   if (narrow_obj) kit_obj_builder_free(narrow_obj);
    825   if (endian) kit_compiler_free(endian);
    826   if (narrow) kit_compiler_free(narrow);
    827   if (expected) kit_compiler_free(expected);
    828 }
    829 
    830 int main(void) {
    831   kit_unit_init(&g_u);
    832   check_input_target_boundary();
    833   check_format_boundary(KIT_OS_MACOS, KIT_OBJ_MACHO, "foreign-macho.a");
    834   check_format_boundary(KIT_OS_WINDOWS, KIT_OBJ_COFF, "foreign-coff.a");
    835   check_platform_and_format_rules();
    836   check_macho_dso_boundary();
    837   check_coff_import_boundary();
    838   check_data_model_boundary();
    839   kit_unit_summary(&g_u, "link_compat_test");
    840   return kit_unit_status(&g_u);
    841 }