Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Too many commands? Dyld Message: malformed mach-o: load commands size

Tags:

ios

ios9

dyld

Some iOS 9 devices in the wild seem to crash with the error message that I receive from the very basic crash reporting in Xcode only

dyld: malformed mach-o: load commands size (16464) > 16384

Unfortunately that's all the info I get. I can't even debug or reproduce locally. Can anyone hint me into the right direction here?

It occurs after updating my Cocoapods, so I guess there's one of them (or their dependency) that misbehaves.

After some investigation of my mach-O binary, I saw that the sizeofcmds is really 16464. If I understand correctly, there seems to be a load command size limit of 16384, can anyone confirm this?

Does that mean I should remove dylibs and everything should be fine?

like image 480
MarkHim Avatar asked Jul 04 '17 09:07

MarkHim


2 Answers

At WWDC18 I went to an Apple engineer who is working on dyld. Here’s what he had to say:

  • The Dyld code is downloadable from https://opensource.apple.com (the one specific to us can be found inside macOS 10.12)
  • For iOS 9 the maximum size of load commands is indeed 16k aka 1 memory page (There’s no way around it! This is imposed by the OS itself. For customer service telling people to update to iOS 10 (all devices that run iOS 9 can except for iPhone 4S) would be viable.)
  • Since iOS 10 the maximum size of commands is 32k
  • Majority of the size of the load commands is determined by strings (paths) of the frameworks (use command otool -L to see them

Possible solutions:

  • Use less libraries (that was our goto solution thus far, but we will change to umbrella libraries (see below))
  • Shortening names (might screw up header lookup of cocoa pods, maybe use head maps to fix that inside the Xcode build process → maybe more (high-level) info in WWDC18 session “Behind the scenes of the Xcode Build Process”)
  • Try to build static archives for libraries (should not have dynamic resources otherwise make copy phases and figure out where resources are)
  • Build frameworks that re-export other frameworks (umbrella frameworks). Use -reexport-l as a linker flag (not done often) → gonna make some runtime overhead when starting the app, also uses a bit more memory (man ld → for info on re-exports)

The engineer recommended to file a bugreport via bugreport.apple.com, because in the future even hitting the 32k limit is possible.

like image 164
erik_m_martens Avatar answered Oct 13 '22 17:10

erik_m_martens


I found a solution that will (at least temporarily) work for me - but I still encourage everyone to provide a real solution and more detailed insights.

Anyway:

Extract your binary

  1. Xcode Archive
  2. Export as IPA
  3. Rename YourApp.ipa to YourApp.zip and extract
  4. Navigate to the subfolder payload to find your YourApp.app
  5. Right click & Show Package Contents of your YourApp.app file
  6. Copy your binary YourApp (no file extension) to a different location

Investigate your mach-o binary

  1. Run otool -f on your binary

Note the align for both architectures are listed which, for me, says 2^14 (16384). This seems to be the threshold for the size of load commands.

  1. Run otool -l on your binary

You'll see that the different architectures and their load commands are listed - as well as their sizeofcmds (size of commands).

Now the funny thing: For arm64, the sizeofcmds (16464) was larger than the align (16384), while it wasn't for armv7.

Now I haven't found enough documentation on this, but I assume that align symbolizes a threshold that should not be reached by the load command size. And also that it adjusts automatically (since we are definitely not having that many frameworks in our app, there have to be apps that have more).

So I guess the error came from this unlikely case, that the sizeofcmds was different in between the architectures AND that one of them was actually valid (so that the align was not automatically adjusted).

Please correct me if I'm wrong, I am just assuming here and I really really want to understand why this happens.

Solve the issue

Remove frameworks until you are under the sizeofcmds for both architectures.

I know this is not scalable, we were lucky (and stupid) that we still had one barely used framework in there that we could easily remove.

Fortunately this only seems to be an issue on iOS9 and will therefore loose relevance over the next months, nevertheless, we should find out if I'm right

Investigation ideas

My assumption that the align is automatically adjusted could be investigated by just putting in more and more frameworks to see if it actually does.

If so, adding frameworks would also solve the original issue - not nice, but at least slightly more scalable.

Sidenote

I don't feel like I shed enough light on the origins of this issue and I had a lot of assumptions. While my solution works, I really hope you feel encouraged to investigate this as well and give a better answer.

like image 38
MarkHim Avatar answered Oct 13 '22 18:10

MarkHim