Jan 22

So you're trying to get your homemade app on your pwned iPhone

Recently I was working on an iPhone app for work, for demo purposes, but my company cheaped out on the Apple iPhone Developers registration. 

More accurately, the process of binding a multi-million-$-a-year company with Apple Inc. takes a very long time, so we took the back-door and just pwned our test iPhone devices (firmware 2.1).
"How hard can it possibly be to install apps on a jail-broken iPhone?" we thought, Well as it turns out, it's pretty difficult, especially for Mac-first-timers like myself. 
In the end, I overcame this obstacle - but not before compiling a compiler, installing a gazillion support apps, compiling my app with at least 6 different compilers, doing it on WinXP, Ubuntu, on the iPhone itself, and on the Mac.
So I thought why not share with you the way that actually produced the working result.

Well I got a working app using 2 compilers, and only on the Mac machine (intel, Mas OS 10.5.4):
  • Open Toolchain:  LLVM-based compiler, which you need to compile from scratch. Instructions can be found here.
  • The ARM compiler that came with the iPhone SDK itself.
(but I hear that compiling under WinXP with Cygwin, which I also tried and gave up on,  can actually work)

First thing I tried, naturally, was to try and use XCode's internal functionality of deploying & debugging on the device, but that requires code signing, and to get a valid certificate - you must pay. Apple: 1 - Roy: 0. 

So I went to find a solution, and for a very long time I was convinced it was too hard for a total Mac beginner. But as the due date of my app was getting closer, and still we weren't legally registered with Apple's dev. program, I started to look more closely at deploying the app manually.
I found iphone-dev, which is an alternative to xcode's compiler. At the time I thought that the SDK's compiler was the problem. So I followed all the instructions on the Wiki and soon enough I had a working GNU c compiler: arm-apple-darwin-gcc.
And that compiler was very successful at compiling the HelloWorld apps, even ones I had made with XCode templates! I was very pleased. Apple:1 - Roy:1!!
Few things I had learned though:
  •  XIB files must be compiled into NIB files before they are bundled up together with the rest of the app.
It's done with iPhone SDK's 'ibtool'. So I added this into my Makefile:
compilexib: $(XIBS) 
%.nib: %.xib
ibtool --errors --warnings --notices --output-format human-readable-text --compile $@ $<

  • The compiler doesn't handle property assigning very well. It doesn't support this method of assiging: myObj.someprop = someVal;, instead you must use the Objective-C way of: [myObj setSomeProp: someVal];.
  • The executable must be signed somehow, but as the device jailbreaking frees it from complying to Apple's code signing conventions, you can sign it any way you want. Easiest way: "ldid" from Saurik's house. Download "ldid" from Cydia and follow the instructions.
Now, the biggest problem I had with my HelloWorld apps was linking. Compiling went fine in most cases, and in case of problems the compiler notifies in a somewhat comprehendible way what is the problem so it can be fixed. But the linker, it obviously has a very different notion of what is an understandable error message. And I found myself just trying different permutations of the LDFLAGS in my Makefile until I got it right. This is what I finally came up with:
LDFLAGS = -lobjc -framework CoreFoundation -framework Foundation -framework UIKit -framework CoreGraphics -framework CoreLocation
LDFLAGS += -L"/usr/lib"
This is actually all you need for open toolchain's linker.
 CFLAGS should be:
CFLAGS = -fobjc-abi-version=2 -lobjc
CFLAGS += -I"/var/include/"
CFLAGS += -I"/usr/include/"
CFLAGS += -I"/usr/local/arm-apple-darwin/include"
I used /var/include to softlink the frameworks I used in my project:
lrwxr-xr-x  1 root  wheel  130 Jan 20 14:59 CoreFoundation -> /Developer/Platforms/iPhoneOS.platform//Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/
But this compiler is not without its own shortcomings. It's based on the Mac OS 10.4 SDK, which apparently has no GC in it, so when I tried to compile my actual app I got this linker error:
undefined symbols: _objc_assign_ivar
A wretched error, barely any information about it online. And it was the only error in the build, so you can imagine my frustration at that stage. But a fishing tour around Google revealed that, as I mentioned, the problem is that OSX 10.4 SDK doesn't support some function that was supported by 10.5 (the environment I was developing on in XCode).
I had no intention to look around every corner of my code to find these spots where theres a problem, and anyway since this is a linker problem I had no way of finding these spots.
Ok, so then I thought - iphone-dev's compiler is a bust, I gotta give that SDK compiler another go. And I did, and sure enough it worked. But only after some tinkering, of course.
First off, a different compiler and its own headers:
CFLAGS = -fobjc-abi-version=2
CFLAGS += -I"/var/include/"
CFLAGS += -I"/Developer/Platforms/iPhoneOS.platform/Developer/usr/include"
CFLAGS += -I"/Developer/Platforms/iPhoneOS.platform/Developer/usr/lib/gcc/arm-apple-darwin9/4.0.1/include"
CFLAGS += -I"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/usr/include"
And the linker needs its own libraries to link to:
LDFLAGS = -lobjc -framework CoreFoundation -framework Foundation -framework UIKit -framework CoreGraphics -framework CoreLocation
LDFLAGS += -L"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/usr/lib"
LDFLAGS += -F"/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks"
But after all that was set, took me a while to find all the depenencies - I was good to go!
My application, including UI built with Interface Builder, was compiling and running smoothly on the pwned device.
All you need is to put your executable, resources and Info.plist in a *.app directory, and send that app to your iPhone's /Applications/ directory (I used plain old scp).
To ease my work on the device I used BossPrefs and OpenSSH from Cydia.
BossPrefs has a nice function of "Fast respring" in the "Power" menu, that will reindex the /Applications directory and find your app there.
Another last tip: tail the syslog, download "syslogd to /var/log/syslog" from cydia and "tail -f /var/log/syslog".
I hope this was helpful.
Good luck building your own iPhone apps!