From 863981e96738983919de841ec669e157e6bdaeb0 Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Sun, 11 Sep 2016 04:34:46 -0300 Subject: Linux-libre 4.7.1-gnu --- scripts/gdb/linux/.gitignore | 1 + scripts/gdb/linux/Makefile | 14 +++- scripts/gdb/linux/constants.py.in | 52 +++++++++++++ scripts/gdb/linux/cpus.py | 38 ++++++++++ scripts/gdb/linux/dmesg.py | 11 +-- scripts/gdb/linux/lists.py | 21 +++++ scripts/gdb/linux/modules.py | 24 +++--- scripts/gdb/linux/proc.py | 156 ++++++++++++++++++++++++++++++++++++++ scripts/gdb/linux/symbols.py | 2 +- scripts/gdb/linux/tasks.py | 19 +++++ scripts/gdb/linux/utils.py | 32 +++++++- 11 files changed, 348 insertions(+), 22 deletions(-) create mode 100644 scripts/gdb/linux/constants.py.in (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore index 52e4e6114..257354384 100644 --- a/scripts/gdb/linux/.gitignore +++ b/scripts/gdb/linux/.gitignore @@ -1,2 +1,3 @@ *.pyc *.pyo +constants.py diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile index 6cf1ecf61..8b00031f5 100644 --- a/scripts/gdb/linux/Makefile +++ b/scripts/gdb/linux/Makefile @@ -8,4 +8,16 @@ ifneq ($(KBUILD_SRC),) endif @: -clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) +quiet_cmd_gen_constants_py = GEN $@ + cmd_gen_constants_py = \ + $(CPP) -E -x c -P $(c_flags) $< > $@ ;\ + sed -i '1,//d;' $@ + +targets += constants.py +$(obj)/constants.py: $(SRCTREE)/$(obj)/constants.py.in FORCE + $(call if_changed_dep,gen_constants_py) + +build_constants_py: $(obj)/constants.py + @: + +clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) $(obj)/constants.py diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in new file mode 100644 index 000000000..7986f4e0d --- /dev/null +++ b/scripts/gdb/linux/constants.py.in @@ -0,0 +1,52 @@ +/* + * gdb helper commands and functions for Linux kernel debugging + * + * Kernel constants derived from include files. + * + * Copyright (c) 2016 Linaro Ltd + * + * Authors: + * Kieran Bingham + * + * This work is licensed under the terms of the GNU GPL version 2. + * + */ + +#include +#include + +/* We need to stringify expanded macros so that they can be parsed */ + +#define STRING(x) #x +#define XSTRING(x) STRING(x) + +#define LX_VALUE(x) LX_##x = x +#define LX_GDBPARSED(x) LX_##x = gdb.parse_and_eval(XSTRING(x)) + +/* + * IS_ENABLED generates (a || b) which is not compatible with python + * We can only switch on configuration items we know are available + * Therefore - IS_BUILTIN() is more appropriate + */ +#define LX_CONFIG(x) LX_##x = IS_BUILTIN(x) + +/* The build system will take care of deleting everything above this marker */ + + +import gdb + +/* linux/fs.h */ +LX_VALUE(MS_RDONLY) +LX_VALUE(MS_SYNCHRONOUS) +LX_VALUE(MS_MANDLOCK) +LX_VALUE(MS_DIRSYNC) +LX_VALUE(MS_NOATIME) +LX_VALUE(MS_NODIRATIME) + +/* linux/mount.h */ +LX_VALUE(MNT_NOSUID) +LX_VALUE(MNT_NODEV) +LX_VALUE(MNT_NOEXEC) +LX_VALUE(MNT_NOATIME) +LX_VALUE(MNT_NODIRATIME) +LX_VALUE(MNT_RELATIME) diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py index 4297b83fe..ca11e8df3 100644 --- a/scripts/gdb/linux/cpus.py +++ b/scripts/gdb/linux/cpus.py @@ -97,9 +97,47 @@ def cpu_list(mask_name): bits >>= 1 bit += 1 + yield int(cpu) + + +def each_online_cpu(): + for cpu in cpu_list("__cpu_online_mask"): + yield cpu + + +def each_present_cpu(): + for cpu in cpu_list("__cpu_present_mask"): + yield cpu + + +def each_possible_cpu(): + for cpu in cpu_list("__cpu_possible_mask"): + yield cpu + + +def each_active_cpu(): + for cpu in cpu_list("__cpu_active_mask"): yield cpu +class LxCpus(gdb.Command): + """List CPU status arrays + +Displays the known state of each CPU based on the kernel masks +and can help identify the state of hotplugged CPUs""" + + def __init__(self): + super(LxCpus, self).__init__("lx-cpus", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + gdb.write("Possible CPUs : {}\n".format(list(each_possible_cpu()))) + gdb.write("Present CPUs : {}\n".format(list(each_present_cpu()))) + gdb.write("Online CPUs : {}\n".format(list(each_online_cpu()))) + gdb.write("Active CPUs : {}\n".format(list(each_active_cpu()))) + +LxCpus() + + class PerCpu(gdb.Function): """Return per-cpu variable. diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py index 927d0d2a3..f9b92ece7 100644 --- a/scripts/gdb/linux/dmesg.py +++ b/scripts/gdb/linux/dmesg.py @@ -33,11 +33,12 @@ class LxDmesg(gdb.Command): if log_first_idx < log_next_idx: log_buf_2nd_half = -1 length = log_next_idx - log_first_idx - log_buf = inf.read_memory(start, length) + log_buf = utils.read_memoryview(inf, start, length).tobytes() else: log_buf_2nd_half = log_buf_len - log_first_idx - log_buf = inf.read_memory(start, log_buf_2nd_half) + \ - inf.read_memory(log_buf_addr, log_next_idx) + a = utils.read_memoryview(inf, start, log_buf_2nd_half) + b = utils.read_memoryview(inf, log_buf_addr, log_next_idx) + log_buf = a.tobytes() + b.tobytes() pos = 0 while pos < log_buf.__len__(): @@ -50,10 +51,10 @@ class LxDmesg(gdb.Command): continue text_len = utils.read_u16(log_buf[pos + 10:pos + 12]) - text = log_buf[pos + 16:pos + 16 + text_len] + text = log_buf[pos + 16:pos + 16 + text_len].decode() time_stamp = utils.read_u64(log_buf[pos:pos + 8]) - for line in memoryview(text).tobytes().splitlines(): + for line in text.splitlines(): gdb.write("[{time:12.6f}] {line}\n".format( time=time_stamp / 1000000000.0, line=line)) diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py index 3a3775bc1..2f335fbd8 100644 --- a/scripts/gdb/linux/lists.py +++ b/scripts/gdb/linux/lists.py @@ -18,6 +18,27 @@ from linux import utils list_head = utils.CachedType("struct list_head") +def list_for_each(head): + if head.type == list_head.get_type().pointer(): + head = head.dereference() + elif head.type != list_head.get_type(): + raise gdb.GdbError("Must be struct list_head not {}" + .format(head.type)) + + node = head['next'].dereference() + while node.address != head.address: + yield node.address + node = node['next'].dereference() + + +def list_for_each_entry(head, gdbtype, member): + for node in list_for_each(head): + if node.type != list_head.get_type().pointer(): + raise TypeError("Type {} found. Expected struct list_head *." + .format(node.type)) + yield utils.container_of(node, gdbtype, member) + + def list_check(head): nb = 0 if (head.type == list_head.get_type().pointer()): diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py index 0a35d6dbf..441b23239 100644 --- a/scripts/gdb/linux/modules.py +++ b/scripts/gdb/linux/modules.py @@ -13,7 +13,7 @@ import gdb -from linux import cpus, utils +from linux import cpus, utils, lists module_type = utils.CachedType("struct module") @@ -21,14 +21,14 @@ module_type = utils.CachedType("struct module") def module_list(): global module_type + modules = utils.gdb_eval_or_none("modules") + if modules is None: + return + module_ptr_type = module_type.get_type().pointer() - modules = gdb.parse_and_eval("modules") - entry = modules['next'] - end_of_list = modules.address - while entry != end_of_list: - yield utils.container_of(entry, module_ptr_type, "list") - entry = entry['next'] + for module in lists.list_for_each_entry(modules, module_ptr_type, "list"): + yield module def find_module_by_name(name): @@ -78,19 +78,17 @@ class LxLsmod(gdb.Command): address=str(layout['base']).split()[0], name=module['name'].string(), size=str(layout['size']), - ref=str(module['refcnt']['counter']))) + ref=str(module['refcnt']['counter'] - 1))) - source_list = module['source_list'] t = self._module_use_type.get_type().pointer() - entry = source_list['next'] first = True - while entry != source_list.address: - use = utils.container_of(entry, t, "source_list") + sources = module['source_list'] + for use in lists.list_for_each_entry(sources, t, "source_list"): gdb.write("{separator}{name}".format( separator=" " if first else ",", name=use['source']['name'].string())) first = False - entry = entry['next'] + gdb.write("\n") diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py index 6e6709c18..38b1f09d1 100644 --- a/scripts/gdb/linux/proc.py +++ b/scripts/gdb/linux/proc.py @@ -12,6 +12,10 @@ # import gdb +from linux import constants +from linux import utils +from linux import tasks +from linux import lists class LxCmdLine(gdb.Command): @@ -39,3 +43,155 @@ class LxVersion(gdb.Command): gdb.write(gdb.parse_and_eval("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_MS_SYNCHRONOUS: ",sync", + constants.LX_MS_MANDLOCK: ",mand", + constants.LX_MS_DIRSYNC: ",dirsync", + constants.LX_MS_NOATIME: ",noatime", + constants.LX_MS_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: + 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") + + 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_MS_RDONLY) else "rw" + + gdb.write( + "{} {} {} {}{}{} 0 0\n" + .format(devname, + pathname, + fstype, + rd, + info_opts(FS_INFO, s_flags), + info_opts(MNT_INFO, m_flags))) + +LxMounts() diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index 9a0f8923f..004b0ac7f 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -153,7 +153,7 @@ lx-symbols command.""" saved_state['breakpoint'].enabled = saved_state['enabled'] def invoke(self, arg, from_tty): - self.module_paths = arg.split() + self.module_paths = [os.path.expanduser(p) for p in arg.split()] self.module_paths.append(os.getcwd()) # enforce update diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py index 862a4ae24..1bf949c43 100644 --- a/scripts/gdb/linux/tasks.py +++ b/scripts/gdb/linux/tasks.py @@ -114,3 +114,22 @@ variable.""" LxThreadInfoFunc() + + +class LxThreadInfoByPidFunc (gdb.Function): + """Calculate Linux thread_info from task variable found by pid + +$lx_thread_info_by_pid(PID): Given PID, return the corresponding thread_info +variable.""" + + def __init__(self): + super(LxThreadInfoByPidFunc, self).__init__("lx_thread_info_by_pid") + + def invoke(self, pid): + task = get_task_by_pid(pid) + if task: + return get_thread_info(task.dereference()) + else: + raise gdb.GdbError("No task of PID " + str(pid)) + +LxThreadInfoByPidFunc() diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py index 0893b326a..50805874c 100644 --- a/scripts/gdb/linux/utils.py +++ b/scripts/gdb/linux/utils.py @@ -87,11 +87,24 @@ def get_target_endianness(): return target_endianness +def read_memoryview(inf, start, length): + return memoryview(inf.read_memory(start, length)) + + def read_u16(buffer): + value = [0, 0] + + if type(buffer[0]) is str: + value[0] = ord(buffer[0]) + value[1] = ord(buffer[1]) + else: + value[0] = buffer[0] + value[1] = buffer[1] + if get_target_endianness() == LITTLE_ENDIAN: - return ord(buffer[0]) + (ord(buffer[1]) << 8) + return value[0] + (value[1] << 8) else: - return ord(buffer[1]) + (ord(buffer[0]) << 8) + return value[1] + (value[0] << 8) def read_u32(buffer): @@ -154,3 +167,18 @@ def get_gdbserver_type(): if gdbserver_type is not None and hasattr(gdb, 'events'): gdb.events.exited.connect(exit_handler) return gdbserver_type + + +def gdb_eval_or_none(expresssion): + try: + return gdb.parse_and_eval(expresssion) + except: + return None + + +def dentry_name(d): + parent = d['d_parent'] + if parent == d or parent == 0: + return "" + p = dentry_name(d['d_parent']) + "/" + return p + d['d_iname'].string() -- cgit v1.2.3-54-g00ecf