32

I need to debug the startup for an ios application on an actual device... and by start up I mean the very first instruction that is is executed when the OS hands control over to the app. Not "main". Also, this application doesn't have any symbols (ie. the debug information isn't available.. yet). I don't care if I have to debug at the CPU instruction level. I know how to do that (done it for over 30 years). I want the debugger to stop when control is about to transfer to the app. When I use the Attach|by Name command and run, it just says "Finished running".

Oh, and this application was not built in XCode. It is, however an application I built, signed and provisioned and moved to the device. The application does run since I can see the console output. Just in case you're thinking I'm some hacker trying to debug someone's application.

How's that for a tall order? I'll bet nobody can answer this... I've not been able to find any information on how I could do this with an XCode-built project. I wonder if it is simply not possible or "allowed" by the Apple overlords?

What do you say, Stack Overflow gods?

UPDATE: I should clarify something. This application is not built with any commercially available or open-source tool. I work with a tools vendor creating compilers, frameworks, and IDEs. IOW, you cannot get this tool... yet. In the process of bootstrapping a new tool chain, one regularly must resort to some very low-level raw debugging. Especially if there are bugs in the code generated by the tools.

Richard Slater
  • 6,313
  • 4
  • 53
  • 81
Allen Bauer
  • 16,657
  • 2
  • 56
  • 74
  • I doubt you'd be able to do it without being an Apple employee. – James May 27 '12 at 23:28
  • 10
    If that is the case, the this is just another case of Apple being about as developer tool hostile as you can get. Even MS in it's most anti-competitive years was never this hostile. – Allen Bauer May 27 '12 at 23:46
  • I'm just assuming that if he wants to debug the kernel so he can see what happens before Main() that he'll need a tool like a Lauterbach and other special hardware. Debugging a device is quite different from debugging your own machine. I have no experience doing this with Apple HW but I do with Nokia HW. – James May 28 '12 at 00:23
  • 2
    I've said nothing about the kernel. This is pure user-space debugging. I'm just asking how to get a debugger to stop once control is transferred to the app with the added wrinkle that there are no symbols and/or line-number information. – Allen Bauer May 28 '12 at 01:14
  • 2
    You really need to come up with a better explanation of what you are trying to DO, and what you hope to find by debugging starting at the earliest point possible. Are you seeing a crash? Is your program having some initial starting state you do not understand? This whole question just has the air of you making a problem WAY more complex than it needs to be. – Kendall Helmstetter Gelner May 28 '12 at 04:27
  • 2
    Well, I did explain that I work with a programming tools vendor which means that what most people take for granted I don't have. Most folks have compilers, debuggers, IDEs already done and ready to go. I'm in the process of *creating* those things and need to be able to bootstrap the process so that the customers can then take for granted that they're tool and it's vendor has already gone through the pain of making things work. – Allen Bauer May 28 '12 at 04:44
  • 2
    Allen - Microsoft's dark moments were pretty dark, but at least we could fall back on Periscope CPU break-out switches. (FREEZE! Ok, nobody move. Where are we?) Remember the undocumented stack twiddling in the startup code of 16 bit Windows executables? We had that reverse engineered for a long time before Microsoft acknowledged that the magic dance was required for the app to run, and even longer before they explained why it was necessary. p.s. Good luck with your latest puzzle! :> – dthorpe May 28 '12 at 05:44
  • If it was for one executable I would patch the entrypoint to jump to entrypoint, run it and then attach the debugger, restore original bytes of entrypoint and do whatever I would have done with it. I wonder if it works in iOS or OSX. – pani Jun 27 '12 at 08:14

3 Answers3

23

I'm going to answer my own question because I think I've stumbled upon a solution. If anyone has anything more elegant and simple than this, please answer as well. On to the steps:

Starting with a raw monolithic iOS executable (not a bundled .app, but the actual binary mach-o file that is the machine code).

  1. Create a new like-named empty Xcode project. Build and run it on the device.
  2. Locate the output bundle's .app folder.
  3. Copy the above raw iOS executable over the existing one in the .app bundle's folder.
  4. The application will now have an invalid signature and cannot be deployed and run.
  5. Run codesign against the app bundle (you can find out the command-line by running xcodebuild on the above Xcode project).
  6. In the bundle's .app folder, run otool -h -l on the binary image. Locate the LC_UNIXTHREAD load command and find the value associated with the 'pc' register. This is address where the os loader will jump to your application. If this address is odd, then these are Thumb instructions otherwise it will be ARM (I think that's how it works).
  7. Add a symbolic breakpoint (I used GDB instead of LLDB) and enter the address as '*0x00001234' as the symbol.
  8. Select Product|Perform Action|Run Without Building.

Assuming that GDB is able to evaluate the breakpoint expression and set the break point, and you've selected Product|Debug Workflow|Show Disassembly When Debugging, the process should break at the very first instruction to be executed in the application.

You can now single step the instructions and use the GDB console to get/set register values.

Allen Bauer
  • 16,657
  • 2
  • 56
  • 74
  • 2
    This is mildly off topic, but it's related to your point about using GDB in lieu of LLDB. When using LLDB and debugging an app on the simulator the "modules view" is totally blank. If you want to figure out which system dylibs are in memory with your app you can't!? The workaround is to configure the project to use GDB instead, but making that project configuration change causes Xcode to issue a new warning. – Mark Edington May 30 '12 at 00:10
  • 1
    Glad you found an answer, I thought I had mentioned otool in my other response but found it got spell-corrected to just "tool". Also good to know about the LC_UNIXTHREAD command pointing you to the entry... – Kendall Helmstetter Gelner Jun 01 '12 at 05:52
  • 1
    @Mark: I'd file a radar on that, or talk to the XCode guys at WWDC. – Kendall Helmstetter Gelner Jun 01 '12 at 05:53
2

Your question does not make sense - main is the entry point into the application. It is the first code that should be encountered, unless possibly you have initialize() overridden for some classes (but even then I think main would get hit before the runtime).

I think you are seeing some kind of odd error on launch and you think you want to set a breakpoint on entry to catch it, but far more likely what would help you is to describe the problem on launch and let one of the 4000 people who have seen and fixed the same crash help you...

However, if you really want to use GDB to break on an application with no symbols (but that you launch from XCode) you can have GDB break on an assembly address as per:

How to break on assembly instruction at a given address in gdb?

To find the address of main (or other methods) you can use tool or atos, some examples in this question:

Matching up offsets in iOS crash dump to disassembled binary

ADDITION:

If for some reason XCode cannot launch your application for debugging, you could also jailbreak and install GDB on the device itself which would give complete control over debugging. If XCode can launch you application I see no reason why being able to break at an arbitrary memory address does not give you the ability you seek...

Community
  • 1
  • 1
Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • 16
    Uh.. "main" is *not* the actual entry-point. It is called from the actual "startup" code which contains the entry point. – Allen Bauer May 27 '12 at 21:20
  • Ok, technically true... but he said "transfers control to the app", not some wrapper code or system library... what would be the point? – Kendall Helmstetter Gelner May 27 '12 at 21:35
  • 7
    I consider the app to be the whole of the machine code bytes (in this case ARM v7) generated by some tool. So "transfers control to the app" means "once the CPU crosses the boundary (via a BL or BLX instruction) from operating system to the first instruction of the app as defined by the mach-o executable binary. – Allen Bauer May 27 '12 at 21:38
  • Why though? Why debug starting there? Since you CANNOT change that code, there is zero point to starting there in GDB, or even logically thinking of starting to debug at that point. Control passing into YOUR application means just that from the programmers perspective, execution passing into realms YOU control. Again technically I know what you are saying, but in the case of what the question is asking for my advice is more helpful. – Kendall Helmstetter Gelner May 28 '12 at 04:22
  • 7
    @Kendall, look at Allen's profile, especially his bio and the link to his blog. He has his reasons (which, BTW, really shouldn't matter - the question is valid regardless of whether you understand why it's being asked :-)). – Ken White May 28 '12 at 04:29
  • 5
    Thanks, Ken ;-). I am purposely vague about the *reasons*, yes. However I'm trying to be as clear about exactly what I'm asking for at it's most basic level. I clearly get that for folks that aren't in the business of *creating* programming tools (compilers, debuggers, IDEs, etc) haven't really thought about the pain involved with starting from nothing. Most folks merely take for granted that programming tools are "just there." – Allen Bauer May 28 '12 at 04:48
  • 1
    On a Q&A site devoted to helping people, I am all about answering the question not asked - the point of any question is to solve a problem. Without knowing the problem it's easy to think you may very well be asking the wrong question, as many others have before. I didn't get my reputation points through charm or good looks but by helping people and solving problems, and that was not always through answering the exact question asked. Your addition does provide the clarification needed to provide a real answer, although basically the jailbreaking addendum I added should help you. – Kendall Helmstetter Gelner May 28 '12 at 16:48
  • 1
    I also do not see why the ability to break at an address does not help you in what you are trying to do (though can you not find out how to do that in LLDB, which I now see is a tag in the question?) – Kendall Helmstetter Gelner May 28 '12 at 16:51
1

One solution for applications with webviews is to run them in the iOS Simulator, and connect to that with the remote-debugger in macOS Safari. This is off-topic but maybe the one or other could benefit.

http://hiediutley.com/2011/11/22/debugging-ios-apps-using-safari-web-inspector/

Or use NetCat for iOS... not the most perfect solution, but at least you see what's going on.

bryanjclark
  • 6,247
  • 2
  • 35
  • 68