|
35 | 35 | #include "PathUtils.h" |
36 | 36 | #include "CheckUnload.h" // createOCSectionIndices |
37 | 37 | #include "ReportMemoryMap.h" |
38 | | -#include "xxhash.h" |
39 | 38 |
|
40 | 39 | #if !defined(mingw32_HOST_OS) && defined(HAVE_SIGNAL_H) |
41 | 40 | #include "posix/Signals.h" |
@@ -445,176 +444,6 @@ void initLinker (void) |
445 | 444 | initLinker_(1); |
446 | 445 | } |
447 | 446 |
|
448 | | -// Helper to pre-link big archives into a temporary object file so the |
449 | | -// internal linker can load a single .o instead of many members. |
450 | | -#if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO) |
451 | | -#include <errno.h> |
452 | | -#include <sys/stat.h> |
453 | | -#include <sys/types.h> |
454 | | -#include <limits.h> |
455 | | -#include <time.h> |
456 | | - |
457 | | -static const char *ghci_basename_posix(const char *p) |
458 | | -{ |
459 | | - const char *last = p; |
460 | | - if (!p) return p; |
461 | | - for (const char *s = p; *s; ++s) { |
462 | | - if (*s == '/') last = s + 1; |
463 | | - } |
464 | | - return last; |
465 | | -} |
466 | | - |
467 | | -static const char *ghci_tmpdir(void) |
468 | | -{ |
469 | | - const char *t = getenv("TMPDIR"); |
470 | | - return t && *t ? t : "/tmp"; |
471 | | -} |
472 | | - |
473 | | -static bool ghci_read_file_prefix(const char *path, char *buf, size_t bufsz) |
474 | | -{ |
475 | | - FILE *f = fopen(path, "rb"); |
476 | | - if (!f) return false; |
477 | | - size_t n = fread(buf, 1, bufsz - 1, f); |
478 | | - buf[n] = '\0'; |
479 | | - fclose(f); |
480 | | - return true; |
481 | | -} |
482 | | - |
483 | | -static bool ghci_compute_cache_key(const char *path, char out_hex[65]) |
484 | | -{ |
485 | | - FILE *f = fopen(path, "rb"); |
486 | | - if (!f) return false; |
487 | | - XXH64_state_t* st = XXH64_createState(); |
488 | | - if (!st) { fclose(f); return false; } |
489 | | - if (XXH64_reset(st, 0) != XXH_OK) { XXH64_freeState(st); fclose(f); return false; } |
490 | | - unsigned char buf[256 * 1024]; |
491 | | - size_t n; |
492 | | - while ((n = fread(buf, 1, sizeof(buf), f)) > 0) { |
493 | | - if (XXH64_update(st, buf, n) != XXH_OK) { |
494 | | - XXH64_freeState(st); fclose(f); return false; |
495 | | - } |
496 | | - } |
497 | | - fclose(f); |
498 | | - XXH64_hash_t hv = XXH64_digest(st); |
499 | | - XXH64_freeState(st); |
500 | | - // render as 16-char hex (zero-padded) |
501 | | - // use unsigned long long to satisfy printf on most platforms |
502 | | - unsigned long long v = (unsigned long long)hv; |
503 | | - // ensure buffer large enough; we reserved 65 earlier |
504 | | - snprintf(out_hex, 65, "%016llx", v); |
505 | | - return true; |
506 | | -} |
507 | | - |
508 | | -static pathchar *ghci_prelink_archive_to_tmp(pathchar *archivePath, size_t threshold) |
509 | | -{ |
510 | | - struct_stat st; |
511 | | - if (pathstat(archivePath, &st) != 0) return NULL; |
512 | | - if (threshold == 0 || (size_t)st.st_size < threshold) return NULL; |
513 | | - |
514 | | - // Build cache target name using SHA-256 and include basename for readability |
515 | | - char hex[65]; |
516 | | - if (!ghci_compute_cache_key((const char *)archivePath, hex)) { |
517 | | - return NULL; |
518 | | - } |
519 | | - |
520 | | - const char *tmpdir = ghci_tmpdir(); |
521 | | - const char *base = ghci_basename_posix((const char *)archivePath); |
522 | | - int target_needed = snprintf(NULL, 0, "%s/ghc-prelink-%s-%s.o", tmpdir, base, hex); |
523 | | - pathchar *target = stgMallocBytes((target_needed + 1) * pathsize, "ghci_prelink(target)"); |
524 | | - snprintf((char *)target, target_needed + 1, "%s/ghc-prelink-%s-%s.o", tmpdir, base, hex); |
525 | | - |
526 | | - // If cached object exists, use it |
527 | | - struct stat sb; |
528 | | - if (stat((const char *)target, &sb) == 0 && sb.st_size > 0) { |
529 | | - return target; |
530 | | - } |
531 | | - |
532 | | - // Cross-process lock using a directory |
533 | | - char lock_path[PATH_MAX]; |
534 | | - snprintf(lock_path, sizeof(lock_path), "%s/ghc-prelink-%s-%s.building", tmpdir, base, hex); |
535 | | - if (mkdir(lock_path, 0700) == 0) { |
536 | | - // We are the builder |
537 | | - int tmp_needed = snprintf(NULL, 0, "%s/ghc-prelink-%d-%s.tmp.o", tmpdir, (int)getpid(), base); |
538 | | - pathchar *tmp_out = stgMallocBytes((tmp_needed + 1) * pathsize, "ghci_prelink(tmp)"); |
539 | | - snprintf((char *)tmp_out, tmp_needed + 1, "%s/ghc-prelink-%d-%s.tmp.o", tmpdir, (int)getpid(), base); |
540 | | - |
541 | | - // Log for diagnostics |
542 | | - char log_path[PATH_MAX]; |
543 | | - snprintf(log_path, sizeof(log_path), "%s/ghc-prelink-%s.log", tmpdir, hex); |
544 | | - |
545 | | - const char *cc = getenv("CC"); |
546 | | - if (!cc || !*cc) cc = "cc"; |
547 | | - const char *ldflags = getenv("LDFLAGS"); |
548 | | - if (!ldflags) ldflags = ""; |
549 | | - char cmd[4096]; |
550 | | -# if defined(OBJFORMAT_ELF) |
551 | | - int n = snprintf(cmd, sizeof(cmd), |
552 | | - "%s %s -nostdlib -Wl,-r -Wl,--whole-archive '%s' -Wl,--no-whole-archive -o '%s' >'%s' 2>&1", |
553 | | - cc, ldflags, (const char *)archivePath, (const char *)tmp_out, log_path); |
554 | | -# elif defined(OBJFORMAT_MACHO) |
555 | | - int n = snprintf(cmd, sizeof(cmd), |
556 | | - "%s %s -nostdlib -Wl,-r -Wl,-all_load '%s' -o '%s' >'%s' 2>&1", |
557 | | - cc, ldflags, (const char *)archivePath, (const char *)tmp_out, log_path); |
558 | | -# endif |
559 | | - if (n < 0 || (size_t)n >= sizeof(cmd)) { |
560 | | - errorBelch("prelink: command too long while building cache for %s", base); |
561 | | - stgFree(tmp_out); |
562 | | - rmdir(lock_path); |
563 | | - stgFree(target); |
564 | | - return NULL; |
565 | | - } |
566 | | - |
567 | | - IF_DEBUG(linker, debugBelch("prelinking large archive: %" PATH_FMT " -> %s (cc=%s)\n", archivePath, (char *)tmp_out, cc)); |
568 | | - int rc = system(cmd); |
569 | | - if (rc != 0) { |
570 | | - char buf[1024]; |
571 | | - buf[0] = '\0'; |
572 | | - if (ghci_read_file_prefix(log_path, buf, sizeof(buf))) { |
573 | | - errorBelch("prelink failed (rc=%d) for %s; command: %s\n%s", rc, base, cmd, buf); |
574 | | - } else { |
575 | | - errorBelch("prelink failed (rc=%d) for %s; command: %s", rc, base, cmd); |
576 | | - } |
577 | | - unlink((const char *)tmp_out); |
578 | | - unlink(log_path); |
579 | | - rmdir(lock_path); |
580 | | - stgFree(tmp_out); |
581 | | - stgFree(target); |
582 | | - return NULL; |
583 | | - } |
584 | | - |
585 | | - // Atomically move into place |
586 | | - if (rename((const char *)tmp_out, (const char *)target) != 0) { |
587 | | - errorBelch("prelink: failed to rename '%s' to '%s' (errno=%d)", (char *)tmp_out, (char *)target, errno); |
588 | | - unlink((const char *)tmp_out); |
589 | | - unlink(log_path); |
590 | | - rmdir(lock_path); |
591 | | - stgFree(tmp_out); |
592 | | - stgFree(target); |
593 | | - return NULL; |
594 | | - } |
595 | | - unlink(log_path); |
596 | | - rmdir(lock_path); |
597 | | - stgFree(tmp_out); |
598 | | - return target; |
599 | | - } else { |
600 | | - // Someone else is building; wait for target to appear |
601 | | - const int max_ms = 30000; // 30 seconds |
602 | | - int waited = 0; |
603 | | - while (waited < max_ms) { |
604 | | - if (stat((const char *)target, &sb) == 0 && sb.st_size > 0) { |
605 | | - return target; |
606 | | - } |
607 | | - struct timespec ts = {0, 100 * 1000 * 1000}; // 100ms |
608 | | - nanosleep(&ts, NULL); |
609 | | - waited += 100; |
610 | | - } |
611 | | - // Give up |
612 | | - stgFree(target); |
613 | | - return NULL; |
614 | | - } |
615 | | -} |
616 | | -#endif |
617 | | - |
618 | 447 | void |
619 | 448 | initLinker_ (int retain_cafs) |
620 | 449 | { |
@@ -1639,55 +1468,9 @@ static HsInt loadObj_ (pathchar *path) |
1639 | 1468 | return 1; // success |
1640 | 1469 | } |
1641 | 1470 |
|
1642 | | - // Optionally pre-link large archives into a temporary .o to speed up loading. |
1643 | | - // Runtime configuration precedence: |
1644 | | - // 1) +RTS --linker-prelink-archive-threshold=<size> |
1645 | | - // 2) env GHCI_PRELINK_ARCHIVE_THRESHOLD=<size> |
1646 | | - // 3) default 100M |
1647 | | - static size_t ghci_prelink_threshold = (size_t)-1; |
1648 | | - if (ghci_prelink_threshold == (size_t)-1) { |
1649 | | - // Env var takes precedence over the RTS field |
1650 | | - size_t val; |
1651 | | - const char *env = getenv("GHCI_PRELINK_ARCHIVE_THRESHOLD"); |
1652 | | - if (env && *env) { |
1653 | | - char *endp = NULL; |
1654 | | - unsigned long long n = strtoull(env, &endp, 10); |
1655 | | - if (endp && (*endp == 'K' || *endp == 'k')) { |
1656 | | - val = (size_t)n * 1024ULL; |
1657 | | - } else if (endp && (*endp == 'M' || *endp == 'm')) { |
1658 | | - val = (size_t)n * 1024ULL * 1024ULL; |
1659 | | - } else if (endp && (*endp == 'G' || *endp == 'g')) { |
1660 | | - val = (size_t)n * 1024ULL * 1024ULL * 1024ULL; |
1661 | | - } else { |
1662 | | - val = (size_t)n; |
1663 | | - } |
1664 | | - } else { |
1665 | | - // use the RTS field (set by +RTS or defaulted) |
1666 | | - val = (size_t)RtsFlags.MiscFlags.linkerPrelinkArchiveThreshold; |
1667 | | - } |
1668 | | - ghci_prelink_threshold = val; |
1669 | | - } |
1670 | | - |
1671 | 1471 | // Things that look like object files (e.g. end in `.o`) may nevertheless be |
1672 | 1472 | // archives, as noted in Note [Object merging] in GHC.Driver.Pipeline.Execute. |
1673 | 1473 | if (isArchive(path)) { |
1674 | | - // Try pre-linking if the archive is large enough |
1675 | | - pathchar *tmp_o = NULL; |
1676 | | -#if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO) |
1677 | | - tmp_o = ghci_prelink_archive_to_tmp(path, ghci_prelink_threshold); |
1678 | | -#endif |
1679 | | - if (tmp_o != NULL) { |
1680 | | - HsInt ok = loadObj_(tmp_o); |
1681 | | - if (!ok) { |
1682 | | - IF_DEBUG(linker, debugBelch("prelinked loading failed for %" PATH_FMT ", falling back to archive loader\n", path)); |
1683 | | - // Fall back to archive loader |
1684 | | - stgFree(tmp_o); |
1685 | | - } else { |
1686 | | - // Keep the temp file on disk to allow later unload/debug. Do not free oc->fileName here. |
1687 | | - stgFree(tmp_o); |
1688 | | - return 1; |
1689 | | - } |
1690 | | - } |
1691 | 1474 | if (loadArchive_(path)) { |
1692 | 1475 | return 1; // success |
1693 | 1476 | } else { |
|
0 commit comments