iOS Static Libraries Are, Like, Really Bad
landonf.bikemonkey.org
landonf.bikemonkey.org
Apple has made a decision that allowing 3rd parties to dynamically load code (outside of Apple certified frameworks) is a security issue on a mobile platform in particular. I don't have a solid counter argument, although there are certainly some technical constraints they could put in place to help mitigate the risk.
Anyway, agree with your essay in general. But I also understand how we got here.
Cheers
Note that on Mac OS X when using code signing, including when distributing via the Mac App Store, dynamic libraries are supported.
If Apple were to add dynamic libs, they would presumably be separate binary files, with their own signatures. This could raise the concern noted by 0x0.
No. That's not how it works on Mac OS X today, where bundled shared libraries are supported.
Separate binary files have individual hashes, which are included in the package manifest file. The manifest is then signed, so a single signature covers all hashed files in the manifest.
Curiously though, in all of the MAS apps I've checked, bundled dylibs are explicitly not hashed in the manifest. This is the developers choice, but perhaps a default?
Is it harder for Apple to check if remote code loading functionality (and other potential security issues) is included in frameworks compared to bundles?
The problem being proposed here is that the ability to have embedded frameworks would somehow weaken this strong protection against loading new code at runtime, although I don't really see how personally.
For example, if every application links to a static version of some image loading library, then all of the applications must be patched if there is a vulnerability in that library.
Whereas if they all share the same copy, you patch the library, and they all get fixed.
I'm aware that model works better when the same vendor is providing all of the binaries, but there are cases where it's also appropriate for general ISVs.
So even if an issue was found in a shared framework, it has to be fixed for every app that includes it.
As for the shared framework; that's not true necessarily. Not all system frameworks are included in the app bundle.
Really, it's up to the app maintainer to update their program, and if it has a vulnerability, in theory the sandbox will prevent it from doing damage to others.
In the mobile space, it would be even more beneficial if platform holders and ISVs actually followed this; the memory and space usage savings could be substantial.
http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html
http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries...
https://www.usenix.org/legacy/publications/library/proceedin...
Linux distributions heavily depend on this for the GCC runtime libraries (such as libgcc_s); it's how they provide backwards compatibility.
Many operating system distributors also rely on symbol versioning for their shared libraries as well so they can compatibly evolve interfaces for consumers.
So my original point stands, if someone incompatibly updates a shared library without accounting for versioning, they're doing it wrong.
The biggest downside is that updates to shared libraries, done incorrectly, can break applications. That said, modern package managers allow an application to list what versions of a library is or isn't compatible with.
It'll be interesting to see if anyone ever comes up with a good solution that mixes the strengths of mobile platforms' security model and modern desktop package managers together. It seems quite nontrivial. (Does the library inherit the permissions of the app? Does it have it's own? what if I push malicious code in an update? Bundled prevents having to think about these problems.)
[1]: http://wiki.gentoo.org/wiki/Why_not_bundle_dependencies
If Apple allowed dynamically loaded libraries across the OS, then subtle issues in an App update could cause that one update to break seemingly unrelated apps. Windows developers call this DLL hell, and even with manifests and SxS, Microsoft still doesn't have an attractive solution to the problem.
Meanwhile, from a security standpoint, the sandbox should prevent apps from interfering with the files of each other and the OS.
And from a performance perspective, the few kilobytes (even entire megabytes!) of duplicated code segments is inconsequential on a phone with 1GB of RAM and very few context switches across apps.
And if you have to distribute static libs Cocapods again makes it much easier.
EDIT: Issues I had with integrating third party libs into projects as dependencies in the past, not issues in regards to the blog post.
Apple needs to fix this before they've shamelessly let it sit for an entire decade. CocoaPods is just a partial band-aid.
This is true for both, people using existing pods and for people creating new pods. The whole process is just frustrating IMO.
In addition Cocoapods does not solve the problems described in the article.
$ xxd -i some_fileIt is also sad state of affairs that Objective-C builds up on C's tradition of not having proper namespace support.
Apple considers it a security issue, so presumably they have figured out attacks that exploit dynamic library loading.
In general though, "Because another platform does it" isn't always a good argument for a particular feature in a platform, particularly when that choice involves a tradeoff. Taken to an extreme, you would end up with a lowest common denominator of samey platforms that just copy each other's design decisions.
In this case Apple has decided that security is a top priority for them even if it comes at a price in terms of developer convenience. Other platforms are free to make different tradeoffs, and that gives users and developers a diverse choice of distinctive platforms available to them. This is a good thing. iOS doesn't have to become Android any more than Android has to become iOS.
- Epoch OS
- Symbian
- Windows CE/Pocket PC
- Windows Phone 7/8
- Android
- Blackberry
- Bada
The above vendors do have/had secure deployments mechanisms despite support for dynamic libraries.
If you want to share code this becomes a non-issue - you include the code which can detect things like architecture and target at compile time and then its possible to not just transcend the boundary between iOS and OS X, but also Windows Desktop + RT, Android, other *nix flavours and that great operating system that will be released in 7 years time that we don't even know about yet...
a static library is a convenience of pre-compiled code, but also an inconvenience where you can't see inside or implement the many cool things you can with the preprocessor, meta-programming or even scripts that generate code or enforce constraints if you feel so inclined...
Why do you really want static libraries to be usable? Are they really a good thing? Is it really any more convenient than just including a folder of source code in the project?
The only real argument I can see is if you want to keep your code secret which I am philosophically opposed to in the general case...
Here is only one example of why including via source code can be a bad thing:
> In the years past, for example, I saw issues related to a specific linker bug that resulted in improper relocation of Mach-O symbols during final linking of the executable, and crashes that thus only occurred in the specific user's application, and could only be reproduced with a specific set of linker input.
In addition: the writers frameworks/libraries are all (!!) open source! And you claim the only reason for using libraries is keeping the source secret. Yeah!
there are a few arguments there but they are not especially compelling imo and I feel the advantages of using naked code far outweigh the disadvantages of using a library (the only advantage i can think of which is genuine is hiding your code as binary). let me give my counter to some of the arguments made for why a library is a good thing by way of demonstration:
> a single atomic distribution bundle that applications developers can drag and drop into their projects.
this is programming - drag and drop is a fantastic luxury and a terrifying one if you like to understand what happens. in any case dragging and dropping a folder in to xcode containing code and resources is just as easy but has the benefit of being absolutely transparent.
> One of the significant features of frameworks on Mac OS X is the ability to bundle resources. This doesn't just include images, nibs, and other visual data, but also bundled helper tools, XPCServices[1], and additional libraries/frameworks that their framework itself depends on.
loose files have this desirable property except for being in a convenient package - including a folder reference or duplicating it as a group in xcode is a drag and drop operation.
> One of the features that is possible to achieve with static libraries is the management of symbol visibility.
Yeah... loose code files have this /better/ than a library typically does if you like all symbols to be visible. (otherwise its a valid point and lets you hide your code again...)
> Dependent Libraries
including dependencies is a pain, but it makes sure people can use your code immediately. i like one-step processes - every project i've done in the last 5 years or so works with a single check-out from source control - in some cases even if you are lacking the ide or other important tools. the bloat is not relevant today, even on mobile platforms... although i don't seem capable of creating many multiple megabytes of code no matter how much i do except for including some enormous 3rd party library (freetype is the last one i had this problem with - i never use it any more).
> result in builds of the library being unique
this is a genuine problem - kinda - if your code doesn't build under multiple platforms and compilers to have the same behaviour then you have much worse technical debt than this to fix first... like making your code actually cross-platform and deterministic (if you actually need that).
however all of that aside I think my point about being super cross platform across *nix, iOS, OS X, Windows RT/Desktop, Android, Windows Phone 8 and unknown future operating systems trumps the lot...
EDIT: as a real world example consider stb_image.c - a fantastically useful header providing a very clean image loading interface for the most common formats (especially compared to any of the OS library alternatives on all of the platforms where the amount of boilerplate and needless operations is quite staggering for something so simple). I've used this on all of the above platforms and the only problem I have is that the author is not so keen on using the maximum level of warnings like I do... (I like the compiler to never be confused and have the best information available about my program, and incidentally libraries deny the compiler valuable information in many cases too...)
2. Integrating by source code is valid for some use cases, but it is definitely not for all. E.g. low level libraries like crash reporters which can not be platform independent. Or libraries that rely on platform specifics, like UIKit on iOS.
There is not one approach, one way that fits all. For some libraries/frameworks are the best, for others direct source code inclusion is best.
> low level libraries like crash reporters which can not be platform independent.
> Or libraries that rely on platform specifics, like UIKit on iOS.
this is actually not true and i can quite comfortably demonstrate this and have worked in multiple code bases that do such things across all the major platforms, including games consoles...
these pieces are the unavoidable platform dependent bits and can be conditionally compiled accordingly - they are however usually quite small, and also something which a library can not do
this is imo far and way the biggest strength of source code inclusing and precisely what i refer to by 'you can run on every current platform and even ones that don't exist yet'
even 'cross-platform' APIs like OpenGL necessitate this because they make mistakes, or have ES flavour on mobile with breaking changes vs. Desktop OpenGL.
EDIT: "> In the years past, for example, I saw issues related to a specific linker bug that resulted in improper relocation of Mach-O symbols during final linking of the executable, and crashes that thus only occurred in the specific user's application, and could only be reproduced with a specific set of linker input."
ah i guess you mean this. in which case how does a library avoid this? the linker inputs are usually roughly equivalent to libraries... the compiler generated a bad object file consistently? I'll agree that compiler and linker bugs exist but they are the exception and not the case - that effects source code and not the library is indeed an advantage of a pre-compiled library. I consider it vanishingly unimportant against being able to work across /all platforms/.
Another thing is "replacement" - switch one version with another. This way I've found that sqlite 3.8.2 was misbehaving for us lately, which points to be some kind of MSVC related error (compiled 64-bit for windows using VS2010).
Quickly switching to the previous one, without recompiling did another thing - I had to think less about other possible changes, since only one component changed - not the whole executable.
Then there are times where I wished everything was statically compiled :) - On all fronts (Linux, OSX, Windows) there are too many gotchas of pointing the right way to your dlls.
I seem to favor Windows (get the DLLs first from the apps' folder... well not strictly, but in general) - but then that's because Windows did not have good place to put the dlls to begin with (unlike unix).
Most libraries I've dealt with have mostly amounted to interfaces to respective web apis. In those, having source code distribution makes perfect sense, even preferable when the libraries are fairly simple and that gives the freedom to compile them as one wishes. Doing it with sub-xcode projects I've found to be fairly simple, even if mucking around with header search paths is still a pain.
Granted, I haven't really experienced library author side of things, but most of these issues are about drag'n'drop ease of use, which I don't think requires dynamic libs but is more of a tooling side problem, and frankly has always been a problem in the c/c++ land at least.
The lack of two-level namespace is a real problem that I've actually encountered. Then it was a simple rename of a class name away, but I can easily fathom a situation where it isn't that easy (like the example of embedded sqlite3).
Also I hadn't realised that the simulator and device builds are lipo'ed manually, that indeed should have a better way. But would dynamic libs really help in that situation, you would still ship x86+arm glued together without notion of the platform, even worse, you might be shipping the x86 as extra weight on appstore builds.
We have moved beyond the era of scarcity and even on desktop platforms, lots of people are including static libraries so that they are not dependent on the upstream developers' idea of what is needed. So much of this code is open source that it makes sense to build your own static library, and maybe leave out cruft that you don't need.
Sure it would be nice to have a choice, but static libraries are not evil.
Edit: A solution I think works okay is simply adding
max-width: 500px;
to the css properties for p.The monitor my browser is on has 2560 width, but the client area of the browser window itself isn't much more than 1200 pixels wide. I keep terminals and the tree-style tabs to the left of the browser.
You just can't win with people who have ridiculously-sized browser windows.
http://www.gnu.org/licenses/lgpl-2.1.html
I would emphasize the terms "work that uses the library" vs "derivative work of the library" which have historically been used to differentiate between static vs dynamic linking.
In addition, how would I go about doing this in Android? And how does Apple distribute LGPL libraries (like Webkit) without "[allowing] Joe Enduser [to] replace an LGPLv2 library on iOS"?
I do agree that the iPhoneSimulator and iPhoneOS SDKs should be better integrated at compile time. Yet, they were architected as completely different environments, so I suspect that won't be an easy request to fulfill.
Anyway, what benefit would having a dynamically loaded library give you when each app runs sandboxed? I can just imagine a scenario where one framework uses a version of ASINetworking and another uses AFNetworking and they both attempt to use some version of JSONKit to parse out the result. Objective-C and its amazing namespace collision management will just carefully (and by carefully, I mean, not at all) pick one implementation over the other and leave a happy warning for all to see.
The question of "what benefit would having a dynamically loaded library give you when each app runs sandboxed" seems to be answered in great detail under "Debugging Info and Consistency" and "Missing Shared Library Features"
Cocopods is PAINFUL to get compliant with ruby sometimes, but other then that, works for a lot of the problems you'll see. It is a leap though.