Files
Dario Gabriel LipicarandClaude Opus 5 dbe1d63677 feat(abi): define logos_module_set_call_caller, gated on protocol 0.6
The definition lands BEFORE the protocol declares the export. logos-protocol
only DECLARES the module-impl C ABI and every backend owes the definition;
that gap shipped twice, each time as an undefined symbol at dlopen, on Linux
only, invisible on macOS. Declaring first would turn this repo, logos-rust-sdk
and logos-module-builder red the night the bump merged. Defining first costs
nothing: the guard is MAJOR-aware >= 0.6 and the current pin is 0.5, so
nothing is emitted today and the ABI check sees declared == defined.

This is the case #146 made possible. The next-MAJOR probe resolves the
emitter at MAJOR+1, where a >= 6 guard IS true, so the emitted set there is
legitimately a SUPERSET of the declared one. The probe used to demand
equality and would have rejected this outright.

cpp/logos_caller.h carries the LogosCaller type (std-typed, Qt-free) and
logos::currentCaller(), reading a thread-local stack the generated export
pushes to.

Two things the audit corrected, both worth reading:

* A present-but-unreadable `instance` is DROPPED and the module still
  identified. This backend already did that; Rust returned Unknown, and each
  had a passing test pinning its own answer, so neither suite could see the
  divergence. The protocol header now states the rule normatively and Rust
  is aligned to it.

* The accessors are explicitly HIDDEN on ELF. The header argued this state
  must not be unified across images and then relied on being inline to
  achieve it — which is false: a function-local static in an inline function
  emits STB_GNU_UNIQUE at default visibility and the loader collapses every
  image's copy into one, even under RTLD_LOCAL. Measured across two dlopen'd
  images: default visibility let a push in A be read by B; hidden restored
  isolation. logos-module-builder sets no visibility anywhere, so real
  plugins were built the first way. An anonymous namespace would be worse —
  vague linkage is load-bearing WITHIN an image, since the generated TU
  pushes and the author's TU reads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 16:59:13 -03:00

43 lines
1.7 KiB
Nix

# Installs the Qt-free base SDK headers in the source-export layout
# ($out/include/cpp/...). The transport/protocol sources ship from
# logos-protocol and the Qt developer layer from logos-qt-sdk; build
# systems that need those add the respective include roots.
{ pkgs, common, src, logos-protocol }:
pkgs.stdenv.mkDerivation {
pname = "${common.pname}-headers";
# qtbase\'s setup hook errors in qtPreHook unless a wrapper hook ran or
# this is set; the wrapper hooks are absent on Windows (they cannot even
# evaluate for a mingw host) and would skip a PE anyway.
dontWrapQtApps = true;
version = common.version;
inherit src;
inherit (common) meta;
dontBuild = true;
dontConfigure = true;
installPhase = ''
runHook preInstall
mkdir -p $out/include/cpp
# logos_lp_client.h MUST sit in the same directory as the headers it
# includes (logos_result.h, logos_json.h). A cdylib module's generated
# dep wrapper includes "logos_lp_client.h" via the include/cpp source-
# export root, and a quoted include resolves siblings relative to the
# including file. If logos_lp_client.h lived only at the top-level
# include/ (the CMake-export layout) while logos_result.h is reached via
# include/cpp/, a single TU would pull logos_result.h through two
# distinct realpaths and #pragma once could not dedup them
# (redefinition of StdLogosResult). Ship every std header in BOTH roots.
for file in logos_module_context.h logos_json.h logos_result.h logos_caller.h logos_lp_client.h logos_async_result.h logos_host_services.h logos_host_core.h; do
cp cpp/$file $out/include/cpp/
cp cpp/$file $out/include/
done
runHook postInstall
'';
}