[lldb-dev] Stepping into function generates EXC_BAD_INSTRUCTION signal

Mario Zechner badlogicgames at gmail.com
Wed Nov 26 09:22:53 PST 2014


I dug a little deeper, inspecting the GDB remote packets send by LLDB to
perform the stepping. It appears when sending memory breakpoint commands
used for stepping, the size of the instruction being replaced isn't taken
into account, or writing back the original instruction isn't done properly.
The following log shows what happens when stepping into the previously
mentioned function:

(lldb) s
Process 166 stopped
* thread #1: tid = 0x0fd9, 0x002602e0
AttachTestIOSDev`[J]java.lang.Object.<init>(__$env=0x016bffc8,
__$this=0x017864b0)V + 12 at Object.java:136, queue =
'com.apple.main-thread', stop reason = step in
    frame #0: 0x002602e0
AttachTestIOSDev`[J]java.lang.Object.<init>(__$env=0x016bffc8,
__$this=0x017864b0)V + 12 at Object.java:136
(lldb) disassemble -p
AttachTestIOSDev`[J]java.lang.Object.<init>()V + 12 at Object.java:136:
-> 0x2602e0:  ldr    r2, [r1]
   0x2602e2:  ldr    r2, [r2, #0x30]
   0x2602e4:  tst.w  r2, #0x100000
   0x2602e8:  it     ne
(lldb) s
Process 166 stopped
* thread #1: tid = 0x0fd9, 0x002602ec
AttachTestIOSDev`[J]java.lang.Object.<init>(__$env=0x016bffc8,
__$this=0x017864b0)V + 24 at Object.java:136, queue =
'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION
(code=EXC_ARM_UNDEFINED, subcode=0xffd1b001)
    frame #0: 0x002602ec
AttachTestIOSDev`[J]java.lang.Object.<init>(__$env=0x016bffc8,
__$this=0x017864b0)V + 24 at Object.java:136
(lldb) disassemble -p
AttachTestIOSDev`[J]java.lang.Object.<init>()V + 24 at Object.java:136:
-> 0x2602ec:  .long  0xb001ffd1                ; unknown opcode
   0x2602f0:  pop    {r7, pc}

AttachTestIOSDev`[J]java.lang.Object.<init>()V + 30:
   0x2602f2:  nop

AttachTestIOSDev`[J]java.lang.Object.clone()Ljava/lang/Object; at
Object.java:154:
   0x2602f4:  push   {r4, r5, r7, lr}
(lldb) disassemble -f
AttachTestIOSDev`[J]java.lang.Object.<init>()V at Object.java:136:
   0x2602d4:  push   {r7, lr}
   0x2602d6:  mov    r7, sp
   0x2602d8:  sub    sp, #0x4
   0x2602da:  movs   r2, #0x0
   0x2602dc:  str    r2, [sp]
   0x2602de:  str    r1, [sp]
   0x2602e0:  ldr    r2, [r1]
   0x2602e2:  ldr    r2, [r2, #0x30]
   0x2602e4:  tst.w  r2, #0x100000
   0x2602e8:  it     ne
   0x2602ea:  blne   0x44b290                  ; _bcRegisterFinalizer
   0x2602ee:  add    sp, #0x4
   0x2602f0:  pop    {r7, pc}

AttachTestIOSDev`[J]java.lang.Object.<init>()V + 30:
   0x2602f2:  nop

The first step succeeds and ends up right after the prologue, at 0x2602e0:
 ldr    r2, [r1]. The next step ends up at 0x2602ec:  .long  0xb001ffd1
which is wrong, it should be 0x2602ea:  blne   0x44b290.

The GDB remote conversation between lldb and the debugserver on the device
(only relevant parts):

# First step
lldb->debugserver: $Z0,2602e0,2#73
debugserver->lldb: $OK#00
lldb->debugserver: $vCont;c:0fd9#15
debugserver->lldb: (320)
$T05thread:fd9;qaddr:37ebfad0;threads:fd9,ffa,ffb,ffd,fff,1009,100a,100b;00:c8ff6b01;01:b0647801;02:00000000;03:c87d6a00;04:00000000;05:c8ff6b01;06:fc6a6501;07:0c6a6501;08:90e96b01;09:28000000;0a:74a0ea37;0b:c8ff6b01;0c:b09e5b00;0d:086a6501;0e:d1b22000;0f:

# Second step
lldb->debugserver: $Z0,2602ea,2#a4
debugserver->lldb: $OK#00
lldb->debugserver: $vCont;c:0fd9#15
debugserver->lldb: (324)
$T92thread:fd9;qaddr:37ebfad0;threads:fd9,ffa,ffb,ffd,fff,1009,100a,100b;00:c8ff6b01;01:b0647801;02:01004300;03:c87d6a00;04:00000000;05:c8ff6b01;06:fc6a6501;07:0c6a6501;08:90e96b01;09:28000000;0a:74a0ea37;0b:c8ff6b01;0c:b09e5b00;0d:086a6501;0e:d1b22000;0f:

For the first step, a 2 byte memory breakpoint is written to 0x2602e0
($Z0,2602e0,2#73), which is where the first step ended up. The instruction
that got replaced is 2 bytes long. The GDB command wrote a 2 bytes memory
breakpoint to the address, so all is good.

For the second step, a 2 byte memory breakpoint is written to 0x2602ea
($Z0,2602ea,2#a4). But instead of ending up at 0x2602ec, which is in the
middle of the 4-byte blne instruction.

Is it correct for LLDB to set a 2 byte memory breakpoint instead of a
4-byte memory breakpoint in this case? The PC will be set to an invalid
address, which then causes the EXC_BAD_INSTRUCTION.

Am i understanding this correctly? Is there a way for me to fix this?

On Wed, Nov 26, 2014 at 5:26 PM, Mario Zechner <badlogicgames at gmail.com>
wrote:

> Hi,
>
> we generate thumbv7 binaries for iOS devices. We deploy, launch and debug
> those via LLDB. Stepping into functions seems to almost always generate a
> EXC_BAD_INSTRUCTION signal. The signal is not generated when running the
> app without the debugger attached. It is also not generated when we attach
> a debugger, but simply let the app run without breakpoints or any stepping.
>
> Here's one of these function's LLVM IR:
>
> =======================
> define external void @"[J]java.lang.Object.<init>()V"(%Env* %p0, %Object*
> %p1) nounwind noinline optsize {
> label0:
>     call void @"llvm.dbg.declare"(metadata !{%Env* %p0}, metadata !19),
> !dbg !{i32 136, i32 0, metadata !{i32 786478, metadata !0, metadata !1,
> metadata !"[J]java.lang.Object.<init>()V", metadata
> !"[J]java.lang.Object.<init>()V", metadata !"", i32 136, metadata !15, i1
> false, i1 true, i32 0, i32 0, null, i32 256, i1 false, void (%Env*,
> %Object*)* @"[J]java.lang.Object.<init>()V", null, null, metadata !17, i32
> 136}, null}
>     %r0 = alloca %Object*
>     store %Object* null, %Object** %r0
>     call void @"llvm.dbg.declare"(metadata !{%Object** %r0}, metadata
> !21), !dbg !{i32 136, i32 0, metadata !14, null}
>     store %Object* %p1, %Object** %r0
>     call void @"register_finalizable"(%Env* %p0, %Object* %p1), !dbg !{i32
> 136, i32 0, metadata !18, null}
>     ret void, !dbg !{i32 136, i32 0, metadata !18, null}
> }
> =======================
>
> The corresponding thumbv7 assembler code as generated by LLVM:
>
> =======================
> .globl "_[J]java.lang.Object.<init>()V"
> .align 2
> .code 16                      @ @"[J]java.lang.Object.<init>()V"
> .thumb_func "_[J]java.lang.Object.<init>()V"
> "_[J]java.lang.Object.<init>()V":
> .cfi_startproc
> Lfunc_begin18:
> .loc 1 136 0                 @ Object.java:136:0
> @ BB#0:                                 @ %label0
> .loc 1 136 0                 @ Object.java:136:0
> push {r7, lr}
> mov r7, sp
> sub sp, #4
> @DEBUG_VALUE: [J]java.lang.Object.<init>()V:__$env <- R0
> movs r2, #0
> str r2, [sp]
> str r1, [sp]
> .loc 1 136 0 prologue_end    @ Object.java:136:0
> Ltmp6:
> ldr r2, [r1]
> ldr r2, [r2, #48]
> tst.w r2, #1048576
> Ltmp7:
> @DEBUG_VALUE: [J]java.lang.Object.<init>()V:__$env <- R0
> it ne
> blxne __bcRegisterFinalizer
> add sp, #4
> pop {r7, pc}
> Ltmp8:
> Lfunc_end18:
> "L_[J]java.lang.Object.<init>()V_end":
>
> .cfi_endproc
> =======================
>
> Now, when stepping into this function, LLDB receives a signal from the
> debug server:
>
> =======================
> (lldb) s
> Process 176 stopped
> * thread #1: tid = 0x11f5, 0x0023e2ec
> AttachTestIOSDev`[J]java.lang.Object.<init>(__$env=0x0169efc8,
> __$this=0x0174cd10)V + 24 at Object.java:136, queue =
> 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION
> (code=EXC_ARM_UNDEFINED, subcode=0xffd1b001)
>     frame #0: 0x0023e2ec
> AttachTestIOSDev`[J]java.lang.Object.<init>(__$env=0x0169efc8,
> __$this=0x0174cd10)V + 24 at Object.java:136
> =======================
>
> Disassembling around the PC gives:
>
> =======================
> (lldb) disassemble --pc
> AttachTestIOSDev`[J]java.lang.Object.<init>()V + 24 at Object.java:136:
> -> 0x23e2ec:  .long  0xb001ffd1                ; unknown opcode
>    0x23e2f0:  pop    {r7, pc}
>
> AttachTestIOSDev`[J]java.lang.Object.<init>()V + 30:
>    0x23e2f2:  nop
>
> Disassembling until the beginning of the frame gives:
>
> (lldb) disassemble -f
> AttachTestIOSDev`[J]java.lang.Object.<init>()V at Object.java:136:
>    0x23e2d4:  push   {r7, lr}
>    0x23e2d6:  mov    r7, sp
>    0x23e2d8:  sub    sp, #0x4
>    0x23e2da:  movs   r2, #0x0
>    0x23e2dc:  str    r2, [sp]
>    0x23e2de:  str    r1, [sp]
>    0x23e2e0:  ldr    r2, [r1]
>    0x23e2e2:  ldr    r2, [r2, #0x30]
>    0x23e2e4:  tst.w  r2, #0x100000
>    0x23e2e8:  it     ne
>    0x23e2ea:  blne   0x429290                  ; _bcRegisterFinalizer
>    0x23e2ee:  add    sp, #0x4
>    0x23e2f0:  pop    {r7, pc}
>
> Accprding to this, execution should never end up at address 0x23e2ec.
> That's right in the middle of the blne and add instructions in the second
> disassembly. I have a hunch that the debugserver on the device may
> interfere here, e.g. add a trap instruction to implement the stepping. I'm
> not quite sure what to make of it.
>
> I'd appreciate any hints. If you require more information, i got plenty of
> logs :)
>
> Thanks,
> Mario
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/lldb-dev/attachments/20141126/d9cf590a/attachment.html>


More information about the lldb-dev mailing list