Repository navigation
Headless link -undefined dynamic_lookup allows undefined symbols, crashes at first call (Mac-only folderfrompath example) #884
Description
Rationale
The headless link uses -undefined dynamic_lookup, so a DECLARED-but-UNDEFINED symbol compiles AND links cleanly, then SEGFAULTS at first call. This creates a live landmine: every Mac-only declaration in shared headers is a silent trap for headless callers. Concrete cost during #859 rewrite: folderfrompath is declared in file.h (shared header) but Mac-only in implementation; calling it from frontier-cli crashed the CLI on every startup. With lldb attach unavailable in this environment, diagnosis required log-probe bisection to identify the correct portable primitive (portable_folderfrompath). This is a build-system debt that will recur with any future Mac-only symbol referenced by headless code.
Current State
Link-time issue (2026-08-10):
- Headless link:
-undefined dynamic_lookupflag allows undefined symbols - Symbol compiles and links cleanly with no error
- At runtime: first call to undefined symbol → SEGFAULT
- No compile-time or link-time warning
Concrete example (cost to #859):
folderfrompathdeclared in file.h (shared header)- Implementation is Mac-only (not in headless build)
- frontier-cli calls it → compiles and links fine → crashes on startup
- Diagnosis required: manual bisection via log probes (lldb unavailable in environment)
- Root cause: should use portable_folderfrompath (exists and is portable)
Risk scope:
- Every Mac-only function in shared headers is a potential landmine
- Headless callers unaware of platform restrictions
- Silent linking + runtime crash is worst-case diagnosis scenario
Desired Outcome
Prevent undefined symbols from silently compiling and crashing at runtime:
- Remove
-undefined dynamic_lookupfrom headless link (make undefined symbols a LINK error) - OR implement a link-time or CI symbol audit (catch undefined symbols before runtime)
- OR annotate all Mac-only declarations in shared headers (document the trap)
- Verify: no headless executable contains undefined symbols
Recommendation
Option A (preferred, if not load-bearing):
Remove -undefined dynamic_lookup from headless link
Make undefined symbols a LINK-TIME ERROR
Investigate why the flag was added (may have historical reason)
If removal breaks something, address the root cause properly
Option B (safety audit, if flag must stay):
Add CI check: nm -u <headless_binary> | grep -v '^U'
Compare against allowlist of expected-undefined (if any)
Fail build if unexpected undefined symbols found
Option C (documentation, at minimum):
Audit shared headers for Mac-only functions
Annotate each with // Mac-only or similar
Add section to docs/QUICK_REFERENCE.md: "Mac-only symbols in shared headers: folderfrompath"
Document portable alternatives (e.g., portable_folderfrompath)
Recommended: Option A first (investigate and remove flag if safe), then Option B (CI audit) as belt-and-suspenders.
Exit Criteria
- Investigate why
-undefined dynamic_lookupis in headless link - Remove flag if not load-bearing; if it is, document the reason
- No undefined symbols in headless executable (verify via
nm -u) - CI check added to catch future undefined symbols (if flag remains)
- Shared header annotations documenting Mac-only symbols
- portable_folderfrompath (or alternative) documented as recommended for headless
Related Context
- Found by: Virgin.root Frontier.pathString and guest-DB references point to deleted worktree (menu-port-phase-c-bridge); 8 occurrences #859 rewrite, 2026-08-10 (frontier-cli crash on startup)
- Related issues: Virgin.root Frontier.pathString and guest-DB references point to deleted worktree (menu-port-phase-c-bridge); 8 occurrences #859 (path corruption investigation, where it bit)
- Code: Headless link configuration, file.h (shared header), folderfrompath (Mac-only implementation)
- Diagnosis cost: Manual log-probe bisection (lldb unavailable in environment)
- Root cause class: Linker permissiveness + missing platform-specific annotations
- Precedent: portable_folderfrompath exists and is correct for headless
Methodology Note
The -undefined dynamic_lookup flag is a macOS feature that allows linking against symbols not yet defined, deferring resolution to runtime. This is useful for certain plugin/dynamic loading scenarios but is dangerous for headless CLIs where undefined symbols should be compile/link-time errors. The flag deserves investigation: it may be historical, or there may be a legitimate reason. But the current state (silent crash at startup) is indefensible.
Prevention for Future
- Audit all Mac-only declarations in shared headers (file.h, others)
- Document with annotations: // Mac-only: use portable_X for headless
- Add to code-review checklist: "New shared header declarations must note platform restrictions"
- CI symbol audit prevents recurrence
Activity