How to debug a "class JKserializer is implemented in both" case (Part 2)
In the first part, found out that the executable included two definitions of the JKSerializer class. The one, used at compile time, corresponding to version 1.4 of JSONKit and another, deriving from 1.5 which was present at runtime.
In order, 1.5 came up first in the class dump, followed by the 1.4 as used by the app. Not sure how class-dump derives that order but does appear to be relevant as we’ll see further on.
The next thing is to find all the files where JKSerializer is actually coming from. This can either be a source file or a static library that includes JSONKit.
Using find with grep, every file under the current directory that has “JKSerializer” in its contents will match, printing its name in the process.
find . -type f -exec grep -q "JKSerializer" '{}' \; -print
Running the above under the project’s directory, yields the following files.
./External/BugSense-iOS.frame-ork/Versions/A/BugSense-iOS ./JSONKit/JSONKit.m
So it seems that BugSense includes JSONKit as part of its distribution. Could that be version 1.5? Looking at the symbol table on the file for the i386 architecture (the one running on the simulator)
nm -arch i386 ./External/BugSense-iOS.framework/Versions/A/BugSense-iOS | less
You will find the culprit.
+[JKSerializer serializeObject:options:encodeOption:block:delegate:selector:error:]
That explains the second definition of the JKSerializer class in the executable. What about the randomness of the crash though?
It turns out that it depends on the order of linking the binary with libraries under Xcode. If BugSense comes first on the list, its definition of JKSerializer takes precedence. Moving the libJSONKit.a library on top, no longer causes the crash.
Kudos to atnan for his article on lipo and ar tools and also a contributor to JSONKit.
Extra Tidbit The way the compiler (LLVM) resolves any symbols is by using a symbol table. Using nm, you can list the contents of the symbol table.
nm "/Users/qnoid/Library/Application Support/iPhone Simulator/5.1/Applications/9CEF83D3-C9DE-4951-95DF-CC1A3DB223F8/TBUndefined.app/TBUndefined"
Looking for JKSerializer, you can find the two class definitions. (as previously shown by the class dump)
1044 < 0005c0b4 S _OBJC_CLASS_$_JKSerializer 1045 < 0005bca4 s _OBJC_CLASS_$_JKSerializer
The only difference between the two being that one is defined a a local symbol while the other one as global. Would like to know how that is decided and if what role (if any) plays in resolving the class.
Symbol tables are usually implemented as hash tables.
Looking at the source code of the symbol table for LLVM, under line 71 you can see a lookup by name to the underlying hash table. This implementation of the hash table does not use a key for looking up a value in the corresponding bucket. Not iterating through the bucket, seemingly returning the first definition of JKSerializer, the 1.5 version one.