Initial commit: Linux kernel 4.19 for Unplugged OS

This commit is contained in:
ziv committed 2025-10-06 13:52:42 +00:00
commit d30235b04c
80519 files changed
+35800681

No files matched your search

+1
View File
@@ -0,0 +1 @@
subdir-y := linux
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0
always := gdb-scripts
SRCTREE := $(abspath $(srctree))
$(obj)/gdb-scripts:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(SRCTREE)/$(obj)/*.py $(objtree)/$(obj)
endif
@:
quiet_cmd_gen_constants_py = GEN $@
cmd_gen_constants_py = \
$(CPP) -E -x c -P $(c_flags) $< > $@ ;\
sed -i '1,/<!-- end-c-headers -->/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
+1
View File
@@ -0,0 +1 @@
# nothing to do for the initialization of this package
+59
View File
@@ -0,0 +1,59 @@
/*
* gdb helper commands and functions for Linux kernel debugging
*
* Kernel constants derived from include files.
*
* Copyright (c) 2016 Linaro Ltd
*
* Authors:
* Kieran Bingham <kieran.bingham@linaro.org>
*
* This work is licensed under the terms of the GNU GPL version 2.
*
*/
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/of_fdt.h>
/* 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 */
<!-- end-c-headers -->
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)
/* linux/of_fdt.h> */
LX_VALUE(OF_DT_HEADER)
/* Kernel Configs */
LX_CONFIG(CONFIG_OF)
+173
View File
@@ -0,0 +1,173 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# per-cpu tools
#
# Copyright (c) Siemens AG, 2011-2013
#
# Authors:
# Jan Kiszka <jan.kiszka@siemens.com>
#
# This work is licensed under the terms of the GNU GPL version 2.
#
import gdb
from linux import tasks, utils
MAX_CPUS = 4096
def get_current_cpu():
if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU:
return gdb.selected_thread().num - 1
elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB:
tid = gdb.selected_thread().ptid[2]
if tid > (0x100000000 - MAX_CPUS - 2):
return 0x100000000 - tid - 2
else:
return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu']
else:
raise gdb.GdbError("Sorry, obtaining the current CPU is not yet "
"supported with this gdb server.")
def per_cpu(var_ptr, cpu):
if cpu == -1:
cpu = get_current_cpu()
if utils.is_target_arch("sparc:v9"):
offset = gdb.parse_and_eval(
"trap_block[{0}].__per_cpu_base".format(str(cpu)))
else:
try:
offset = gdb.parse_and_eval(
"__per_cpu_offset[{0}]".format(str(cpu)))
except gdb.error:
# !CONFIG_SMP case
offset = 0
pointer = var_ptr.cast(utils.get_long_type()) + offset
return pointer.cast(var_ptr.type).dereference()
cpu_mask = {}
def cpu_mask_invalidate(event):
global cpu_mask
cpu_mask = {}
gdb.events.stop.disconnect(cpu_mask_invalidate)
if hasattr(gdb.events, 'new_objfile'):
gdb.events.new_objfile.disconnect(cpu_mask_invalidate)
def cpu_list(mask_name):
global cpu_mask
mask = None
if mask_name in cpu_mask:
mask = cpu_mask[mask_name]
if mask is None:
mask = gdb.parse_and_eval(mask_name + ".bits")
if hasattr(gdb, 'events'):
cpu_mask[mask_name] = mask
gdb.events.stop.connect(cpu_mask_invalidate)
if hasattr(gdb.events, 'new_objfile'):
gdb.events.new_objfile.connect(cpu_mask_invalidate)
bits_per_entry = mask[0].type.sizeof * 8
num_entries = mask.type.sizeof * 8 / bits_per_entry
entry = -1
bits = 0
while True:
while bits == 0:
entry += 1
if entry == num_entries:
return
bits = mask[entry]
if bits != 0:
bit = 0
break
while bits & 1 == 0:
bits >>= 1
bit += 1
cpu = entry * bits_per_entry + bit
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.
$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
given CPU number. If CPU is omitted, the CPU of the current context is used.
Note that VAR has to be quoted as string."""
def __init__(self):
super(PerCpu, self).__init__("lx_per_cpu")
def invoke(self, var_name, cpu=-1):
var_ptr = gdb.parse_and_eval("&" + var_name.string())
return per_cpu(var_ptr, cpu)
PerCpu()
class LxCurrentFunc(gdb.Function):
"""Return current task.
$lx_current([CPU]): Return the per-cpu task variable for the given CPU
number. If CPU is omitted, the CPU of the current context is used."""
def __init__(self):
super(LxCurrentFunc, self).__init__("lx_current")
def invoke(self, cpu=-1):
var_ptr = gdb.parse_and_eval("&current_task")
return per_cpu(var_ptr, cpu).dereference()
LxCurrentFunc()
+73
View File
@@ -0,0 +1,73 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# kernel log buffer dump
#
# Copyright (c) Siemens AG, 2011, 2012
#
# Authors:
# Jan Kiszka <jan.kiszka@siemens.com>
#
# This work is licensed under the terms of the GNU GPL version 2.
#
import gdb
import sys
from linux import utils
class LxDmesg(gdb.Command):
"""Print Linux kernel log buffer."""
def __init__(self):
super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
log_buf_addr = int(str(gdb.parse_and_eval(
"(void *)'printk.c'::log_buf")).split()[0], 16)
log_first_idx = int(gdb.parse_and_eval("'printk.c'::log_first_idx"))
log_next_idx = int(gdb.parse_and_eval("'printk.c'::log_next_idx"))
log_buf_len = int(gdb.parse_and_eval("'printk.c'::log_buf_len"))
inf = gdb.inferiors()[0]
start = log_buf_addr + log_first_idx
if log_first_idx < log_next_idx:
log_buf_2nd_half = -1
length = log_next_idx - log_first_idx
log_buf = utils.read_memoryview(inf, start, length).tobytes()
else:
log_buf_2nd_half = log_buf_len - log_first_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__():
length = utils.read_u16(log_buf[pos + 8:pos + 10])
if length == 0:
if log_buf_2nd_half == -1:
gdb.write("Corrupted log buffer!\n")
break
pos = log_buf_2nd_half
continue
text_len = utils.read_u16(log_buf[pos + 10:pos + 12])
text = log_buf[pos + 16:pos + 16 + text_len].decode(
encoding='utf8', errors='replace')
time_stamp = utils.read_u64(log_buf[pos:pos + 8])
for line in text.splitlines():
msg = u"[{time:12.6f}] {line}\n".format(
time=time_stamp / 1000000000.0,
line=line)
# With python2 gdb.write will attempt to convert unicode to
# ascii and might fail so pass an utf8-encoded str instead.
if sys.hexversion < 0x03000000:
msg = msg.encode(encoding='utf8', errors='replace')
gdb.write(msg)
pos += length
LxDmesg()
+113
View File
@@ -0,0 +1,113 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# list tools
#
# Copyright (c) Thiebaud Weksteen, 2015
#
# Authors:
# Thiebaud Weksteen <thiebaud@weksteen.fr>
#
# This work is licensed under the terms of the GNU GPL version 2.
#
import gdb
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()):
head = head.dereference()
elif (head.type != list_head.get_type()):
raise gdb.GdbError('argument must be of type (struct list_head [*])')
c = head
try:
gdb.write("Starting with: {}\n".format(c))
except gdb.MemoryError:
gdb.write('head is not accessible\n')
return
while True:
p = c['prev'].dereference()
n = c['next'].dereference()
try:
if p['next'] != c.address:
gdb.write('prev.next != current: '
'current@{current_addr}={current} '
'prev@{p_addr}={p}\n'.format(
current_addr=c.address,
current=c,
p_addr=p.address,
p=p,
))
return
except gdb.MemoryError:
gdb.write('prev is not accessible: '
'current@{current_addr}={current}\n'.format(
current_addr=c.address,
current=c
))
return
try:
if n['prev'] != c.address:
gdb.write('next.prev != current: '
'current@{current_addr}={current} '
'next@{n_addr}={n}\n'.format(
current_addr=c.address,
current=c,
n_addr=n.address,
n=n,
))
return
except gdb.MemoryError:
gdb.write('next is not accessible: '
'current@{current_addr}={current}\n'.format(
current_addr=c.address,
current=c
))
return
c = n
nb += 1
if c == head:
gdb.write("list is consistent: {} node(s)\n".format(nb))
return
class LxListChk(gdb.Command):
"""Verify a list consistency"""
def __init__(self):
super(LxListChk, self).__init__("lx-list-check", gdb.COMMAND_DATA,
gdb.COMPLETE_EXPRESSION)
def invoke(self, arg, from_tty):
argv = gdb.string_to_argv(arg)
if len(argv) != 1:
raise gdb.GdbError("lx-list-check takes one argument")
list_check(gdb.parse_and_eval(argv[0]))
LxListChk()
+95
View File
@@ -0,0 +1,95 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# module tools
#
# Copyright (c) Siemens AG, 2013
#
# Authors:
# Jan Kiszka <jan.kiszka@siemens.com>
#
# This work is licensed under the terms of the GNU GPL version 2.
#
import gdb
from linux import cpus, utils, lists
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()
for module in lists.list_for_each_entry(modules, module_ptr_type, "list"):
yield module
def find_module_by_name(name):
for module in module_list():
if module['name'].string() == name:
return module
return None
class LxModule(gdb.Function):
"""Find module by name and return the module variable.
$lx_module("MODULE"): Given the name MODULE, iterate over all loaded modules
of the target and return that module variable which MODULE matches."""
def __init__(self):
super(LxModule, self).__init__("lx_module")
def invoke(self, mod_name):
mod_name = mod_name.string()
module = find_module_by_name(mod_name)
if module:
return module.dereference()
else:
raise gdb.GdbError("Unable to find MODULE " + mod_name)
LxModule()
class LxLsmod(gdb.Command):
"""List currently loaded modules."""
_module_use_type = utils.CachedType("struct module_use")
def __init__(self):
super(LxLsmod, self).__init__("lx-lsmod", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
gdb.write(
"Address{0} Module Size Used by\n".format(
" " if utils.get_long_type().sizeof == 8 else ""))
for module in module_list():
layout = module['core_layout']
gdb.write("{address} {name:<19} {size:>8} {ref}".format(
address=str(layout['base']).split()[0],
name=module['name'].string(),
size=str(layout['size']),
ref=str(module['refcnt']['counter'] - 1)))
t = self._module_use_type.get_type().pointer()
first = True
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
gdb.write("\n")
LxLsmod()
+270
View File
@@ -0,0 +1,270 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# Kernel proc information reader
#
# Copyright (c) 2016 Linaro Ltd
#
# Authors:
# Kieran Bingham <kieran.bingham@linaro.org>
#
# 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_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()
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:
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()
+177
View File
@@ -0,0 +1,177 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# load kernel and module symbols
#
# Copyright (c) Siemens AG, 2011-2013
#
# Authors:
# Jan Kiszka <jan.kiszka@siemens.com>
#
# This work is licensed under the terms of the GNU GPL version 2.
#
import gdb
import os
import re
from linux import modules
if hasattr(gdb, 'Breakpoint'):
class LoadModuleBreakpoint(gdb.Breakpoint):
def __init__(self, spec, gdb_command):
super(LoadModuleBreakpoint, self).__init__(spec, internal=True)
self.silent = True
self.gdb_command = gdb_command
def stop(self):
module = gdb.parse_and_eval("mod")
module_name = module['name'].string()
cmd = self.gdb_command
# enforce update if object file is not found
cmd.module_files_updated = False
# Disable pagination while reporting symbol (re-)loading.
# The console input is blocked in this context so that we would
# get stuck waiting for the user to acknowledge paged output.
show_pagination = gdb.execute("show pagination", to_string=True)
pagination = show_pagination.endswith("on.\n")
gdb.execute("set pagination off")
if module_name in cmd.loaded_modules:
gdb.write("refreshing all symbols to reload module "
"'{0}'\n".format(module_name))
cmd.load_all_symbols()
else:
cmd.load_module_symbols(module)
# restore pagination state
gdb.execute("set pagination %s" % ("on" if pagination else "off"))
return False
class LxSymbols(gdb.Command):
"""(Re-)load symbols of Linux kernel and currently loaded modules.
The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
are scanned recursively, starting in the same directory. Optionally, the module
search path can be extended by a space separated list of paths passed to the
lx-symbols command."""
module_paths = []
module_files = []
module_files_updated = False
loaded_modules = []
breakpoint = None
def __init__(self):
super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES,
gdb.COMPLETE_FILENAME)
def _update_module_files(self):
self.module_files = []
for path in self.module_paths:
gdb.write("scanning for modules in {0}\n".format(path))
for root, dirs, files in os.walk(path):
for name in files:
if name.endswith(".ko"):
self.module_files.append(root + "/" + name)
self.module_files_updated = True
def _get_module_file(self, module_name):
module_pattern = ".*/{0}\.ko$".format(
module_name.replace("_", r"[_\-]"))
for name in self.module_files:
if re.match(module_pattern, name) and os.path.exists(name):
return name
return None
def _section_arguments(self, module):
try:
sect_attrs = module['sect_attrs'].dereference()
except gdb.error:
return ""
attrs = sect_attrs['attrs']
section_name_to_address = {
attrs[n]['battr']['attr']['name'].string(): attrs[n]['address']
for n in range(int(sect_attrs['nsections']))}
args = []
for section_name in [".data", ".data..read_mostly", ".rodata", ".bss",
".text", ".text.hot", ".text.unlikely"]:
address = section_name_to_address.get(section_name)
if address:
args.append(" -s {name} {addr}".format(
name=section_name, addr=str(address)))
return "".join(args)
def load_module_symbols(self, module):
module_name = module['name'].string()
module_addr = str(module['core_layout']['base']).split()[0]
module_file = self._get_module_file(module_name)
if not module_file and not self.module_files_updated:
self._update_module_files()
module_file = self._get_module_file(module_name)
if module_file:
gdb.write("loading @{addr}: {filename}\n".format(
addr=module_addr, filename=module_file))
cmdline = "add-symbol-file {filename} {addr}{sections}".format(
filename=module_file,
addr=module_addr,
sections=self._section_arguments(module))
gdb.execute(cmdline, to_string=True)
if module_name not in self.loaded_modules:
self.loaded_modules.append(module_name)
else:
gdb.write("no module object found for '{0}'\n".format(module_name))
def load_all_symbols(self):
gdb.write("loading vmlinux\n")
# Dropping symbols will disable all breakpoints. So save their states
# and restore them afterward.
saved_states = []
if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
for bp in gdb.breakpoints():
saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
# drop all current symbols and reload vmlinux
gdb.execute("symbol-file", to_string=True)
gdb.execute("symbol-file vmlinux")
self.loaded_modules = []
module_list = modules.module_list()
if not module_list:
gdb.write("no modules found\n")
else:
[self.load_module_symbols(module) for module in module_list]
for saved_state in saved_states:
saved_state['breakpoint'].enabled = saved_state['enabled']
def invoke(self, arg, from_tty):
self.module_paths = [os.path.expanduser(p) for p in arg.split()]
self.module_paths.append(os.getcwd())
# enforce update
self.module_files = []
self.module_files_updated = False
self.load_all_symbols()
if hasattr(gdb, 'Breakpoint'):
if self.breakpoint is not None:
self.breakpoint.delete()
self.breakpoint = None
self.breakpoint = LoadModuleBreakpoint(
"kernel/module.c:do_init_module", self)
else:
gdb.write("Note: symbol update on module loading not supported "
"with this gdb version\n")
LxSymbols()
+137
View File
@@ -0,0 +1,137 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# task & thread tools
#
# Copyright (c) Siemens AG, 2011-2013
#
# Authors:
# Jan Kiszka <jan.kiszka@siemens.com>
#
# This work is licensed under the terms of the GNU GPL version 2.
#
import gdb
from linux import utils
task_type = utils.CachedType("struct task_struct")
def task_lists():
task_ptr_type = task_type.get_type().pointer()
init_task = gdb.parse_and_eval("init_task").address
t = g = init_task
while True:
while True:
yield t
t = utils.container_of(t['thread_group']['next'],
task_ptr_type, "thread_group")
if t == g:
break
t = g = utils.container_of(g['tasks']['next'],
task_ptr_type, "tasks")
if t == init_task:
return
def get_task_by_pid(pid):
for task in task_lists():
if int(task['pid']) == pid:
return task
return None
class LxTaskByPidFunc(gdb.Function):
"""Find Linux task by PID and return the task_struct variable.
$lx_task_by_pid(PID): Given PID, iterate over all tasks of the target and
return that task_struct variable which PID matches."""
def __init__(self):
super(LxTaskByPidFunc, self).__init__("lx_task_by_pid")
def invoke(self, pid):
task = get_task_by_pid(pid)
if task:
return task.dereference()
else:
raise gdb.GdbError("No task of PID " + str(pid))
LxTaskByPidFunc()
class LxPs(gdb.Command):
"""Dump Linux tasks."""
def __init__(self):
super(LxPs, self).__init__("lx-ps", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
for task in task_lists():
gdb.write("{address} {pid} {comm}\n".format(
address=task,
pid=task["pid"],
comm=task["comm"].string()))
LxPs()
thread_info_type = utils.CachedType("struct thread_info")
ia64_task_size = None
def get_thread_info(task):
thread_info_ptr_type = thread_info_type.get_type().pointer()
if utils.is_target_arch("ia64"):
global ia64_task_size
if ia64_task_size is None:
ia64_task_size = gdb.parse_and_eval("sizeof(struct task_struct)")
thread_info_addr = task.address + ia64_task_size
thread_info = thread_info_addr.cast(thread_info_ptr_type)
else:
if task.type.fields()[0].type == thread_info_type.get_type():
return task['thread_info']
thread_info = task['stack'].cast(thread_info_ptr_type)
return thread_info.dereference()
class LxThreadInfoFunc (gdb.Function):
"""Calculate Linux thread_info from task variable.
$lx_thread_info(TASK): Given TASK, return the corresponding thread_info
variable."""
def __init__(self):
super(LxThreadInfoFunc, self).__init__("lx_thread_info")
def invoke(self, task):
return get_thread_info(task)
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()
+184
View File
@@ -0,0 +1,184 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# common utilities
#
# Copyright (c) Siemens AG, 2011-2013
#
# Authors:
# Jan Kiszka <jan.kiszka@siemens.com>
#
# This work is licensed under the terms of the GNU GPL version 2.
#
import gdb
class CachedType:
def __init__(self, name):
self._type = None
self._name = name
def _new_objfile_handler(self, event):
self._type = None
gdb.events.new_objfile.disconnect(self._new_objfile_handler)
def get_type(self):
if self._type is None:
self._type = gdb.lookup_type(self._name)
if self._type is None:
raise gdb.GdbError(
"cannot resolve type '{0}'".format(self._name))
if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'):
gdb.events.new_objfile.connect(self._new_objfile_handler)
return self._type
long_type = CachedType("long")
def get_long_type():
global long_type
return long_type.get_type()
def offset_of(typeobj, field):
element = gdb.Value(0).cast(typeobj)
return int(str(element[field].address).split()[0], 16)
def container_of(ptr, typeobj, member):
return (ptr.cast(get_long_type()) -
offset_of(typeobj, member)).cast(typeobj)
class ContainerOf(gdb.Function):
"""Return pointer to containing data structure.
$container_of(PTR, "TYPE", "ELEMENT"): Given PTR, return a pointer to the
data structure of the type TYPE in which PTR is the address of ELEMENT.
Note that TYPE and ELEMENT have to be quoted as strings."""
def __init__(self):
super(ContainerOf, self).__init__("container_of")
def invoke(self, ptr, typename, elementname):
return container_of(ptr, gdb.lookup_type(typename.string()).pointer(),
elementname.string())
ContainerOf()
BIG_ENDIAN = 0
LITTLE_ENDIAN = 1
target_endianness = None
def get_target_endianness():
global target_endianness
if target_endianness is None:
endian = gdb.execute("show endian", to_string=True)
if "little endian" in endian:
target_endianness = LITTLE_ENDIAN
elif "big endian" in endian:
target_endianness = BIG_ENDIAN
else:
raise gdb.GdbError("unknown endianness '{0}'".format(str(endian)))
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 value[0] + (value[1] << 8)
else:
return value[1] + (value[0] << 8)
def read_u32(buffer):
if get_target_endianness() == LITTLE_ENDIAN:
return read_u16(buffer[0:2]) + (read_u16(buffer[2:4]) << 16)
else:
return read_u16(buffer[2:4]) + (read_u16(buffer[0:2]) << 16)
def read_u64(buffer):
if get_target_endianness() == LITTLE_ENDIAN:
return read_u32(buffer[0:4]) + (read_u32(buffer[4:8]) << 32)
else:
return read_u32(buffer[4:8]) + (read_u32(buffer[0:4]) << 32)
target_arch = None
def is_target_arch(arch):
if hasattr(gdb.Frame, 'architecture'):
return arch in gdb.newest_frame().architecture().name()
else:
global target_arch
if target_arch is None:
target_arch = gdb.execute("show architecture", to_string=True)
return arch in target_arch
GDBSERVER_QEMU = 0
GDBSERVER_KGDB = 1
gdbserver_type = None
def get_gdbserver_type():
def exit_handler(event):
global gdbserver_type
gdbserver_type = None
gdb.events.exited.disconnect(exit_handler)
def probe_qemu():
try:
return gdb.execute("monitor info version", to_string=True) != ""
except:
return False
def probe_kgdb():
try:
thread_info = gdb.execute("info thread 2", to_string=True)
return "shadowCPU0" in thread_info
except:
return False
global gdbserver_type
if gdbserver_type is None:
if probe_qemu():
gdbserver_type = GDBSERVER_QEMU
elif probe_kgdb():
gdbserver_type = GDBSERVER_KGDB
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()
+33
View File
@@ -0,0 +1,33 @@
#
# gdb helper commands and functions for Linux kernel debugging
#
# loader module
#
# Copyright (c) Siemens AG, 2012, 2013
#
# Authors:
# Jan Kiszka <jan.kiszka@siemens.com>
#
# This work is licensed under the terms of the GNU GPL version 2.
#
import os
sys.path.insert(0, os.path.dirname(__file__) + "/scripts/gdb")
try:
gdb.parse_and_eval("0")
gdb.execute("", to_string=True)
except:
gdb.write("NOTE: gdb 7.2 or later required for Linux helper scripts to "
"work.\n")
else:
import linux.utils
import linux.symbols
import linux.modules
import linux.dmesg
import linux.tasks
import linux.cpus
import linux.lists
import linux.proc
import linux.constants