[lldb-dev] Stepping into function generates EXC_BAD_INSTRUCTION signal

Mario Zechner badlogicgames at gmail.com
Mon Dec 1 05:07:29 PST 2014


Well, i wrote a very long mail detailing my journey to resolve issue #2
(hanging after setting target.use-fast-stepping=false), only to eventually
realize that it doesn't hang but instead just waits for the above loop to
complete.

This means turning off target.use-fast-stepping is not an option and i'm
back to square one. I'd be grateful for any pointers on how to fix issue #1
(EXC_BAD_INSTRUCTION). I guess i'll start by investigating the "run to next
branch" stepping algorithm in LLDB, though my understanding is likely not
sufficient to make a dent.

Thanks,
Mario




On Mon, Dec 1, 2014 at 11:05 AM, Mario Zechner <badlogicgames at gmail.com>
wrote:

> Hi,
>
> setting target.use-fast-stepping to false did indeed solve this issue,
> albeit at the cost of increased runtime obviously. However, i ran into
> another issue right after i stepped out of the previously problematic
> function: http://sht.tl/bdAKRC
>
> Trying to source-level step this function (with use-fast-stepping=false)
> results in 1) the disassembly getting all kinds of messed up and 2) the
> process not stepping but hanging at the `cmp r1, #0` instruction. The
> original assembly code around that PC looks like this:
>
> LBB24_1:                                @ %label0
>                                         @ =>This Inner Loop Header: Depth=1
> @DEBUG_VALUE:
> [J]java.lang.Thread.<init>(Ljava/lang/Runnable;Ljava/lang/String;)V:__$env
> <- R5
> ldrexd r1, r2, [r0]
> strexd r1, r6, r6, [r0]
> cmp r1, #0
> bne LBB24_1
> @ BB#2:                                 @ %label0
> @DEBUG_VALUE:
> [J]java.lang.Thread.<init>(Ljava/lang/Runnable;Ljava/lang/String;)V:__$env
> <- R5
> dmb ish
> movs r1, #5
>
> A simple loop, which is actually part of an inlined function. We had some
> issues with inlined functions previously, i assume this issue is related.
> Interestingly enough, the back trace is also a bit wonky:
>
> (lldb) bt
>
> * thread #1: tid = 0x18082, 0x0021a9b4
> AttachTestIOSDev`[J]java.lang.Thread.<init>(Ljava/lang/Runnable;Ljava/lang/String;)V
> [inlined] [j]java.lang.Thread.threadPtr(J)[set] + 14 at Thread.java:1, stop
> reason = trace
>
>   * frame #0: 0x0021a9b4
> AttachTestIOSDev`[J]java.lang.Thread.<init>(Ljava/lang/Runnable;Ljava/lang/String;)V
> [inlined] [j]java.lang.Thread.threadPtr(J)[set] + 14 at Thread.java:1
>
>     frame #1: 0x0021a9a6
> AttachTestIOSDev`[J]java.lang.Thread.<init>(__$env=0x01662fc8,
> __$this=0x64da3833, runnable=0xa4f07400, threadName=0x00286000)V + 46 at
> Thread.java:138
> There should be a lot more frame. I'm gonna try to dig up some more
> details.
>
> Thanks a lot!
> Mario
>
>
>
> On Sun, Nov 30, 2014 at 1:32 AM, Jason Molenda <jason at molenda.com> wrote:
>
>> The size of the breakpoint instruction is set by
>> GetSoftwareBreakpointTrapOpcode().  In your case, most likely you're in
>> PlatformDarwin::GetSoftwareBreakpointTrapOpcode() - lldb uses the symbol
>> table (from the binary file) to determine if the code in a given function
>> is arm or thumb.  If it's arm, a 4 byte breakpoint is used.  If it's thumb,
>> a 2 byte breakpoint.  Of course thumbv2 of T32 instructions can be 4 bytes
>> -- the blne instruction is in your program -- but I assume the 2 byte
>> breakpoint instruction still works correctly in these cases; the cpu sees
>> the 2-byte instruction and stops execution.
>>
>> I am a little wary about the fact that this comes after an it
>> instruction, I kind of vaguely remember issues with that instruction's
>> behavior.
>>
>> It shouldn't make any difference but you might want to try
>>
>> (lldb) settings set target.use-fast-stepping false
>>
>> which will force lldb to single instruction step through the function.
>> Right now lldb is looking at the instruction stream and putting breakpoints
>> on branch/call/jump instructions to do your high-level "step" command,
>> instead of stopping on every instruction.  It is possible there could be a
>> problem with that approach and the it instruction.  Please report back if
>> this changes the behavior.
>>
>> J
>>
>>
>> > On Nov 26, 2014, at 9:22 AM, Mario Zechner <badlogicgames at gmail.com>
>> wrote:
>> >
>> > 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
>> >
>> > _______________________________________________
>> > lldb-dev mailing list
>> > lldb-dev at cs.uiuc.edu
>> > http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/lldb-dev/attachments/20141201/b9d13cec/attachment.html>


More information about the lldb-dev mailing list