Foundation & CoreFoundation spike for freebsd-libxpc (superseded — see banner)

Companion spike to the freebsd-libxpc-plan: which Foundation and CoreFoundation implementation(s) do we use for the downstream daemon ports (configd, mDNSResponder, IPConfiguration, asl, notifyd, modern launchd)? Three CoreFoundation implementations exist in the open-source world plus two Foundation implementations; this doc audits each, measures what each daemon actually consumes from them, analyzes symbol-level coexistence, and recommends a layered approach.

SUPERSEDED 2026-05-15. The recommendation in §8 below — "build a hybrid libCFRuntime alongside libgnustep-corebase" — is no longer the project's plan. The current authoritative source for the CoreFoundation question is freebsd-launchctl-corefoundation-spike, which audited launchctl.c's actual symbol surface (49 distinct CF function calls + 17 types + 25 constants + 1 SPI symbol) against the three candidate implementations and found:

Current plan: vendor swift-corelibs-foundation's Sources/CoreFoundation/ as src/libCoreFoundation/ in freebsd-launchd-mach, build with DEPLOYMENT_RUNTIME_SWIFT=0 (avoids pulling the Swift runtime onto every system service), install as /usr/lib/system/libCoreFoundation.so.6. GNUstep is not on the freebsd-launchd-mach ISO — it remains the framework/app layer for the separate gershwin desktop overlay/fork.

The factual content below (per-implementation audits in §2–3, per-port symbol-usage audit in §4, coexistence analysis in §5, Mach gotchas in §6) is preserved as historical record. The recommendations in §1.4, §7, and §8 are not the current plan — read the launchctl spike instead.

Contents

  1. Vocabulary: Foundation vs CoreFoundation
  2. CoreFoundation implementations compared
  3. Foundation implementations compared
  4. Per-port CF/NS symbol usage audit
  5. Coexistence: what can be intermixed, what cannot
  6. Mach-specific gotchas
  7. Install layout — no modifications to libs-corebase upstream
  8. Recommendations
  9. References

1. Vocabulary: Foundation vs CoreFoundation

These two libraries get conflated regularly, so let's pin them down:

LibraryLanguageWhat's in itApple ships
CoreFoundation (CF*) Pure C Value types (CFString, CFArray, CFDictionary, CFNumber, CFData, CFDate), CFRunLoop, CFMachPort, CFPreferences, CFBundle, CFPlugIn, CFPropertyList, CFNotificationCenter, etc. Yes — in /System/Library/Frameworks/CoreFoundation.framework
Foundation (NS*) Objective-C (newer Apple parts: Swift) NSString, NSArray, NSDictionary, NSObject, NSXPCConnection, NSURL, NSURLSession, NSPropertyListSerialization, NSDistributedNotificationCenter, etc. Yes — in /System/Library/Frameworks/Foundation.framework

CoreFoundation sits below Foundation in the stack. Foundation classes are toll-free-bridged wrappers around the corresponding CoreFoundation types on macOS — NSString and CFStringRef are the same object. Apple's daemon code is overwhelmingly written in plain C against CoreFoundation; the Objective-C Foundation API is for application/framework callers.

Practical implication: when looking at any port, ask whether it uses CF* calls, NS* classes, or both. The answer dictates whether we need a CoreFoundation port, a Foundation port, or both. For the daemons in scope here, the answer is CoreFoundation only for the bulk of them — Foundation is essentially absent.

1.1. Component-by-component answer

Direct yes/no for every component we're porting or have already ported. Foundation = needs NS* classes (libgnustep-base). CoreFoundation = needs CF* functions (libgnustep-corebase + libCFRuntime).

ComponentFoundation?CoreFoundation?Why
mach.ko (kernel) no no Pure kernel C; no userland framework deps possible
libmach no no Userland syscall-trap wrappers; pure C, libc only
libdispatch (swift-corelibs-libdispatch + Mach backend) no no Pure C concurrency primitives; uses blocks runtime but not Foundation/CF
libxpc no no Pure C IPC; uses bundled libnv for serialization. The ravynOS/NextBSD libxpc tree is 100% C.
Apple launchd (clean launchd-842.92.1 import per the libxpc plan's Phase 5) no no 0 CF call sites, 0 NS references verified by grep across all 17 source files. Uses Apple's own launch_data_t bplist parser.
freebsd-launchd's current minimal rewrite yes — minimally no 1 .m file (src/src/core.m, 544 LoC) uses NSPropertyListSerialization for plist parsing. 33 NS references, 8 unique classes (NSString, NSDictionary, NSArray, NSData, NSNumber, NSError, NSPropertyListImmutable, NSPropertyListFormat) — all used as values, no NSObject subclasses. Removable: switch to libplist or CFLite plist parsing and the ObjC runtime requirement disappears.
launchctl (Apple verbatim) no no Pure C. The freebsd-launchd minimal launchctl is also pure C.
liblaunch (Apple / NextBSD / ravynOS / freebsd-launchd versions) no no Pure C launch_data_t client over AF_UNIX. 0 NS, 0 CF.
asl / syslogd / aslmanager / libasl no no All four pure C. 0 CF call sites, 0 NS references in the NextBSD tree (total ~34k LoC audited). Talk Mach + sockets + launchd directly.
notifyd no no Pure C (7,501 LoC). Modern Apple notifyd uses libxpc for client API but no CF/NS.
libnotify no no Pure C (5,736 LoC). 0 CF, 0 NS.
SystemConfiguration framework (SystemConfiguration.fproj, the client-facing lib) no yes — heavy Pure C against CF (69,656 LoC, 6,925 CF calls, 211 unique CF symbols, 0 NS). 30 CFMachPort sites = needs libCFRuntime for Mach-coupled CF.
configd daemon (configd.tproj only) light yes — heavy 841 CF calls, 99 unique CF symbols. 5 NS references concentrated in 1-2 .m files (NCDaemon shim). 1 user ObjC class (NCDaemon : NSObject) plus the small daemon-entry shim.
configd plugins (IPMonitor, EventFactory, PreferencesMonitor, KernelEventMonitor, LinkConfiguration, etc.) yes yes — heavy Most of the 720 NS references in configd come from here. 20 .m files across the tree. 4 user ObjC classes (ConfigAgent, DNSAgent, ProxyAgent, AgentController). Caveat: AgentController subclasses NWNetworkAgentRegistration from a private Apple Network.framework — flag as a separate porting blocker for IPMonitor's NWI agent path.
scutil no (small) yes Mostly C, CF-heavy. A few .m files but no ObjC subclasses essential to the CLI.
mDNSResponder daemon core (mDNSCore/, mDNSPosix/) no light Pure C in the daemon core. CF usage is in the macOS-specific helpers (mDNSMacOSX/) that BSD ports replace.
IPConfiguration (Apple's bootp client) no yes — heavy Per the ipconfiguration plan — pure C, no NS, heavy CF (CFArray/CFDict throughout, plus CFRunLoop).

1.2. Headline answers

Translated to dependencies:

1.3. Which implementation serves each component?

For every component that needs Foundation or CoreFoundation, what specifically supplies the symbols? The choice is between:

ComponentServes Foundation need withServes CoreFoundation need withNotes / blockers
mach.kon/an/aNo Foundation/CF deps
libmachn/an/aNo Foundation/CF deps
libdispatchn/an/aNo Foundation/CF deps
libxpcn/an/aNo Foundation/CF deps
Apple launchd (clean import)n/an/aNo Foundation/CF deps
launchctln/an/aNo Foundation/CF deps
liblaunchn/an/aNo Foundation/CF deps
asl / syslogd / aslmanager / libasln/an/aNo Foundation/CF deps
notifyd / libnotifyn/an/aNo Foundation/CF deps
freebsd-launchd rewrite libgnustep-base alone n/a Uses only basic NS classes (NSString, NSDictionary, NSArray, NSData, NSNumber, NSError, NSPropertyListSerialization, NSPropertyListImmutable, NSPropertyListFormat). All have been in libgnustep-base for decades. No private Apple frameworks. Or removable — swap NSPropertyListSerialization for libplist / CFLite plist parsing, drop ObjC entirely.
SystemConfiguration framework (SystemConfiguration.fproj) n/a (0 NS) libgnustep-corebase + libCFRuntime 211 unique CF symbols. Most served by corebase; the 30 CFMachPort sites and any CFRunLoop v1 Mach-port wait require libCFRuntime supplementation. No Foundation needed.
configd daemon (configd.tproj) libgnustep-base alone libgnustep-corebase + libCFRuntime 99 unique CF symbols (heavy — needs libCFRuntime for Mach paths). 5 NS references and 1 user ObjC class (NCDaemon : NSObject) — trivially served by libgnustep-base.
configd plugins (IPMonitor, EventFactory, PreferencesMonitor, KernelEventMonitor) libgnustep-base + private-Apple-framework blockers libgnustep-corebase + libCFRuntime Real blocker: AgentController subclasses NWNetworkAgentRegistration from Apple's private Network.framework — not in libgnustep-base, not in any open Foundation. Similarly EFEventFactory is from EventFactory.framework. These plugins need either a private-framework shim or partial omission. The bulk of NS use (NSString/NSArray/NSDictionary as values) is fine with libgnustep-base. Concentrated in IPMonitor; other plugins lighter.
scutil libgnustep-base (minimal) libgnustep-corebase + libCFRuntime Mostly C. The few .m files don't subclass anything Apple-private.
mDNSResponder daemon core n/a libgnustep-corebase alone (value types only) Daemon core (mDNSCore/mDNSPosix) is pure C with light CF (value types). No libCFRuntime needed in the core; only the macOS-specific helpers (which BSD ports skip) use heavier CF.
IPConfiguration n/a libgnustep-corebase + libCFRuntime Heavy CF including CFMachPort, CFRunLoop v1, CFPropertyList. No NS. Same shape as SystemConfiguration framework from a Foundation/CF standpoint.

1.4. Headline answers (restated more directly)

For Foundation: libgnustep-base alone serves every component that needs Foundation. No component requires Apple's closed Foundation or swift-corelibs-foundation. The freebsd-launchd rewrite uses NSPropertyListSerialization (long-standing libgnustep-base API). configd's daemon shim and plugins use basic NS-as-value patterns plus a small number of : NSObject subclasses — all fine for libgnustep-base. Only one real blocker exists at the Foundation level: configd's IPMonitor plugin subclasses NWNetworkAgentRegistration from Apple's private Network.framework, which has no open-source equivalent. That's a separate IPMonitor-specific porting problem, not a Foundation impl choice.

For CoreFoundation: libgnustep-corebase alone is NOT enough for the heavy-CF components. SystemConfiguration framework, configd, scutil, and IPConfiguration all use CFMachPort / CFRunLoop v1 Mach sources / CFPreferences / CFBundle plugin loader — all absent or stubbed in corebase. They need the supplementary libCFRuntime.so sourced from swift-corelibs-foundation CF (primary) + CF-Lite-1153.18 (Mach .c parts donor). The lighter components (mDNSResponder core) can get by with corebase alone.

swift-corelibs-foundation Foundation is NOT used. Not as a Foundation impl, not for Foundation needs of any component. It's only mined as a SOURCE for the CoreFoundation parts inside libCFRuntime.so (its Sources/CoreFoundation/ subdir). Its NS* Swift classes never ship.

2. CoreFoundation implementations compared

Three open-source CoreFoundation implementations exist. None is a clean drop-in for our daemon ports as-is; each has gaps.

Project basics

ImplementationRepoLicenseLatest updateMaintenance
GNUstep libs-corebase gnustep/libs-corebase LGPL 2.1 2021-09-30 (per ChangeLog) dormant
Apple CF-Lite apple-oss-distributions/CF tag CF-1153.18 APSL 2.0 2015-06-23 abandoned — post-2015 commits are metadata-only re-imports
swift-corelibs-foundation CoreFoundation swiftlang/swift-corelibs-foundation at Sources/CoreFoundation/ Apache 2.0 (Runtime Library Exception) 2026-04-16, active active — Apple-maintained, weekly-to-biweekly commits

Per-subsystem coverage

P = present and functional, p = partial / stub / API-only, A = absent.

CF subsystemGNUstep corebaseApple CF-Lite 1153.18swift-corelibs CF
CFString / CFArray / CFDictionary / CFNumber / CFData / CFDateP (ICU-backed)PP
CFTimeZone / CFLocale / CFCalendarPPP
CFCharacterSet / CFUUIDPPP
CFPropertyList (XML)PPP
CFPropertyList (binary plist)p — XML-only; CFBinaryPList absentPP
CFURLPPP + CFURLComponents
CFRunLoop sources0 / timers / observersP (poll-based)P (Mach-native)P (kqueue on BSD, epoll on Linux, Mach on Darwin)
CFRunLoop version1 Mach sourcesp — struct exists; getPort treated as pollable fd at CFRunLoop.c:705. TODO at :580 and :882Pp — on non-Darwin, __CFPort typedefs to int (kqueue/epoll fd), not mach_port_t
CFMachPortAP (CFMachPort.c)A on non-Darwin — header exists, body is #if TARGET_OS_MAC-gated
CFMessagePortAPA on non-Darwin (same gating as CFMachPort)
CFPreferencesAP (CFPreferences.c + CFApplicationPreferences.c + CFXMLPreferencesDomain.c)P — with TARGET_OS_BSD paths; stores plists under $HOME
CFBundle (basic)p — 268-line .m NSBundle-backed shim, drags in libgnustep-baseP (12 CFBundle_*.c files)P (12 files including extras like CFBundle_Main.c, _Executable.c)
CFBundle plugin/localized-string fnsAPP
CFPlugInAP (4 files: CFPlugIn.c + _Factory + _Instance + _PlugIn)p (only CFPlugIn.c)
CFStreamPP (+CFConcreteStreams, CFSocketStream)P
CFNotificationCenterAA (header in CFPriv.h; no impl)A (same)
CFHost / CFHTTPMessageAA (lives in CFNetwork, not CF-Lite)A
CFUserNotificationAPA on non-Darwin (header only)
CFFileDescriptorAA in open CF-Lite treeA
Apple-private extensions (CFStringIsValidDNSName, CFDataCopyVMData, etc.)Apartial in CFPriv.h / CFBundlePriv.hpartial in matching *Priv.h

Build system & FreeBSD portability

ImplementationBuild systemFreeBSD-ready?Patching scope
GNUstep corebase autoconf + gnustep-make yes — ships in FreeBSD ports tree none for value-type subset; greenfield for Mach (no Mach paths exist anywhere)
Apple CF-Lite 1153.18 Apple's internal Xcode/B&I; MakefileLinux for Linux noMakefileLinux deliberately excludes Mach files, no FreeBSD path significant — would need to rewrite the build, fix endianness for FreeBSD-aarch64, audit mach_port_t ABI compatibility with our libmach. Also assumes XNU's modern Mach (vouchers, mach_port_construct, mk_timer) that our libmach doesn't expose.
swift-corelibs-foundation CF CMake (and SwiftPM via Foundation umbrella) code is BSD-aware but CMake doesn't special-case FreeBSD moderate — CMakeLists detection needs FreeBSD case; Mach paths to be opened up via defined(__FreeBSD__) && defined(HAVE_LIBMACH); CFMachPort.c / CFMessagePort.c / CFUserNotification.c need .c bodies (borrow from CF-Lite). The Mach voucher / construct / mk_timer gaps still apply.

Per-implementation verdict

ImplVerdictRole in our stack
GNUstep corebase not viable as primary base — has value-types but none of the IPC/IO subsystems daemons depend on; version1 RunLoop sources are a fake-Mach stub Keep installed for GNUstep apps that already link it; do NOT rely on it for daemons. Possibly useful as reference for value-types implementations.
Apple CF-Lite 1153.18 parts donor only — 10+ years frozen, never had working non-Darwin build, but has the only open-source CFMachPort.c / CFMessagePort.c / CFPreferences.c / full CFBundle_* + CFPlugIn_* against real Mach Source-of-truth for Mach-side API contracts. Pluck specific .c files (CFMachPort, CFMessagePort, CFUserNotification) and graft onto the primary base. Don't try to build CF-Lite as a standalone library.
swift-corelibs-foundation CF primary base — recommended Actively maintained, Apache 2.0, already has TARGET_OS_BSD / __FreeBSD__ branches in CFRunLoop and CFPlatform with kqueue, ships CFPreferences and full CFBundle_* with BSD paths. Gap exactly maps to what's special about our project: CFMachPort, CFMessagePort, CFUserNotification have headers but no .c on non-Darwin. Bridge by porting CF-1153.18's Mach .c files onto libmach.

3. Foundation implementations compared

Two open-source Foundation implementations. The two cannot coexist in one process — both export NSString, NSArray, etc. as competing class definitions. Consumers must pick one.

Project basics

ImplementationRepoLicenseRuntimeFreeBSD support
GNUstep libs-base (libgnustep-base) gnustep/libs-base LGPL (library), GPL (tools) libobjc2 (or older libobjc/GCC) tier-1 — FreshPorts lang/gnustep-base, long-standing
swift-corelibs-foundation swiftlang/swift-corelibs-foundation Apache 2.0 Swift runtime (no ObjC) unofficial — Swift 6.2 added FreeBSD as a platform; corelibs-foundation not regularly CI'd on it

API coverage

P = present and functional, p = partial / has gaps, S = stub / API-only, A = absent.

Class familyGNUstep libs-baseswift-corelibs-foundation
NSString / NSData / NSArray / NSDictionary / NSSet / NSNumber / NSValuePP
NSDate / NSCalendar / NSTimeZone / NSLocalePP
NSCoder / NSKeyedArchiver/Unarchiver / NSSecureCodingPP
NSPropertyListSerializationP (NSPropertyList.m)P (PropertyListSerialization.swift)
NSURL / NSURLRequestPP
NSURLSessionp — experimental, added in 1.31.0P (in FoundationNetworking, libcurl-backed)
NSStream / NSInputStream / NSOutputStreamPP
NSFileManager / NSFileHandlePP
NSNotificationCenterPP
NSDistributedNotificationCenterP (NSDistributedNotificationCenter.m)A
NSThread / NSLock / NSOperation / NSOperationQueuePP
NSRunLoop / NSPort / NSPortMessageP (deep, with NSConnection integration)p (primitives only)
NSConnection / NSDistantObject (Distributed Objects)P (full historical impl)A
NSXPCConnection / NSXPCInterface / NSXPCListener / NSXPCListenerEndpointS — class skeletons exist (Source/NSXPCConnection.m), every method body returns [self notImplemented: _cmd]. Added Nov 2019, shipped in 1.27.0.A — not declared at all
NSBundlePP
NSProcessInfo / NSTask / NSPipePP
NSUserDefaultsPP
NSHost / NSNetServiceP (Avahi-backed mDNS)p (Host only; NetService partial)
NSError / NSException / NSAutoreleasePool / NSDecimalNumberPP (NSAutoreleasePool is a no-op for Swift)

NSXPCConnection availability

GNUstep: NSXPCConnection, NSXPCListener, NSXPCInterface, NSXPCListenerEndpoint are declared with Apple-compatible method signatures (added Nov 2019 by Gregory Casamento, shipped in libs-base 1.27.0). Bodies uniformly call [self notImplemented: _cmd]. A real implementation against our libxpc would be a tractable downstream contribution — the headers and class shapes are already there.

swift-corelibs-foundation: NSXPCConnection is completely absent. The class is not declared in any header. Release notes call out these facilities as "not available outside of Darwin."

Verdict

ImplVerdict for our use case
GNUstep libs-base recommended — builds on FreeBSD today, ships NSPropertyListSerialization (what freebsd-launchd's core.m already uses), has Distributed Objects infrastructure as a stepping-stone toward NSXPCConnection, and an existing NSXPCConnection stub that's filled-in-able against libxpc. Aligns with the project's scope_split rule ("GNUstep owns the framework / application layer").
swift-corelibs-foundation not appropriate for our daemon-driven stack — Swift not ObjC (incompatible with freebsd-launchd's core.m), no NSXPCConnection, no Distributed Objects, not regularly CI'd on FreeBSD, drags in its own bundled CoreFoundation that would clash with anything else providing CF. May be relevant later if a Swift-on-FreeBSD subproject emerges.

4. Per-port CF/NS symbol usage audit

Empirical measurement of how much each Apple-source port actually consumes from CoreFoundation and Foundation. Numbers from grep/find/wc over the local clones of NextBSD, ravynOS, freebsd-launchd, and freebsd-launchd-mach. Filtered for false positives (NSEC, NSIG, NSOLE from tv_nsec, CONSOLE, etc.).

Summary

PortCF call sitesNS references (real)Mach-coupled CFObjC runtime needed
launchd (Apple verbatim, ravynOS / nextbsd copies)00nono
freebsd-launchd rewrite033 (8 unique NS classes, plist-only, all in one .m)noyes (1 .m file)
configd (whole tree)12,968 (264 unique CF symbols)720 (26 unique classes, 5 user subclasses)yes (42 CFMachPort sites)yes (20 .m files)
configd.tproj (daemon proper)841 (99 unique)5yesyes
SystemConfiguration.fproj6,925 (211 unique)0yes (30 CFMachPort sites)no
asl / syslogd / aslmanager / libasl00nono
notifyd / libnotify00nono

Notes:

Top CF functions across configd

The 264 unique CF symbols configd consumes are dominated by a small core (~30 functions) plus a long tail. Top 30 calls by count:

2626 CFSTR              2496 CFRelease            641 CFDictionaryGetValue
 488 CFArrayGetCount     469 CFEqual              408 CFDictionarySetValue
 390 CFArrayGetValueAtIndex                       282 CFRetain
 226 CFArrayAppendValue  209 CFDictionaryRemoveValue
 198 CFStringAppendFormat 195 CFRangeMake
 184 CFDictionaryCreateMutable                    176 CFArrayCreateMutable
 157 CFStringCreateWithFormat                     151 CFStringCreateWithCString
 151 CFDictionaryCreateMutableCopy                110 CFAllocatorDeallocate
 109 CFStringGetLength   100 CFDictionaryGetCount  98 CFDictionaryContainsKey
  94 CFNumberGetValue     77 CFAllocatorAllocate   73 CFNumberCreate
  65 CFArrayContainsValue 64 CFDictionaryAddValue  60 CFBooleanGetValue
  59 CFDataGetBytePtr     54 CFArrayCreateMutableCopy

Mach-coupled CF in configd (the corebase-incompatible surface):

15 CFMachPortGetPort      9 CFMachPortCreateWithPort
 9 CFMachPortCreateRunLoopSource  7 CFMachPortInvalidate
 2 CFMachPortCreate                Total: 42 sites, 0 CFMessagePort

42 sites across configd plus 30 in SystemConfiguration.fproj = ~72 total Mach-coupled call sites. This is the surface that requires real CFMachPort over our libmach.

5. Coexistence: what can be intermixed, what cannot

5.1. Two CoreFoundation implementations in one process

Cannot coexist in one process. All CF implementations export the same C-linkage names (CFStringCreateWithCString, CFRelease, kCFAllocatorDefault, kCFBooleanTrue, etc.). The ELF dynamic linker resolves each symbol from whichever .so appears first in DT_NEEDED order — silently. Internal type tables (__kCFArrayTypeID, _kCFRuntimeNotATypeID) and runtime registration would race on init. Behavior unpredictable.

Hard rule: one CoreFoundation impl per process. The choice is at link time, not runtime.

5.2. Two Foundation implementations in one process

Cannot coexist in one process. Both libgnustep-base and swift-corelibs-foundation define OBJC_CLASS_$_NSString (or the Swift mangled equivalent), NSArray, NSDictionary, etc. The ObjC runtime would refuse the second class registration or silently keep the first. Additionally:

Hard rule: one Foundation impl per process. Pick one based on what consumers need.

5.3. Library coexistence on disk

Different libraries CAN live on disk simultaneously — it's the in-process linking that's constrained. Practical pattern: ship multiple Foundations / CF impls; have downstream daemons explicitly pick which one to link.

PairOn disk?In one process?Notes
libgnustep-corebase + Apple CF-LiteSymbol clash on every CF function
libgnustep-corebase + swift-corelibs CFSame problem; swift-corelibs CF is a CF-Lite descendant
libgnustep-base + swift-corelibs FoundationObjC class registration conflict
libgnustep-base + libgnustep-corebaseDesigned to coexist; current GNUstep pattern
swift-corelibs Foundation + bundled swift-corelibs CFDesigned as a single ecosystem; bundled CF is intentional
libgnustep-corebase + supplementary libCFRuntime.so (Option B below)If supplementary library exports ONLY missing symbols (CFMachPort*, CFPreferences*, etc.) and links AGAINST corebase — no overlap

5.4. Intermixing options for filling corebase's gaps

OptionApproachProsCons
A. Hybrid library Merge corebase value types + Mach .c files from CF-Lite + swift-corelibs RunLoop into a single libCoreFoundation.so. Single symbol set, single library, single -lCoreFoundation for consumers. Internal struct layouts differ between corebase and CF-Lite (CFRuntimeBase packing, isa/swizzling layouts). Would need adapter glue per CFType. Equivalent in cost to forking CF-Lite slowly.
B. Supplementary library (recommended) Ship a separate libCFRuntime.so that exports ONLY corebase's missing symbols (CFMachPort, CFMessagePort, CFPreferences, CFBundle plugin loader, CFFileDescriptor, CFPlugIn). Link against libgnustep-corebase via DT_NEEDED. New types registered via corebase's _CFRuntimeRegisterClass. Clean separation. Consumers link -lgnustep-corebase -lCFRuntime. corebase upstream stays untouched. Needs ABI alignment between corebase's CFRuntimeBase and CF-Lite's (different packing). Requires writing the missing CF code from scratch, or porting CF-Lite's implementations onto corebase's CFRuntime base type.
C. Upstream contributions to libs-corebase Add CFMachPort, CFMessagePort, CFPreferences, CFFileDescriptor, full CFPlugIn, CFNotificationCenter to corebase upstream. Single library, no fragmentation, benefits all GNUstep deployments. GNUstep upstream conservative on Mach-specific FreeBSD-only code. Long timeline (months-to-years) for review and merge.
D. Replace corebase entirely with swift-corelibs-foundation CF Drop libgnustep-corebase from the stack; use swift-corelibs CF as the sole CF impl. Single library, full Apple CF surface, actively maintained. Breaks existing GNUstep apps that already link libgnustep-corebase; needs libgnustep-base rebuild against new CF; swift-corelibs-foundation assumes its own Foundation owns NSCF bridging — conflicts with libgnustep-base.

Recommended: Option B + bridge from swift-corelibs-foundation CF. Build libCFRuntime.so with implementations of the missing CF subsystems (CFMachPort, CFMessagePort, CFRunLoop v1 Mach sources, CFPreferences, CFBundle plugin loader, CFFileDescriptor, CFPlugIn). Source those implementations from swift-corelibs-foundation's CF tree (the most-current open code) with Mach paths enabled via __FreeBSD__. Where swift-corelibs has only header (no .c) for Mach-specific files, port CF-Lite-1153.18's matching .c file. Result: corebase upstream untouched, FreeBSD's CoreFoundation surface complete enough for daemon ports.

6. Mach-specific gotchas

Mach support is the single dimension that separates "drop-in build" from "real porting work":

ImplMach statePatching gap
GNUstep corebase No Mach paths at all. CFRunLoop.c:705 uses mach_port_t port but it's a typedef'd int referring to a poll fd. Pure POSIX poll loop. Greenfield — would need to write CFMachPort, CFMessagePort, CFRunLoop v1 Mach sources from scratch.
Apple CF-Lite 1153.18 Pure Mach. CFMachPort.c and CFMessagePort.c have zero non-Apple #ifdefs — written against mach/port.h, mach_msg, dispatch sources. CFRunLoop.c has only macOS/iOS/Windows arms. Assumes the full XNU Mach ABI: mach_msg, mach_port_construct, mach_port_allocate, mach_port_request_notification (dead-name + send-once), mach_voucher_t, mk_timer_create/arm/destroy (XNU kernel-side Mach timer ports, not standard IPC). Our libmach has the basics. Vouchers, mach_port_construct/destruct, mk_timer are all gaps. Estimated 500-1500 LOC of #ifdef'd fallbacks per file.
swift-corelibs-foundation CF #if TARGET_OS_MAC / __APPLE__ gates around almost all CFMachPort and CFRunLoop-v1 code paths. Non-Apple builds replace with dispatch_source_t-based event delivery. __CFPort typedefs to int on BSD. Opt-in: enable via defined(__FreeBSD__) && defined(HAVE_LIBMACH). Voucher paths already conditional on __has_include(<mach/mach_voucher.h>) — clean if our libmach doesn't ship that header. mk_timer emulation via dispatch sources may be feasible. Estimated 2-4 weeks of focused work to enable CFMachPort path.

Key architectural decision for libCFRuntime.so: on FreeBSD with our libmach, does CFRunLoopSourceContext1.getPort return a real mach_port_t, or do we route Mach receive through a kqueue user-filter wrapper? The former is closer to Apple semantics; the latter integrates more naturally with libdispatch's BSD backend. This decision affects both libCFRuntime.so AND the libdispatch Mach backend (Phase 0 of the libxpc plan) — they should agree.

7. Install layout — no modifications to libs-corebase upstream

The constraint is explicit: libs-corebase upstream stays untouched. No modifications to its GNUmakefile, header tree, or SONAME. Supplementary code lives next door.

Concrete proposal

This proposal places extras alongside corebase without colliding with it. Specific paths use Gershwin's current layout pattern (/System/Library/...) where Gershwin already installs libdispatch/libgnustep-base/libgnustep-corebase, but the same shape works at any prefix — FreeBSD-base-system style (/usr/lib) or ports-style (/usr/local/lib) works equivalently.

ArtifactInstall pathRationale
libCFRuntime.so — supplementary CF subsystems (CFMachPort, CFMessagePort, CFPreferences, CFBundle plugin loader, CFFileDescriptor, CFPlugIn) same directory as libgnustep-corebase.so Existing GNUstep ld.so.conf drop-in already covers this dir. After install, service ldconfig restart picks up the new .so.
Supplementary headers (CFMachPort.h, CFMessagePort.h, CFPreferences.h, CFFileDescriptor.h, CFNotificationCenter.h, CFPlugIn.h) $HEADERS/CoreFoundationExtras/ (separate from corebase's $HEADERS/CoreFoundation/) Mixing into corebase's header dir would risk being clobbered on corebase upgrade. Separate directory keeps the upgrade-safe boundary clean. Downstream daemons add -I$HEADERS/CoreFoundationExtras -I$HEADERS to their CFLAGS.
pkg-config metadata $LIBS/pkgconfig/CoreFoundationExtras.pc Requires: gnustep-corebase + Libs: -lCFRuntime — consumers can pkg-config --cflags --libs CoreFoundationExtras
DT_NEEDED libCFRuntime.so declares NEEDED libgnustep-corebase.so.X Forces CFRetain/CFRelease/CFRuntimeCreateInstance to resolve to corebase. Verify with objdump -p libCFRuntime.so | grep NEEDED.
SONAME independent: libCFRuntime.so.1 Bumping corebase's minor doesn't force a CFRuntime rebuild as long as corebase's CFRuntimeBase ABI is stable.

What this layout enables

Foundation layer

For NS, the recommendation is straightforward: libgnustep-base only. swift-corelibs-foundation stays out of the system Foundation slot. Future Swift-on-FreeBSD work can pull in swift-corelibs-foundation as a separate package (different SONAME, different headers dir, only loaded into Swift-aware processes).

8. Recommendations (superseded — see banner at top)

The recommendations preserved below were the conclusion of this spike at the time of writing (early 2026). They are no longer the project's plan. See the banner at the top of this doc for the current direction, or jump straight to freebsd-launchctl-corefoundation-spike for the audit-driven replacement.

Short version of what changed: the launchctl audit (2026-05-15) found that libs-corebase's plist code paths are stubbed (XML read returns NULL, binary read #if 0'd, binary write empty body) — fatal for launchctl, which exists to load .plist files. swift-corelibs-foundation's pure-C CF ships the real Apple plist driver and the full SPI surface. The "hybrid libCFRuntime + libgnustep-corebase" recommendation below was based on an earlier read of libs-corebase that didn't dig into the plist stubs.

Original recommendations (kept as historical record):

For freebsd-launchd-mach

  1. Build libCFRuntime.so as a sibling of libxpc — the supplementary CF library following the Option B layout. Source the CFMachPort / CFMessagePort / CFRunLoop v1 implementations from swift-corelibs-foundation CF with Mach paths enabled via __FreeBSD__ && HAVE_LIBMACH. Fall back to CF-Lite-1153.18 for any .c files swift-corelibs only ships as headers (CFMachPort.c, CFMessagePort.c, CFUserNotification.c).
  2. Keep libgnustep-corebase as the CF value-type backbone. Its CFString / CFArray / CFDictionary / CFNumber / CFData / CFPropertyList are battle-tested. Don't reinvent.
  3. Keep libgnustep-base as the only Foundation impl. Per scope_split — GNUstep owns Foundation. Don't ship swift-corelibs-foundation.
  4. Reconsider freebsd-launchd's core.m — once libCFRuntime ships CFPropertyList via swift-corelibs CF (which already supports binary plist), the only reason freebsd-launchd's launchd is in Objective-C goes away. Could convert core.m to core.c against CFLite-style plist API. Removes the ObjC runtime dependency from launchd entirely. Optional cleanup, not required.

For downstream daemons

  1. launchd (clean Apple launchd-842.92.1 import per the libxpc plan Phase 5): plain C, no CF, no NS. No new infrastructure required.
  2. asl / syslogd / aslmanager / libasl: plain C, no CF, no NS. Direct ports from apple-oss-distributions — no Foundation work.
  3. notifyd / libnotify: same — plain C, no CF, no NS.
  4. configd / SystemConfiguration.framework: link -lgnustep-corebase -lCFRuntime. The 20 .m files in configd add an ObjC runtime requirement (libgnustep-base for the 5 user-defined ObjC classes; flag NWNetworkAgentRegistration as a private-Network.framework blocker to address separately).
  5. mDNSResponder: daemon core is plain C. The macOS-specific helper code that uses CF can be skipped (FreeBSD doesn't need it).
  6. IPConfiguration: heavy CF, no NS — same as configd minus the ObjC subclasses. -lgnustep-corebase -lCFRuntime.

Current recommendations (per launchctl-corefoundation-spike):

  1. Vendor swift-corelibs-foundation's Sources/CoreFoundation/ as src/libCoreFoundation/. Build with DEPLOYMENT_RUNTIME_SWIFT=0. Install at /usr/lib/system/libCoreFoundation.so.6.
  2. Install ICU as a build dep (FreeBSD pkg). Keep all CF source files in scope (CFLocale / CFCalendar / CFDateFormatter / CFCharacterSet etc.) for future configd / IPConfiguration / SystemConfiguration consumers.
  3. Drop the upstream BlockRuntime/ subdir — redundant with the libBlocksRuntime.so already bundled by our vendored swift-corelibs-libdispatch build.
  4. All Apple-source system services (launchctl, configd, IPConfiguration, mDNSResponder, asl, notifyd, DiskArbitration) link -lCoreFoundation from /usr/lib/system/. Apple's upstream MIG-generated Mach IPC stubs are used as-is — we now have mach.ko, so the earlier "GNUstep DO over AF_UNIX" rewrites in the daemon plans (for the sockets-version freebsd-launchd fork) are not done here.
  5. GNUstep stays in the gershwin desktop overlay — a separate fork on top of freebsd-launchd-mach. Not on this ISO. Its libobjc2 + libgnustep-base + libgnustep-corebase serve the framework/app layer (NSAppKit equivalents, GUI), which the system services do not need.
  6. NSXPCConnection remains a downstream GNUstep enhancement opportunity if a future GNUstep desktop app wants Cocoa-shape IPC into our libxpc-managed system services. Not on this critical path.

9. References

Repos audited

Related plans

Last updated 2026-05-11. Spike compiled from four parallel agent investigations: CoreFoundation implementation comparison, Foundation implementation comparison, per-port symbol audit (grep over local clones of NextBSD, ravynOS, freebsd-launchd), and coexistence/install-layout analysis. Every URL has been verified. Numbers from wc -l, grep -c, and git log over the cited paths. Where research couldn't determine an answer (mDNSResponder and IPConfiguration audit deferred — no local clones), the document says so explicitly.