Skip to content

Latest commit

 

History

History
148 lines (113 loc) · 6.34 KB

File metadata and controls

148 lines (113 loc) · 6.34 KB

33 — Authoring a Runtime Adapter

Reader: someone making a library the host supplies reachable from an mcpp-built artifact on Linux — a graphics driver, a Vulkan ICD, a proprietary runtime.

The question this chapter answers: why an artifact cannot see a library that is plainly installed on the machine, and what a package has to declare to fix it.

Not here: packaging a library mcpp installs, which is 32 — Authoring a Payload and is the right answer whenever the library can be redistributed; and the runtime contract's fields, which are 04 — The mcpp.toml Manifest §[runtime].

Before: 32 — Authoring a Payload. After: 34 — Authoring a Board-Support Package.

The failure this exists for

The library is installed. The loader finds its manifest. The dlopen fails anyway:

DRIVER: Found the following files: /usr/share/vulkan/icd.d/lvp_icd.json …
ERROR:  libvulkan_lvp.so: cannot open shared object file

The libraries are in /usr/lib/x86_64-linux-gnu. What cannot reach them is the process: an mcpp-built binary runs under mcpp's own glibc, with its own search path,

interp: …/xpkgs/xim-x-glibc/2.39/lib64/ld-linux-x86-64.so.2
rpath : …/xim-x-glibc/2.39/lib64:…/xim-x-gcc/…/lib64:$ORIGIN

so a bare-soname dlopen from inside that process does not search the host's library path at all. Nothing is broken; the artifact is simply not looking there, which is the property that makes an mcpp build reproducible in the first place.

The definition of an adapter

A symlink farm plus the metadata that makes it reachable. Nothing is vendored, nothing is redistributed, and the package carries no upstream bytes. runtime.library_dirs puts a package-owned directory of symlinks on the artifact's runtime search path, and the chain resolves.

A project declares the adapter as an ordinary dependency and does nothing else.

The reason the driver itself is not a package

A proprietary driver's userspace is in ABI lockstep with a kernel module, and its licence forbids redistribution. Neither is a packaging problem that effort solves, so such a driver is modelled as a host capability — something the machine either has or does not — and the adapter is how an artifact reaches it.

An open driver is a different case and takes the other answer: it is a payload (xim:mesa-lavapipe for the CPU, xim:mesa for AMD hardware), and a machine using one needs nothing from the farm. Since 2026.09.05 an adapter also prefers the payload when one is published and its symbol set covers the host copy, so what the farm actually records is proprietary userspace and packaging backlog.

The three things an adapter gets wrong

The pattern list must cover transitive dependencies. The whole chain has to resolve through the same directory. Mesa's software rasteriser pulls in LLVM; an NVIDIA driver pulls its own family. Listing the ICD alone produces the same cannot open shared object file one level down.

libstdc++ belongs in the list, and it is not an oversight. mcpp links libstdc++ statically — it is absent from a built binary's NEEDED — so a dlopened C++ driver has nothing to resolve against unless the host copy is provided here.

Nothing may be required. A machine with no such driver at all is a legitimate configuration, and every CI runner in this ecosystem is one. The farm is then empty and the program reports what it actually found. An adapter that errors on a missing host library turns a supported configuration into a build failure.

The check mcpp runs on this surface

The libraries an adapter publishes are reached by dlopen, so no link edge names them and the runtime closure check — which walks DT_NEEDED from the artifact — cannot arrive at them. mcpp walks them separately, after the link, and reports what it finds as a warning:

warning: 1 of 13 libraries a dependency published for dlopen cannot be loaded
on this artifact's search path:
    libur_adapter_cuda.so.0 needs libnvidia-ml.so.1

Three states are separated, and only one of them is reported:

The library needs a SONAME that is Meaning Reported
on the artifact's search path nothing to say no
present in the farm as a dangling link the machine has no such driver no
absent everywhere the adapter did not carry it yes

The middle row is why this is a warning and not an error: a dangling link is the documented shape of a host driver that is not installed, and a check that failed there would turn a supported configuration into a build failure.

The full result, including both denominators, is published as runtime.dlopen_surface in resolution.json:

{ "members": 13, "walked": 13,
  "findings": [ { "library": "libur_adapter_cuda.so.0", "dir": "...",
                  "soname": "libnvidia-ml.so.1", "kind": "missing" } ] }

members and walked are published even when nothing is reported. A farm that failed to build enumerates nothing, and "no findings" would otherwise be indistinguishable from "nothing was examined".

A build the check does not apply to publishes the same record with a reason and no reading:

{ "members": 0, "walked": 0, "findings": [],
  "reason": "this build produces no program; the surface is reached from a process and belongs to whatever runs" }

The four reasons are: the runtime binding is not hermetic, allow_host_libs is set, the build produces no program, and the build produced no linked artifact. A target that is not Linux publishes no record at all, because the record is ELF-shaped and an empty answer about a format the build never produces would be its own confusion.

A test that reads this record should treat a reason as "not measured" rather than as a clean result -- "did not apply" and "was never run" are the pair this record exists to keep apart.

Current limitations

  • Linux only, by construction. macOS's dyld and the Windows PE loader have no equivalent layer, so a project targeting them declares no adapter.
  • An adapter cannot make a driver work that the machine does not have. It removes one obstacle — reachability — and reports the rest as absence.
  • The farm's contents are decided when the adapter is installed. A driver installed afterwards is not picked up until the adapter is reinstalled.