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