Received: by 2002:a05:6a10:9848:0:0:0:0 with SMTP id x8csp3851869pxf; Mon, 15 Mar 2021 22:19:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJy96pe3HHcAg9WV9rV1Dl8eMsBbzWxPKHfYE9UkDnG/sb5F4fp96wgFSPiKFpaKu8u840XD X-Received: by 2002:aa7:cd0e:: with SMTP id b14mr34447658edw.354.1615871997015; Mon, 15 Mar 2021 22:19:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1615871997; cv=none; d=google.com; s=arc-20160816; b=D4FwLuz7GYyyFIfn6SRtBF5l+wNJPP1u/fWPI7sXSz0ncpvwG7Qw1rWA9NNxFiiton rPNfzGOewR6xz4I9tgBwuqBwKrAbYwGZ7Qa+d//mVuw/JQB8AbLLjGbd7mNTJC7NBYJB ol9f7hMacM3KokmWPVN3sCP9vH6o5Omu+nLe86JQxQBA801p/ZHhQJtFpM25Adhzgx6p p98la0ilbwpFefjBBywrVkMnT8Omn2OknLeg+zgBdcWViCUEjvj+Z5GQfmHGoYxp5cnj yEZLXQCSWYKHPLcJfgyECok5M+b64gU9MDBVIO7t19P5ZxA5HNf3S2fChb3DZkHioaCM zLfQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=xs80L4S7if4oQ7sYgkwvzm47LtF8hDXaNV9qxN93CWM=; b=lqGzXNZXAEAn2pR6s8QQNszaDFAWfGgye9vXU2Nz0XIULH6f0r8sDRC1z7ovBxg/GD ZHqKrGR7pBL4N0pC8cNjhfT0Vdeecwx8WCAgBN3JzbgRezdc13PG/klzQ6BPh2Rkp40T ZEHA9KMQrAF4VUB81M00KA/XgSP/gAvTCrWRpk776nWLfO8KsxtR2gkXj+asKYxd39VI LTCDUj3kCWsA37d3rElW8rM0miku86owD/Gjv3DNN3TaKNXrACoNdoHLbShBt+Acin5C LilUCtMZWGPXGTb60uZM41/vhPv/qPGKzQ01asTMjQtwHOjA7R6i70r/NMFIXkqZ0UQH OGnQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=hl2cuQNd; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id v25si12287815edx.598.2021.03.15.22.19.34; Mon, 15 Mar 2021 22:19:57 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=hl2cuQNd; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232494AbhCOWKn (ORCPT + 99 others); Mon, 15 Mar 2021 18:10:43 -0400 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:30907 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232629AbhCOWKi (ORCPT ); Mon, 15 Mar 2021 18:10:38 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1615846237; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xs80L4S7if4oQ7sYgkwvzm47LtF8hDXaNV9qxN93CWM=; b=hl2cuQNdOLAM8cuQHN/yDYPLndAxX83ZkUiTvu6NRyKxc7cbyfS2wqrGJxwxsV6LR0mmVM /D49RPF5E8kJMXIoV2nEka9VaByOFSVM81lzsQA6D4JFRGnmrXIT+9s+NKofbGOfMaCUaR x1E8pn7CffbfM6KoK3ERV6H28vlGW+Q= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-474-3JeqLqiZM1GMdXfm4fr1Wg-1; Mon, 15 Mar 2021 18:10:33 -0400 X-MC-Unique: 3JeqLqiZM1GMdXfm4fr1Wg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 997BC87A826; Mon, 15 Mar 2021 22:10:31 +0000 (UTC) Received: from localhost.localdomain (unknown [10.35.207.30]) by smtp.corp.redhat.com (Postfix) with ESMTP id 416DA5E1A4; Mon, 15 Mar 2021 22:10:27 +0000 (UTC) From: Maxim Levitsky To: kvm@vger.kernel.org Cc: Vitaly Kuznetsov , linux-kernel@vger.kernel.org, Thomas Gleixner , Wanpeng Li , Kieran Bingham , Jessica Yu , Jan Kiszka , Andrew Morton , x86@kernel.org (maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT)), Joerg Roedel , Sean Christopherson , Jim Mattson , Borislav Petkov , Stefano Garzarella , Maxim Levitsky , "H. Peter Anvin" , Paolo Bonzini , Ingo Molnar Subject: [PATCH 1/3] scripts/gdb: rework lx-symbols gdb script Date: Tue, 16 Mar 2021 00:10:18 +0200 Message-Id: <20210315221020.661693-2-mlevitsk@redhat.com> In-Reply-To: <20210315221020.661693-1-mlevitsk@redhat.com> References: <20210315221020.661693-1-mlevitsk@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Fix several issues that are present in lx-symbols script: * Track module unloads by placing another software breakpoint at 'free_module' (force uninline this symbol just in case), and use remove-symbol-file gdb command to unload the symobls of the module that is unloading. That gives the gdb a chance to mark all software breakpoints from this module as pending again. Also remove the module from the 'known' module list once it is unloaded. * Since we now track module unload, we don't need to reload all symbols anymore when 'known' module loaded again (that can't happen anymore). This allows reloading a module in the debugged kernel to finish much faster, while lx-symbols tracks module loads and unloads. * Disable/enable all gdb breakpoints on both module load and unload breakpoint hits, and not only in 'load_all_symbols' as was done before. (load_all_symbols is no longer called on breakpoint hit) That allows gdb to avoid getting confused about the state of the (now two) internal breakpoints we place. Otherwise it will leave them in the kernel code segment, when continuing which triggers a guest kernel panic as soon as it skips over the 'int3' instruction and executes the garbage tail of the optcode on which the breakpoint was placed. Signed-off-by: Maxim Levitsky --- kernel/module.c | 8 ++- scripts/gdb/linux/symbols.py | 106 +++++++++++++++++++++++++---------- 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index 30479355ab850..ea81fc06ea1f5 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -901,8 +901,12 @@ int module_refcount(struct module *mod) } EXPORT_SYMBOL(module_refcount); -/* This exists whether we can unload or not */ -static void free_module(struct module *mod); +/* This exists whether we can unload or not + * Keep it uninlined to provide a reliable breakpoint target, + * e.g. for the gdb helper command 'lx-symbols'. + */ + +static noinline void free_module(struct module *mod); SYSCALL_DEFINE2(delete_module, const char __user *, name_user, unsigned int, flags) diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index 1be9763cf8bb2..4ce879548a1ae 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -17,6 +17,24 @@ import re from linux import modules, utils +def save_state(): + breakpoints = [] + if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: + for bp in gdb.breakpoints(): + breakpoints.append({'breakpoint': bp, 'enabled': bp.enabled}) + bp.enabled = False + + show_pagination = gdb.execute("show pagination", to_string=True) + pagination = show_pagination.endswith("on.\n") + gdb.execute("set pagination off") + + return {"breakpoints":breakpoints, "show_pagination": show_pagination} + +def load_state(state): + for breakpoint in state["breakpoints"]: + breakpoint['breakpoint'].enabled = breakpoint['enabled'] + gdb.execute("set pagination %s" % ("on" if state["show_pagination"] else "off")) + if hasattr(gdb, 'Breakpoint'): class LoadModuleBreakpoint(gdb.Breakpoint): @@ -30,26 +48,38 @@ if hasattr(gdb, 'Breakpoint'): module_name = module['name'].string() cmd = self.gdb_command + # module already loaded, false alarm + if module_name in cmd.loaded_modules: + return False + # 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") + state = save_state() + cmd.load_module_symbols(module) + load_state(state) + return False - 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) + class UnLoadModuleBreakpoint(gdb.Breakpoint): + def __init__(self, spec, gdb_command): + super(UnLoadModuleBreakpoint, 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 - # restore pagination state - gdb.execute("set pagination %s" % ("on" if pagination else "off")) + if not module_name in cmd.loaded_modules: + return False + state = save_state() + cmd.unload_module_symbols(module) + load_state(state) return False @@ -64,8 +94,9 @@ lx-symbols command.""" module_paths = [] module_files = [] module_files_updated = False - loaded_modules = [] - breakpoint = None + loaded_modules = {} + module_load_breakpoint = None + module_unload_breakpoint = None def __init__(self): super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES, @@ -129,21 +160,32 @@ lx-symbols command.""" 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) + self.loaded_modules[module_name] = {"module_file": module_file, + "module_addr": module_addr} else: gdb.write("no module object found for '{0}'\n".format(module_name)) + def unload_module_symbols(self, module): + module_name = module['name'].string() + + module_file = self.loaded_modules[module_name]["module_file"] + module_addr = self.loaded_modules[module_name]["module_addr"] + + gdb.write("unloading @{addr}: {filename}\n".format( + addr=module_addr, filename=module_file)) + cmdline = "remove-symbol-file {filename}".format( + filename=module_file) + + gdb.execute(cmdline, to_string=True) + del self.loaded_modules[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}) + state = save_state() # drop all current symbols and reload vmlinux orig_vmlinux = 'vmlinux' @@ -153,15 +195,14 @@ lx-symbols command.""" gdb.execute("symbol-file", to_string=True) gdb.execute("symbol-file {0}".format(orig_vmlinux)) - self.loaded_modules = [] + 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'] + load_state(state) def invoke(self, arg, from_tty): self.module_paths = [os.path.expanduser(p) for p in arg.split()] @@ -174,11 +215,18 @@ lx-symbols command.""" 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) + if self.module_load_breakpoint is not None: + self.module_load_breakpoint.delete() + self.module_load_breakpoint = None + self.module_load_breakpoint = \ + LoadModuleBreakpoint("kernel/module.c:do_init_module", self) + + if self.module_unload_breakpoint is not None: + self.module_unload_breakpoint.delete() + self.module_unload_breakpoint = None + self.module_unload_breakpoint = \ + UnLoadModuleBreakpoint("kernel/module.c:free_module", self) + else: gdb.write("Note: symbol update on module loading not supported " "with this gdb version\n") -- 2.26.2