llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
27.61k stars 11.35k forks source link

Cannot read registers using commands placed in a .lldbinit file #96877

Open DavidSpickett opened 1 month ago

DavidSpickett commented 1 month ago

This is adapted from a report received on Discord where someone was trying to debug a Linux kernel image and wanted to relocate a file using the value stored in a register as the offset.

I reproduced this with a simpler setup and I think it's down to behaviour differences for commands inside a .lldbinit file versus being in a sourced file using -s or entered into the interactive prompt.

The commands are:

$ cat .lldbinit
target create /tmp/test.o
b main
run
process status
register read sp
expr `$sp`
target modules load --file /tmp/test.o .text `$sp`

What I expect to happen is we run to main and are able to read sp then use it to relocate the text section. This works fine if you source the file with -s:

$ ./bin/lldb -s lldb-commands
(lldb) command source -s 0 'lldb-commands'
Executing commands in '/home/david.spickett/build-llvm-aarch64/lldb-commands'.
(lldb) target create /tmp/test.o
Current executable set to '/tmp/test.o' (aarch64).
(lldb) b main
Breakpoint 1: where = test.o`main at test.c:2:10, address = 0x000000000000071c
(lldb) run
Process 56857 launched: '/tmp/test.o' (aarch64)
Process 56857 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaaaa71c test.o`main at test.c:2:10
   1    int main() {
-> 2      return 0;
   3    }
(lldb) process status
Process 56857 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaaaa71c test.o`main at test.c:2:10
   1    int main() {
-> 2      return 0;
   3    }
(lldb) register read sp
      sp = 0x0000fffffffff200
(lldb) expr `$sp`
(long) $1 = 281474976707072
(lldb) target modules load --file /tmp/test.o .text `$sp`
section '.text' loaded at 0xfffffffff200

It does not work if you put the same commands in a .lldbinit file:

$ ./bin/lldb
Current executable set to '/tmp/test.o' (aarch64).
Breakpoint 1: where = test.o`main at test.c:2:10, address = 0x000000000000071c
Process 56955 launched: '/tmp/test.o' (aarch64)
Process 56955 stopped
error: Command requires a process which is currently stopped.
error: <user expression 0>:1:1: use of undeclared identifier '$sp'
    1 | $sp
      | ^
error: invalid load address string '$sp'
Process 56955 stopped
* thread #1, name = 'test.o', stop reason = signal SIGSEGV: address not mapped to object (fault address: 0x5b0)
    frame #0: 0xffffffffffffffff

We even print that the target has stopped but then we say it isn't. So I suspect that the stop event does not propagate during the time a .lldbinit is running.

llvmbot commented 1 month ago

@llvm/issue-subscribers-lldb

Author: David Spickett (DavidSpickett)

This is adapted from a report received on Discord where someone was trying to debug a Linux kernel image and wanted to relocate a file using the value stored in a register as the offset. I reproduced this with a simpler setup and I think it's down to behaviour differences for commands inside a `.lldbinit` file versus being in a sourced file using `-s` or entered into the interactive prompt. The commands are: ``` $ cat .lldbinit target create /tmp/test.o b main run process status register read sp expr `$sp` target modules load --file /tmp/test.o .text `$sp` ``` What I expect to happen is we run to main and are able to read `sp` then use it to relocate the text section. This works fine if you source the file with `-s`: ``` $ ./bin/lldb -s lldb-commands (lldb) command source -s 0 'lldb-commands' Executing commands in '/home/david.spickett/build-llvm-aarch64/lldb-commands'. (lldb) target create /tmp/test.o Current executable set to '/tmp/test.o' (aarch64). (lldb) b main Breakpoint 1: where = test.o`main at test.c:2:10, address = 0x000000000000071c (lldb) run Process 56857 launched: '/tmp/test.o' (aarch64) Process 56857 stopped * thread #1, name = 'test.o', stop reason = breakpoint 1.1 frame #0: 0x0000aaaaaaaaa71c test.o`main at test.c:2:10 1 int main() { -> 2 return 0; 3 } (lldb) process status Process 56857 stopped * thread #1, name = 'test.o', stop reason = breakpoint 1.1 frame #0: 0x0000aaaaaaaaa71c test.o`main at test.c:2:10 1 int main() { -> 2 return 0; 3 } (lldb) register read sp sp = 0x0000fffffffff200 (lldb) expr `$sp` (long) $1 = 281474976707072 (lldb) target modules load --file /tmp/test.o .text `$sp` section '.text' loaded at 0xfffffffff200 ``` It does not work if you put the same commands in a `.lldbinit` file: ``` $ ./bin/lldb Current executable set to '/tmp/test.o' (aarch64). Breakpoint 1: where = test.o`main at test.c:2:10, address = 0x000000000000071c Process 56955 launched: '/tmp/test.o' (aarch64) Process 56955 stopped error: Command requires a process which is currently stopped. error: <user expression 0>:1:1: use of undeclared identifier '$sp' 1 | $sp | ^ error: invalid load address string '$sp' Process 56955 stopped * thread #1, name = 'test.o', stop reason = signal SIGSEGV: address not mapped to object (fault address: 0x5b0) frame #0: 0xffffffffffffffff ``` We even print that the target has stopped but then we say it isn't. So I suspect that the stop event does not propagate during the time a `.lldbinit` is running.
DavidSpickett commented 1 month ago

@JDevlieghere have we documented what is expected to work in a .lldbinit file? https://lldb.llvm.org/search.html?q=lldbinit&check_keywords=yes&area=default# doesn't list anything.

I know we generally use them for interface things, prompt text, settings, aliases etc. but nowhere says you can't put debug commands in there.

jimingham commented 1 month ago

The ~/.lldbinit file is meant to be read in "before any targets are created". For instance, if you do:

$ lldb foo

First the .lldbinit is read in, THEN target create foo. So creating targets in the .lldbinit seems counter to this design.

Moreover, the .lldbinit is meant to be read in for EVERY lldb session, so making targets in the .lldbinit is a weird thing to do.

And putting the rest of your commands in a source file somewhere, making a shell alias for lldb -s my_special_setup if this is something you are going to do over and over seems a quite straightforward solution.

So I don't think we need to make this work. But it would be a good idea to document that the commands in the .lldbinit file should all be ones that can run before the first target is created.

Really, the .lldbinit is the first in the chain of -S and -O options, none of those should do anything that requires or creates a target.

jimingham commented 1 month ago

I don't think it's worth the effort to go through the commands marking which ones can run in the .lldbinit. This is kind of a corner case, so unless someone is really ambitious, seems to me good enough to just document this fact, and then enforce the restrictions by having violations of them not work.