mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
8.5 KiB
275 lines
8.5 KiB
# |
|
# gdb helper commands and functions for Linux kernel debugging |
|
# |
|
# Kernel proc information reader |
|
# |
|
# Copyright (c) 2016 Linaro Ltd |
|
# |
|
# Authors: |
|
# Kieran Bingham <[email protected]> |
|
# |
|
# This work is licensed under the terms of the GNU GPL version 2. |
|
# |
|
|
|
import gdb |
|
from linux import constants |
|
from linux import utils |
|
from linux import tasks |
|
from linux import lists |
|
from struct import * |
|
|
|
|
|
class LxCmdLine(gdb.Command): |
|
""" Report the Linux Commandline used in the current kernel. |
|
Equivalent to cat /proc/cmdline on a running target""" |
|
|
|
def __init__(self): |
|
super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA) |
|
|
|
def invoke(self, arg, from_tty): |
|
gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n") |
|
|
|
|
|
LxCmdLine() |
|
|
|
|
|
class LxVersion(gdb.Command): |
|
""" Report the Linux Version of the current kernel. |
|
Equivalent to cat /proc/version on a running target""" |
|
|
|
def __init__(self): |
|
super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA) |
|
|
|
def invoke(self, arg, from_tty): |
|
# linux_banner should contain a newline |
|
gdb.write(gdb.parse_and_eval("(char *)linux_banner").string()) |
|
|
|
|
|
LxVersion() |
|
|
|
|
|
# Resource Structure Printers |
|
# /proc/iomem |
|
# /proc/ioports |
|
|
|
def get_resources(resource, depth): |
|
while resource: |
|
yield resource, depth |
|
|
|
child = resource['child'] |
|
if child: |
|
for res, deep in get_resources(child, depth + 1): |
|
yield res, deep |
|
|
|
resource = resource['sibling'] |
|
|
|
|
|
def show_lx_resources(resource_str): |
|
resource = gdb.parse_and_eval(resource_str) |
|
width = 4 if resource['end'] < 0x10000 else 8 |
|
# Iterate straight to the first child |
|
for res, depth in get_resources(resource['child'], 0): |
|
start = int(res['start']) |
|
end = int(res['end']) |
|
gdb.write(" " * depth * 2 + |
|
"{0:0{1}x}-".format(start, width) + |
|
"{0:0{1}x} : ".format(end, width) + |
|
res['name'].string() + "\n") |
|
|
|
|
|
class LxIOMem(gdb.Command): |
|
"""Identify the IO memory resource locations defined by the kernel |
|
|
|
Equivalent to cat /proc/iomem on a running target""" |
|
|
|
def __init__(self): |
|
super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA) |
|
|
|
def invoke(self, arg, from_tty): |
|
return show_lx_resources("iomem_resource") |
|
|
|
|
|
LxIOMem() |
|
|
|
|
|
class LxIOPorts(gdb.Command): |
|
"""Identify the IO port resource locations defined by the kernel |
|
|
|
Equivalent to cat /proc/ioports on a running target""" |
|
|
|
def __init__(self): |
|
super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA) |
|
|
|
def invoke(self, arg, from_tty): |
|
return show_lx_resources("ioport_resource") |
|
|
|
|
|
LxIOPorts() |
|
|
|
|
|
# Mount namespace viewer |
|
# /proc/mounts |
|
|
|
def info_opts(lst, opt): |
|
opts = "" |
|
for key, string in lst.items(): |
|
if opt & key: |
|
opts += string |
|
return opts |
|
|
|
|
|
FS_INFO = {constants.LX_SB_SYNCHRONOUS: ",sync", |
|
constants.LX_SB_MANDLOCK: ",mand", |
|
constants.LX_SB_DIRSYNC: ",dirsync", |
|
constants.LX_SB_NOATIME: ",noatime", |
|
constants.LX_SB_NODIRATIME: ",nodiratime"} |
|
|
|
MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid", |
|
constants.LX_MNT_NODEV: ",nodev", |
|
constants.LX_MNT_NOEXEC: ",noexec", |
|
constants.LX_MNT_NOATIME: ",noatime", |
|
constants.LX_MNT_NODIRATIME: ",nodiratime", |
|
constants.LX_MNT_RELATIME: ",relatime"} |
|
|
|
mount_type = utils.CachedType("struct mount") |
|
mount_ptr_type = mount_type.get_type().pointer() |
|
|
|
|
|
class LxMounts(gdb.Command): |
|
"""Report the VFS mounts of the current process namespace. |
|
|
|
Equivalent to cat /proc/mounts on a running target |
|
An integer value can be supplied to display the mount |
|
values of that process namespace""" |
|
|
|
def __init__(self): |
|
super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA) |
|
|
|
# Equivalent to proc_namespace.c:show_vfsmnt |
|
# However, that has the ability to call into s_op functions |
|
# whereas we cannot and must make do with the information we can obtain. |
|
def invoke(self, arg, from_tty): |
|
argv = gdb.string_to_argv(arg) |
|
if len(argv) >= 1: |
|
try: |
|
pid = int(argv[0]) |
|
except gdb.error: |
|
raise gdb.GdbError("Provide a PID as integer value") |
|
else: |
|
pid = 1 |
|
|
|
task = tasks.get_task_by_pid(pid) |
|
if not task: |
|
raise gdb.GdbError("Couldn't find a process with PID {}" |
|
.format(pid)) |
|
|
|
namespace = task['nsproxy']['mnt_ns'] |
|
if not namespace: |
|
raise gdb.GdbError("No namespace for current process") |
|
|
|
gdb.write("{:^18} {:^15} {:>9} {} {} options\n".format( |
|
"mount", "super_block", "devname", "pathname", "fstype")) |
|
|
|
for vfs in lists.list_for_each_entry(namespace['list'], |
|
mount_ptr_type, "mnt_list"): |
|
devname = vfs['mnt_devname'].string() |
|
devname = devname if devname else "none" |
|
|
|
pathname = "" |
|
parent = vfs |
|
while True: |
|
mntpoint = parent['mnt_mountpoint'] |
|
pathname = utils.dentry_name(mntpoint) + pathname |
|
if (parent == parent['mnt_parent']): |
|
break |
|
parent = parent['mnt_parent'] |
|
|
|
if (pathname == ""): |
|
pathname = "/" |
|
|
|
superblock = vfs['mnt']['mnt_sb'] |
|
fstype = superblock['s_type']['name'].string() |
|
s_flags = int(superblock['s_flags']) |
|
m_flags = int(vfs['mnt']['mnt_flags']) |
|
rd = "ro" if (s_flags & constants.LX_SB_RDONLY) else "rw" |
|
|
|
gdb.write("{} {} {} {} {} {}{}{} 0 0\n".format( |
|
vfs.format_string(), superblock.format_string(), devname, |
|
pathname, fstype, rd, info_opts(FS_INFO, s_flags), |
|
info_opts(MNT_INFO, m_flags))) |
|
|
|
|
|
LxMounts() |
|
|
|
|
|
class LxFdtDump(gdb.Command): |
|
"""Output Flattened Device Tree header and dump FDT blob to the filename |
|
specified as the command argument. Equivalent to |
|
'cat /proc/fdt > fdtdump.dtb' on a running target""" |
|
|
|
def __init__(self): |
|
super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA, |
|
gdb.COMPLETE_FILENAME) |
|
|
|
def fdthdr_to_cpu(self, fdt_header): |
|
|
|
fdt_header_be = ">IIIIIII" |
|
fdt_header_le = "<IIIIIII" |
|
|
|
if utils.get_target_endianness() == 1: |
|
output_fmt = fdt_header_le |
|
else: |
|
output_fmt = fdt_header_be |
|
|
|
return unpack(output_fmt, pack(fdt_header_be, |
|
fdt_header['magic'], |
|
fdt_header['totalsize'], |
|
fdt_header['off_dt_struct'], |
|
fdt_header['off_dt_strings'], |
|
fdt_header['off_mem_rsvmap'], |
|
fdt_header['version'], |
|
fdt_header['last_comp_version'])) |
|
|
|
def invoke(self, arg, from_tty): |
|
|
|
if not constants.LX_CONFIG_OF: |
|
raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n") |
|
|
|
if len(arg) == 0: |
|
filename = "fdtdump.dtb" |
|
else: |
|
filename = arg |
|
|
|
py_fdt_header_ptr = gdb.parse_and_eval( |
|
"(const struct fdt_header *) initial_boot_params") |
|
py_fdt_header = py_fdt_header_ptr.dereference() |
|
|
|
fdt_header = self.fdthdr_to_cpu(py_fdt_header) |
|
|
|
if fdt_header[0] != constants.LX_OF_DT_HEADER: |
|
raise gdb.GdbError("No flattened device tree magic found\n") |
|
|
|
gdb.write("fdt_magic: 0x{:02X}\n".format(fdt_header[0])) |
|
gdb.write("fdt_totalsize: 0x{:02X}\n".format(fdt_header[1])) |
|
gdb.write("off_dt_struct: 0x{:02X}\n".format(fdt_header[2])) |
|
gdb.write("off_dt_strings: 0x{:02X}\n".format(fdt_header[3])) |
|
gdb.write("off_mem_rsvmap: 0x{:02X}\n".format(fdt_header[4])) |
|
gdb.write("version: {}\n".format(fdt_header[5])) |
|
gdb.write("last_comp_version: {}\n".format(fdt_header[6])) |
|
|
|
inf = gdb.inferiors()[0] |
|
fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr, |
|
fdt_header[1]).tobytes() |
|
|
|
try: |
|
f = open(filename, 'wb') |
|
except gdb.error: |
|
raise gdb.GdbError("Could not open file to dump fdt") |
|
|
|
f.write(fdt_buf) |
|
f.close() |
|
|
|
gdb.write("Dumped fdt blob to " + filename + "\n") |
|
|
|
|
|
LxFdtDump()
|
|
|