Skip to content

c_import is not self-contained: resolves clang resource dir (and other toolchain bits) from an external LLVM at runtime #312

@ehartford

Description

@ehartford

Invariant being violated

After bootstrap the seed must depend on nothing external from LLVM. We build the entire static LLVM/Clang/lld SDK ourselves (tools/build-static-llvm.sh), and the release binary embeds those static resources — libclang is statically linked, the stdlib and runtime objects are embedded. A clean release host has no LLVM at all (not in /usr, not in .deps), and a system LLVM is never trusted (we didn't build it; it won't have the static .a/resources we need). See AGENTS.md → Self-Contained Toolchain and docs/with-bootstrap-runbook.mdFailure Policy.

c_import violates this: several runtime paths reach for an external toolchain. They happen to succeed on a dev box that has LLVM installed, which is exactly why this stayed hidden until a clean Linux release host failed with 'stddef.h' file not found.

Bug 1 (core) — get_clang_resource_dir() should not exist in this form

rt/clang_bridge.w:709 resolves clang's builtin-header resource dir (stddef.h, stdarg.h, stdint.h, …) entirely from external locations at runtime:

  • WITH_CLANG_RESOURCE_DIR env (:712)
  • LLVM_PREFIX env → <prefix>/lib/clang (:716)
  • find_clang_resource_dir_from_llvm_config() → shells out to llvm-config --libdir (:697) — a system tool we didn't build
  • /usr/local/llvm/lib/clang (:726) — a system path
  • find_clang_resource_dir_under() (:666) scans an external dir for the <v> subdir

These builtin headers are produced by our own SDK build at $LLVM_PREFIX/lib/clang/<v>/include, but the build never embeds them, so the runtime has no choice but to hunt externally. The function's existence is the bug.

Consumed at 4 sites that pass -resource-dir <external> to clang_parseTranslationUnit: rt/clang_bridge.w:1221, :2226, :2504, :2592.

Fix: embed lib/clang/<v>/include/ into the binary the same way the stdlib is embedded (build/runtime.wEmbeddedStdlibData.w) — generate a raw-string data module + lookup, or incbin a tarball via .EmbedObjectFiles. At first c_import, materialize to a versioned cache dir (e.g. ~/.cache/with/clang-resource/<llvm-ver>/) and point -resource-dir there. Delete the env/llvm-config//usr/local/llvm probes, or demote them to an explicit override-only last resort (never the default). The build should fail loudly if the resource headers aren't present to embed.

Symptom / regression context: the v0.14.6 Linux FFI test (spec_ss16_1_ffi_direct_call) only passed because I set WITH_CLANG_RESOURCE_DIR to the host's .deps LLVM — i.e. the test validated against an external resource, masking that c_import is not self-contained on a bare host.

Related runtime-external-toolchain gaps (same theme, different resource — triage separately)

These are not the LLVM static resources we build, so they may have different resolutions, but they're the same class of "reaches outside the binary at runtime":

  • Bug 2 — get_sdk_path() shells out to xcrun --show-sdk-path (rt/clang_bridge.w:650) for the macOS platform sysroot. This is the platform's libc headers (e.g. stdio.h itself), inherently the user's system when importing them — but the runtime shell-out to Xcode tooling is fragile and fails on a host without Xcode. Decide: pin/cache vs accept as a platform requirement.
  • Bug 3 — macro extraction shells out to cc (rt/clang_bridge.w:2282, :2435: cc -E -dM …) to evaluate object-like macros during c_import, instead of going through the embedded libclang. External C-compiler dependency at runtime; fails on a clean host with no cc.

Acceptance

  • A release binary on a host with no LLVM/Clang/cc/llvm-config/Xcode-resource-dir installed can use c_import("stdio.h") and build, using only resources embedded at build time.
  • No runtime filesystem probe for an LLVM/Clang resource dir, archive, or llvm-config.
  • spec_ss16_1_ffi_direct_call passes on the Linux release host without WITH_CLANG_RESOURCE_DIR set.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions