run-gdb-user: killed it, all that was needed was to pass --userland

Fixes part of https://github.com/cirosantilli/linux-kernel-module-cheat/issues/63
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-06-02 00:00:03 +00:00
parent 402d1343df
commit 7fda133215
4 changed files with 20 additions and 58 deletions

View File

@@ -1944,6 +1944,8 @@ You will generally want to use <<gdbserver>> for this as it is more reliable, bu
Known limitations of direct userland debugging:
* the kernel might switch context to another process or to the kernel itself e.g. on a system call, and then TODO confirm the PIC would go to weird places and source code would be missing.
+
Solutions to this are being researched at: <<lx-ps>>.
* TODO step into shared libraries. If I attempt to load them explicitly:
+
....
@@ -1967,13 +1969,13 @@ For executables from the link:userland/[] directory such as link:userland/posix/
* Shell 2:
+
....
./run-gdb-user userland/posix/count.c main
./run-gdb --userland userland/posix/count.c main
....
+
Alternatively, we could also pass the full path to the executable:
+
....
./run-gdb-user "$(./getvar userland_build_dir)/posix/count.out" main
./run-gdb --userland "$(./getvar userland_build_dir)/posix/count.out" main
....
+
Path resolution is analogous to <<baremetal-setup-getting-started,that of `./run --baremetal`>>.
@@ -1992,7 +1994,7 @@ BusyBox custom init process:
* Shell 2:
+
....
./run-gdb-user "$(./getvar buildroot_build_build_dir)"/busybox-*/busybox ls_main
./run-gdb --userland "$(./getvar buildroot_build_build_dir)"/busybox-*/busybox ls_main
....
This follows BusyBox' convention of calling the main for each executable as `<exec>_main` since the `busybox` executable has many "mains".
@@ -2007,7 +2009,7 @@ BusyBox default init process:
* Shell 2:
+
....
./run-gdb-user "$(./getvar buildroot_build_build_dir)"/busybox-*/busybox init_main
./run-gdb --userland "$(./getvar buildroot_build_build_dir)"/busybox-*/busybox init_main
....
`init` cannot be debugged with <<gdbserver>> without modifying the source, or else `/sbin/init` exits early with:
@@ -2028,19 +2030,20 @@ Non-init process:
* Shell 2:
+
....
./run-gdb-user userland/linux/myinsmod.c main
./run-gdb --userland userland/linux/rand_check.c main
....
* Shell 1 after the boot finishes:
+
....
./linux/myinsmod.out hello.ko
./linux/rand_check.out
....
This is the least reliable setup as there might be other processes that use the given virtual address.
[[gdb-step-debug-userland-non-init-without-gdb-wait]]
===== GDB step debug userland non-init without --gdb-wait
TODO: without `--gdb-wait` and the `break main` that we do inside `./run-gdb-user` says:
TODO: if I try <<gdb-step-debug-userland-non-init>> without `--gdb-wait` and the `break main` that we do inside `./run-gdb` says:
....
Cannot access memory at address 0x10604
@@ -2195,7 +2198,7 @@ so we see that the affinity was restricted to the second core from the start.
Let's do a QEMU observation to justify this example being in the repository with <<gdb-step-debug-userland-non-init,userland breakpoints>>.
We will run our `./linux/sched_getaffinity.out` infinitely many time, on core 0 and core 1 alternatively:
We will run our `./linux/sched_getaffinity.out` infinitely many times, on core 0 and core 1 alternatively:
....
./run \
@@ -2208,7 +2211,7 @@ We will run our `./linux/sched_getaffinity.out` infinitely many time, on core 0
on another shell:
....
./run-gdb-user "$(./getvar userland_build_dir)/linux/sched_getaffinity.out" main
./run-gdb --userland "$(./getvar userland_build_dir)/linux/sched_getaffinity.out" main
....
Then, inside GDB:
@@ -2239,7 +2242,7 @@ TODO we then tried:
and:
....
./run-gdb-user "$(./getvar userland_build_dir)/linux/sched_getaffinity_threads.out"
./run-gdb --userland "$(./getvar userland_build_dir)/linux/sched_getaffinity_threads.out"
....
to switch between two simultaneous live threads with different affinities, it just didn't break on our threads:
@@ -7807,7 +7810,7 @@ TODO `--arch arm` and `--arch aarch64` does not count firmware instructions prop
* We can also discount the instructions after `init` runs by using `readelf` to get the initial address of `init`. One easy way to do that now is to just run:
+
....
./run-gdb-user "$(./getvar userland_build_dir)/linux/poweroff.out" main
./run-gdb --userland "$(./getvar userland_build_dir)/linux/poweroff.out" main
....
+
And get that from the traces, e.g. if the address is `4003a0`, then we search:
@@ -10518,7 +10521,7 @@ We are unable to use `gdbserver` because of networking: <<gem5-host-to-guest-net
The alternative is to do as in <<gdb-step-debug-userland-processes>>.
Next, follow the exact same steps explained at <<gdb-step-debug-userland-non-init-without--d>>, but passing `-g` to every command as usual.
Next, follow the exact same steps explained at <<gdb-step-debug-userland-non-init-without-gdb-wait>>, but passing `--emulator gem5` to every command as usual.
But then TODO (I'll still go crazy one of those days): for `arm`, while debugging `./linux/myinsmod.out hello.ko`, after then line:
@@ -10532,7 +10535,7 @@ I press `n`, it just runs the program until the end, instead of stopping on the
TODO:
....
./run-gdb-user --arch arm --emulator gem5 gem5-1.0/gem5/util/m5/m5 main
./run-gdb --arch arm --emulator gem5 --userland gem5-1.0/gem5/util/m5/m5 main
....
breaks when `m5` is run on guest, but does not show the source code.

View File

@@ -29,7 +29,7 @@ class PathProperties:
# https://stackoverflow.com/questions/51310756/how-to-gdb-step-debug-a-dynamically-linked-executable-in-qemu-user-mode/51343326#51343326
# * when writing assembly code, we have to constantly think about it:
# https://stackoverflow.com/questions/2463150/what-is-the-fpie-option-for-position-independent-executables-in-gcc-and-ld/51308031#51308031
# As of 91986fb2955f96e06d1c5ffcc5536ba9f0af1fd9, our Buildroot toolchain
# As of lkmc 91986fb2955f96e06d1c5ffcc5536ba9f0af1fd9, our Buildroot toolchain
# does not have it enabled by default, but the Ubuntu 18.04 host toolchain does.
'-fno-pie', LF,
'-no-pie', LF,

View File

@@ -149,13 +149,15 @@ the script is a .py file next to the source code.
break_at = ['-ex', 'break {}'.format(self.env['break_at']), LF]
else:
break_at = []
linux_full_system = (self.env['baremetal'] is None and self.env['userland'] is None)
if self.env['userland']:
image = self.env['image']
linux_full_system = False
elif self.env['baremetal']:
image = self.env['image']
linux_full_system = False
else:
image = self.env['vmlinux']
linux_full_system = True
cmd = (
[self.env['gdb_path'], LF] +
before

View File

@@ -1,43 +0,0 @@
#!/usr/bin/env python3
import os
import common
import lkmc.import_path
class Main(common.LkmcCliFunction):
def __init__(self):
super().__init__(
description='''GDB step debug guest userland processes without gdbserver.
More information at: https://github.com/cirosantilli/linux-kernel-module-cheat#gdb-step-debug-userland-processes
'''
)
self.add_argument(
'executable',
help='Path to the executable to be debugged relative to the Buildroot build directory.'
)
self.add_argument(
'break_at',
default=None,
help='Break at this point, e.g. main.',
nargs='?'
)
def timed_main(self):
raise Exception("This is known to be broken, but fixing shouldn't be too hard! Keyword: get_argparse. See also: https://github.com/cirosantilli/linux-kernel-module-cheat/issues/63")
executable = self.env['image']
addr = self.get_elf_entry(os.path.join(self.env['buildroot_build_build_dir'], executable))
args = {}
args['before'] = '-ex \"add-symbol-file {} {}\"'.format(executable, hex(addr))
# Or else lx-symbols throws for arm:
# gdb.MemoryError: Cannot access memory at address 0xbf0040cc
# TODO understand better.
# Also, lx-symbols overrides the add-symbol-file commands.
args['no_lxsymbols'] = True
args['break_at'] = self.env['break_at']
rungdb = lkmc.import_path.import_path_main('run-gdb')
return rungdb(**args)
if __name__ == '__main__':
Main().cli()