| Issue |
180924
|
| Summary |
LLDB cannot step off of an inferior's embedded trap instruction if there is in LLDB breakpoint at the same location
|
| Labels |
lldb
|
| Assignees |
|
| Reporter |
DavidSpickett
|
https://github.com/llvm/llvm-project/issues/56268 was fixed, but in testing that I found some strange interactions when a breakpoint is placed by LLDB on top of a breakpoint instruction in the inferior program.
# Reproducer Program
```
int main() {
__builtin_debugtrap();
return 0;
}
```
```
$ ./bin/clang /tmp/test.c -o /tmp/test.o -g
```
```
$ ./bin/llvm-objdump -d /tmp/test.o
<...>
0000000000000714 <main>:
714: d10043ff sub sp, sp, #0x10
718: 2a1f03e0 mov w0, wzr
71c: b9000fff str wzr, [sp, #0xc]
720: d43e0000 brk #0xf000
724: 910043ff add sp, sp, #0x10
728: d65f03c0 ret
```
Note that the `brk` is some way into the `main` function, but with the help of debug info, we know that it's the first instruction after the prologue so that's where `b main` ends up.
```
<...>
.Ltmp1:
.loc 0 2 3 prologue_end // /tmp/test.c:2:3
brk #0xf000
```
# Scenario 1: No LLDB breakpoint
We hit the inferior `brk` and can continue. This is expected behaviour.
```
$ ./bin/lldb /tmp/test.o
(lldb) target create "/tmp/test.o"
Current executable set to '/tmp/test.o' (aarch64).
(lldb) run
Process 116373 launched: '/tmp/test.o' (aarch64)
Process 116373 stopped
* thread #1, name = 'test.o', stop reason = signal SIGTRAP
frame #0: 0x0000aaaaaaaa0724 test.o`main at test.c:3:3
1 int main() {
2 __builtin_debugtrap();
-> 3 return 0;
4 }
(lldb) c
Process 116373 resuming
Process 116373 exited with status = 0 (0x00000000)
```
# Scenario 2: break on `main`, run, continue from LLDB breakpoint
```
$ ./bin/lldb /tmp/test.o
(lldb) target create "/tmp/test.o"
Current executable set to '/tmp/test.o' (aarch64).
(lldb) b main
Breakpoint 1: where = test.o`main + 12 at test.c:2:3, address = 0x0000000000000720
(lldb) run
Process 116408 launched: '/tmp/test.o' (aarch64)
Process 116408 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
1 int main() {
-> 2 __builtin_debugtrap();
3 return 0;
4 }
```
The initial stop is due to the LLDB placed breakpoint. This is expected.
```
(lldb) c
Process 116408 resuming
Process 116408 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
1 int main() {
-> 2 __builtin_debugtrap();
3 return 0;
4 }
```
Continuing sends us into an endless loop of some kind. We don't detect the inferior's `brk` and step past it. The logs show the breakpoint being hit over and over again. This may be part of the single step logic. Which is usually: disable breakpoint, single step until PC changes, re-enable breakpoint.
```
(lldb) log enable lldb break
(lldb) c
<...>
intern-state Hit breakpoint location: 1.1:
module = /tmp/test.o
compile unit = test.c
function = main
location = /tmp/test.c:2:3
address = 0x0000aaaaaaaa0720
resolved = true
hardware = false
hit count = 37479 <<<<<<<<<<< hit count is constantly increasing
, stopping.
intern-state GDBRemoteCommunicationClient::SendGDBStoppointTypePacket() add at addr = 0xaaaaaaaa0720
Process 116408 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
1 int main() {
-> 2 __builtin_debugtrap();
3 return 0;
4 }
```
Maybe there's a niche argument that this is correct, but in 99% of cases, we'd want to continue past it. So what if we remove the LLDB breakpoint?
```
(lldb) breakpoint delete 1
1 breakpoints deleted; 0 breakpoint locations disabled.
(lldb) c
Process 116408 resuming
Process 116408 stopped
* thread #1, name = 'test.o', stop reason = trace
frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
1 int main() {
-> 2 __builtin_debugtrap();
3 return 0;
4 }
```
I would expect us to hit the inferior's `brk` and know to step over it, but for some reason once we've hit an LLDB breakpoint and continued, that doesn't work even if the breakpoint is removed.
# Scenario 3: Hit the LLDB breakpoint once, remove it, then continue
```
$ ./bin/lldb /tmp/test.o
(lldb) target create "/tmp/test.o"
Current executable set to '/tmp/test.o' (aarch64).
(lldb) b main
Breakpoint 1: where = test.o`main + 12 at test.c:2:3, address = 0x0000000000000720
(lldb) run
Process 116507 launched: '/tmp/test.o' (aarch64)
Process 116507 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
1 int main() {
-> 2 __builtin_debugtrap();
3 return 0;
4 }
(lldb) breakpoint delete 1
1 breakpoints deleted; 0 breakpoint locations disabled.
(lldb) c
Process 116507 resuming
Process 116507 stopped
* thread #1, name = 'test.o', stop reason = signal SIGTRAP
frame #0: 0x0000aaaaaaaa0724 test.o`main at test.c:3:3
1 int main() {
2 __builtin_debugtrap();
-> 3 return 0;
4 }
(lldb) c
Process 116507 resuming
Process 116507 exited with status = 0 (0x00000000)
```
This is working as expected.
It's as if continuing after the first LLDB breakpoint hit without removing the LLDB breakpoint puts LLDB into a state where it will never check if we're on an inferior's `brk` instruction.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs