Wednesday, August 5, 2009

class-dump-z 0.1-11s released.

class-dump-z 0.1-11s is released. (When iKeyEx 3.0 is released I'll upgrade the versioning to 0.2. Let's hope I won't exhaust all alphabets first.)

Change log:
  • Won't crash on 3.1 binaries now, but external class info is absent. For example, for SpringBoard, SBAppWindow : UIView will be shown as SBAppWindow : XXUnknownSuperclass.
  • Private ivar detection. From whether the ivar symbol is exported we can guess if that ivar is @private/@package or @protected/@public. class-dump-z 0.1-11s now recognizes this piece of information.
  • -h super now works even the method includes non-id/void/SEL types.
  • Argument reordering now works on Linux (again).

Technical detail on why class-dump-z 0.1-11r crash with 3.1 SpringBoard, and why the external class info cannot be extracted in 0.1-11s.

If you use class-dump-z 0.1-11r on the 3.1 SpringBoard, you'll get an exception saying
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct NULL not valid
Abort trap

This is because a code similar to this is called:
std::string s = NULL;

Debugging reveals such an error occurs at MachO_File_ObjC::get_superclass_name. What happened is that, the definition of SBAppWindow class of 3.1 SpringBoard resolves to:
 class_t OBJC_CLASS_$_SBAppWindow = {
isa: OBJC_METACLASS_$_SBAppWindow,
superclass: NULL,
cache: NULL,
vtable: NULL,
data: (pointer to Data)

The superclass pointer is NULL. Which is fine, as such already happens before 3.1. But how can the system know the superclass if the pointer is NULL? Before 3.1, this location is in fact referenced by an entry in the relocation table. At runtime, the dynamic linker will replace data referenced in the relocation table by the real address.

The problem is the 3.1 SpringBoard has no relocation table! We can check:
Load command 6
cmdsize 80
ilocalsym 0
nlocalsym 1
iextdefsym 1
nextdefsym 1
iundefsym 2
nundefsym 1330
tocoff 0
ntoc 0
modtaboff 0
nmodtab 0
extrefsymoff 0
nextrefsyms 0
indirectsymoff 1138584
nindirectsyms 1943
extreloff 0
nextrel 0
locreloff 0
nlocrel 0

Because of this, class-dump-z 0.1-11r cannot resolve what that address will be, and returns NULL. The code afterward see this NULL unexpected and result in exception.

0.1-11s now checks for this failure, but it's just a bad workaround. Because every externally referred class, like NSObject, UIView, etc. will become XXUnknownSuperclass.

Now, if we don't know the superclass, how can the iPhoneOS be? There, I introduce you the Load Command 0x80000022:
Load command 4
cmd ?(0x80000022) Unknown load command
cmdsize 48
00000000 00000000 00106000 00006c6c 00000000 00000000 0010cc6c 00005324
00111f90 00000198

It is run without the 3.1 SDK. Because this is an unknown command, nm totally fails on the 3.1 SpringBoard.
nm: for architecture armv6 object: SpringBoard malformed object (unknown load command 4)

Googling suggests it appears only on 3.1+ and Snow Leopard+. That means the ABI will be changed on SL? And relocation table won't be used? Maybe. With a 3.1 SDK, we can see that the load command is in fact called LC_DYLD_INFO_ONLY. The content is

struct dyld_info_only_32 {
uint32_t rebase_off;
uint32_t rebase_size;
uint32_t bind_off; // 0x106000
uint32_t bind_size; // 0x6c6c
uint32_t weak_bind_off;
uint32_t weak_bind_size;
uint32_t lazy_bind_off; // 0x10cc6c
uint32_t lazy_bind_size; // 0x5324
uint32_t export_off; // 0x111f90
uint32_t export_size; // 0x198

This load command is not documented on Apple's site either, and google yields no useful result at all. But we can find the string _OBJC_CLASS_$_UIWindow at 0x106ea0 inside the so called "bind" table, so definitely something important can be found here.

No comments:

Post a Comment