From patchwork Thu Jan 12 12:55:51 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Rudo X-Patchwork-Id: 18881 Received: (qmail 34763 invoked by alias); 12 Jan 2017 12:56:10 -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 33928 invoked by uid 89); 12 Jan 2017 12:56:09 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=1.0 required=5.0 tests=AWL, BAYES_50, KAM_LAZY_DOMAIN_SECURITY, RCVD_IN_DNSWL_LOW autolearn=no version=3.3.2 spammy=the_bfd_section, streq, objfilesh, UD:objfiles.h X-HELO: mx0a-001b2d01.pphosted.com Received: from mx0b-001b2d01.pphosted.com (HELO mx0a-001b2d01.pphosted.com) (148.163.158.5) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 12 Jan 2017 12:55:59 +0000 Received: from pps.filterd (m0098419.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.17/8.16.0.17) with SMTP id v0CCrPAs103862 for ; Thu, 12 Jan 2017 07:55:57 -0500 Received: from e06smtp09.uk.ibm.com (e06smtp09.uk.ibm.com [195.75.94.105]) by mx0b-001b2d01.pphosted.com with ESMTP id 27x896vjyx-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 12 Jan 2017 07:55:57 -0500 Received: from localhost by e06smtp09.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 12 Jan 2017 12:55:55 -0000 Received: from d06dlp03.portsmouth.uk.ibm.com (9.149.20.15) by e06smtp09.uk.ibm.com (192.168.101.139) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 12 Jan 2017 12:55:53 -0000 Received: from b06cxnps3074.portsmouth.uk.ibm.com (d06relay09.portsmouth.uk.ibm.com [9.149.109.194]) by d06dlp03.portsmouth.uk.ibm.com (Postfix) with ESMTP id D18BA1B0806E for ; Thu, 12 Jan 2017 12:58:34 +0000 (GMT) Received: from d06av22.portsmouth.uk.ibm.com (d06av22.portsmouth.uk.ibm.com [9.149.105.58]) by b06cxnps3074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v0CCtrTa13369724 for ; Thu, 12 Jan 2017 12:55:53 GMT Received: from d06av22.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 217E04C040 for ; Thu, 12 Jan 2017 11:54:07 +0000 (GMT) Received: from d06av22.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 02D2E4C059 for ; Thu, 12 Jan 2017 11:54:07 +0000 (GMT) Received: from ThinkPad (unknown [9.152.212.192]) by d06av22.portsmouth.uk.ibm.com (Postfix) with ESMTP for ; Thu, 12 Jan 2017 11:54:06 +0000 (GMT) Date: Thu, 12 Jan 2017 13:55:51 +0100 From: Philipp Rudo To: gdb-patches@sourceware.org Subject: Re: [RFC 0/7] Support for Linux kernel debugging In-Reply-To: <20170112113217.48852-1-prudo@linux.vnet.ibm.com> References: <20170112113217.48852-1-prudo@linux.vnet.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Content-Scanned: Fidelis XPS MAILER x-cbid: 17011212-0036-0000-0000-0000035546DD X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17011212-0037-0000-0000-000014BB51A3 Message-Id: <20170112135551.6009db66@ThinkPad> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-01-12_10:, , 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-1612050000 definitions=main-1701120182 X-IsSubscribed: yes Hi apparently patch 4 got lost on its way to the mailing list. Lets try it this way... Philipp From 16d2d2759f68475b2792a1426ddcfc2d727607ba Mon Sep 17 00:00:00 2001 From: Philipp Rudo Date: Tue, 22 Nov 2016 11:19:17 +0100 Subject: [RFC 4/7] Add kernel module support for linux-kernel target 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 read from. 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. (COMMON_OBS): Add lk-modules.o. --- gdb/Makefile.in | 4 + gdb/lk-low.c | 101 ++++++++++++++ gdb/lk-low.h | 29 +++- gdb/lk-modules.c | 412 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/lk-modules.h | 29 ++++ gdb/solib.c | 8 ++ gdb/solib.h | 5 + 7 files changed, 586 insertions(+), 2 deletions(-) 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 fef5c2e..be8a91d 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1094,6 +1094,7 @@ SFILES = \ linespec.c \ lk-lists.c \ lk-low.c \ + lk-modules.c \ location.c \ m2-exp.y \ m2-lang.c \ @@ -1342,6 +1343,7 @@ HFILES_NO_SRCDIR = \ linux-tdep.h \ lk-lists.h \ lk-low.h \ + lk-modules.h \ location.h \ m2-lang.h \ m32r-tdep.h \ @@ -1701,6 +1703,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ language.o \ linespec.o \ lk-lists.o \ + lk-modules.o \ location.o \ m2-lang.o \ m2-typeprint.o \ @@ -2534,6 +2537,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/lk-low.c b/gdb/lk-low.c index ee753a4..81ff61c 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" @@ -564,6 +565,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. */ @@ -599,6 +640,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 () { @@ -619,10 +663,61 @@ 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, list); + LK_DECLARE_FIELD (module, name); + LK_DECLARE_FIELD (module, source_list); + LK_DECLARE_FIELD (module, arch); + LK_DECLARE_FIELD (module, init); + 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); + } + else if (LK_DECLARE_FIELD_SILENT (module, module_core)) /* linux -4.4 */ + { + LK_DECLARE_FIELD (module, init_size); + LK_DECLARE_FIELD (module, core_size); + + LK_DECLARE_FIELD (module, core_text_size); + LK_DECLARE_FIELD (module, core_ro_size); + } + else + { + error (_("Could not find module base. Abort.")); + } + + 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. Abort.")); + 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 */ @@ -720,12 +815,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->get_module_text_offset); 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. */ @@ -838,6 +938,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 57b7bf4..cde78b6 100644 --- a/gdb/lk-low.h +++ b/gdb/lk-low.h @@ -27,6 +27,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 /* Private data structs for this target. */ /* Forward declarations. */ @@ -200,13 +202,27 @@ 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 + .text section. */ + +typedef CORE_ADDR (*lk_hook_get_module_text_offset) (CORE_ADDR mod); + /* Hook to return the per_cpu_offset of cpu CPU. Only architectures that do not use the __per_cpu_offset array to determin the offset have to supply this hook. */ typedef CORE_ADDR (*lk_hook_get_percpu_offset) (int cpu); -/* Hook to map a running task to a logical CPU. Required if the target beneath - uses a different PID as struct rq. */ +/* Hook to map a running task to a logical CPU. Required if the target + beneath uses a different PID as struct rq. */ typedef int (*lk_hook_map_running_task_to_cpu) (struct thread_info *ti); struct lk_private_hooks @@ -214,6 +230,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_get_module_text_offset get_module_text_offset; + /* 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 0000000..8bb04d6 --- /dev/null +++ b/gdb/lk-modules.c @@ -0,0 +1,412 @@ +/* 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; + +/* Info for single section type. */ + +struct lm_info_sec +{ + CORE_ADDR start; + CORE_ADDR offset; + unsigned int size; +}; + +/* Link map info to include in an allocated so_list entry. */ + +struct lm_info +{ + CORE_ADDR base; + unsigned int size; + + struct lm_info_sec text; + struct lm_info_sec init_text; + struct lm_info_sec ro_data; + struct lm_info_sec rw_data; + struct lm_info_sec percpu; +}; + +/* Check if debug info for module NAME are 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, ".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; +} + +/* Allocate and fill a copy of struct lm_info for module at address MOD. */ + +struct lm_info * +lk_modules_read_lm_info (CORE_ADDR mod) +{ + struct lm_info *lmi = XNEW (struct lm_info); + struct cleanup *old_chain = make_cleanup (xfree, lmi); + + if (LK_FIELD (module, module_core)) /* linux -4.4 */ + { + lmi->base = lk_read_addr (mod + LK_OFFSET (module, module_core)); + lmi->size = lk_read_addr (mod + LK_OFFSET (module, core_size)); + + lmi->text.start = lmi->base; + lmi->text.offset = LK_HOOK->get_module_text_offset (mod); + lmi->text.size = lk_read_uint (mod + LK_OFFSET (module, core_text_size)); + + lmi->ro_data.start = lmi->base + lmi->text.size; + lmi->ro_data.offset = 0; + lmi->ro_data.size = lk_read_uint (mod + LK_OFFSET (module, + core_ro_size)); + } + else /* linux 4.5+ */ + { + CORE_ADDR mod_core = mod + LK_OFFSET (module, core_layout); + + lmi->base = lk_read_addr (mod_core + + LK_OFFSET (module_layout, base)); + lmi->size = lk_read_uint (mod_core + + LK_OFFSET (module_layout, size)); + + lmi->text.start = lmi->base; + lmi->text.offset = LK_HOOK->get_module_text_offset (mod); + lmi->text.size = lk_read_uint (mod_core + + LK_OFFSET (module_layout, text_size)); + + lmi->ro_data.start = lmi->base + lmi->text.size; + lmi->ro_data.offset = 0; + lmi->ro_data.size = lk_read_uint (mod_core + + LK_OFFSET (module_layout, ro_size)); + } + + lmi->rw_data.start = lmi->base + lmi->ro_data.size; + lmi->rw_data.offset = 0; + lmi->rw_data.size = lmi->size - lmi->ro_data.size; + + lmi->init_text.start = lk_read_addr (mod + LK_OFFSET (module, init)); + lmi->init_text.offset = 0; + + lmi->percpu.start = lk_read_addr (mod + LK_OFFSET (module, percpu)); + lmi->percpu.size = lk_read_uint (mod + LK_OFFSET (module, percpu_size)); + lmi->percpu.offset = 0; + + discard_cleanups (old_chain); + return lmi; +} + +/* 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 = lk_modules_read_lm_info (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 SEC to section type LMI_SEC. Helper function for + lk_modules_relocate_section_addresses. */ + +void +lk_modules_relocate_sec (struct target_section *sec, + struct lm_info_sec *lmi_sec) +{ + unsigned int alignment = 1; + + alignment = 1 << sec->the_bfd_section->alignment_power; + + /* Adjust offset to section alignment. */ + if (lmi_sec->offset % alignment != 0) + lmi_sec->offset += alignment - (lmi_sec->offset % alignment); + + sec->addr += lmi_sec->start + lmi_sec->offset; + sec->endaddr += lmi_sec->start + lmi_sec->offset; + lmi_sec->offset += sec->endaddr - sec->addr; +} + +/* Function for relocate_section_addresses hook. */ + +void +lk_modules_relocate_section_addresses (struct so_list *so, + struct target_section *sec) +{ + struct lm_info *lmi = so->lm_info; + unsigned int flags = sec->the_bfd_section->flags; + const char *name = sec->the_bfd_section->name; + + if (streq (name, ".modinfo") || streq (name, "__versions")) + return; + + /* FIXME: Make dependent on module state, i.e. only map .init sections if + * state is MODULE_STATE_COMING. */ + if (startswith (name, ".init")) + lk_modules_relocate_sec (sec, &lmi->init_text); + else if (endswith (name, ".percpu")) + lk_modules_relocate_sec (sec, &lmi->percpu); + else if (flags & SEC_CODE) + lk_modules_relocate_sec (sec, &lmi->text); + else if (flags & SEC_READONLY) + lk_modules_relocate_sec (sec, &lmi->ro_data); + else if (flags & SEC_ALLOC) + lk_modules_relocate_sec (sec, &lmi->rw_data); + + /* Set address range to be displayed with info shared. + size = text + (ro + rw) data without .init sections. */ + if (so->addr_low == so->addr_high) + { + so->addr_low = lmi->base; + so->addr_high = lmi->base + lmi->size; + } +} + +/* Function for free_so hook. */ + +void +lk_modules_free_so (struct so_list *so) +{ + xfree (so->lm_info); +} + +/* Function for clear_so hook. */ + +void +lk_modules_clear_so (struct so_list *so) +{ + if (so->lm_info != NULL) + memset (so->lm_info, 0, sizeof (struct lm_info)); +} + +/* 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. */ + +int +lk_modules_same (struct so_list *gdb, struct so_list *inf) +{ + return streq (gdb->so_name, inf->so_name); +} + +/* 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 0000000..47e6dde --- /dev/null +++ b/gdb/lk-modules.h @@ -0,0 +1,29 @@ +/* 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__ + +extern struct target_so_ops *lk_modules_so_ops; + +/* 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 fc45133..595828a 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -107,6 +107,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 dd07636..99b5cda 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -28,6 +28,11 @@ struct program_space; #include "symfile-add-flags.h" +/* 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. */