kgdb is perfect

baremetal works on docker. Missing deps + virtfs now skips missing dirs.
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2018-11-02 00:00:00 +00:00
parent 1cd1e58b02
commit 9571747859
5 changed files with 108 additions and 47 deletions

View File

@@ -1059,8 +1059,8 @@ Shell 2:
In GDB, hit `Ctrl-C`, and note how it says:
....
scanning for modules in /full/path/to/linux-kernel-module-cheat/out/kernel_modules/x86_64/kernel_modules
loading @0xffffffffc0000000: /full/path/to/linux-kernel-module-cheat/out/kernel_modules/x86_64/kernel_modules/timer.ko
scanning for modules in /root/linux-kernel-module-cheat/out/kernel_modules/x86_64/kernel_modules
loading @0xffffffffc0000000: /root/linux-kernel-module-cheat/out/kernel_modules/x86_64/kernel_modules/timer.ko
....
That's `lx-symbols` working! Now simply:
@@ -1839,9 +1839,9 @@ See also: https://stackoverflow.com/questions/13496389/gdb-remote-protocol-how-t
KGDB is kernel dark magic that allows you to GDB the kernel on real hardware without any extra hardware support.
It is useless with QEMU since we already have full system visibility with `-gdb`, but this is a good way to learn it.
It is useless with QEMU since we already have full system visibility with `-gdb`. So the goal of this setup is just to prepare you for what to expect when you will be in the treches of real hardware.
Cheaper than JTAG (free) and easier to setup (all you need is serial), but with less visibility as it depends on the kernel working, so e.g.: dies on panic, does not see boot sequence.
KGDB is cheaper than JTAG (free) and easier to setup (all you need is serial), but with less visibility as it depends on the kernel working, so e.g.: dies on panic, does not see boot sequence.
First run the kernel with:
@@ -1872,7 +1872,9 @@ Entering kdb (current=0x(____ptrval____), pid 1) on processor 0 due to Keyboard
KGDB expects the connection at `ttyS1`, our second serial port after `ttyS0` which contains the terminal.
So now we can connect to the serial port with GDB:
The last line is the KDB prompt, and is covered at: <<kdb>>. Typing now shows nothing because that prompt is expecting input from `ttyS1`.
Instad, we connect to the serial port `ttyS1` with GDB:
....
./run-gdb --kgdb --no-continue
@@ -1924,31 +1926,11 @@ See also:
=== KGDB ARM
GDB not connecting to KGDB in `arm` and `aarch64`.
TODO: we would need a second serial for KGDB to work, but it is not currently supported on `arm` and `aarch64` with `-M virt` that we use: https://unix.stackexchange.com/questions/479085/can-qemu-m-virt-on-arm-aarch64-have-multiple-serial-ttys-like-such-as-pl011-t/479340#479340
Main question: https://stackoverflow.com/questions/14155577/how-to-use-kgdb-on-arm
One possible workaround for this would be to use <<kdb-arm>>.
The main console just hangs on:
....
Entering kdb (current=0xf8ce07d3, pid 1) due to Keyboard Entry
kdb>
....
and GDB shell gives:
....
Reading symbols from vmlinux...done.
Remote debugging using localhost:1234
Ignoring packet error, continuing...
warning: unrecognized item "timeout" in "qSupported" response
Ignoring packet error, continuing...
Remote replied unexpectedly to 'vMustReplyEmpty': timeout
....
I wanted to try to and run run KGDB on a second serial to see if it makes a difference, but QEMU `-M virt` does not seem to support it: https://stackoverflow.com/questions/53080745/can-qemu-m-virt-on-arm-aarch64-have-multiple-serial-ttys-like-such-as-pl011-t
Tested on d089c4660615abaf5ae16255fc0195cf989ce437.
Main more generic question: https://stackoverflow.com/questions/14155577/how-to-use-kgdb-on-arm
=== KGDB kernel modules
@@ -1976,43 +1958,96 @@ TODO: if I `-ex lx-symbols` to the `gdb` command, just like done for QEMU `-gdb`
=== KDB
If you modify `run` to use:
KDB is a way to use KDB directly in your main console, without GDB.
Advantage over KGDB: you can do everything in one serial. This can actually be important if you only have one serial for both shell and .
Disadvantage: not as much functionality as GDB, especially when you use Python scripts.
Disadvantage: not as much functionality as GDB, especially when you use Python scripts. Notably, TODO confirm you can't see the the kernel source code and line step as from GDB, since the kernel source is not available on guest (ah, if only debugging information supported full source, or if the kernel had a crazy mechanism to embed it).
TODO: only works in <<graphics,graphic mode>>. On the serial, prompt hangs, and the characters I type don't show up at all.
Run QEMU as:
In QEMU:
....
./run --kdb
....
This passes `kgdboc=ttyS0` to the Linux CLI, therefore using our main console. Then QEMU:
....
[0]kdb> go
....
Boot finishes, then:
And now the `kdb>` prompt is responsive because it is listening to the main console.
After boot finishes, run the usual:
....
/count.sh &
/kgdb.sh
....
Source: link:rootfs_overlay/kgdb-mod.sh[].
And you are back in KDB. Now you can:
And you are back in KDB. Now you can count with:
....
[0]kdb> help
[0]kdb> bp __x64_sys_write
[0]kdb> go
[0]kdb> go
[0]kdb> go
[0]kdb> go
....
And you will break whenever `__x64_sys_write` is hit.
TODO: `bp __x64_sys_write` is failing with `illegal numeric value` as of 10dd9178c6dccf1964002cc9368a5aa83b345487. I think it worked before, so needs bisection.
You can get see further commands with:
....
[0]kdb> help
....
The other KDB commands allow you to instruction steps, view memory, registers and some higher level kernel runtime data.
But TODO I don't think you can see where you are in the kernel source code and line step as from GDB, since the kernel source is not available on guest (ah, if only debugging information supported full source).
==== KDB graphic
You can also use KDB directly from the <<graphics,graphic>> window with:
....
./run --graphic --kdb
....
This setup could be used to debug the kernel on machines without serial, such as modern desktops.
This works because `--graphics` This adds `kbd` (which stands for `KeyBoarD`!) to `kgdboc`.
==== KDB ARM
TODO neither `arm` and `aarch64` are working as of 1cd1e58b023791606498ca509256cc48e95e4f5b + 1.
`arm` seems to place and hit the breakpoint correctly, but no matter how many `go` commands I do, the `count.sh` stdout simply does not show.
`aarch64` seems to place the breakpoint correctly, but after the first `go` the kernel oopses with warning:
....
WARNING: CPU: 0 PID: 46 at /root/linux-kernel-module-cheat/submodules/linux/kernel/smp.c:416 smp_call_function_many+0xdc/0x358
....
and stack trace:
....
smp_call_function_many+0xdc/0x358
kick_all_cpus_sync+0x30/0x38
kgdb_flush_swbreak_addr+0x3c/0x48
dbg_deactivate_sw_breakpoints+0x7c/0xb8
kgdb_cpu_enter+0x284/0x6a8
kgdb_handle_exception+0x138/0x240
kgdb_brk_fn+0x2c/0x40
brk_handler+0x7c/0xc8
do_debug_exception+0xa4/0x1c0
el1_dbg+0x18/0x78
__arm64_sys_write+0x0/0x30
el0_svc_handler+0x74/0x90
el0_svc+0x8/0xc
....
My theory is that every serious ARM developer has either serial or JTAG, and no one ever tests this, and the kernel code is just broken.
== gdbserver
@@ -8876,7 +8911,7 @@ Large input may also require tweaking:
The easiest thing to do, is to link:https://superuser.com/questions/231002/how-can-i-search-within-the-output-buffer-of-a-tmux-shell/1253137#1253137[scroll up on the host shell] after the build, and look for a line of type:
....
Running /full/path/to/linux-kernel-module-cheat/out/aarch64/buildroot/build/parsec-benchmark-custom/ext/splash2x/apps/ocean_ncp/inst/aarch64-linux.gcc/bin/ocean_ncp -n2050 -p1 -e1e-07 -r20000 -t28800
Running /root/linux-kernel-module-cheat/out/aarch64/buildroot/build/parsec-benchmark-custom/ext/splash2x/apps/ocean_ncp/inst/aarch64-linux.gcc/bin/ocean_ncp -n2050 -p1 -e1e-07 -r20000 -t28800
....
and then tweak the command found in `test.sh` accordingly.

View File

@@ -12,6 +12,7 @@ CT_DEBUG_GDB=y
CT_GDB_CROSS_SIM=y
# For Docker.
# https://stackoverflow.com/questions/17466017/how-to-solve-you-must-not-be-root-to-run-crosstool-ng-when-using-ct-ng/53099177#53099177
CT_EXPERIMENTAL=y
CT_ALLOW_BUILD_AS_ROOT=y
CT_ALLOW_BUILD_AS_ROOT_SURE=y

View File

@@ -117,10 +117,19 @@ scons \
"
fi
if "$baremetal"; then
# http://crosstool-ng.github.io/docs/os-setup/
pkgs="${pkgs} \
bison \
docbook2x \
flex \
gcc \
gperf \
help2man \
libncurses5-dev \
libtool-bin \
make \
python-dev \
texinfo \
"
fi
command -v apt-get >/dev/null 2>&1 || {

View File

@@ -0,0 +1 @@
https://github.com/cirosantilli/linux-kernel-module-cheat#hello-host

29
run
View File

@@ -89,15 +89,19 @@ def main(args, extra_args=None):
elif common.is_arm:
console_type = 'ttyAMA'
console = '{}{}'.format(console_type, console_count)
console_count += 1
if not (args.arch == 'x86_64' and args.graphic):
console_count += 1
kernel_cli += ' console={}'.format(console)
extra_console = '{}{}'.format(console_type, console_count)
console_count += 1
if args.kdb or args.kgdb:
kernel_cli += ' kgdbwait'
if args.kdb:
kernel_cli += ' kgdboc={},115200'.format(console)
if args.graphic:
kdb_cmd = 'kbd,'
else:
kdb_cmd = ''
kernel_cli += ' kgdboc={}{},115200'.format(kdb_cmd, console)
if args.kgdb:
kernel_cli += ' kgdboc={},115200'.format(extra_console)
if kernel_cli_after_dash:
@@ -249,6 +253,20 @@ def main(args, extra_args=None):
if args.kvm:
extra_emulator_args.append('-enable-kvm')
extra_emulator_args.extend(['-serial', 'tcp::{},server,nowait'.format(common.extra_serial_port)])
virtfs_data = [
(common.p9_dir, 'host_data'),
(common.out_dir, 'host_out'),
(common.out_rootfs_overlay_dir, 'host_out_rootfs_overlay'),
(common.rootfs_overlay_dir, 'host_rootfs_overlay'),
]
virtfs_cmd = []
for virtfs_dir, virtfs_tag in virtfs_data:
if os.path.exists(virtfs_dir):
virtfs_cmd.extend([
'-virtfs',
'local,path={virtfs_dir},mount_tag={virtfs_tag},security_model=mapped,id={virtfs_tag}' \
.format(virtfs_dir=virtfs_dir, virtfs_tag=virtfs_tag
)])
cmd.extend(
[
qemu_executable,
@@ -260,11 +278,8 @@ def main(args, extra_args=None):
'-netdev', 'user,hostfwd=tcp::{}-:{},hostfwd=tcp::{}-:22,id=net0'.format(common.qemu_hostfwd_generic_port, common.qemu_hostfwd_generic_port, common.qemu_hostfwd_ssh_port),
'-no-reboot',
'-smp', str(args.cpus),
'-virtfs', 'local,path={},mount_tag=host_data,security_model=mapped,id=host_data'.format(common.p9_dir),
'-virtfs', 'local,path={},mount_tag=host_out,security_model=mapped,id=host_out'.format(common.out_dir),
'-virtfs', 'local,path={},mount_tag=host_out_rootfs_overlay,security_model=mapped,id=host_out_rootfs_overlay'.format(common.out_rootfs_overlay_dir),
'-virtfs', 'local,path={},mount_tag=host_rootfs_overlay,security_model=mapped,id=host_rootfs_overlay'.format(common.rootfs_overlay_dir),
] +
virtfs_cmd +
qemu_user_and_system_options +
serial_monitor +
vnc
@@ -518,7 +533,7 @@ to the program running on the split.
'''
)
parser.add_argument(
'--userland', default=defaults['userland'],
'-u', '--userland', default=defaults['userland'],
help='''\
Run the given userland executable in user mode instead of booting the Linux kernel
in full system mode. In gem5, user mode is called Syscall Emulation (SE) mode and