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 }