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:
libs-base contributes zero CF C surface (Obj-C NS only).libs-corebase has the right shape but its plist parser is stubbed (XML read returns NULL, binary read #if 0'd, binary write empty body) — fatal for launchctl.CFPriv.h, CFLogUtilities.h).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.
These two libraries get conflated regularly, so let's pin them down:
| Library | Language | What's in it | Apple 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.
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).
| Component | Foundation? | 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). |
.m files live..m file for plist parsing. Removable.Translated to dependencies:
For every component that needs Foundation or CoreFoundation, what specifically supplies the symbols? The choice is between:
NS* classes)CF* — CFString/Array/Dict/Number/Data/Date/PropertyList plus a poll-based CFRunLoop)| Component | Serves Foundation need with | Serves CoreFoundation need with | Notes / blockers |
|---|---|---|---|
| mach.ko | n/a | n/a | No Foundation/CF deps |
| libmach | n/a | n/a | No Foundation/CF deps |
| libdispatch | n/a | n/a | No Foundation/CF deps |
| libxpc | n/a | n/a | No Foundation/CF deps |
| Apple launchd (clean import) | n/a | n/a | No Foundation/CF deps |
| launchctl | n/a | n/a | No Foundation/CF deps |
| liblaunch | n/a | n/a | No Foundation/CF deps |
| asl / syslogd / aslmanager / libasl | n/a | n/a | No Foundation/CF deps |
| notifyd / libnotify | n/a | n/a | No 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. |
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.
Three open-source CoreFoundation implementations exist. None is a clean drop-in for our daemon ports as-is; each has gaps.
| Implementation | Repo | License | Latest update | Maintenance |
|---|---|---|---|---|
| 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 |
P = present and functional, p = partial / stub / API-only, A = absent.
| CF subsystem | GNUstep corebase | Apple CF-Lite 1153.18 | swift-corelibs CF |
|---|---|---|---|
| CFString / CFArray / CFDictionary / CFNumber / CFData / CFDate | P (ICU-backed) | P | P |
| CFTimeZone / CFLocale / CFCalendar | P | P | P |
| CFCharacterSet / CFUUID | P | P | P |
| CFPropertyList (XML) | P | P | P |
| CFPropertyList (binary plist) | p — XML-only; CFBinaryPList absent | P | P |
| CFURL | P | P | P + CFURLComponents |
| CFRunLoop sources0 / timers / observers | P (poll-based) | P (Mach-native) | P (kqueue on BSD, epoll on Linux, Mach on Darwin) |
| CFRunLoop version1 Mach sources | p — struct exists; getPort treated as pollable fd at CFRunLoop.c:705. TODO at :580 and :882 | P | p — on non-Darwin, __CFPort typedefs to int (kqueue/epoll fd), not mach_port_t |
| CFMachPort | A | P (CFMachPort.c) | A on non-Darwin — header exists, body is #if TARGET_OS_MAC-gated |
| CFMessagePort | A | P | A on non-Darwin (same gating as CFMachPort) |
| CFPreferences | A | P (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-base | P (12 CFBundle_*.c files) | P (12 files including extras like CFBundle_Main.c, _Executable.c) |
| CFBundle plugin/localized-string fns | A | P | P |
| CFPlugIn | A | P (4 files: CFPlugIn.c + _Factory + _Instance + _PlugIn) | p (only CFPlugIn.c) |
| CFStream | P | P (+CFConcreteStreams, CFSocketStream) | P |
| CFNotificationCenter | A | A (header in CFPriv.h; no impl) | A (same) |
| CFHost / CFHTTPMessage | A | A (lives in CFNetwork, not CF-Lite) | A |
| CFUserNotification | A | P | A on non-Darwin (header only) |
| CFFileDescriptor | A | A in open CF-Lite tree | A |
Apple-private extensions (CFStringIsValidDNSName, CFDataCopyVMData, etc.) | A | partial in CFPriv.h / CFBundlePriv.h | partial in matching *Priv.h |
| Implementation | Build system | FreeBSD-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 |
no — MakefileLinux 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. |
| Impl | Verdict | Role 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. |
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.
| Implementation | Repo | License | Runtime | FreeBSD 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 |
P = present and functional, p = partial / has gaps, S = stub / API-only, A = absent.
| Class family | GNUstep libs-base | swift-corelibs-foundation |
|---|---|---|
| NSString / NSData / NSArray / NSDictionary / NSSet / NSNumber / NSValue | P | P |
| NSDate / NSCalendar / NSTimeZone / NSLocale | P | P |
| NSCoder / NSKeyedArchiver/Unarchiver / NSSecureCoding | P | P |
| NSPropertyListSerialization | P (NSPropertyList.m) | P (PropertyListSerialization.swift) |
| NSURL / NSURLRequest | P | P |
| NSURLSession | p — experimental, added in 1.31.0 | P (in FoundationNetworking, libcurl-backed) |
| NSStream / NSInputStream / NSOutputStream | P | P |
| NSFileManager / NSFileHandle | P | P |
| NSNotificationCenter | P | P |
| NSDistributedNotificationCenter | P (NSDistributedNotificationCenter.m) | A |
| NSThread / NSLock / NSOperation / NSOperationQueue | P | P |
| NSRunLoop / NSPort / NSPortMessage | P (deep, with NSConnection integration) | p (primitives only) |
| NSConnection / NSDistantObject (Distributed Objects) | P (full historical impl) | A |
| NSXPCConnection / NSXPCInterface / NSXPCListener / NSXPCListenerEndpoint | S — 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 |
| NSBundle | P | P |
| NSProcessInfo / NSTask / NSPipe | P | P |
| NSUserDefaults | P | P |
| NSHost / NSNetService | P (Avahi-backed mDNS) | p (Host only; NetService partial) |
| NSError / NSException / NSAutoreleasePool / NSDecimalNumber | P | P (NSAutoreleasePool is a no-op for Swift) |
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."
| Impl | Verdict 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. |
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.).
| Port | CF call sites | NS references (real) | Mach-coupled CF | ObjC runtime needed |
|---|---|---|---|---|
| launchd (Apple verbatim, ravynOS / nextbsd copies) | 0 | 0 | no | no |
| freebsd-launchd rewrite | 0 | 33 (8 unique NS classes, plist-only, all in one .m) | no | yes (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) | 5 | yes | yes |
| SystemConfiguration.fproj | 6,925 (211 unique) | 0 | yes (30 CFMachPort sites) | no |
| asl / syslogd / aslmanager / libasl | 0 | 0 | no | no |
| notifyd / libnotify | 0 | 0 | no | no |
Notes:
launchd-842.92.1 is plain C with no CoreFoundation and no Foundation. Confirmed via grep. Uses launchd's own launch_data_t bplist parser.src/src/core.m (544 LoC). Used for plist parsing via NSPropertyListSerialization. Replacing that one file with libplist or CFLite plist parsing eliminates the ObjC runtime requirement entirely from the freebsd-launchd port.Plugins/, sctest/, EventFactory/. Only 5 user-defined ObjC classes total (3 NSObject subclasses, 1 EFEventFactory subclass, 1 NWNetworkAgentRegistration subclass — the last implies a private Network.framework dependency in IPMonitor that's a separate porting blocker).mDNSCore/, mDNSPosix/) is pure C with no CF; CF surface concentrated in macOS-specific helpers we don't need. IPConfiguration is heavy CF, no NS (per the existing freebsd-ipconfiguration-plan).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.
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.
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.
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.
| Pair | On disk? | In one process? | Notes |
|---|---|---|---|
| libgnustep-corebase + Apple CF-Lite | ✅ | ❌ | Symbol clash on every CF function |
| libgnustep-corebase + swift-corelibs CF | ✅ | ❌ | Same problem; swift-corelibs CF is a CF-Lite descendant |
| libgnustep-base + swift-corelibs Foundation | ✅ | ❌ | ObjC class registration conflict |
| libgnustep-base + libgnustep-corebase | ✅ | ✅ | Designed to coexist; current GNUstep pattern |
| swift-corelibs Foundation + bundled swift-corelibs CF | ✅ | ✅ | Designed 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 |
| Option | Approach | Pros | Cons |
|---|---|---|---|
| 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.
Mach support is the single dimension that separates "drop-in build" from "real porting work":
| Impl | Mach state | Patching 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.
The constraint is explicit: libs-corebase upstream stays untouched. No modifications to its GNUmakefile, header tree, or SONAME. Supplementary code lives next door.
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.
| Artifact | Install path | Rationale |
|---|---|---|
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. |
-lgnustep-corebase -lCFRuntime. They get the full CF surface they need without anyone modifying corebase.-lgnustep-corebase as today. No change.make install over their files; libCFRuntime sits next door, untouched.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).
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.
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).scope_split — GNUstep owns Foundation. Don't ship swift-corelibs-foundation.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.launchd-842.92.1 import per the libxpc plan Phase 5): plain C, no CF, no NS. No new infrastructure required.apple-oss-distributions — no Foundation work.-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).-lgnustep-corebase -lCFRuntime.Sources/CoreFoundation/ as src/libCoreFoundation/. Build with DEPLOYMENT_RUNTIME_SWIFT=0. Install at /usr/lib/system/libCoreFoundation.so.6.BlockRuntime/ subdir — redundant with the libBlocksRuntime.so already bundled by our vendored swift-corelibs-libdispatch build.-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.CF-1153.18, 2015-06-23)freebsd-launchd/configd/launchd-842.92.1 (2014)freebsd-libxpc-plan — the parent porting plan this spike informsfreebsd-launchd-mach-plan — mach.ko (kernel side)freebsd-launchd-plan — the minimal launchd portfreebsd-configd-plan — configd phasingfreebsd-ipconfiguration-plan — deferred network config portLast 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.