On Mac OS X and in the iOS simulator (both x86), we can trap to the debugger (LLDB) using the int3
instruction in inline assembly. This is nice because it traps to a particular line of code but we can continue immediately by hitting continue in the debugger.
Is there a way to do this on iOS hardware?
An answer to an older question mentions raise(SIGINT)
which as far as I can see (from examining signal.h
) does not exist. Another answer mentions the trap
assembly instruction, which causes a build error ("Unrecognized instruction mnemonic"). Also unrecognized is the BKPT
assembly instruction mentioned in ARM documentation.
I've tried __builtin_trap()
which almost, almost does what I want, but does not allow me to continue. I keep hitting it unless I advance the instruction pointer manually using jump +1
or register write pc `$pc+8\`
, which is much less convenient than just hitting continue.
I'm building for iOS 9 for 32- and 64-bit devices using Xcode 7.3.1. Any help is appreciated!
The Xcode debugger provides several methods to step through your code and inspect variables. You can precisely control execution of your code from a breakpoint, stepping into and out of called functions as necessary to determine where your bug occurs.
When you run an application in Xcode, the debugger is automatically started and attached to the process of the application. Click the Run button in the top left or press Command + R. From the moment the application is up and running, we can start inspecting the process and, if necessary, debug it.
Apple's libc signal.h
includes XNU's sys/signal.h
, which does define SIGINT (on all platforms):
// [...]
#define SIGHUP 1 /* hangup */
#define SIGINT 2 /* interrupt */
#define SIGQUIT 3 /* quit */
// [...]
So while I cannot confirm that this practice does indeed work (due to lack of an iOS 9 device on my part), the barrier that held you back should not actually be a problem.
As for the assembly instructions, BKPT
is a valid ARM instruction, though only for A32. The A64 variant is called BRK
.
If you're building fat binaries and use either of those unconditionally, you will always run into compiler error.
Also note that both instructions require an immediate value (which is passed to the debugger). Omitting that value will produce a compiler error as well.
That said, you should be able to insert debug instructions for A32 and A64 with a simple #ifdef
:
#ifdef __aarch64__
asm volatile("BRK 0");
#else
asm volatile("BKPT 0");
#endif
You can substitute 0
by any value of your choosing between 0
and 255
.
A note on the TRAP
instruction: While Apple's assembler seems to accept this instruction for A32 and translates it to 0xe7ffdefe
, it will issue an "unrecognized instruction mnemonic" on A64, analogous to the BKPT
instruction.
I was also unable to find any reference to the instruction on the ARM Information Center or in Apple's documentation.
I've had a similar problem using Swift on OS X:
1) raise(SIGINT)
didn't work for me in background processes (e.g: SceneKit's Scene Renderer Protocol). (perhaps a missing handler?)
2) __builtin_trap()
doesn't allow continuing after
3) asm(" int3 ")
requires ObjC and header files, which scared me off at first. But it wasn't too bad. Just added three lines in two new files:
---- NSObject+MachineTrap.h ----
void machineTrap(void);
---- NSObject+MachineTrap.h ----
#import "NSObject+MachineTrap.h"
void machineTrap(void) { asm (" int3 "); } /// Program has TRAPPED to DEBUGGER ///`
(My choice of int3
over say BRK
, BKPT
, SVC
may not be correct.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With