From patchwork Mon Jun 12 17:08:31 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Rudo X-Patchwork-Id: 20969 Received: (qmail 65949 invoked by alias); 12 Jun 2017 17:08:59 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 65864 invoked by uid 89); 12 Jun 2017 17:08:58 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.2 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KHOP_DYNAMIC, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.2 spammy=only, only!, accumulated, beneath X-HELO: mx0a-001b2d01.pphosted.com Received: from mx0a-001b2d01.pphosted.com (HELO mx0a-001b2d01.pphosted.com) (148.163.156.1) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 12 Jun 2017 17:08:49 +0000 Received: from pps.filterd (m0098410.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v5CH8k7H090550 for ; Mon, 12 Jun 2017 13:08:52 -0400 Received: from e06smtp11.uk.ibm.com (e06smtp11.uk.ibm.com [195.75.94.107]) by mx0a-001b2d01.pphosted.com with ESMTP id 2b1vbt0dfk-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Mon, 12 Jun 2017 13:08:52 -0400 Received: from localhost by e06smtp11.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 12 Jun 2017 18:08:45 +0100 Received: from b06cxnps4076.portsmouth.uk.ibm.com (9.149.109.198) by e06smtp11.uk.ibm.com (192.168.101.141) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 12 Jun 2017 18:08:43 +0100 Received: from d06av22.portsmouth.uk.ibm.com (d06av22.portsmouth.uk.ibm.com [9.149.105.58]) by b06cxnps4076.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v5CH8gve38273170; Mon, 12 Jun 2017 17:08:42 GMT Received: from d06av22.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 614C84C046; Mon, 12 Jun 2017 18:06:57 +0100 (BST) Received: from d06av22.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 293834C044; Mon, 12 Jun 2017 18:06:57 +0100 (BST) Received: from tuxmaker.boeblingen.de.ibm.com (unknown [9.152.85.9]) by d06av22.portsmouth.uk.ibm.com (Postfix) with ESMTPS; Mon, 12 Jun 2017 18:06:57 +0100 (BST) From: Philipp Rudo To: gdb-patches@sourceware.org Cc: omair.javaid@linaro.org, yao.qi@linaro.org, peter.griffin@linaro.org, arnez@linux.vnet.ibm.com Subject: [RFC v4 4/9] Add kernel module support for linux-kernel target Date: Mon, 12 Jun 2017 19:08:31 +0200 In-Reply-To: <20170612170836.25174-1-prudo@linux.vnet.ibm.com> References: <20170612170836.25174-1-prudo@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17061217-0040-0000-0000-000003C80061 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17061217-0041-0000-0000-00002060AADC Message-Id: <20170612170836.25174-5-prudo@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-06-12_11:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=43 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1703280000 definitions=main-1706120298 X-IsSubscribed: yes This patch implements module support for the new linux-kernel target by adding a target_so_ops. In addition this patch adds handling for kernel virtual addresses. This is necessary because kernel modules, unlike task_structs, live in kernel virtual address space. Thus addresses need to be translated before they can be used. We achieve this by adding an implementation for the targets to_xfer_partial hook, which translates the addresses before passing them down to the target beneath. gdb/ChangeLog: * lk-modules.h: New file. * lk-modules.c: New file. * lk-low.h (lk_hook_is_kvaddr, lk_hook_vtop) (lk_hook_get_module_text_offset): New arch dependent hooks. (sturct lk_private_hooks): Add new hooks. (LK_MODULES_NAME_LEN, LK_UTS_NAME_LEN): New define. * lk-low.c (lk-modules.h): New include. (lk_kvtop, restore_current_target, lk_xfer_partial): New functions. (lk_init_private_data): Declare needed debug symbols. (lk_try_push_target): Assert for new hooks and set solib_ops. (init_linux_kernel_ops): Add implementation for to_xfer_partial. * solib.c (get_solib_search_path): New function. * solib.h (get_solib_search_path): New export. * Makefile.in (SFILES, ALLDEPFILES): Add lk-modules.c. (HFILES_NO_SRCDIR): Add lk-modules.h. (ALL_TARGET_OBS): Add lk-modules.o. * configure.tgt (lk_target_obs): Add lk-modules.o. --- gdb/Makefile.in | 4 + gdb/configure.tgt | 2 +- gdb/lk-low.c | 110 ++++++++++++ gdb/lk-low.h | 28 +++ gdb/lk-modules.c | 498 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/lk-modules.h | 149 ++++++++++++++++ gdb/solib.c | 8 + gdb/solib.h | 5 + 8 files changed, 803 insertions(+), 1 deletion(-) create mode 100644 gdb/lk-modules.c create mode 100644 gdb/lk-modules.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index a73ca26a29..7c5d26dc6c 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -829,6 +829,7 @@ ALL_TARGET_OBS = \ linux-tdep.o \ lk-lists.o \ lk-low.o \ + lk-modules.o \ lm32-tdep.o \ m32c-tdep.o \ m32r-linux-tdep.o \ @@ -1131,6 +1132,7 @@ SFILES = \ linespec.c \ lk-lists.c \ lk-low.c \ + lk-modules.c \ location.c \ m2-exp.y \ m2-lang.c \ @@ -1382,6 +1384,7 @@ HFILES_NO_SRCDIR = \ linux-tdep.h \ lk-lists.h \ lk-low.h \ + lk-modules.h \ location.h \ m2-lang.h \ m32r-tdep.h \ @@ -2590,6 +2593,7 @@ ALLDEPFILES = \ linux-tdep.c \ lk-lists.c \ lk-low.c \ + lk-modules.c \ lm32-tdep.c \ m32r-linux-nat.c \ m32r-linux-tdep.c \ diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 35db698860..19c5311b23 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -38,7 +38,7 @@ esac # List of objectfiles for Linux kernel support. To be included into *-linux* # targets wich support Linux kernel debugging. -lk_target_obs="lk-lists.o lk-low.o" +lk_target_obs="lk-lists.o lk-low.o lk-modules.o" # map target info into gdb names. diff --git a/gdb/lk-low.c b/gdb/lk-low.c index e482ad08e7..c4ca472ea4 100644 --- a/gdb/lk-low.c +++ b/gdb/lk-low.c @@ -29,6 +29,7 @@ #include "inferior.h" #include "lk-lists.h" #include "lk-low.h" +#include "lk-modules.h" #include "objfiles.h" #include "observer.h" #include "solib.h" @@ -534,6 +535,46 @@ lk_thread_name (struct target_ops *target, struct thread_info *ti) return buf; } +/* Translate a kernel virtual address ADDR to a physical address. */ + +CORE_ADDR +lk_kvtop (CORE_ADDR addr) +{ + CORE_ADDR pgd = lk_read_addr (LK_ADDR (init_mm) + + LK_OFFSET (mm_struct, pgd)); + return LK_HOOK->vtop (pgd, addr); +} + +/* Restore current_target to TARGET. */ +static void +restore_current_target (void *target) +{ + current_target.beneath = (struct target_ops *) target; +} + +/* Function for targets to_xfer_partial hook. */ + +enum target_xfer_status +lk_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, gdb_byte *readbuf, + const gdb_byte *writebuf, ULONGEST offset, ULONGEST len, + ULONGEST *xfered_len) +{ + enum target_xfer_status ret_val; + struct cleanup *old_chain = make_cleanup (restore_current_target, ops); + + current_target.beneath = ops->beneath; + + if (LK_HOOK->is_kvaddr (offset)) + offset = lk_kvtop (offset); + + ret_val = ops->beneath->to_xfer_partial (ops->beneath, object, annex, + readbuf, writebuf, offset, len, + xfered_len); + do_cleanups (old_chain); + return ret_val; +} + /* Functions to initialize and free target_ops and its private data. As well as functions for targets to_open/close/detach hooks. */ @@ -569,6 +610,9 @@ lk_init_private () /* Initialize architecture independent private data. Must be called _after_ symbol tables were initialized. */ +/* FIXME: throw error more fine-grained. */ +/* FIXME: make independent of compile options. */ + static void lk_init_private_data () { @@ -589,10 +633,70 @@ lk_init_private_data () LK_DECLARE_FIELD (cpumask, bits); + LK_DECLARE_FIELD (mm_struct, pgd); + + LK_DECLARE_FIELD (pgd_t, pgd); + + LK_DECLARE_FIELD (module, state); + LK_DECLARE_FIELD (module, list); + LK_DECLARE_FIELD (module, name); + LK_DECLARE_FIELD (module, source_list); + LK_DECLARE_FIELD (module, arch); + // FIXME only exists if compiled with CONFIG_SMT + LK_DECLARE_FIELD (module, percpu); + LK_DECLARE_FIELD (module, percpu_size); + + /* Module offset moved to new struct module_layout with linux 4.5. + It must be checked in code which of this fields exist. */ + if (LK_DECLARE_FIELD_SILENT (module_layout, base)) /* linux 4.5+ */ + { + LK_DECLARE_FIELD (module, init_layout); + LK_DECLARE_FIELD (module, core_layout); + + LK_DECLARE_FIELD (module_layout, size); + LK_DECLARE_FIELD (module_layout, text_size); + LK_DECLARE_FIELD (module_layout, ro_size); + + /* Declare silent as existence needs to be checked in code. */ + LK_DECLARE_FIELD_SILENT (module_layout, ro_after_init_size); /* linux 4.8+ */ + } + else if (LK_DECLARE_FIELD_SILENT (module, module_core)) /* linux -4.4 */ + { + LK_DECLARE_FIELD (module, module_init); + + LK_DECLARE_FIELD (module, init_size); + LK_DECLARE_FIELD (module, core_size); + + LK_DECLARE_FIELD (module, init_text_size); + LK_DECLARE_FIELD (module, core_text_size); + + LK_DECLARE_FIELD (module, init_ro_size); + LK_DECLARE_FIELD (module, core_ro_size); + } + else + { + error (_("Could not find module base. Aborting.")); + } + + LK_DECLARE_FIELD (module_use, source_list); + LK_DECLARE_FIELD (module_use, source); + + LK_DECLARE_FIELD (uts_namespace, name); + + LK_DECLARE_STRUCT_ALIAS (new_utsname, utsname); + LK_DECLARE_STRUCT_ALIAS (old_utsname, utsname); + LK_DECLARE_STRUCT_ALIAS (oldold_utsname, utsname); + if (LK_STRUCT (utsname) == NULL) + error (_("Could not find struct utsname. Aborting.")); + LK_DECLARE_FIELD (utsname, version); + LK_DECLARE_FIELD (utsname, release); + LK_DECLARE_ADDR (init_task); LK_DECLARE_ADDR (runqueues); LK_DECLARE_ADDR (__per_cpu_offset); LK_DECLARE_ADDR (init_mm); + LK_DECLARE_ADDR (modules); + LK_DECLARE_ADDR (init_uts_ns); LK_DECLARE_ADDR_ALIAS (__cpu_online_mask, cpu_online_mask); /* linux 4.5+ */ LK_DECLARE_ADDR_ALIAS (cpu_online_bits, cpu_online_mask); /* linux -4.4 */ @@ -691,12 +795,17 @@ lk_try_push_target () gdbarch_lk_init_private (gdbarch); /* Check for required arch hooks. */ gdb_assert (LK_HOOK->get_registers); + gdb_assert (LK_HOOK->is_kvaddr); + gdb_assert (LK_HOOK->vtop); + gdb_assert (LK_HOOK->adjust_module_layout); lk_init_ptid_map (); lk_update_thread_list (linux_kernel_ops); if (!target_is_pushed (linux_kernel_ops)) push_target (linux_kernel_ops); + + set_solib_ops (gdbarch, lk_modules_so_ops); } /* Function for targets to_open hook. */ @@ -809,6 +918,7 @@ init_linux_kernel_ops (void) t->to_update_thread_list = lk_update_thread_list; t->to_pid_to_str = lk_pid_to_str; t->to_thread_name = lk_thread_name; + t->to_xfer_partial = lk_xfer_partial; t->to_stratum = thread_stratum; t->to_magic = OPS_MAGIC; diff --git a/gdb/lk-low.h b/gdb/lk-low.h index be8c5556df..7a20db3aaf 100644 --- a/gdb/lk-low.h +++ b/gdb/lk-low.h @@ -20,6 +20,7 @@ #ifndef __LK_LOW_H__ #define __LK_LOW_H__ +#include "lk-modules.h" #include "target.h" extern struct target_ops *linux_kernel_ops; @@ -27,6 +28,8 @@ extern struct target_ops *linux_kernel_ops; /* Copy constants defined in Linux kernel. */ #define LK_TASK_COMM_LEN 16 #define LK_BITS_PER_BYTE 8 +#define LK_MODULE_NAME_LEN 56 +#define LK_UTS_NAME_LEN 64 /* Definitions used in linux kernel target. */ #define LK_CPU_INVAL -1U @@ -204,6 +207,22 @@ typedef void (*lk_hook_get_registers) (CORE_ADDR task, struct regcache *regcache, int regnum); +/* Hook to check if address ADDR is a kernel virtual address. + NOTE: This hook is called in the context of target beneath. */ +typedef int (*lk_hook_is_kvaddr) (CORE_ADDR addr); + +/* Hook to translate virtual adress ADDR to a pysical address using page + table located at PGD. + NOTE: This hook is called in the context of target beneath. */ +typedef CORE_ADDR (*lk_hook_vtop) (CORE_ADDR addr, CORE_ADDR pgd); + +/* Hook to get the offset between a modules base and the start of its + first section. + Equivalent to /include/linux/moduleloader.h:module_frob_arch_sections + */ +typedef void (*lk_hook_adjust_module_layout) (CORE_ADDR mod, + lk_module *module); + /* Hook to return the per_cpu_offset of cpu CPU. Only architectures that do not use the __per_cpu_offset array to determine the offset have to supply this hook. */ @@ -218,6 +237,15 @@ struct lk_private_hooks /* required */ lk_hook_get_registers get_registers; + /* required */ + lk_hook_is_kvaddr is_kvaddr; + + /* required */ + lk_hook_vtop vtop; + + /* reqired */ + lk_hook_adjust_module_layout adjust_module_layout; + /* optional, required if __per_cpu_offset array is not used to determine offset. */ lk_hook_get_percpu_offset get_percpu_offset; diff --git a/gdb/lk-modules.c b/gdb/lk-modules.c new file mode 100644 index 0000000000..ad5b22e834 --- /dev/null +++ b/gdb/lk-modules.c @@ -0,0 +1,498 @@ +/* Handle Linux kernel modules as shared libraries. + + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" + +#include "common/filestuff.h" +#include "filenames.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdb_regex.h" +#include "lk-lists.h" +#include "lk-low.h" +#include "lk-modules.h" +#include "objfiles.h" +#include "observer.h" +#include "readline/readline.h" +#include "solib.h" +#include "solist.h" +#include "utils.h" + +#include +#include + +struct target_so_ops *lk_modules_so_ops = NULL; + + +/* See comment in lk-modules.h. */ + +void +lk_module_section_type::clear () +{ + base = 0; + offset = 0; + size = 0; +} + +/* Constructor for module_layout located at address LAYOUT. + Linux 4.5+ only! */ + +lk_module_layout::lk_module_layout (CORE_ADDR layout) +{ + base = lk_read_addr (layout + LK_OFFSET (module_layout, base)); + size = lk_read_uint (layout + LK_OFFSET (module_layout, size)); + + text.base = base; + text.size = lk_read_uint (layout + LK_OFFSET (module_layout, text_size)); + + ro_data.base = base + text.size; + ro_data.size = lk_read_uint (layout + LK_OFFSET (module_layout, ro_size)); + + if (LK_FIELD (module_layout, ro_after_init_size)) /* linux 4.8+ */ + { + ro_after_init_data.base = base + ro_data.size; + ro_after_init_data.size + = lk_read_uint (layout + LK_OFFSET (module_layout, ro_after_init_size)); + + rw_data.base = base + ro_after_init_data.size; + } + else /* linux 4.5 - 4.8 */ + { + rw_data.base = base + ro_data.size; + } + + rw_data.size = size; +} + +/* See comment in lk-modules.h. */ + +void +lk_module_layout::clear () +{ + base = 0; + size = 0; + + text.clear (); + ro_data.clear (); + rw_data.clear (); +} + +/* Init MODULE from struct module located at address MOD. + Linux -4.4 only! */ + +void +lk_module_init_module_from_struct_module (CORE_ADDR mod, lk_module *module) +{ + module->core = new lk_module_layout; + module->init = new lk_module_layout; + + /* Short-hand access. */ + lk_module_layout *core = module->core; + lk_module_layout *init = module->init; + + /* Core layout. */ + core->base = lk_read_addr (mod + LK_OFFSET (module, module_core)); + core->size = lk_read_uint (mod + LK_OFFSET (module, core_size)); + + core->text.base = core->base; + core->text.size = lk_read_uint (mod + LK_OFFSET (module, core_text_size)); + + core->ro_data.base = core->base + core->text.size; + core->ro_data.size = lk_read_uint (mod + LK_OFFSET (module, core_ro_size)); + + core->rw_data.base = core->base + core->ro_data.size; + core->rw_data.size = core->size; + + /* Init layout. */ + init->base = lk_read_addr (mod + LK_OFFSET (module, module_init)); + init->size = lk_read_uint (mod + LK_OFFSET (module, init_size)); + + init->text.base = init->base; + init->text.size = lk_read_uint (mod + LK_OFFSET (module, init_text_size)); + + init->ro_data.base = init->base + init->text.size; + init->ro_data.size = lk_read_uint (mod + LK_OFFSET (module, init_ro_size)); + + init->rw_data.base = init->base + init->ro_data.size; + init->rw_data.size = init->size; +} + +/* Constructor for kernel module located at address MOD. */ + +lk_module::lk_module (CORE_ADDR mod) +{ + state = (lk_module_state) lk_read_int (mod + LK_OFFSET (module, state)); + + if (LK_FIELD (module, module_core)) /* linux -4.4 */ + { + lk_module_init_module_from_struct_module (mod, this); + } + else /* linux 4.5+ */ + { + core = new lk_module_layout (mod + LK_OFFSET (module, core_layout)); + init = new lk_module_layout (mod + LK_OFFSET (module, init_layout)); + } + + // FIXME only exists if compiled with CONFIG_SMT + percpu.base = lk_read_addr (mod + LK_OFFSET (module, percpu)); + percpu.size = lk_read_uint (mod + LK_OFFSET (module, percpu_size)); + + /* Architectures can adjust the sections in the kernel (via + module_frob_arch_sections in /kernelmodule.c:layout_and_allocate). + So give them the chance to do the same in GDB, too. */ + LK_HOOK->adjust_module_layout (mod, this); +} + +lk_module::~lk_module () +{ + delete core; + delete init; +} + +/* See comment in lk-modules.h. */ + +void +lk_module::clear () +{ + state = LK_MODULE_STATE_INVALID; + + core->clear (); + init->clear (); + + percpu.clear (); +} + +/* Check if debug info for module NAME is loaded. */ + +bool +lk_modules_debug_info_loaded (const std::string &name) +{ + struct so_list *so; + + for (so = master_so_list (); so; so = so->next) + { + if (name == so->so_original_name) + return (so->symbols_loaded && objfile_has_symbols (so->objfile)); + } + + return false; +} + +/* Replace tags, like '$release', with corresponding data in + solib_search_path. + + Known tags: + $release Linux kernel release, same as 'uname -r' + + Returns the expanded path. */ + +static std::string +lk_modules_expand_search_path () +{ + char release[LK_UTS_NAME_LEN + 1]; + CORE_ADDR utsname; + + utsname = LK_ADDR (init_uts_ns) + LK_OFFSET (uts_namespace, name); + read_memory_string (utsname + LK_OFFSET (utsname, release), + release, LK_UTS_NAME_LEN); + release[LK_UTS_NAME_LEN] = '\0'; + + std::string search_path = get_solib_search_path (); + substitute_path_component (search_path, "$release", release); + + return search_path; +} + +/* With kernel modules there is the problem that the kernel only stores + the modules name but not the path from wich it was loaded from. + Thus we need to map the name to a path GDB can read from. We use file + modules.order to do so. It is created by kbuild containing the order in + which the modules appear in the Makefile and is also used by modprobe. + The drawback of this method is that it needs the modules.order file and + all relative paths, starting from , must be exactly the + same as decribed in it. */ + +/* Open file /modules.order and return its file + pointer. */ + +FILE * +lk_modules_open_mod_order () +{ + FILE *mod_order; + std::string filename = concat_path (lk_modules_expand_search_path (), + "modules.order"); + mod_order = gdb_fopen_cloexec (filename.c_str (), "r"); + + if (!mod_order) + { + error (_("\ +Can not find file module.order at %s \ +to load module symbol files.\n\ +Please check if solib-search-path is set correctly."), + filename.c_str ()); + } + + return mod_order; +} + +/* Build map between module name and path to binary file by reading file + modules.order. Returns unordered_map with module name as key and its + path as value. */ + +std::unordered_map +lk_modules_build_path_map () +{ + std::unordered_map umap; + FILE *mod_order; + struct cleanup *old_chain; + char line[SO_NAME_MAX_PATH_SIZE + 1]; + + mod_order = lk_modules_open_mod_order (); + old_chain = make_cleanup_fclose (mod_order); + + line[SO_NAME_MAX_PATH_SIZE] = '\0'; + std::string search_path = lk_modules_expand_search_path (); + while (fgets (line, SO_NAME_MAX_PATH_SIZE, mod_order)) + { + /* Remove trailing newline. */ + line[strlen (line) - 1] = '\0'; + + std::string name = lbasename (line); + + /* 3 = strlen (".ko"). */ + if (!endswith (name.c_str (), ".ko") + || name.length () >= LK_MODULE_NAME_LEN + 3) + continue; + + name = name.substr (0, name.length () - 3); + + /* Kernel modules are named after the files they are stored in with + all minus '-' replaced by underscore '_'. Do the same to enable + mapping. */ + for (size_t p = name.find('-'); p != std::string::npos; + p = name.find ('-', p + 1)) + name[p] = '_'; + + umap[name] = concat_path(search_path, line); + } + + do_cleanups (old_chain); + return umap; +} + +/* Function for current_sos hook. */ + +struct so_list * +lk_modules_current_sos (void) +{ + CORE_ADDR modules, next; + FILE *mod_order; + struct so_list *list = NULL; + std::unordered_map umap; + + umap = lk_modules_build_path_map (); + modules = LK_ADDR (modules); + lk_list_for_each (next, modules, module, list) + { + char name[LK_MODULE_NAME_LEN]; + CORE_ADDR mod, name_addr; + + mod = LK_CONTAINER_OF (next, module, list); + name_addr = mod + LK_OFFSET (module, name); + read_memory_string (name_addr, name, LK_MODULE_NAME_LEN); + + if (umap.count (name)) + { + struct so_list *newso = XCNEW (struct so_list); + + newso->next = list; + list = newso; + newso->lm_info = new lk_module (mod); + strncpy (newso->so_original_name, name, SO_NAME_MAX_PATH_SIZE); + strncpy (newso->so_name, umap[name].c_str (), SO_NAME_MAX_PATH_SIZE); + newso->pspace = current_program_space; + } + } + + return list; +} + +/* Relocate target_section GDB_SEC using section type LK_SEC. Helper + function for lk_modules_relocate_section_addresses. */ + +void +lk_modules_relocate_sec (struct target_section *gdb_sec, + lk_module_section_type &lk_sec) +{ + unsigned int alignment = 1 << gdb_sec->the_bfd_section->alignment_power; + CORE_ADDR base = lk_sec.base + lk_sec.offset; + + /* Adjust offset to section alignment. */ + if (base % alignment != 0) + base += alignment - (base % alignment); + + gdb_sec->addr += base; + gdb_sec->endaddr += base; + lk_sec.offset += gdb_sec->endaddr - gdb_sec->addr; +} + +/* Function for relocate_section_addresses hook. */ + +void +lk_modules_relocate_section_addresses (struct so_list *so, + struct target_section *sec) +{ + lk_module *module = (lk_module *) so->lm_info; + unsigned int flags = sec->the_bfd_section->flags; + const char *name = sec->the_bfd_section->name; + + /* Remove unmapped sections. */ + if (streq (name, ".modinfo") || streq (name, "__versions") + || (startswith (name, ".init") + && module->state != LK_MODULE_STATE_COMING)) + { + // FIXME Not mapping a section != unmapping a section + // + // In particular this causes GDB to map the "unmapped" section at the + // same address as the last mapped section (e.g. init.text starts at + // the same address like .text). Thus printing symbols definied in the + // unmapped section returnes random values instead of . + return; + } + + /* module->percpu.base points to data of cpu0. To access the data of + a cpu other than cpu0 the corresponding percpu offset needs to added + by hand. */ + if (endswith (name, ".percpu")) + { + lk_modules_relocate_sec (sec, module->percpu); + return; + } + + lk_module_layout *layout; + if (startswith (name, ".init")) + layout = module->init; + else + layout = module->core; + + if (flags & SEC_CODE) + lk_modules_relocate_sec (sec, layout->text); + else if (endswith (name, ".ro_after_init")) + lk_modules_relocate_sec (sec, layout->ro_after_init_data); + else if (flags & SEC_READONLY) + lk_modules_relocate_sec (sec, layout->ro_data); + else if (flags & SEC_ALLOC) + lk_modules_relocate_sec (sec, layout->rw_data); + + /* Set address range displayed with 'info shared' to match core_layout. */ + if (so->addr_low == so->addr_high) + { + so->addr_low = module->core->base; + so->addr_high = module->core->base + module->core->size; + } +} + +/* Function for free_so hook. */ + +void +lk_modules_free_so (struct so_list *so) +{ + delete so->lm_info; +} + +/* Function for clear_so hook. */ + +void +lk_modules_clear_so (struct so_list *so) +{ + lk_module *module = (lk_module *) so->lm_info; + gdb_assert (module != NULL); + + module->clear (); +} + +/* Function for clear_solib hook. */ + +void +lk_modules_clear_solib () +{ + /* Nothing to do. */ +} + +/* Function for clear_create_inferior_hook hook. */ + +void +lk_modules_create_inferior_hook (int from_tty) +{ + /* Nothing to do. */ +} + +/* Function for clear_create_inferior_hook hook. */ + +int +lk_modules_in_dynsym_resolve_code (CORE_ADDR pc) +{ + return 0; +} + +/* Function for same hook. Assume the module to be different if its + state changed. Otherwise there might be problems with pruning the .init + sections if the module was first seen during init. */ + +int +lk_modules_same (struct so_list *gdb, struct so_list *inf) +{ + lk_module_state gdb_state = ((lk_module *) gdb->lm_info)->state; + lk_module_state inf_state = ((lk_module *) inf->lm_info)->state; + + return streq (gdb->so_name, inf->so_name) && gdb_state == inf_state; +} + +/* Initialize linux modules solib target. */ + +void +init_lk_modules_so_ops (void) +{ + struct target_so_ops *t; + + if (lk_modules_so_ops != NULL) + return; + + t = XCNEW (struct target_so_ops); + t->relocate_section_addresses = lk_modules_relocate_section_addresses; + t->free_so = lk_modules_free_so; + t->clear_so = lk_modules_clear_so; + t->clear_solib = lk_modules_clear_solib; + t->solib_create_inferior_hook = lk_modules_create_inferior_hook; + t->current_sos = lk_modules_current_sos; + t->bfd_open = solib_bfd_open; + t->in_dynsym_resolve_code = lk_modules_in_dynsym_resolve_code; + t->same = lk_modules_same; + + lk_modules_so_ops = t; +} + +/* Provide a prototype to silence -Wmissing-prototypes. */ +extern initialize_file_ftype _initialize_lk_modules; + +void +_initialize_lk_modules (void) +{ + init_lk_modules_so_ops (); +} diff --git a/gdb/lk-modules.h b/gdb/lk-modules.h new file mode 100644 index 0000000000..ef54af4221 --- /dev/null +++ b/gdb/lk-modules.h @@ -0,0 +1,149 @@ +/* Handle kernel modules as shared libraries. + + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef __LK_MODULES_H__ +#define __LK_MODULES_H__ + +#include "solist.h" + + +extern struct target_so_ops *lk_modules_so_ops; + + +/* Copied from /include/linux/module.h. */ + +enum +lk_module_state +{ + LK_MODULE_STATE_INVALID = -1, /* Invalid state, GDB only. */ + LK_MODULE_STATE_LIVE, /* Normal state. */ + LK_MODULE_STATE_COMING, /* Full formed, running module_init. */ + LK_MODULE_STATE_GOING, /* Going away. */ + LK_MODULE_STATE_UNFORMED, /* Still setting it up. */ +}; + +/* The kernel modules .ko files are only partially linked, i.e. they contain + section headers but no program headers. When the module is loaded the + sections are relocated by the kernel (/kernel/module.c:load_module + & layout_and_allocate & layout_sections). Here the kernel destinguishes + between different section types + + * text (SHF_EXECINSTR | SHF_ALLOC) + * read-only data (SHF_ALLOC & !SHF_WRITE) + * read-only-after-init data (SHF_ALLOC | SHF_RO_AFTER_INIT) + * read-write data (SHF_WRITE | SHF_ALLOC) + + combined in module layouts. + + There are two module layouts, init and core. The init module layout + contains all sections where the section name starts with ".init". The + content of the init module layout gets unlinked once the module is + loaded. The core module layout contains the "normal" text and data + needed during module runtime. + + Furthermore there can be a + + * data..percpu (/kernel/module.c:percpu_modalloc) + + section which gets relocated to the other percpu data. */ + +struct lk_module_section_type +{ + /* Lowest address of this section type. */ + CORE_ADDR base = 0; + + /* Size of this section type. Calculated by GDB during relocation. */ + CORE_ADDR offset = 0; + + /* Accumulated size of all section types "below". Read from kernel + (except for rw_data). */ + unsigned int size = 0; + + /* Reset this module_section to default (all zero). Needed for + target_so_ops->clear_so hook. */ + void clear (); +}; + +/* Copied from /include/linux/module.h: struct module_layout. */ + +struct lk_module_layout +{ + lk_module_layout () = default; + lk_module_layout (CORE_ADDR layout); + + /* Reset this module_layout to default (all zero). Needed for + target_so_ops->clear_so hook. */ + void clear (); + + /* Lowest address of this module layout. */ + CORE_ADDR base = 0; + + /* Total size. */ + unsigned int size = 0; + + /* Executable text. + text.size = text. */ + lk_module_section_type text; + + /* Read-only data. + ro_data.size = text + ro_data. */ + lk_module_section_type ro_data; + + /* Read-only-after-init data. + ro_after_init_data.size = text + ro_data + ro_after_init_data. */ + lk_module_section_type ro_after_init_data; /* linux 4.8+ */ + + /* Writable data. + rw_data.size = layout.size. */ + lk_module_section_type rw_data; +}; + +/* Link map info to include in an allocated so_list entry. */ + +struct lk_module : public lm_info_base +{ + lk_module () = delete; + lk_module (CORE_ADDR mod); + + ~lk_module (); + + /* Reset this module to default (all zero). Needed for + target_so_ops->clear_so hook. */ + void clear (); + + /* This modules state as seen from the kernel. */ + lk_module_state state = LK_MODULE_STATE_INVALID; + + /* "core" layout, i.e. text + data after init. */ + lk_module_layout *core; + + /* "init" layout, i.e. text + data only needed during init + (LK_MODULE_STATE_COMING). It is unmapped after initialization. */ + lk_module_layout *init; + + /* Per CPU data. Mapped together with "ordinary" per cpu data. */ + lk_module_section_type percpu; +}; + + +/* Check if debug info for module NAME are loaded. Needed by lsmod command. */ + +extern bool lk_modules_debug_info_loaded (const std::string &name); + +#endif /* __LK_MODULES_H__ */ diff --git a/gdb/solib.c b/gdb/solib.c index 491c18a685..4e72c929ce 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -104,6 +104,14 @@ show_solib_search_path (struct ui_file *file, int from_tty, value); } +/* see solib.h. */ + +const char * +get_solib_search_path () +{ + return solib_search_path ? solib_search_path : ""; +} + /* Same as HAVE_DOS_BASED_FILE_SYSTEM, but useable as an rvalue. */ #if (HAVE_DOS_BASED_FILE_SYSTEM) # define DOS_BASED_FILE_SYSTEM 1 diff --git a/gdb/solib.h b/gdb/solib.h index e91fb75d3d..b7608d275f 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -31,6 +31,11 @@ struct program_space; /* List of known shared objects */ #define so_list_head current_program_space->so_list +/* Returns the solib_search_path. The returned string is malloc'ed and must be + freed by the caller. */ + +extern const char *get_solib_search_path (); + /* Called when we free all symtabs, to free the shared library information as well. */