Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
In my last post, you learned how the compiler compiles the source files. This time around, you’ll see how everything is pulled together.
Now that we’ve compiled all the files, we can link them together.
Here’s the full linker invocation; it’s far shorter than the compilerinvocation:
Ld /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Products/Debug-iphonesimulator/ImportSwift.app/ImportSwift normal x86_64
cd /Users/jeremy/Workpad/BNR/ImportSwift
export IPHONEOS_DEPLOYMENT_TARGET=10.1
export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch x86_64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.1.sdk -L/Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Products/Debug-iphonesimulator -F/Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Products/Debug-iphonesimulator -filelist /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/ImportSwift.LinkFileList -Xlinker -rpath -Xlinker @executable_path/Frameworks -mios-simulator-version-min=10.1 -Xlinker -object_path_lto -Xlinker /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/ImportSwift_lto.o -Xlinker -export_dynamic -Xlinker -no_deduplicate -Xlinker -objc_abi_version -Xlinker 2 -fobjc-arc -fobjc-link-runtime -Xlinker -dependency_info -Xlinker /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/ImportSwift_dependency_info.dat -o /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Products/Debug-iphonesimulator/ImportSwift.app/ImportSwift
Ld
$(CONFIGURATION_BUILD_DIR)/$(EXECUTABLE_PATH)
normal
x86_64
Link the normal build variant executable for x86_64 in place in theCONFIGURATION_BUILD_DIR
.
(You might wonder where the “d” is in “link”. The “d” is actually in “load”,and you might still hear a “linker” called a “linker-loader”,but we mostly take the loading bit for granted these days.)
Frontend, architecture, and search path/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang-arch x86_64-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.1.sdk
Rather than invoke the linker directly,Xcode opts to let the clang frontend manage all the details for it.In order to do that, it needs to pass many of the options used duringcompilation in order to preserve the same “environment” for linking,so we’ll meet many familiar flags this time around,like -arch
and -isysroot
for setting the target architectureand the effective system root directory for the baked-in searchpaths.
-L/Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Products/Debug-iphonesimulator
-F/Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Products/Debug-iphonesimulator
And more search path munging here.
Listing the input object files-filelist /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/ImportSwift.LinkFileList
This is new! And as you might gather from that .LinkFileList
path extension, it’s specifically aimed at the linker.
It’s exactly what it looks like: a path to a file that lists all the filesto link. This avoids sticking oodles of file paths on the command line asarguments in their own right. The file itself is just a newline-delimited listof file paths. In this case, it’s really short, because we only compiledtwo object files:
/Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/AppDelegate.o
/Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/main.o
Our first linker-escaped argument: Runtime search path-Xlinker -rpath -Xlinker @executable_path/Frameworks
This batch of arguments is making sure the dynamic loader dyld
finds embeddedframeworks when it goes to link the app for running.
If you’ve rooted around inside a .app
bundle on disk,you’ll see that there’s an executable, and alongside it, there’s a Frameworksfolder, and in there is where all the embedded frameworks go.
Embedded frameworks get linked in as”eh, you know, @rpath/libwhatever.dylib, it’s on the runtime search path, gofind it, OK?” This batch of argumentsis what makes sure that that embedded Frameworks directory in the app bundlewinds up on that search path.
Syntactically speaking, this is our first linker-pass-through flag.-Xlinker
is a way to tunnel arguments through the clang
driver to theunderlying linker when there’s a flag the linker understandsbut clang
does not.
The args Xcode is trying to pass to the linker are-rpath @executable_path/Frameworks
,but to get both the flag and its argument past clang
,it has to escape both words with -Xlinker
,hence the use of -Xlinker
in front of each intended linker argument.
The -rpath
flag tells the linker to add the path that is its argumentto the runpath search path list in the final linked output file.The runpath search path list is in turn used by the dynamic loader dyld
to find the dynamic libraries (dylibs) it should link the binary againstwhen the dylib path is runpath-relative.Runpath-relative dylib paths begin with @rpath/
.
So, what’s that @executable_path
bit in the argument?That’s yet another magic path symbol for the dynamic linker;it’s used to talk about path relative to whatever the executable isthat dyld is trying to link in order to run.
Deployment target (again)-mios-simulator-version-min=10.1
Yawn, machine-related flag, seen this show already, moving on.
Linker configuration: LTO and Obj-C-Xlinker -object_path_lto -Xlinker /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/ImportSwift_lto.o-Xlinker -export_dynamic-Xlinker -no_deduplicate-Xlinker -objc_abi_version -Xlinker 2
Here’s a good batch of linker pass-throughs.Let’s elide the -Xlinker
escapes and take a closer look at them one by one.
Persisting any temporary object file used for LTO-object_path_lto /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/ImportSwift_lto.o
This flag tells the linker where to write any temporary object file it needsto create while performing link-time optimization (LTO).Without the flag, it’d pick a spot, do its work, then delete the temporaryfile; with the flag, it leaves the file around, so other tools can pokearound and, say, read out debugging info.
Disabling global inlining and removal
-export_dynamic
This prevents LTO from inlining or otherwise removing global functions.The thinking behind the name seems to be, “what ends up needing to be exportedis dynamically determined, so don’t assume nothing will notice if you mungeexported identifiers”.This flag would let a plug-in loaded by the appreliably rendezvous with a global symbol declared by the app;many apps might not need this, but it looks like Xcode plays it safe.
Disabling global inlining and removal
-no_deduplicate
This disables the deduplication pass in the linker.The deduplication pass would normally find identical functionsand replace duplicates with an alias of the first copy it encountered.
Funny enough, this is listed in the “Rarely used Options” section of themanpage for ld
. I have the sneaking suspicion this flag gets used a bitmore than that manpage would have you think, seeing as Xcode turns it onautomatically!
Specifying the Obj-C ABI version
-objc_abi_version 2
This tells the linker which Obj-C ABI version to use.Remember that we earlier told the compiler which to use via-fobjc-abi-version=2
.
And that’s the end of our unescaped linker flag bundle.Back to flags as they are written.
Enabling ARC
-fobjc-arc
This is the ARC opt-in flag we saw earlier during compilation.
-fobjc-link-runtime
This flag tells the clang driver to ask the linker to link inthe objc
runtime libraryas well as Foundation.
This is actually redundant with -fobjc-arc
—you can’t do ARC withoutan Obj-C runtime!—but clang was changed tolet that one slide.
Persisting link-time file dependency info
-Xlinker -dependency_info -Xlinker /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Intermediates/ImportSwift.build/Debug-iphonesimulator/ImportSwift.build/Objects-normal/x86_64/ImportSwift_dependency_info.dat
This tells the linker where to output dependency info.Normally, it doesn’t write this info anywhere.
Dependency info uses an undocumented binary file formatWhen set, the targeted path will be populated with a binary file formatcomprising a single opcode byte and then a NUL-terminated C string.The first such pair is always the linker version that wrote the file.The result appears to effectively encode all the various filepathslinking ended up depending on in various ways; if any of thosefiles changed, you’d know it’s time to relink to update the output binary.
For details, check out Apple’s open source developer tools code for ld64:src/ld/Options.h defines an anonymous enum for the opcodes,starting with depLinkerVersion
,while the end ofsrc/ld/Options.cppdefines the function dumpDependency
, which does the actual writing in thebinary file format. This function is called throughout option handlingto record files opened by -L
, -F
, -filelist
, and so on.
Specifying the output path-o /Users/jeremy/Library/Developer/Xcode/DerivedData/ImportSwift-fvxpitolsctgjmemgmjamfehgmaf/Build/Products/Debug-iphonesimulator/ImportSwift.app/ImportSwift
At last, the output path! This is where Xcode tells the linker to leave the linked executable.Notice that this is inside the app bundle in built products directory: this is where you’d run the app from.
Building and running this minimal app produces an ever-exciting blank, white view. You’d never ship this—at least, not since Apple started bundling a flashlight with iOS, you wouldn’t—but the plumbing that works to put it all together is identical to that used in an app you would ship.
Xcode tucks a lot of complexity and underdocumented wisdombehind the “Build & Run” command.Some flags, like -no_deduplicate
,are documented only superficially.What they do is kind of clear, but why you’d want to use them remains obscure,and it’s not clear why Xcode feels it’s necessary to provide them.Other flags, like -dependency_info
, are not documented at all.Explaining them required going to the source,which includedLLVM pull request reviews,a GitHub mirror of the clang Subversion repo,and an attempt to provide version history for the ld64 sourcethat Apple provides only in tarball-snapshot form via itsApple Open Sourcecollection.
We’ve fleshed out the very high-level sketch of what goes into building and linking an Obj-C app. It remains to dig into how an Obj-C app links against Swift code in similar detail. The next time you run into compile or link errors, come back to this article—when you have to lift the curtain, you won’t be overwhelmed, but prepared.
Our introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
The Combine framework in Swift is a powerful declarative API for the asynchronous processing of values over time. It takes full advantage of Swift...
SwiftUI has changed a great many things about how developers create applications for iOS, and not just in the way we lay out our...