Dead code stripping doesn't fully strip unused code

Hi guys,
I'm trying to understand the concept of dead code stripping of Apple.
I hope you can help me to answer these questions. I've spent many times but couldn't figure it out. :smiling_face_with_tear:

Reproduced steps:

  1. Create a new, empty project
  2. Archive and export => The app size is 100KB
  3. Add some 3rd party dependencies as static framework.
    Example: Pulse & PulseUI (Dont add any additional code)
  4. Archive and export => The app size is 3MB

Expectation:
Since I don't use Pulse, I expect the 1st executable file size and the 2nd executable file size are equal (~ 100KB)

What I've tried

  1. I used the nm command to see all the symbols in the 2 executable files above.
    Since I don't see any Pulse symbols in the 2nd executable, I assume that all of Pulse's symbols have been stripped.
    However, I realized that the 2nd executable contains many more symbols than the 1st executable.

For example:

U _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesV015ForegroundColorB0OAA19AttributedStringKeyADMc
U _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesV015ForegroundColorB0OMn
U _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesV015ForegroundColorB0ON
U _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesV15foregroundColorAF010ForegroundhB0Ovg
U _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesV15foregroundColorAF010ForegroundhB0OvpMV
U _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesVMn
U _$s9CryptoKit12SymmetricKeyV4dataACx_tc10Foundation15ContiguousBytesRzlufC
U _$s9CryptoKit12SymmetricKeyVMa
U _$s9CryptoKit24HashedAuthenticationCodeVMn
U _$s9CryptoKit4HMACV18authenticationCode3for5usingAA020HashedAuthenticationE0VyxGqd___AA12SymmetricKeyVt10Foundation12DataProtocolRd__lFZ
U _$s9CryptoKit6SHA256VAA12HashFunctionAAMc
U _$s9CryptoKit6SHA256VMa
U _$s9CryptoKit6SHA256VMn
....
  1. Based on this article, seems like we can use "-why_load" to understand why the symbol has been loaded.
    However, I've tried to add "-why_load" to "Other Swift Flags" in Build settings but I don't see any special results.

Questions

  1. How can I use "-why_load" on the executable file?
  2. Why the 2nd executable contains those extra symbols?
    I guess because Pulse uses CryptoKit and SwiftUI. If it's correct, can we remove it?
  3. 1 interesting thing is that if I set "Dead Code Stripping" in my main project to "NO", and then use nm to check the executable file, I don't see any Pulse symbols. How is that possible? (I expect in this case I will see Pulse symbols)

Example projects:
I have an example project that contains 2 executable files that I mentioned above.

If you search the forum there’s a few threads on this already. Here’s also a link with some outstanding issues:

1 Like

Swift compiler keeps all public symbols alive by default. There is a compiler flag called -internalize-at-link which allows them to be stripped at link time.

why_load is a linker flag. If you want to use it on Xcode projects, you should add it to "Other Linker Flags".

3 Likes