1# 2# gdb helper commands and functions for Linux kernel debugging 3# 4# Kernel proc information reader 5# 6# Copyright (c) 2016 Linaro Ltd 7# 8# Authors: 9# Kieran Bingham <kieran.bingham@linaro.org> 10# 11# This work is licensed under the terms of the GNU GPL version 2. 12# 13 14import gdb 15from linux import constants 16from linux import utils 17from linux import tasks 18from linux import lists 19from struct import * 20 21 22class LxCmdLine(gdb.Command): 23 """ Report the Linux Commandline used in the current kernel. 24 Equivalent to cat /proc/cmdline on a running target""" 25 26 def __init__(self): 27 super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA) 28 29 def invoke(self, arg, from_tty): 30 gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n") 31 32LxCmdLine() 33 34 35class LxVersion(gdb.Command): 36 """ Report the Linux Version of the current kernel. 37 Equivalent to cat /proc/version on a running target""" 38 39 def __init__(self): 40 super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA) 41 42 def invoke(self, arg, from_tty): 43 # linux_banner should contain a newline 44 gdb.write(gdb.parse_and_eval("linux_banner").string()) 45 46LxVersion() 47 48 49# Resource Structure Printers 50# /proc/iomem 51# /proc/ioports 52 53def get_resources(resource, depth): 54 while resource: 55 yield resource, depth 56 57 child = resource['child'] 58 if child: 59 for res, deep in get_resources(child, depth + 1): 60 yield res, deep 61 62 resource = resource['sibling'] 63 64 65def show_lx_resources(resource_str): 66 resource = gdb.parse_and_eval(resource_str) 67 width = 4 if resource['end'] < 0x10000 else 8 68 # Iterate straight to the first child 69 for res, depth in get_resources(resource['child'], 0): 70 start = int(res['start']) 71 end = int(res['end']) 72 gdb.write(" " * depth * 2 + 73 "{0:0{1}x}-".format(start, width) + 74 "{0:0{1}x} : ".format(end, width) + 75 res['name'].string() + "\n") 76 77 78class LxIOMem(gdb.Command): 79 """Identify the IO memory resource locations defined by the kernel 80 81Equivalent to cat /proc/iomem on a running target""" 82 83 def __init__(self): 84 super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA) 85 86 def invoke(self, arg, from_tty): 87 return show_lx_resources("iomem_resource") 88 89LxIOMem() 90 91 92class LxIOPorts(gdb.Command): 93 """Identify the IO port resource locations defined by the kernel 94 95Equivalent to cat /proc/ioports on a running target""" 96 97 def __init__(self): 98 super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA) 99 100 def invoke(self, arg, from_tty): 101 return show_lx_resources("ioport_resource") 102 103LxIOPorts() 104 105 106# Mount namespace viewer 107# /proc/mounts 108 109def info_opts(lst, opt): 110 opts = "" 111 for key, string in lst.items(): 112 if opt & key: 113 opts += string 114 return opts 115 116 117FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync", 118 constants.LX_MS_MANDLOCK: ",mand", 119 constants.LX_MS_DIRSYNC: ",dirsync", 120 constants.LX_MS_NOATIME: ",noatime", 121 constants.LX_MS_NODIRATIME: ",nodiratime"} 122 123MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid", 124 constants.LX_MNT_NODEV: ",nodev", 125 constants.LX_MNT_NOEXEC: ",noexec", 126 constants.LX_MNT_NOATIME: ",noatime", 127 constants.LX_MNT_NODIRATIME: ",nodiratime", 128 constants.LX_MNT_RELATIME: ",relatime"} 129 130mount_type = utils.CachedType("struct mount") 131mount_ptr_type = mount_type.get_type().pointer() 132 133 134class LxMounts(gdb.Command): 135 """Report the VFS mounts of the current process namespace. 136 137Equivalent to cat /proc/mounts on a running target 138An integer value can be supplied to display the mount 139values of that process namespace""" 140 141 def __init__(self): 142 super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA) 143 144 # Equivalent to proc_namespace.c:show_vfsmnt 145 # However, that has the ability to call into s_op functions 146 # whereas we cannot and must make do with the information we can obtain. 147 def invoke(self, arg, from_tty): 148 argv = gdb.string_to_argv(arg) 149 if len(argv) >= 1: 150 try: 151 pid = int(argv[0]) 152 except: 153 raise gdb.GdbError("Provide a PID as integer value") 154 else: 155 pid = 1 156 157 task = tasks.get_task_by_pid(pid) 158 if not task: 159 raise gdb.GdbError("Couldn't find a process with PID {}" 160 .format(pid)) 161 162 namespace = task['nsproxy']['mnt_ns'] 163 if not namespace: 164 raise gdb.GdbError("No namespace for current process") 165 166 for vfs in lists.list_for_each_entry(namespace['list'], 167 mount_ptr_type, "mnt_list"): 168 devname = vfs['mnt_devname'].string() 169 devname = devname if devname else "none" 170 171 pathname = "" 172 parent = vfs 173 while True: 174 mntpoint = parent['mnt_mountpoint'] 175 pathname = utils.dentry_name(mntpoint) + pathname 176 if (parent == parent['mnt_parent']): 177 break 178 parent = parent['mnt_parent'] 179 180 if (pathname == ""): 181 pathname = "/" 182 183 superblock = vfs['mnt']['mnt_sb'] 184 fstype = superblock['s_type']['name'].string() 185 s_flags = int(superblock['s_flags']) 186 m_flags = int(vfs['mnt']['mnt_flags']) 187 rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw" 188 189 gdb.write( 190 "{} {} {} {}{}{} 0 0\n" 191 .format(devname, 192 pathname, 193 fstype, 194 rd, 195 info_opts(FS_INFO, s_flags), 196 info_opts(MNT_INFO, m_flags))) 197 198LxMounts() 199 200 201class LxFdtDump(gdb.Command): 202 """Output Flattened Device Tree header and dump FDT blob to the filename 203 specified as the command argument. Equivalent to 204 'cat /proc/fdt > fdtdump.dtb' on a running target""" 205 206 def __init__(self): 207 super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA, 208 gdb.COMPLETE_FILENAME) 209 210 def fdthdr_to_cpu(self, fdt_header): 211 212 fdt_header_be = ">IIIIIII" 213 fdt_header_le = "<IIIIIII" 214 215 if utils.get_target_endianness() == 1: 216 output_fmt = fdt_header_le 217 else: 218 output_fmt = fdt_header_be 219 220 return unpack(output_fmt, pack(fdt_header_be, 221 fdt_header['magic'], 222 fdt_header['totalsize'], 223 fdt_header['off_dt_struct'], 224 fdt_header['off_dt_strings'], 225 fdt_header['off_mem_rsvmap'], 226 fdt_header['version'], 227 fdt_header['last_comp_version'])) 228 229 def invoke(self, arg, from_tty): 230 231 if not constants.LX_CONFIG_OF: 232 raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n") 233 234 if len(arg) == 0: 235 filename = "fdtdump.dtb" 236 else: 237 filename = arg 238 239 py_fdt_header_ptr = gdb.parse_and_eval( 240 "(const struct fdt_header *) initial_boot_params") 241 py_fdt_header = py_fdt_header_ptr.dereference() 242 243 fdt_header = self.fdthdr_to_cpu(py_fdt_header) 244 245 if fdt_header[0] != constants.LX_OF_DT_HEADER: 246 raise gdb.GdbError("No flattened device tree magic found\n") 247 248 gdb.write("fdt_magic: 0x{:02X}\n".format(fdt_header[0])) 249 gdb.write("fdt_totalsize: 0x{:02X}\n".format(fdt_header[1])) 250 gdb.write("off_dt_struct: 0x{:02X}\n".format(fdt_header[2])) 251 gdb.write("off_dt_strings: 0x{:02X}\n".format(fdt_header[3])) 252 gdb.write("off_mem_rsvmap: 0x{:02X}\n".format(fdt_header[4])) 253 gdb.write("version: {}\n".format(fdt_header[5])) 254 gdb.write("last_comp_version: {}\n".format(fdt_header[6])) 255 256 inf = gdb.inferiors()[0] 257 fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr, 258 fdt_header[1]).tobytes() 259 260 try: 261 f = open(filename, 'wb') 262 except: 263 raise gdb.GdbError("Could not open file to dump fdt") 264 265 f.write(fdt_buf) 266 f.close() 267 268 gdb.write("Dumped fdt blob to " + filename + "\n") 269 270LxFdtDump() 271