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