From patchwork Thu Feb 25 19:15:05 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 62909 Delivered-To: patch@linaro.org Received: by 10.112.199.169 with SMTP id jl9csp328967lbc; Thu, 25 Feb 2016 11:16:35 -0800 (PST) X-Received: by 10.66.190.40 with SMTP id gn8mr64984456pac.64.1456427795213; Thu, 25 Feb 2016 11:16:35 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id 79si14136747pfo.227.2016.02.25.11.16.34 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 25 Feb 2016 11:16:35 -0800 (PST) Received-SPF: pass (google.com: domain of gdb-patches-return-129944-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; spf=pass (google.com: domain of gdb-patches-return-129944-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gdb-patches-return-129944-patch=linaro.org@sourceware.org; dkim=pass header.i=@sourceware.org DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id:in-reply-to :references; q=dns; s=default; b=iYnI2Kg4oq/5LDlJi6GQL1V4yu1o4KY 3ioIqG/c+w8Fn6g+VicrvgvzvYUK394WklOkm8RQPQEKYMmEUUKYvZrLzh0LPrZB VNyV7hGZ7YjvPQtexBq4n4S7XjgjdhQ0ksM/ASZQGOpmpGgWw40jDTH0tnRG9BLy uZ35s9BSAiu8= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id:in-reply-to :references; s=default; bh=HyYj8gDXN/l95z2a/2DUyl1IMz8=; b=x6YNp BjytfxvocecPFPPt5ln+i4u0eyeimWv0ha8bKIW5MjnYQCLy0iMeEEh5vE3avuM3 AR3aCDGBPwKfhb2f1CWb6eB/Yv8rAVXVCBudhpIeG9VArIq1jMlDHfOf2B8QDcrq XP5TK31eCdFZtH25I5ZV29qEXU2S5WoEHeOMyY= Received: (qmail 23944 invoked by alias); 25 Feb 2016 19:15:51 -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 23733 invoked by uid 89); 25 Feb 2016 19:15:47 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.6 required=5.0 tests=AWL, BAYES_50, KAM_ASCII_DIVIDERS, KAM_STOCKGEN, SPF_PASS autolearn=no version=3.3.2 spammy=SIGTERM, sigterm, FOUND, abfd X-HELO: mail-wm0-f45.google.com Received: from mail-wm0-f45.google.com (HELO mail-wm0-f45.google.com) (74.125.82.45) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Thu, 25 Feb 2016 19:15:38 +0000 Received: by mail-wm0-f45.google.com with SMTP id a4so41508642wme.1 for ; Thu, 25 Feb 2016 11:15:37 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=cuIpTt016sk8CWd5UE+ji7BNppwF6KvRQWAwM5BhKcM=; b=JhyafSdfKX+evO9d8bKsEr9WLgAdhvko+mLpbXvq3O4iFP+Wwt6uZOfDFFx10lnVvU 4PCXm08iZ+1oV6GgSWUH4ANtuG7SK4vdjaOvid4m0iDwF9fRHC67UrQpwTvOz1tMdHfR 0war4XH+8Z2DQs+wKD/hSoc1GAOy+11C87Mfgbk1/h1wMfgo+ch+AN8f7gxVNYBqBBSj zZ5zQkb7qaweM56ELOoLW4/I1lkAMBCxQZ5wQCnjyTyt/1UwRaS4ugDQZZSCM/Ekp5T3 BE7QA5Wc2aMFvl7WOJSXky1U+52YSiCwPhaSBFNRXgjY4AQbTbCZtey8W93bYZL7fzUM BLQw== X-Gm-Message-State: AG10YOQyz7n7+g6/QLq9pQuHBZLONEXwS0Xt4rnzuKyAeW52WCb6mcR6AT0RxtCySA40U1ak X-Received: by 10.194.143.82 with SMTP id sc18mr45390950wjb.14.1456427735229; Thu, 25 Feb 2016 11:15:35 -0800 (PST) Received: from localhost.localdomain (cpc87017-aztw30-2-0-cust65.18-1.cable.virginm.net. [92.232.232.66]) by smtp.gmail.com with ESMTPSA id i10sm4506874wmf.14.2016.02.25.11.15.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 25 Feb 2016 11:15:34 -0800 (PST) From: Kieran Bingham To: gdb-patches@sourceware.org Cc: arnez@linux.vnet.ibm.com, jeffm@suse.com, peter.griffin@linaro.org, lee.jones@linaro.org, yao.qi@linaro.org, russell.wayman@linaro.org, kernel@stlinux.com, Kieran Bingham Subject: [PATCH 3/4] gdb/lkd: Add Linux Kernel Awareness target Date: Thu, 25 Feb 2016 19:15:05 +0000 Message-Id: <1456427706-30077-4-git-send-email-kieran.bingham@linaro.org> In-Reply-To: <1456427706-30077-1-git-send-email-kieran.bingham@linaro.org> References: <1456427706-30077-1-git-send-email-kieran.bingham@linaro.org> Provide infrastructure and thread awareness for the Linux Kernel This functionality is heavily derived from the functionality created by ST Microelectronics which provides a greater feature set. This specific implementation is stripped down to provide thread awareness only. Other functionality that can already be provided by python infrastructure such as information gathering tools specific to linux have been submitted to the linux kernel project separately. Kernel module support is also already working and available through the python extensions provided by the linux kernel and has been removed from this submission. Current known limitations: * In this patch set there is a current known issue with handling of the internal gdb thread objects. The LKD_LOADING states allow the underlying target (in the usual case, the remote layer) to add threads which conflict with the thread objects created by LKD. This was not an issue in the ST usage as a custom remote layer specific to their underlying JTAG debugger (the STMC2) was used * If the user stops the target while it is executing in User Space the kernel awareness is unable to read memory in the protected regions of the MMU On ARM/SH, ST's implementation handles this by directly controlling the MMU from the LKD layers, and ensuring that the CPU can read the required memory regions. As there is no current generic API for manipulating an MMU over GDB's remote protocol, this functionality has been removed from this initial patch set. --- gdb/Makefile.in | 23 +- gdb/config.in | 3 + gdb/configure | 23 + gdb/configure.ac | 15 + gdb/configure.tgt | 8 + gdb/lkd/lkd-main.c | 2160 +++++++++++++++++++++++++++++++++++++++++++++++++ gdb/lkd/lkd-process.c | 892 ++++++++++++++++++++ gdb/lkd/lkd-process.h | 63 ++ gdb/lkd/lkd.h | 486 +++++++++++ 9 files changed, 3670 insertions(+), 3 deletions(-) create mode 100644 gdb/lkd/lkd-main.c create mode 100644 gdb/lkd/lkd-process.c create mode 100644 gdb/lkd/lkd-process.h create mode 100644 gdb/lkd/lkd.h -- 2.5.0 diff --git a/gdb/Makefile.in b/gdb/Makefile.in index ec2af52dc44d..ff324c393f23 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -721,7 +721,8 @@ ALL_TARGET_OBS = \ symfile-mem.o \ windows-tdep.o \ linux-record.o \ - ravenscar-thread.o + ravenscar-thread.o \ + lkd-main.o lkd-process.o # Host-dependent makefile fragment comes in here. @host_makefile_frag@ @@ -988,7 +989,7 @@ common/common-exceptions.h target/target.h common/symbol.h \ common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \ common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h nat/amd64-linux-siginfo.h\ nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h \ -tid-parse.h +tid-parse.h lkd/lkd.h lkd/lkd-process.h # Header files that already have srcdir in them, or which are in objdir. @@ -1355,6 +1356,7 @@ init.c: $(INIT_FILES) -e '/[a-z0-9A-Z_]*-exp.tab.[co]$$/d' \ -e 's/\.[co]$$/.c/' \ -e 's,signals\.c,common/signals\.c,' \ + -e 's,\(lkd-.*\.c\),lkd/\1,' \ -e 's|^\([^ /][^ ]*\)|$(srcdir)/\1|g' | \ while read f; do \ sed -n -e 's/^_initialize_\([a-z_0-9A-Z]*\).*/\1/p' $$f 2>/dev/null; \ @@ -1754,7 +1756,8 @@ ALLDEPFILES = \ xcoffread.c \ xstormy16-tdep.c \ xtensa-tdep.c xtensa-config.c \ - xtensa-linux-tdep.c xtensa-linux-nat.c xtensa-xtregs.c + xtensa-linux-tdep.c xtensa-linux-nat.c xtensa-xtregs.c \ + lkd/lkd-main.c lkd/lkd-process.c # Some files need explicit build rules (due to -Werror problems) or due # to sub-directory fun 'n' games. @@ -2717,6 +2720,20 @@ py-varobj.o: $(srcdir)/python/py-varobj.c $(POSTCOMPILE) # +# gdb/lkd/ dependencies +# +# Need to explicitly specify the compile rule as make will do nothing +# or try to compile the object file into the sub-directory. + +lkd-main.o: $(srcdir)/lkd/lkd-main.c +lkd-process.o: $(srcdir)/lkd/lkd-process.c + +# Generalised lkd-compile rule +lkd-%: + $(COMPILE) $< + $(POSTCOMPILE) + +# # Dependency tracking. Most of this is conditional on GNU Make being # found by configure; if GNU Make is not found, we fall back to a # simpler scheme. diff --git a/gdb/config.in b/gdb/config.in index dc9da0a73610..676bd60cac9f 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -33,6 +33,9 @@ /* Define to BFD's default target vector. */ #undef DEFAULT_BFD_VEC +/* Define to 1 if Linux Kernel Debug is enabled */ +#undef ENABLE_LKD + /* Define to 1 if translation of program messages to the user's native language is requested. */ #undef ENABLE_NLS diff --git a/gdb/configure b/gdb/configure index b523debe484e..03aae841e917 100755 --- a/gdb/configure +++ b/gdb/configure @@ -816,6 +816,7 @@ with_auto_load_dir with_auto_load_safe_path enable_targets enable_64_bit_bfd +enable_linux_kernel_aware enable_gdbcli enable_gdbmi enable_tui @@ -1506,6 +1507,8 @@ Optional Features: --enable-targets=TARGETS alternative target configurations --enable-64-bit-bfd 64-bit support (on hosts with narrower word sizes) + --enable-linux-kernel-aware + enable linux kernel aware debugging --disable-gdbcli disable command-line interface (CLI) --disable-gdbmi disable machine-interface (MI) --enable-tui enable full-screen terminal user interface (TUI) @@ -5687,6 +5690,26 @@ else want64=false fi +# Check whether to support linux kernel aware debugging +# Check whether --enable-linux-kernel-aware was given. +if test "${enable_linux_kernel_aware+set}" = set; then : + enableval=$enable_linux_kernel_aware; case $enableval in + yes | no) + ;; + *) + as_fn_error "bad value $enableval for --enable-linux-kernel-aware" "$LINENO" 5 ;; + esac +else + enable_linux_kernel_aware=no +fi + + +if test "${enable_linux_kernel_aware}" = "yes"; then + +$as_echo "#define ENABLE_LKD 1" >>confdefs.h + +fi + # Provide defaults for some variables set by the per-host and per-target # configuration. gdb_host_obs=posix-hdep.o diff --git a/gdb/configure.ac b/gdb/configure.ac index 70452d3ff056..a148c1c41fed 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -198,6 +198,21 @@ AS_HELP_STRING([--enable-64-bit-bfd], [64-bit support (on hosts with narrower wo *) AC_MSG_ERROR(bad value ${enableval} for 64-bit-bfd option) ;; esac],[want64=false])dnl +# Check whether to support linux kernel aware debugging +AC_ARG_ENABLE(linux-kernel-aware, +AS_HELP_STRING([--enable-linux-kernel-aware], [enable linux kernel aware debugging]), + [case $enableval in + yes | no) + ;; + *) + AC_MSG_ERROR([bad value $enableval for --enable-linux-kernel-aware]) ;; + esac], + [enable_linux_kernel_aware=no]) + +if test "${enable_linux_kernel_aware}" = "yes"; then + AC_DEFINE(ENABLE_LKD, 1, [Define to 1 if Linux Kernel Debug is enabled]) +fi + # Provide defaults for some variables set by the per-host and per-target # configuration. gdb_host_obs=posix-hdep.o diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 54245c3d5522..8a685afe5c1e 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -725,3 +725,11 @@ for t in x ${gdb_target_obs}; do gdb_have_gcore=true fi done + + +# Add linux kernel awareness obs and set no default OS ABI +if test x"$enable_linux_kernel_aware" = xyes; then + gdb_target_obs="$gdb_target_obs lkd-main.o lkd-process.o" + gdb_osabi= +fi + diff --git a/gdb/lkd/lkd-main.c b/gdb/lkd/lkd-main.c new file mode 100644 index 000000000000..89a3c9dd8de8 --- /dev/null +++ b/gdb/lkd/lkd-main.c @@ -0,0 +1,2160 @@ +/* + Copyright 2005-2013 STMicroelectronics. + + This file contains the architecture neutral part of the Linux + Awareness layer for GDB. + + */ + +#include + +#include "defs.h" +#include "ui-out.h" +#include "arch-utils.h" +#include "block.h" +#include "breakpoint.h" +#include "cli/cli-decode.h" +#include "cli/cli-script.h" +#include "command.h" +#include "completer.h" +#include "dictionary.h" +#include "event-loop.h" +#include "exceptions.h" +#include "exec.h" +#include "frame.h" +#include "frame-unwind.h" +#include "gdb.h" +#include "gdb_assert.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "gdbtypes.h" +#include "inferior.h" +#include "location.h" +#include "objfiles.h" +#include "observer.h" +#include "regcache.h" +#include "solib.h" +#include "solist.h" +#include "symtab.h" +#include "psympriv.h" +#include "target.h" +#include "top.h" + +#include "bfd.h" +#include "libbfd.h" +#include "elf-bfd.h" + +#include "tui/tui.h" + + +#include "lkd.h" +#include "lkd-process.h" + +/******************************* from shtdi.c *********************************/ +/* Signal Handling. */ +#include + +/* A target interrupt has been requested. */ + +/* A SIGTERM has been requested. */ +static volatile int terminate_requested = 0; + +/* Remember the regular SIG handlers while ours are installed. */ +static void (*old_interrupt_handler) (int); +static void (*old_terminate_handler) (int); +static void terminate_once (int signo); +static void disable_terminate (void); +static void enable_terminate (void); + +/******************************************************************************/ + +#define BENEATH linux_aware_ops.beneath + +struct lkd_private_data lkd_private; + +/***************************** Added commands *********************************/ +static char linux_awareness_doc[] = ""; + +/* The cmd_list_element where we register all the 'set + linux-awareness...' and 'show linux-awareness...' commands. */ +struct cmd_list_element *set_linux_awareness_cmd_list; +struct cmd_list_element *show_linux_awareness_cmd_list; + +/* The definition of the log domains and the storage for their + associated log levels. */ +struct debug_domain linux_aware_debug_domains_info[] = { + {"debug-task", 0}, + {"debug-target", 0}, + {"debug-init", 0}, + {NULL, 0} +}; + +/* The definition of the log domains and the storage for their + associated log levels. */ +struct linux_awareness_params lkd_params = { + .enabled = 0, + .loaded = LKD_NOTLOADED, + .enable_task_awareness = 1, + .auto_activate = 1, + .skip_schedule_frame = 0, /*RnDCT0001394: changed default behavior */ + .no_colors = 0, + .loglevel = 0 +}; + + +/* 'set target-root-prefix' is introduced as an alias for 'set + solib-absolute-prefix', this variable contains a pointer to the value + of that variable. */ +char **target_root_prefix; +static int target_root_prefix_dirty = 1; + +/********************************* GDB glue ***********************************/ + +/* The target ops that adds the linux awareness. */ +struct target_ops linux_aware_ops; + +/* The structure that gives access to target-dependent knowledge + required by the Linux awareness layer. Declared in lkd.h + and defined in a targetting file (eg. lkd-arm.c). */ +struct linux_awareness_ops *linux_awareness_ops; + +/**************************** Execution control *******************************/ + +/* This ugly variable is a way to pass information from the + target_ops->to_resume to the target_ops->to_wait callbacks. It + indicates if the last execution request was a singlstep. It's used + in linux_aware_wait to perform additional step in some + circumstances. */ +int lkd_stepping; + +/* A simple flag indicating that the target is running (ie. we are + between a target_resume () and a target_wait () call). This is used + to properly disconnect when the debugger is killed by a signal. */ +static int running; + +/* This is the observer registered for the normal_stop event. The + observer callback (normal_stop_callback ()) is called when the user + gets the hand back after a target execution. It's used to collect + some information and to cleanup some state. */ +static struct observer *normal_stop_observer; + +/******************************* Function Prototypes **************************/ + +void set_skip_schedule_frame (char *arg, int from_tty, + struct cmd_list_element *c); + +/*********************** Addresses and Structure descriptions *****************/ + +/* The Linux Awareness Layer needs to know a lot about the addresses + and layout of the data structures used in the kernel. This is + handled through these declaration and the associated macros and + functions (see linux-awareness.h). */ + +/* Storage for the field layout and addresses already gathered. */ +struct field_info *field_info; +struct addr_info *addr_info; + +/* Declaration of the required addresses. */ +DECLARE_ADDR (start_kernel); +DECLARE_ADDR (do_exit); + +DECLARE_ADDR (try_to_unmap); + +DECLARE_ADDR (linux_banner); + +DECLARE_ADDR (system_utsname); +DECLARE_ADDR (init_uts_ns); + +/* Structure fields */ + +DECLARE_FIELD (list_head, next); +DECLARE_FIELD (new_utsname, release); + + +DECLARE_FIELD (pid_namespace, last_pid); +DECLARE_FIELD (qstr, len); +DECLARE_FIELD (qstr, name); + +DECLARE_FIELD (task_struct, mm); +DECLARE_FIELD (task_struct, tasks); +DECLARE_FIELD (task_struct, children); +DECLARE_FIELD (task_struct, thread_group); +DECLARE_FIELD (task_struct, sibling); +DECLARE_FIELD (task_struct, pid); +DECLARE_FIELD (task_struct, tgid); +DECLARE_FIELD (task_struct, namespace); +DECLARE_FIELD (task_struct, nsproxy); +DECLARE_FIELD (task_struct, prio); +DECLARE_FIELD (task_struct, cred); +DECLARE_FIELD (task_struct, comm); /* far offset in the task_struct, to bulk-read everything needed. */ + + +DECLARE_FIELD (thread_info, preempt_count); +DECLARE_FIELD (uts_namespace, name); + + +int max_cores = MAX_CORES; + +/****************************** Task handling *********************************/ + +/* The internal breakpoints used to be notified of interesting task + events. */ +static struct breakpoint *thread_event_low_mem_bp; +static struct breakpoint *thread_event_do_exec_bp; +static struct breakpoint *thread_event_do_exit_bp; +static struct breakpoint *thread_event_do_exec_return_bp; + +/* This value is incremented and decremented by + thread_awareness_(in|ex)hibit (). The target actions (eg. read + register) requested by the platform specific part of the linux + awareness are guaranteed to access the real state of the target, + whatever the user selected as current task in the debugger. To + achieve this, this variable is incremented/decremented around the + calls to linux_awareness_ops, and tested in each target access + method. */ +static int _inhibit_thread_register_awareness; + +/* the core that triggered the event (zero-based)*/ +int stop_core = 0; +/* When doing userspace debug, there's the very annoying limitation + that the debugged application creation isn't handled by the + debugger, but by the user in an environement decorelated from the + debugger. This precludes using the debugger to debug an application + from the start. + To overcome that annoying limitation, tha command 'wait_exe' has + been added. It's used like that: + + (gdb) wait_exe foo + Type commands that will be executed the next time the binary is exec'd: + End with a line saying just "end". + >b main + >p global = 1 + >end + + This tells the debugger to watch for process creation, and that + when a process called 'foo' is launched, the debugger should + execute 'b main' and 'p global = 1' before the process is allowed + to start executing. Once the user launches the process it should + then stop on main. Of course this is just an example; the + machinery is generic enough to allow many other uses. + + Note that putting a control execution command (next, continue...) + inside the wait_exe command list will break things (Unfortunately + it's not possible to detect it genericaly). + + The below struct and list contains the wait_exe requests currently + in fly. + */ +struct waited_exe +{ + struct waited_exe *next; + char *name; + struct command_line *cmds; + uid_t uid; +} *waited_exes = NULL; + +/* The bellow data structures deal with the handling of breakpoints on + virtual memory pages that aren't mapped to memory yet. The idea is + to put a watchpoint on the page table entry that represents the + page and to associate commands that create the final breakpoint + with the watchpoint. */ +struct bp_list +{ + struct bp_list *next; + struct breakpoint *b; +}; + + +/* This key is used to store a reference count associated with GDB + objfiles. Objfiles are shared between userspace thread of the same + application, thus we reference count them to know when we can + discard the information. */ +const struct objfile_data *linux_uprocess_objfile_data_key; + +/****************** Local functions forward declarations **********************/ +static void normal_stop_callback (struct bpstats *bs, int); + +/***************** End Local functions forward declarations *******************/ + +/* Called by ADDR to fetch the address of a symbol declared using + DECLARE_ADDR. */ +int +linux_init_addr (struct addr_info *addr, int check) +{ + if (addr->bmsym.minsym) + return 1; + + addr->bmsym = lookup_minimal_symbol (addr->name, NULL, NULL); + + if (addr->bmsym.minsym) + { + DEBUG (D_INIT, 4, "Checking for address of '%s' : OK\n", addr->name); + } + else + { + DEBUG (D_INIT, 1, "Checking for address of '%s' : NOT FOUND\n", + addr->name); + if (!check) + error ("Couldn't find address of %s", addr->name); + return 0; + } + + /* Chain initialized entries for cleanup. */ + addr->next = addr_info; + addr_info = addr; + + DEBUG (D_INIT, 4, "%s address is %s\n", addr->name, + phex (BMSYMBOL_VALUE_ADDRESS (addr->bmsym), 4)); + return 1; +} + +/* Helper for linux_init_field. */ +static int +find_struct_field (struct type *type, char *field, int *offset, int *size) +{ + int i; + + for (i = 0; i < TYPE_NFIELDS (type); ++i) + { + if (!strcmp (FIELD_NAME (TYPE_FIELDS (type)[i]), field)) + break; + } + + if (i >= TYPE_NFIELDS (type)) + return 0; + + *offset = FIELD_BITPOS (TYPE_FIELDS (type)[i]) / TARGET_CHAR_BIT; + *size = TYPE_LENGTH (check_typedef (TYPE_FIELDS (type)[i].type)); + return 1; +} + +/* Called by F_OFFSET or F_SIZE to compute the description of a field + declared using DECLARE_FIELD. */ +int +linux_init_field (struct field_info *field, int check) +{ + if (field->type != NULL) + return 1; + + field->type = + lookup_symbol (field->struct_name, NULL, STRUCT_DOMAIN, NULL).symbol; + if (field->type) + { + DEBUG (D_INIT, 4, "Checking for 'struct %s' : OK\n", + field->struct_name); + } + else + { + field->type = lookup_symbol (field->struct_name, + NULL, VAR_DOMAIN, NULL).symbol; + + if (field->type + && TYPE_CODE (check_typedef (SYMBOL_TYPE (field->type))) + != TYPE_CODE_STRUCT) + field->type = NULL; + + if (field->type != NULL) + DEBUG (D_INIT, 4, "Checking for 'struct %s' : TYPEDEF\n", + field->struct_name); + else + DEBUG (D_INIT, 1, "Checking for 'struct %s' : NOT FOUND\n", + field->struct_name); + } + + if (field->type == NULL + || !find_struct_field (check_typedef (SYMBOL_TYPE (field->type)), + field->field_name, &field->offset, &field->size)) + { + field->type = NULL; + if (!check) + error ("No such field %s::%s\n", field->struct_name, + field->field_name); + + return 0; + } + + /* Chain initialized entries for cleanup. */ + field->next = field_info; + field_info = field; + + DEBUG (D_INIT, 4, "%s::%s => offset %i size %i\n", field->struct_name, + field->field_name, field->offset, field->size); + return 1; +} + +/* Cleanup all the field and address info that has been gathered. */ +static void +fields_and_addrs_clear (void) +{ + struct field_info *next_field = field_info; + struct addr_info *next_addr = addr_info; + + while (next_field) + { + next_field = field_info->next; + field_info->type = NULL; + field_info->next = NULL; + field_info = next_field; + } + + while (next_addr) + { + next_addr = addr_info->next; + addr_info->bmsym.minsym = NULL; + addr_info->bmsym.objfile = NULL; + addr_info->next = NULL; + addr_info = next_addr; + } +} + +/* If this returns true, we want to access the target registers and + memory whatever the user nominated as the current task. */ +static int +thread_awareness_inhibited (void) +{ + return !lkd_params.enable_task_awareness + || _inhibit_thread_register_awareness; +} + +/* See the description of _inhibit_thread_register_awareness. */ +static void +thread_awareness_inhibit (void) +{ + ++_inhibit_thread_register_awareness; +} + +/* See the description of _inhibit_thread_register_awareness. */ +static void +thread_awareness_exhibit (void *unused) +{ + --_inhibit_thread_register_awareness; +} + +/* Remove the trailing white spaces from target-root-prefix. + FIXME: do this when the string is input, not all the time ! + */ +void +sanitize_path (char *path) +{ + char *dir = path + strlen (path) - 1; + while (dir > path && isspace (*dir)) + { + *dir-- = '\0'; + }; + /* remove trailing '/' too. + **/ + if (*dir == '/') + *dir = '\0'; +} + +char * +linux_aware_get_target_root_prefix (void) +{ + if (target_root_prefix_dirty && *target_root_prefix) + { + sanitize_path (*target_root_prefix); + target_root_prefix_dirty = 0; + } + return *target_root_prefix; +} + + +/****************************************************************************/ + +/* Reads the Linux version string from memory + * into global lkd_private.utsname_release. */ +static int +set_utsname_release (void) +{ + int i = 0; + CORE_ADDR release_addr; + asection *data; + int ret = 0; + int build = 0; + + gdb_assert (lkd_private.utsname_release); + + lkd_private.utsname_release[0] = '\0'; + lkd_private.utsname_release_valid = 0; + + /* Find the address of the string. */ + if (HAS_ADDR (init_uts_ns) + && HAS_FIELD (uts_namespace, name) && HAS_FIELD (new_utsname, release)) + release_addr = ADDR (init_uts_ns) + + F_OFFSET (uts_namespace, name) + F_OFFSET (new_utsname, release); + else if (HAS_ADDR (system_utsname) && HAS_FIELD (new_utsname, release)) + release_addr = ADDR (system_utsname) + F_OFFSET (new_utsname, release); + else + return -1; + + ret = target_read_memory (release_addr, + (gdb_byte *) (lkd_private.utsname_release), + lkd_private.utsname_release_size); + + lkd_private.utsname_release_valid = ret ? 0 : 1; + + return ret; +} + +/******************************************************************************/ +/***************** TASK AWARENESS ******************/ +/******************************************************************************/ + +/* target_ops callback that GDB queries to know if the given PTID is + still an active task. */ +static int +linux_aware_thread_alive (struct target_ops *ops, ptid_t ptid) +{ + /* if we are resetting the thread list, return false + * because shtdi will return true for the h/w thread + * and this would lead to keep a previous occurrence of the ptid. + */ + if (lkd_private.loaded == LKD_LOADING) + return 0; + + if (lkd_private.loaded == LKD_NOTLOADED) + { + if (BENEATH && BENEATH->to_thread_alive) + return BENEATH->to_thread_alive (ops, ptid); + return 0; /* GDB default */ + } + + return (lkd_proc_get_by_ptid (ptid) != NULL); +} + +/* target_ops callback that GDB queries to know core id of the given PTID. */ +static int +linux_aware_core_of_thread (struct target_ops *ops, ptid_t ptid) +{ + if (lkd_private.loaded == LKD_LOADED) + return lkd_proc_core_of_thread (ptid); + else if (BENEATH->to_core_of_thread) + return BENEATH->to_core_of_thread (ops, ptid); + + return CORE_INVAL; +} + +/* target_ops callback that GDB queries add new threads to its thread + list. */ +static void +linux_aware_update_thread_list (struct target_ops *ops) +{ + if (lkd_private.loaded != LKD_LOADED) + { + /* in case the user attaches, but did not yet set the target pack. */ + if (BENEATH->to_update_thread_list) + BENEATH->to_update_thread_list (ops); /* may not exist */ + } + else + lkd_proc_get_list (); +} + +/* + * the default post exec stop handler tries to activatie L-A + * if it previously failed + **/ +static void +linux_aware_post_exec_stop (int step) +{ + if (!step && lkd_private.connected /*make sure kernel code is in target-ram */ + && (lkd_private.loaded == LKD_NOTLOADED) + && (lkd_params.auto_activate == 1)) + { + CORE_ADDR pc = regcache_read_pc (get_current_regcache ()); + + /* if we broke in start_kernel, we may be about to execute + * the user "set" commands, do not rely on lkd_params yet. + **/ + if (pc == ADDR (start_kernel)) + return; + /* In case we need to delay set loaded by the time the mmu is correctly set, + * this will be set at latest by the time the user breaks + **/ + lkd_params.loaded = LKD_LOADED; + lkd_loaded_set (NULL, 0, NULL); + } +} + +extern void nullify_last_target_wait_ptid (void); + +static int +dump_thread_list (struct thread_info *tp, void *ignored) +{ + printf_filtered ("thread_list: {%d.%d}= {%d-%ld-%ld}\n", + tp->global_num, tp->per_inf_num, + ptid_get_pid (tp->ptid), + ptid_get_lwp (tp->ptid), ptid_get_tid (tp->ptid)); + return 0; +} + +void +lkd_reset_thread_list (void) +{ + struct thread_info *tp = NULL; + int pid = ptid_get_pid (inferior_ptid); + int loaded_state = lkd_private.loaded; + struct cleanup *cleanup; + + switch_to_thread (null_ptid); /* Ensure inferior_ptid is invalid. */ + + /* remove all gdb threads, we need to call this as + * gdb and complying targets assume there is always a thread + * but we need to manage our own thread numbering. + */ + init_thread_list (); + + DBG_IF (D_INIT) + iterate_over_threads (dump_thread_list, NULL); + DBG_ENDIF (D_INIT) + /* setup LKD "thread alive" method to return FALSE and "find new threads" + method to call the BENEATH version */ + lkd_private.loaded = LKD_LOADING; + + cleanup = make_cleanup_restore_integer (&print_thread_events); + print_thread_events = 0; + update_thread_list (); + do_cleanups (cleanup); + + DBG_IF (D_INIT) + iterate_over_threads (dump_thread_list, NULL); + DBG_ENDIF (D_INIT) + + tp = any_live_thread_of_process (pid); + gdb_assert (tp != NULL); /* A live thread must exist. */ + switch_to_thread (tp->ptid); + + /* make sure update_inferior_thread will not switch to a wait_ptid + * that is no-more upon resuming + */ + nullify_last_target_wait_ptid (); + + /* return LKD "thread alive" and "find new threads" methods to their + natural states */ + lkd_private.loaded = loaded_state; +} + +/* target_ops callback that GDB queries to translate a PTID to a human + readable string to display. */ +static char * +linux_aware_pid_to_str (struct target_ops *ops, ptid_t ptid) +{ + process_t *ps; + struct thread_info *tp; + + if (lkd_private.loaded == LKD_NOTLOADED) + { + if (BENEATH && BENEATH->to_pid_to_str) + return BENEATH->to_pid_to_str (ops, ptid); + return normal_pid_to_str (ptid); /* GDB default */ + } + + if (!ptid_get_tid (ptid)) /* when quitting typically */ + return "Linux Kernel"; + + tp = find_thread_ptid (ptid); + + if (!tp || !tp->priv) + return ""; + + /* we use the gdb thread private field for storing the process_t */ + ps = (process_t *) tp->priv; + + gdb_assert (ps->comm); + return ps->comm; +} + +/* extra informations with display options + **/ +static char * +extra_thread_info_ext (struct thread_info *thread) +{ + static char msg[256]; + char *pos = msg, *ret; + process_t *ps; + int core; + + if (!(tui_active || lkd_params.no_colors)) + pos += sprintf (pos, "\e[30m"); + else + *pos = '\0'; + ret = pos; /* skip color setting by default */ + + ps = (process_t *) thread->priv; + + DBG_IF (TASK) if (!ps) + { + printf_filtered ("thread_info %p not found in process_list\n", thread); + printf_filtered (" thread_info.ptid = {%d, %ld, %ld}\n", + ptid_get_pid (thread->ptid), + ptid_get_lwp (thread->ptid), + ptid_get_tid (thread->ptid)); + printf_filtered (" thread_info.num = %d.%d\n", thread->global_num, + thread->per_inf_num); + } + DBG_ENDIF (TASK) if (ps) + { + core = ps->core; + + DBG_IF (TASK) if (core != CORE_INVAL) + pos += sprintf (pos, + "lwp=%li, tid=%li, gdbt=%lx, task_str=%lx-%lx", + ptid_get_lwp (PTID_OF (ps)), + ptid_get_tid (PTID_OF (ps)), + (unsigned long) ps->gdb_thread, + (unsigned long) ps->task_struct, + (unsigned long) lkd_proc_get_rq_curr (core)); + else + pos += sprintf (pos, + "lwp=%li, tid=%li, gdbt=%lx, task_str=%lx", + ptid_get_lwp (PTID_OF (ps)), + ptid_get_tid (PTID_OF (ps)), + (unsigned long) ps->gdb_thread, + (unsigned long) ps->task_struct); + DBG_ELSE if (ps->tgid == ptid_get_lwp (PTID_OF (ps))) + /* thread group leader */ + pos += sprintf (pos, "TGID:%i", ps->tgid); + else + /* thread of a thread group */ + pos += sprintf (pos, "|----%li", ptid_get_lwp (PTID_OF (ps))); + DBG_ENDIF (TASK) if (lkd_proc_is_curr_task (ps)) + { + pos += sprintf (pos, " ", core); + + /* highlight currently running threads */ + if (!(tui_active || lkd_params.no_colors)) + { + ret = msg; + msg[3] = '1' + (2 * core); + strcpy (pos, "\e[m"); + } + } + } + + return ret; +} + +/* target_ops callback that GDB queries to get extra information to + display for a given thread. */ +static char * +linux_aware_extra_thread_info (struct target_ops *ops, + struct thread_info *thread) +{ + if (lkd_private.loaded == LKD_LOADED) + { + /* FIXME: Bodge for STWorkbench which expects different format */ + if (lkd_params.no_colors) + { + process_t *ps = (process_t *) thread->priv; + + if (ps) + { + static char msg[256]; + char *pos = msg; + + pos += sprintf (pos, "pid: %li tgid: %i", + ptid_get_lwp (PTID_OF (ps)), ps->tgid); + if (lkd_proc_is_curr_task (ps)) + sprintf (pos, " ", ps->core); + + return msg; + } + } + else + return extra_thread_info_ext (thread); + } + else if (BENEATH->to_extra_thread_info) + return BENEATH->to_extra_thread_info (ops, thread); + + return ""; +} + +static const char * +linux_aware_thread_name (struct target_ops *ops, struct thread_info *thread) +{ + /* All the thread name information has generally been + * returned already through the pid_to_str. + * + * We could refactor this around and 'correct' the naming + * but then you wouldn't get niceties such as + * [Switching to thread 52 (getty)] + */ + + return NULL; +} + +/* target_ops callback queried by GDB to read the registers of the + currently selected task. */ +static void +linux_aware_fetch_registers (struct target_ops *ops, + struct regcache *rc, int regno) +{ + struct cleanup *cleanup; + process_t *ps; + int res; + + DEBUG (TARGET, 2, "fetch_registers %i\n", regno); + + if ((lkd_private.loaded != LKD_LOADED) /*check this first */ + || !(ps = lkd_proc_get_by_ptid (inferior_ptid)) + || lkd_proc_is_curr_task (ps)) + return BENEATH->to_fetch_registers (ops, rc, regno); + + /* Call the platform specific code. */ + thread_awareness_inhibit (); + cleanup = make_cleanup (thread_awareness_exhibit, NULL); + res = + linux_awareness_ops->lo_fetch_context_register (regno, ps->task_struct); + do_cleanups (cleanup); + + if (!res) + warning ("Could not fetch task register."); + + return; +} + +/* target_ops callback queried by GDB to write the registers of the + currently selected task. */ +static void +linux_aware_store_registers (struct target_ops *ops, + struct regcache *rc, int regno) +{ + struct cleanup *cleanup; + process_t *ps; + int res; + + DEBUG (TARGET, 2, "store_registers %i\n", regno); + + if ((lkd_private.loaded != LKD_LOADED) /*check this first */ + || !(ps = lkd_proc_get_by_ptid (inferior_ptid)) + || lkd_proc_is_curr_task (ps)) + return BENEATH->to_store_registers (ops, rc, regno); + + /* Call the platform specific code. */ + thread_awareness_inhibit (); + cleanup = make_cleanup (thread_awareness_exhibit, NULL); + res = + linux_awareness_ops->lo_store_context_register (regno, ps->task_struct); + do_cleanups (cleanup); + + if (res) + warning ("Could not store task register."); + + return; +} + + +/* This is the target_ops callback that is called by GDB to start the + execution of the processor. */ +static void +linux_aware_resume (struct target_ops *ops, + ptid_t pid, int step, enum gdb_signal sig) +{ + struct cleanup *cleanup; + + DEBUG (TARGET, 1, "Resuming %i with sig %i (step %i)\n", + (int) ptid_get_pid (pid), (int) sig, step); + + /* Store the last execution request type. See the stepping + variable comment above, and the usage in linux_aware_wait. */ + lkd_stepping = step; + + /* Call platform dependant resume callback if needed. */ + if (linux_awareness_ops && linux_awareness_ops->lo_pre_exec_start) + { + thread_awareness_inhibit (); + cleanup = make_cleanup (thread_awareness_exhibit, NULL); + linux_awareness_ops->lo_pre_exec_start (); + do_cleanups (cleanup); + } + + if (lkd_private.loaded != LKD_LOADED) + return BENEATH->to_resume (ops, pid, step, sig); + + if (thread_event_do_exec_bp && thread_event_do_exec_return_bp) + { + delete_breakpoint (thread_event_do_exec_bp); + thread_event_do_exec_bp = NULL; + } + + lkd_uninstall_do_exit_event (); + + /* Before restarting first need to clear some caches. */ + thread_awareness_inhibit (); + cleanup = make_cleanup (thread_awareness_exhibit, NULL); + linux_awareness_ops->lo_clear_cache (); + do_cleanups (cleanup); + + /* Perform the execution request. */ + BENEATH->to_resume (ops, pid, step, sig); + + + /* Set the running flag. (See the variable's comment). */ + running = 1; +} + +/* This is the target_ops callback that is called by GDB to wait for the + processor to stop executing. */ +static ptid_t +linux_aware_wait (struct target_ops *ops, + ptid_t ptid, struct target_waitstatus *status, int opts) +{ + struct cleanup *cleanup; + ptid_t stop_ptid; + + /* We aren't running anymore. */ + running = 0; + + if (thread_awareness_inhibited () || !(lkd_private.kflags & KFLAG_DBGINFO)) + return BENEATH->to_wait (ops, ptid, status, opts); + + /* The linux aware wait begins here. */ + thread_awareness_inhibit (); + cleanup = make_cleanup (thread_awareness_exhibit, NULL); + + stop_ptid = BENEATH->to_wait (ops, ptid, status, opts); + if (max_cores > 1) + stop_core = ptid_get_tid (stop_ptid) - 1; + else + stop_core = 0; + + disable_terminate (); + + /*reset the inferior_ptid to the stopped ptid */ + inferior_ptid = stop_ptid; + + /* if we are not just stepping, check for auto-activation */ + linux_aware_post_exec_stop (lkd_stepping); + + if (lkd_private.loaded == LKD_LOADED) + { + CORE_ADDR pc; + CORE_ADDR task; + int i; + struct regcache *regcache; + + /* rescan for new task, but avoid storming the debug connection + **/ + lkd_proc_refresh_info (stop_core); + + /* The above calls might will end up accessing the registers + of the target because of inhibit_thread_awareness(). However, + this will populate a register cache associated with + inferior_ptid, which we haven't updated yet. Force a flush + of these cached values so that they end up associated to + the right context. */ + registers_changed (); + + /* This is normally done by infrun.c:handle_inferior_event (), + but we need it set to access the frames for some operations + below (eg. in check_exec_actions (), where we don't know + what the user will ask in his commands. */ + set_executing (minus_one_ptid, 0); + + regcache = get_thread_regcache (inferior_ptid); + + pc = regcache_read_pc (regcache); + + /* + * Handle the installation of the module's specific init routine hook. + **/ + thread_awareness_inhibit (); + make_cleanup (thread_awareness_exhibit, NULL); + + /* pull-in the symbols of each core's running process if auto-debug + * is activated + **/ + target_root_prefix_dirty = 1; // fixme: code a proper command handler. + + /* wait_process is non-null once we've successfully loaded lkd, + * and we could compute the current process for that core. + **/ + if (wait_process) + { + inferior_ptid = PTID_OF (wait_process); + stop_ptid = inferior_ptid; + } + } + + do_cleanups (cleanup); + + enable_terminate (); + + return stop_ptid; +} + +/* + * arch-common post load op + **/ + +/* + * try to find a BFD + **/ +static bfd * +get_cur_bfd (int from_tty) +{ + /*give precedence to symfile */ + if (symfile_objfile && symfile_objfile->obfd) + return symfile_objfile->obfd; + + if (from_tty && !exec_bfd) + printf_filtered ("No executable file specified\n"); + + return exec_bfd; +} + +/* the post load hook, and the callback to re-install + * what ever LKD patches into the gdb arch.*/ +static void +linux_aware_post_load (char *prog, int fromtty) +{ + DEBUG (D_INIT, 1, "linux_aware_POST_load %s\n", prog); + + /*let the arch specific part decide when loaded = 1 */ + if (linux_awareness_ops->lo_post_load) + linux_awareness_ops->lo_post_load (prog, fromtty); + +} + +/* The target_ops callback called by GDB to load the debugged program + to the target. Just a wrapper for BENEATH->to_load with hooks that + call into the platform specific part. */ +static void +linux_aware_load (struct target_ops *ops, const char *prog, int fromtty) +{ + DEBUG (D_INIT, 1, "linux_aware_load %s\n", prog); + + /* make sure the load conditions are met, so that putting + 'set linux-awareness loaded 1' in his .shgdbinit file does not crash. */ + + if ((BENEATH->to_shortname[0] == 'e' /*exec */ ) + || (BENEATH->to_shortname[0] == 'n' /*none */ )) + { + execute_command ("maint print target-stack", 0); + error ("underlying target is not valid (might be exec or none).\n"); + } + + BENEATH->to_load (ops, prog, fromtty); + + lkd_params.loaded = LKD_NOTLOADED; + lkd_private.loaded = LKD_NOTLOADED; + + if (lkd_params.auto_activate) + { + lkd_params.loaded = LKD_LOADED; + lkd_loaded_set (0, 0, 0); + } + + lkd_private.connected = 1; +} + +/* The target_ops callback called by GDB to load the attach to an + already running program. Just sets 'loaded' to 1, as the program is + already loaded. If you attach with a non standard command, you have + to do 'set linux-awareness loaded 1' by hand. */ +static void +linux_aware_attach (struct target_ops *ops, const char *prog, int fromtty) +{ + DEBUG (D_INIT, 1, "linux_aware_attach %s\n", prog); + + if (BENEATH && BENEATH->to_attach != NULL) + BENEATH->to_attach (ops, prog, fromtty); + + if (lkd_params.auto_activate) + { + lkd_params.loaded = LKD_LOADED; + lkd_loaded_set (0, 0, 0); + } + + lkd_private.connected = 1; + DEBUG (TARGET, 3, "end linux_aware_attach %s\n", prog); +} + +/* Used to trigger the disconnection. */ +static void +linux_aware_disconnect (struct target_ops *target, const char *args, + int from_tty) +{ + DEBUG (D_INIT, 1, "linux_aware_disconnect\n"); + + unpush_target (&linux_aware_ops); + target_disconnect (args, from_tty); + + lkd_private.connected = 0; +} + +/* This callback is called on 'normal stop', ie. when the user gets + the control back. We do various bookkeeping at this point. */ +static void +normal_stop_callback (struct bpstats *bs, int unsused) +{ + /* If there's no userspace breakpoint left, remove the breakpoints + we use to notify about conditions we need to handle like 'out of + memory' or 'process exit'. */ + if (thread_event_low_mem_bp != NULL) + { + int has_bp = 0; + struct bp_location *loc; + struct breakpoint *bp; + + ALL_BREAKPOINTS (bp) for (loc = bp->loc; loc; loc = loc->next) + { + if (loc->address + && linux_awareness_ops->lo_is_user_address (loc->address)) + { + has_bp = 1; + break; + } + } + + if (!has_bp) + { + if (thread_event_low_mem_bp != NULL) + { + delete_breakpoint (thread_event_low_mem_bp); + thread_event_low_mem_bp = NULL; + } + } + } +} + +/* target_has_all_memory() target_ops callback. + Note that if a beneath target exists then return 0 to indicate that the + beneath target to be used if this target cannot handle the request. */ +static int +linux_aware_has_all_memory (struct target_ops *ops) +{ + if (BENEATH && BENEATH->to_has_all_memory) + return 0; + return default_child_has_all_memory (ops); +} + +/* target_has_memory() target_ops callback. */ +static int +linux_aware_has_memory (struct target_ops *ops) +{ + if (BENEATH && BENEATH->to_has_memory) + return BENEATH->to_has_memory (ops); + return default_child_has_memory (ops); +} + +/* target_has_stack() target_ops callback. */ +static int +linux_aware_has_stack (struct target_ops *ops) +{ + if (BENEATH && BENEATH->to_has_stack) + return BENEATH->to_has_stack (ops); + return default_child_has_stack (ops); +} + +/* target_has_registers() target_ops callback. */ +static int +linux_aware_has_registers (struct target_ops *ops) +{ + if (BENEATH && BENEATH->to_has_registers) + return BENEATH->to_has_registers (ops); + return default_child_has_registers (ops); +} + +/* target_has_execution() target_ops callback. */ +static int +linux_aware_has_execution (struct target_ops *ops, ptid_t ptid) +{ + if (BENEATH && BENEATH->to_has_execution) + return BENEATH->to_has_execution (ops, ptid); + return default_child_has_execution (ops, ptid); +} + +/* target_close() target_ops callback. */ +static void +linux_aware_close (struct target_ops *ops) +{ + struct target_waitstatus dummy; + + DEBUG (D_INIT, 1, "Closing... \n"); + + /* We might be called by a signal handler */ + if (running) + { + target_stop (inferior_ptid); + if (BENEATH != NULL && BENEATH->to_wait != NULL) + BENEATH->to_wait (&linux_aware_ops, minus_one_ptid, &dummy, 0); + } + + lkd_params.enabled = 0; + lkd_enabled_set (0, 0, 0); + + if (normal_stop_observer) + { + observer_detach_normal_stop (normal_stop_observer); + normal_stop_observer = NULL; + } + + wait_process = NULL; + + _inhibit_thread_register_awareness = 0; + + if (thread_event_low_mem_bp) + { + delete_breakpoint (thread_event_low_mem_bp); + thread_event_low_mem_bp = NULL; + } + + + lkd_proc_free_list (); + + fields_and_addrs_clear (); + + if (running) + { + /* If we leave the board run, we'd better remove breakpoints + so that it's functional. */ + remove_breakpoints (); + + if (BENEATH != NULL && BENEATH->to_resume != NULL) + BENEATH->to_resume (&linux_aware_ops, inferior_ptid, 0, 0); + } + + lkd_private.banner_file_valid = 0; + lkd_private.banner_mem_valid = 0; + running = 0; +} + +static void +linux_aware_files_info (struct target_ops *target) +{ + printf_filtered (_("Connected to remote linux kernel\n")); +} + +static int +linux_aware_can_async_p (struct target_ops *ops) +{ + return 0; +} + +static int +linux_aware_is_async_p (struct target_ops *ops) +{ + return 0; +} + +/* Setup the target_ops callbacks. */ +static void +init_linux_aware_target (void) +{ + DEBUG (D_INIT, 1, "init_linux_aware_target\n"); + + linux_aware_ops.to_shortname = "linux-aware"; + linux_aware_ops.to_longname = "Linux-aware target interface"; + linux_aware_ops.to_doc = linux_awareness_doc; + + /* Dirty hook to stack above anythin else, event something above + the thread stratum (like starm) */ + linux_aware_ops.to_stratum = LKD_STRATUM_LINUX; + + linux_aware_ops.to_load = linux_aware_load; + linux_aware_ops.to_close = linux_aware_close; + linux_aware_ops.to_attach = linux_aware_attach; + linux_aware_ops.to_disconnect = linux_aware_disconnect; + linux_aware_ops.to_magic = OPS_MAGIC; + + + /* Registers */ + linux_aware_ops.to_fetch_registers = linux_aware_fetch_registers; + linux_aware_ops.to_store_registers = linux_aware_store_registers; + + /* Execution */ + linux_aware_ops.to_resume = linux_aware_resume; + linux_aware_ops.to_wait = linux_aware_wait; + + /* Threads */ + linux_aware_ops.to_thread_alive = linux_aware_thread_alive; + linux_aware_ops.to_update_thread_list = linux_aware_update_thread_list; + linux_aware_ops.to_pid_to_str = linux_aware_pid_to_str; + linux_aware_ops.to_extra_thread_info = linux_aware_extra_thread_info; + linux_aware_ops.to_thread_name = linux_aware_thread_name; + linux_aware_ops.to_core_of_thread = linux_aware_core_of_thread; + linux_aware_ops.to_has_thread_control = tc_none; + + linux_aware_ops.to_has_all_memory = linux_aware_has_all_memory; + linux_aware_ops.to_has_memory = linux_aware_has_memory; + linux_aware_ops.to_has_stack = linux_aware_has_stack; + linux_aware_ops.to_has_registers = linux_aware_has_registers; + linux_aware_ops.to_has_execution = linux_aware_has_execution; + linux_aware_ops.to_files_info = linux_aware_files_info; + + /* Prevent Async operations + * LKD doesn't yet support ASync, + * Particularly on connect/resume, which can break things + * when connecting to an async target such as QEmu + */ + linux_aware_ops.to_can_async_p = linux_aware_can_async_p; + linux_aware_ops.to_is_async_p = linux_aware_is_async_p; +} + + + +/* This function is here to replace the default display for + breakpoints set on code that has been unloaded. This display + routine doesn't display the 0xFFFFFFFF that could confuse the + user. */ +static void +init_bp_mention (struct breakpoint *bpt) +{ + bpt->ops = NULL; + printf_filtered (_("Breakpoint %d (%s) pending."), + bpt->number, event_location_to_string (bpt->location)); +} + +static struct breakpoint_ops init_breakpoints_ops = { + .print_mention = init_bp_mention +}; + +static void +lkd_install_do_exit_event (void) +{ +/* install hook for do_exit */ + if (thread_event_do_exit_bp == NULL) + { + thread_event_do_exit_bp = + create_thread_event_breakpoint (target_gdbarch (), ADDR (do_exit)); + + /*do no remove on resume */ + lkd_private.keep_do_exit_event = 1; + } +} + +void +lkd_uninstall_do_exit_event (void) +{ + /* uninstall hook for do_exit */ + if ((thread_event_do_exit_bp != NULL) && (!lkd_private.keep_do_exit_event)) + { + delete_breakpoint (thread_event_do_exit_bp); + thread_event_do_exit_bp = NULL; + } +} + +/* This callback is called when a new breakpoint is created. */ +static void +linux_aware_create_breakpoint_hook (struct breakpoint *bpt) +{ + struct lm_info *info; + + if (lkd_private.loaded != LKD_LOADED) + return; + + + if (bpt->loc && bpt->loc->address == ~(CORE_ADDR) 0) + { + warning + ("You have inserted a breakpoint on a location that is not currently\n" + "mapped to memory (it is flagged as __init code and the initialization\n" + "phase of the module has completed). The breakpoint will be reset if you\n" + "reload the module."); + /* Display correct breakpoint info and disable the breakpoint. */ + bpt->ops = &init_breakpoints_ops; + bpt->loc->shlib_disabled = 1; + } + else if (bpt->loc + && bpt->loc->address + && bpt->loc->loc_type == bp_loc_software_breakpoint + && linux_awareness_ops->lo_is_user_address (bpt->loc->address)) + { + CORE_ADDR addr = bpt->loc->address; + + /* All the userspace breakpoints are set to a specific task. */ + bpt->thread = ptid_to_global_thread_id (inferior_ptid); + + + lkd_install_do_exit_event (); + + /*Q: does setting a bpt to a usermode pages prevent Linux + * to unmap it ? This seems to say so.*/ + if (HAS_ADDR (try_to_unmap)) + { + if (thread_event_low_mem_bp == NULL) + thread_event_low_mem_bp = + create_thread_event_breakpoint + (target_gdbarch (), ADDR (try_to_unmap)); + } + else + warning ("'try_to_unmap' wasn't found."); + } +} + + +static char * +get_banner_from_file (bfd * cur_bfd) +{ + CORE_ADDR banner_addr = ADDR (linux_banner); + CORE_ADDR section_addr, section_size; + asection *data; + + gdb_assert (lkd_private.banner_file); + + if (lkd_private.banner_file_valid) + return lkd_private.banner_file; + + lkd_private.banner_file_valid = 1; + + /* first try to find linux_banner in .rodata */ + data = bfd_get_section_by_name (cur_bfd, ".rodata"); + if (data) + { + section_addr = bfd_get_section_vma (cur_bfd, data); + section_size = bfd_get_section_size (data); + if ((banner_addr < section_addr) + || (banner_addr >= section_addr + section_size)) + data = NULL; + } + + if (!data) + { + /* then, try .text */ + data = bfd_get_section_by_name (cur_bfd, ".text"); + if (data) + { + section_addr = bfd_get_section_vma (cur_bfd, data); + section_size = bfd_get_section_size (data); + if ((banner_addr < section_addr) + || (banner_addr >= section_addr + section_size)) + data = NULL; + } + } + + if (data) + { + int i = 0; + char c; + bfd_seek (cur_bfd, + data->filepos + banner_addr - + bfd_get_section_vma (cur_bfd, data), SEEK_SET); + do + { + bfd_bread (&c, 1, cur_bfd); + lkd_private.banner_file[i++] = c; + } + while ((c != '\0') && (i < lkd_private.banner_file_size)); + } + else + { + printf_filtered ("Linux banner not found in any of .text or .rodata\n"); + lkd_private.banner_file_valid = 0; + } + + /* for security */ + lkd_private.banner_file[lkd_private.banner_file_size - 1] = '\0'; + + DEBUG (D_INIT, 1, "Got banner from file: %s\n", lkd_private.banner_file); + + return lkd_private.banner_file; +} + +static char * +get_banner (void) +{ + if (lkd_private.banner_mem_valid) + return lkd_private.banner_mem; + + gdb_assert (lkd_private.banner_mem); + + lkd_private.banner_mem_valid = 1; + + read_memory_string (ADDR (linux_banner), + lkd_private.banner_mem, lkd_private.banner_mem_size); + + lkd_private.banner_mem[lkd_private.banner_mem_size - 1] = '\0'; + + DEBUG (D_INIT, 1, "Read banner from mem: %s\n", lkd_private.banner_mem); + + return lkd_private.banner_mem; +} + + + + +/******************************************************************************/ +/************* LINUX AWARENESS INIT / AUTODETECTION / ENABLEMENT ***********/ +/******************************************************************************/ +int +lkd_try_push_target (void) +{ + DEBUG (D_INIT, 1, "lkd_try_push_target\n"); + + if (!BENEATH) + push_target (&linux_aware_ops); + + return LKD_LOADED; +} + +/* This function is called after load, or after attach, when we know + that the kernel code is in memory. (This might be called direclty + by the user by issuing 'set linux-awareness loaded', if he doesn't + use a standard attach mechanism. */ +void +lkd_loaded_set (char *arg, int from_tty, struct cmd_list_element *c) +{ + bfd *cur_bfd; + process_t *ps; + char *banner1; + char *banner2, *file_name; + int i; + + DEBUG (D_INIT, 1, "lkd_loaded_set\n"); + + if (lkd_params.loaded == lkd_private.loaded) + return; + + if ((lkd_params.loaded == LKD_LOADED) && (!lkd_params.enabled)) + { + lkd_enabled_set ((char *) 1, 0, NULL); + /* Could not enable so not debugging a kernel */ + if (!lkd_params.enabled) + goto __sl_fail; + } + + stop_core = 0; + cur_bfd = get_cur_bfd (from_tty); + + /* if user forces loaded = off, also remove auto-load + **/ + if (lkd_params.loaded == LKD_NOTLOADED) + { + struct cleanup *cleanup; + + /* Before switching off first need to clear some caches. */ + thread_awareness_inhibit (); + cleanup = make_cleanup (thread_awareness_exhibit, NULL); + linux_awareness_ops->lo_clear_cache (); + do_cleanups (cleanup); + + lkd_private.loaded = LKD_NOTLOADED; + + if (lkd_params.auto_activate) + { + lkd_params.auto_activate = 0; + if (from_tty) + printf_filtered + ("(also disabling linux-awareness auto-activation)\n"); + } + + lkd_proc_free_list (); + + /* fallback to any thread that makes sense for the beneath target */ + lkd_reset_thread_list (); + + return; + } + + /* if the user want to set loaded = on, do some sanity checks + **/ + if (lkd_params.loaded == LKD_LOADED) + { + /* check if symbol-file was set first */ + if (!get_cur_bfd (from_tty)) + goto __sl_fail; + + cur_bfd = get_cur_bfd (from_tty); + banner2 = get_banner_from_file (cur_bfd); + file_name = bfd_get_filename (cur_bfd); + + lkd_private.target_pointer_type = + builtin_type (target_gdbarch ())->builtin_data_ptr; + + if (linux_awareness_ops->lo_pre_load) + linux_awareness_ops->lo_pre_load (file_name, from_tty); + + lkd_private.loaded = LKD_LOADING; + + gdb_assert (lkd_params.enabled); + + if (!(lkd_private.kflags & KFLAG_DBGINFO)) + { + warning + ("\"set loaded\" failed because kernel has no debug info.\n"); + goto __sl_fail; + } + + if (lkd_try_push_target () != LKD_LOADED) + { + warning + ("\"set loaded\" failed because L-A target could not be pushed.\n"); + goto __sl_fail; + } + + + /* (re)init the thread_list with hardware threads */ + lkd_proc_init (); + + /*do arch-specific fixup (mmu for instance) */ + linux_aware_post_load (file_name, 0); + + /*needs access to mem */ + if (set_utsname_release () != 0 /*EOK*/) + { + warning + ("\"set loaded\" failed because L-A target could not access target memory.\n"); + goto __sl_fail; + } + + /*get banner from mem */ + banner1 = get_banner (); + + /* Check that the kernel in memory corresponds to the + * binary file we were given. + */ + if (banner1 == NULL || banner2 == NULL || strcmp (banner1, banner2)) + { + if (!nquery + ("Kernel banner in debugger file: \n%s\n" + "Kernel banner in target memory: \n%s\n" + "WARNING: do you want to continue ?\n" + "(if the kernels don't match the debugger might crash)", + banner2, banner1)) + { + error ("Aborted (kernel banner mismatch)."); + goto __sl_fail; + } + } + + lkd_proc_invalidate_list (); + + /* scan the linux threads */ + + if (!lkd_proc_refresh_info (stop_core)) + { + if (from_tty) + printf_filtered ("failed: has this kernel started?\n"); + goto __sl_fail; + } + + lkd_proc_set_symfile (); + + + lkd_private.loaded = lkd_params.loaded; + } + + printf_filtered ("Kernel image version: %s\n", lkd_private.utsname_release); + + return; + +__sl_fail: + /* silently fail, we retry later. + **/ + lkd_params.loaded = LKD_NOTLOADED; + lkd_private.loaded = LKD_NOTLOADED; + return; +} + +/* Helper for linux_awareness_fix_debug_info() that locates the lowest + section in a BFD. */ +static void +find_min_load_addr (bfd * abfd, asection * sectp, void *addr) +{ + CORE_ADDR *min_addr = addr; + CORE_ADDR vma; + + if (!(bfd_get_section_flags (abfd, sectp) & SEC_ALLOC)) + return; + if (!(bfd_get_section_flags (abfd, sectp) & SEC_HAS_CONTENTS)) + return; + + vma = bfd_get_section_vma (abfd, sectp); + if (vma < *min_addr) + *min_addr = vma; +} + +/* + * The functionalities that can be built as modules often have a + * cleanup routine (marked with module_exit). When the driver is built + * into the kernel (i.e. not as a module), the cleanup routines aren't + * linked in, but their debug information remains... These routines + * all have very low addresses (as they haven't been relocated). This + * functions try to partially fix the debug info, so that the psymtabs + * don't advertise a too wide range of text addresses. + */ +static void +linux_awareness_fix_debug_info (void) +{ + CORE_ADDR min_load_addr = (CORE_ADDR) - 1; + struct partial_symtab *pst; + + bfd_map_over_sections (symfile_objfile->obfd, find_min_load_addr, + &min_load_addr); + + ALL_OBJFILE_PSYMTABS (symfile_objfile, pst) + { + if (pst->textlow < min_load_addr) + pst->textlow = min_load_addr; + } +} + +/* GDB wants the stack it reads to have a strictly decreasing SP. This + relation isn't true when the backtrace goes from kerne- to + user-space. Overload the inner_than method to hide this + peculiarity. */ +static int +linux_aware_inner_than (CORE_ADDR lhs, CORE_ADDR rhs) +{ + if (linux_awareness_ops->lo_is_kernel_address (rhs) + && linux_awareness_ops->lo_is_user_address (lhs)) + return 0; + + return core_addr_lessthan (lhs, rhs); +} + +void +set_skip_schedule_frame (char *arg, int from_tty, struct cmd_list_element *c) +{ + reinit_frame_cache (); +} + + +/* Function called to init the linux awareness layer once we know + we're debugging a known kernel. */ +static void +linux_awareness_init (void) +{ + struct cmd_list_element *c; + const char *linux_awareness_postinit = "linux-awareness-postinit"; + + DEBUG (D_INIT, 1, "linux_awareness_init\n"); + + /* Stack our layer over the real target stack. */ + lkd_try_push_target (); + + /* this may set the OS ABI (reset the solib ops too!) */ + linux_awareness_ops->lo_init (); + + printf_filtered ("Enabling Linux Kernel Debugger %s build %s.\n", + LKD_VERSION_STRING, __DATE__); + + /* Set this as early as we can */ + lkd_private.target_pointer_type = + builtin_type (target_gdbarch ())->builtin_data_ptr; + + /* Init some data structures. */ + linux_awareness_fix_debug_info (); + + /* Register the various callbacks we use. */ + normal_stop_observer = observer_attach_normal_stop (normal_stop_callback); + observer_attach_breakpoint_created (linux_aware_create_breakpoint_hook); + + + set_gdbarch_inner_than (target_gdbarch (), linux_aware_inner_than); + + + add_info_alias ("tasks", "threads", 0); + add_com_alias ("task", "thread", class_run, 0); + + add_com ("running_task", class_lkd, running_task_command, + "Switch to the currently running task."); + + + add_setshow_integer_cmd ("skip_schedule_frame", + class_lkd, + &lkd_params.skip_schedule_frame, + "Set whether the debugger should hide the schedule() frame for sleeping tasks", + "Show whether the debugger should hide the schedule() frame for sleeping tasks", + "Typical value is between 0 for full stack to 4", + &set_skip_schedule_frame, NULL, + &set_linux_awareness_cmd_list, + &show_linux_awareness_cmd_list); + + + /* Call the user-defined linux_awareness_postinit command if it + exists. (Allows the user to put code in his .gdbinit that will + be run only if the layer is loaded). */ + c = lookup_cmd (&linux_awareness_postinit, cmdlist, "", 1, 1); + if (c != NULL && c->theclass == class_user) + execute_user_command (c, 0); + +} + +/* Helper for the autoactivation symbol lookup. */ +static inline int +linux_awareness_lookup_symbol (const char *name) +{ + int found = lookup_minimal_symbol (name, NULL, NULL).minsym != NULL; + + if (!found) + DEBUG (D_INIT, 1, "Symbol '%s' not found\n", name); + else + DEBUG (D_INIT, 4, "Symbol '%s' found\n", name); + + return found; +} + +/* Helper for the autoactivation symbol lookup. */ +static inline int +linux_awareness_lookup_symtab (const char *name) +{ + int found = lookup_symtab (name) != NULL; + + if (!found) + DEBUG (D_INIT, 1, "Symtab '%s' not found\n", name); + else + DEBUG (D_INIT, 4, "Symtab '%s' found\n", name); + + return found; +} + +/* make sure we have a kernel objfile */ +static int +linux_awareness_check (void) +{ + int build = 0; + int check = 0; + + DEBUG (D_INIT, 1, "linux_awareness_check\n"); + + /* reset flags */ + lkd_private.kflags &= ~(KFLAG_LINUX | KFLAG_DBGINFO); + + /* Look for some specific Linux symbols. */ + if (!linux_awareness_lookup_symbol ("schedule") + || !linux_awareness_lookup_symbol ("linux_banner")) + return 0; /* KO */ + + /* Make sure we have an architecture layer */ + if (!linux_awareness_ops) + { + /* More verbose here, as we believe we are looking at a kernel, + * and the --enable-linux-awareness configure flag has been set */ + warning ("Architecture Layer Missing. Can't enable linux awareness"); + return 0; + } + + lkd_private.kflags |= KFLAG_LINUX; + + /* More checks. */ + lookup_symtab ("page_io.c"); + + /* check for mandatory structure and fields */ + + check = HAS_FIELD (pid_namespace, last_pid) + && HAS_FIELD (task_struct, nsproxy); + + if (!check) /*look for older kernels */ + check = HAS_FIELD (list_head, next) && HAS_FIELD (task_struct, namespace); + + check = check && HAS_FIELD (task_struct, children) + && HAS_FIELD (task_struct, sibling) + && HAS_FIELD (task_struct, thread_group) + && HAS_FIELD (task_struct, pid) + && HAS_FIELD (task_struct, tgid) + && HAS_FIELD (task_struct, comm) + && HAS_FIELD (thread_info, preempt_count); + + /* load some data that GDB seems to loose otherwise */ + if (check && linux_awareness_lookup_symtab ("mmap.c") + && linux_awareness_lookup_symtab ("fork.c") + && linux_awareness_lookup_symtab ("block_dev.c") + && linux_awareness_lookup_symtab ("vmalloc.c") + && linux_awareness_lookup_symtab ("page_alloc.c") + && linux_awareness_ops->lo_check_kernel ()) + lkd_private.kflags |= KFLAG_DBGINFO; + else + warning ("debug information missing."); + + return 1; /* OK */ +} + +/* Function called when we enable or disable the linux awareness + layer. */ +void +lkd_enabled_set (char *args, int from_tty, struct cmd_list_element *c) +{ + static int stored_state = 0; + + DEBUG (D_INIT, 1, "lkd_enabled_set\n"); + + /*if not from tty, we can use args as a parameter. */ + if ((!from_tty) && ((uintptr_t) args == 1)) + lkd_params.enabled = 1; + + if (lkd_params.enabled == stored_state) + return; + + if (lkd_params.enabled == 1) + { + + /* make sure that a bfd is available, + * reevaluate the current objfile + */ + if (linux_awareness_check () && get_cur_bfd (from_tty)) + linux_awareness_init (); + else + { + /* bare machine debuggee, do not auto-enable + * but allow bold user commands...*/ + lkd_params.enabled = 0; + + /* try to push the target anyway, + * so that we get called into to_load */ + if (!BENEATH) + push_target (&linux_aware_ops); + + if (from_tty) + warning ("Could not enable linux-awareness: no objfile ?"); + } + + } + else if (!ptid_equal (inferior_ptid, null_ptid)) /*if not `mourned`already */ + { + struct target_waitstatus dummy; + struct thread_info *tp; + + + /* this will reinit the thread_list. + * and record the current "loaded" status. + **/ + lkd_params.loaded = LKD_NOTLOADED; + lkd_loaded_set (NULL, 0, NULL); + } + + stored_state = lkd_params.enabled; +} + +/* This callback is called each time the user loads a new executable + in GDB, and it tries to determine if it's a Linux kernel. If it's + the case, it loads the linux awareness layer. */ +static void +linux_awareness_on_new_objfile (struct objfile *objf) +{ + DEBUG (D_INIT, 1, "linux_awareness_on_new_objfile\n"); + + /* try pushing target and enabling at least. + **/ + if (lkd_params.auto_activate) + lkd_enabled_set ((char *) 1, 0, 0); +} + +static void +set_linux_awareness (char *arg, int from_tty) +{ + printf_unfiltered + ("'set linux-awareness' must be followed by the name of a print subcommand.\n"); + help_list (set_linux_awareness_cmd_list, "set linux-awareness ", -1, + gdb_stdout); +} + +static void +show_linux_awareness (char *args, int from_tty) +{ + cmd_show_list (show_linux_awareness_cmd_list, from_tty, ""); +} + +static void +set_global_loglevel (char *arg, int from_tty, struct cmd_list_element *c) +{ + struct debug_domain *domain = linux_aware_debug_domains_info; + + while (domain->name != NULL) + domain++->level = lkd_params.loglevel; +} + +volatile int stop_loop = 1; + +/* initialize private data + * when will gdb go OOP at least !?? + **/ +static void +init_private_data (void) +{ + lkd_private.string_buf_size = 4096; + lkd_private.string_buf = + xcalloc (lkd_private.string_buf_size, sizeof (char)); + lkd_private.banner_file_size = 256; + lkd_private.banner_file = + xcalloc (lkd_private.banner_file_size, sizeof (char)); + lkd_private.banner_file_valid = 0; + lkd_private.banner_mem_size = 256; + lkd_private.banner_mem = + xcalloc (lkd_private.banner_mem_size, sizeof (char)); + lkd_private.banner_mem_valid = 0; + lkd_private.utsname_release_size = 256; + lkd_private.utsname_release = + xcalloc (lkd_private.banner_mem_size, sizeof (char)); + lkd_private.utsname_release_valid = 0; + lkd_private.kflags = KFLAG_NOLINUX; +} + +static void +linux_awareness_inferior_created (struct target_ops *ops, int from_tty) +{ + DEBUG (D_INIT, 1, "linux_awareness_inferior_created\n"); + if (lkd_params.auto_activate) + lkd_enabled_set ((char *) 1, 0, 0); +} + +static ptid_t target_thread_ptid; + +static void +linux_awareness_target_thread_changed (ptid_t ptid) +{ + DEBUG (D_INIT, 1, "linux_awareness_target_thread_changed {%d, %ld, %ld}\n", + ptid_get_pid (ptid), ptid_get_lwp (ptid), ptid_get_tid (ptid)); + + if (ptid_equal (ptid, null_ptid) || ptid_equal (ptid, minus_one_ptid)) + target_thread_ptid = null_ptid; + else if (ptid_get_tid (ptid) != CORE_INVAL) + target_thread_ptid = ptid; +} + +int +linux_aware_target_core (void) +{ + gdb_assert (!ptid_equal (target_thread_ptid, null_ptid)); + + return ptid_get_tid (target_thread_ptid) - 1; +} + +/* -Wmissing-prototypes */ +extern initialize_file_ftype _initialize_linux_awareness; + +/* Function called automatically by GDB on each start. */ +void +_initialize_linux_awareness (void) +{ + struct debug_domain *domain; + + while (!stop_loop); + + linux_uprocess_objfile_data_key = register_objfile_data (); + + init_private_data (); + + target_thread_ptid = null_ptid; + + observer_attach_inferior_created (linux_awareness_inferior_created); + observer_attach_new_objfile (linux_awareness_on_new_objfile); + observer_attach_target_thread_changed + (linux_awareness_target_thread_changed); + + init_linux_aware_target (); + add_target (&linux_aware_ops); + + target_root_prefix = &gdb_sysroot; + + add_prefix_cmd ("linux-awareness", + class_lkd, + set_linux_awareness, + "Command for setting linux-awareness variables", + &set_linux_awareness_cmd_list, + "set linux-awareness ", 0, &setlist); + + add_prefix_cmd ("linux-awareness", + class_lkd, + show_linux_awareness, + "Command for showing linux-awareness variables", + &show_linux_awareness_cmd_list, + "show linux-awareness ", 0, &showlist); + + add_setshow_boolean_cmd ("enabled", + class_lkd, + &lkd_params.enabled, + "Set the activation state of the the linux " + "awareness layer", + "Show the activation state of the the linux " + "awareness layer", + NULL, &lkd_enabled_set, NULL, + &set_linux_awareness_cmd_list, + &show_linux_awareness_cmd_list); + + add_setshow_boolean_cmd ("loaded", class_lkd, (int *) &lkd_params.loaded, /*warn: can be '2' to detect transient states */ + "Set the loaded state of the kernel image", + "Show the loaded state of the kernel image", + NULL, &lkd_loaded_set, NULL, + &set_linux_awareness_cmd_list, + &show_linux_awareness_cmd_list); + + domain = linux_aware_debug_domains_info; + + while (domain->name != NULL) + { + static const char fmt[] = + "%s the debug level of the linux awareness " "layer %s part."; + const char *name = domain->name + 6; /* Skip debug- */ + char *help_set = xstrprintf (fmt, "Set", name); + char *help_show = xstrprintf (fmt, "Show", name); + add_setshow_zinteger_cmd ((char *) domain->name, + class_lkd, + &(domain->level), + help_set, + help_show, + NULL, + NULL, NULL, + &set_linux_awareness_cmd_list, + &show_linux_awareness_cmd_list); + xfree (help_set); + xfree (help_show); + ++domain; + } + + add_setshow_zinteger_cmd ("debug-all", + class_lkd, + &lkd_params.loglevel, + "Set the debug level of the linux awareness " + "layer", + "Show the debug level of the linux awareness " + "layer", + NULL, + &set_global_loglevel, NULL, + &set_linux_awareness_cmd_list, + &show_linux_awareness_cmd_list); + + add_setshow_boolean_cmd ("enable_task_awareness", + class_lkd, + &lkd_params.enable_task_awareness, + "Set whether we implement task awareness", + "Show whether we implement task awareness", + NULL, NULL, NULL, + &set_linux_awareness_cmd_list, + &show_linux_awareness_cmd_list); + + add_setshow_boolean_cmd ("auto_activate", + class_lkd, + &lkd_params.auto_activate, + "Set whether we try to autodetect linux kernels.", + "Show whether we try to autodetect linux kernels.", + NULL, NULL, NULL, + &set_linux_awareness_cmd_list, + &show_linux_awareness_cmd_list); + + add_setshow_boolean_cmd ("no-colors", + class_lkd, + &lkd_params.no_colors, + "Set disable thread info coloring.", + "Show thread info coloring status.", + NULL, NULL, NULL, + &set_linux_awareness_cmd_list, + &show_linux_awareness_cmd_list); +} + +/******************************* from shtdi.c *********************************/ + +static void +terminate_once (int signo) +{ + signal (signo, SIG_IGN); /* Ignore further signals */ + terminate_requested = signo; +} + +static void +disable_terminate (void) +{ + terminate_requested = 0; /* Reset */ + old_terminate_handler = signal (SIGTERM, terminate_once); + old_interrupt_handler = signal (SIGINT, terminate_once); +} + +static void +enable_terminate (void) +{ + signal (SIGTERM, old_terminate_handler); + signal (SIGINT, old_interrupt_handler); + if (terminate_requested) + raise (terminate_requested); +} diff --git a/gdb/lkd/lkd-process.c b/gdb/lkd/lkd-process.c new file mode 100644 index 000000000000..5f791da9e5e2 --- /dev/null +++ b/gdb/lkd/lkd-process.c @@ -0,0 +1,892 @@ +/* + Linux Awareness extension target (Linux Kernel Debugger) + Copyright 2011-2013 STMicroelectronics. +*/ + +#include "defs.h" +#include "ui-out.h" +#include "arch-utils.h" +#include "block.h" +#include "breakpoint.h" +#include "cli/cli-decode.h" +#include "cli/cli-script.h" +#include "command.h" +#include "completer.h" +#include "dictionary.h" +#include "event-loop.h" +#include "exceptions.h" +#include "exec.h" +#include "frame.h" +#include "gdb.h" +#include "gdb_assert.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "gdbtypes.h" +#include "inferior.h" +#include "objfiles.h" +#include "observer.h" +#include "regcache.h" +#include "solib.h" +#include "solist.h" +#include "symtab.h" +#include "psympriv.h" +#include "target.h" + +#include "bfd.h" +#include "libbfd.h" +#include "elf-bfd.h" + +#include "tui/tui.h" +#include "lkd.h" +#include "lkd-process.h" + +#define BENEATH linux_aware_ops.beneath + +/* Declaration of the required addresses. */ +DECLARE_ADDR (swapper_pg_dir); +DECLARE_ADDR (init_thread_union); +DECLARE_ADDR (init_task); + +DECLARE_ADDR (init_pid_ns); +DECLARE_FIELD (pid_namespace, last_pid); + +/*realize cur_rq(cpu)->curr*/ +DECLARE_ADDR (__per_cpu_offset); +DECLARE_ADDR (per_cpu__process_counts); +DECLARE_ADDR (process_counts); +DECLARE_ADDR (per_cpu__runqueues); +DECLARE_ADDR (runqueues); + +DECLARE_FIELD (rq, curr); +DECLARE_FIELD (rq, idle); +DECLARE_FIELD (rq, lock); +DECLARE_FIELD (raw_spinlock, magic); + +DECLARE_FIELD (list_head, next); + + +DECLARE_FIELD (task_struct, active_mm); +DECLARE_FIELD (mnt_namespace, list); + /**/ DECLARE_FIELD (path, dentry); + /**/ DECLARE_FIELD (task_struct, mm); +DECLARE_FIELD (task_struct, tasks); +DECLARE_FIELD (task_struct, children); +DECLARE_FIELD (task_struct, thread_group); +DECLARE_FIELD (task_struct, sibling); +DECLARE_FIELD (task_struct, pid); +DECLARE_FIELD (task_struct, tgid); +DECLARE_FIELD (task_struct, namespace); +DECLARE_FIELD (task_struct, nsproxy); +DECLARE_FIELD (task_struct, prio); +DECLARE_FIELD (task_struct, cred); +DECLARE_FIELD (task_struct, comm); /* far offset in the task_struct, to bulk-read everything needed. */ + + + /**/ DECLARE_FIELD (thread_info, preempt_count); +DECLARE_FIELD (vm_area_struct, vm_next); +DECLARE_FIELD (vm_area_struct, vm_file); +DECLARE_FIELD (vm_area_struct, vm_flags); +DECLARE_FIELD (vm_area_struct, vm_start); +DECLARE_FIELD (vm_area_struct, vm_end); +DECLARE_FIELD (vm_area_struct, vm_pgoff); +DECLARE_FIELD (vm_struct, next); +DECLARE_FIELD (vm_struct, size); + +/* The current task. */ + +process_t *process_list = NULL; /*the processes list from the linux prospective */ +process_t *wait_process = NULL; /*process we stopped at in target_wait */ +process_t *running_process[MAX_CORES]; /*scheduled process as seen by each core */ +uint32_t per_cpu_offset[MAX_CORES]; /*__per_cpu_offset*/ + +/* per cpu peeks */ +CORE_ADDR runqueues_addr; +CORE_ADDR rq_curr[MAX_CORES]; /*cur_rq(cpu) */ +CORE_ADDR rq_idle[MAX_CORES]; /*rq->idle */ + +/* process list housekeeping*/ +static int process_counts[MAX_CORES]; +static int last_pid; + +struct mmu_infos mmu_info[MAX_CORES]; + + +static int +find_thread_lwp (struct thread_info *tp, void *arg) +{ + long lwp = *(long*)arg; + + return (ptid_get_lwp(tp->ptid) == lwp); +} + +static int +find_thread_swapper (struct thread_info *tp, void *arg) +{ + long core = *(long*)arg; + + if ((!ptid_get_lwp(tp->ptid)) && (ptid_get_tid(tp->ptid) == core)) + { + DEBUG (TASK, 2, "swapper found: tp->ptid(%d-%ld-%ld) core=%ld\n", + ptid_get_pid(tp->ptid), + ptid_get_lwp(tp->ptid), + ptid_get_tid(tp->ptid), + core); + return 1; + } + return 0; +} + +/* invalidate the cached task list. */ +static void +proc_private_dtor (struct private_thread_info * dummy) +{ + /* nop, do not free. */ +} + +/* Create the 'process_t' for the task pointed by the passed + TASK_STRUCT. */ +static void +get_task_info (CORE_ADDR task_struct, process_t ** ps, + int core /*zero-based */ ) +{ + process_t *l_ps; + size_t size; + unsigned char *task_name; + int i = 0; + int lwp = 0; + ptid_t this_ptid; + + while (*ps && (*ps)->valid) + ps = &((*ps)->next); + + if (*ps == NULL) + *ps = XCNEW (process_t); + + l_ps = *ps; + + if (task_struct == 0) + { + /* create a fake swapper entry now for the additional core + * to keep the gdb_thread ordering + **/ + l_ps->task_struct = 0; + l_ps->mm = 0; + l_ps->tgid = 0; + l_ps->prio = 0; + l_ps->core = -1; + + if (l_ps->comm) + { + xfree (l_ps->comm); + l_ps->comm = NULL; + } + l_ps->comm = xstrdup ("[swapper]"); + } + else + { + size = F_OFFSET (task_struct, comm) + F_SIZE (task_struct, comm); + + task_name = lkd_private.string_buf + F_OFFSET (task_struct, comm); + + /* use scratch area for messing around with strings + * to avoid static arrays and dispersed mallocs and frees + **/ + gdb_assert (lkd_private.string_buf); + gdb_assert (lkd_private.string_buf_size >= size); + + /* the task struct is not likely to change much from one kernel version + * to another. Knowing that comm is one of the far fields, + * try read the task struct in one command */ + read_memory (task_struct, lkd_private.string_buf, size); + + l_ps->task_struct = task_struct; + lwp = extract_unsigned_field (lkd_private.string_buf, task_struct, pid); + l_ps->mm = extract_pointer_field (lkd_private.string_buf, + task_struct, mm); + l_ps->active_mm = extract_pointer_field (lkd_private.string_buf, + task_struct, active_mm); + l_ps->tgid = extract_unsigned_field (lkd_private.string_buf, + task_struct, tgid); + l_ps->prio = extract_unsigned_field (lkd_private.string_buf, + task_struct, prio); + l_ps->core = core; /* for to_core_of_threads */ + + if (!l_ps->mm) + { + int len = strlen ((char *)task_name); + *(task_name + len) = ']'; + *(task_name + len + 1) = '\0'; + *(--task_name) = '['; + } + + if (l_ps->comm) + { + xfree (l_ps->comm); + l_ps->comm = NULL; + } + l_ps->comm = xstrdup ((char*)task_name); + } + + if (core != CORE_INVAL) + { + /* Long usage to map to LWP */ + long core_mapped = core + 1; + + /* swapper[core] */ + gdb_assert (lwp==0); + + this_ptid = ptid_build (ptid_get_pid(inferior_ptid), lwp /* == 0 */ , core_mapped); + l_ps->gdb_thread = + iterate_over_threads (find_thread_swapper, &core_mapped); + } + else + { + this_ptid = ptid_build (ptid_get_pid(inferior_ptid), lwp, CORE_INVAL); + l_ps->gdb_thread = iterate_over_threads (find_thread_lwp, &lwp); + + /*reset the thread core value, if existing */ + if (l_ps->gdb_thread) + { + gdb_assert (!l_ps->gdb_thread->priv); + PTID_OF (l_ps).tid = CORE_INVAL; + } + } + + l_ps->pgd = 0; + l_ps->valid = 1; + + /* allocate if not found + */ + if (!l_ps->gdb_thread) + { + DBG_IF (TASK) + /*sanity check : go through the list and check if lwp already there */ + process_t *tps = process_list; + + while (tps && (tps)->valid) + { + if (lwp && (tps)->gdb_thread && (ptid_get_lwp(PTID_OF (tps)) == lwp)) + gdb_assert (0); + tps = tps->next; + }; + DBG_ENDIF (TASK) + + /* add with info so that pid_to_string works. */ + l_ps->gdb_thread = add_thread_with_info (this_ptid, + (struct private_thread_info *)l_ps); + } + + /* forcibly update the private fields, as some thread may + * already have been created without, like hw threads. + * and this also tell is the gdb_thread is pruned or not!*/ + l_ps->gdb_thread->priv = (struct private_thread_info *)l_ps; + + DEBUG (TASK, 1, "gdb_thread->lwp %ld <=> ps %p\n", + ptid_get_lwp(PTID_OF (*ps)), ps); + + /* the process list freeing is not handled thanks to + * this `private` facility, yet. + */ + l_ps->gdb_thread->private_dtor = proc_private_dtor; + + /* keep trace of the last state to notify a change */ + l_ps->old_ptid = PTID_OF (l_ps); +} + + +/* Returns the 'process_t' corresponding to the passed task_struct or + NULL if not in the list. */ +process_t * +lkd_proc_get_by_task_struct (CORE_ADDR task_struct) +{ + process_t *ps = lkd_proc_get_list (); + + while ((ps != NULL) && (ps->valid == 1)) + { + if (ps->task_struct == task_struct) + return ps; + ps = ps->next; + } + return NULL; +} + +/* Return the process currently scheduled on one core */ +process_t * +lkd_proc_get_running (int core) +{ + process_t *current = NULL; + CORE_ADDR task; + struct thread_info *tp; /*gdb ti */ + ptid_t old_ptid; + + if (core == CORE_INVAL) + return NULL; + + if (running_process[core] == NULL) + { + + gdb_assert (lkd_proc_get_runqueues (0)); + + task = lkd_proc_get_rq_curr (core); + + if (task) + { /* smp cpu is initialized */ + current = lkd_proc_get_by_task_struct (task); + + if (!current) + { + /* this task struct is not known yet AND was not seen + * while running down the tasks lists, so this is presumably + * the swapper of an secondary SMP core. + */ + current = + lkd_proc_get_by_ptid (ptid_build + (ptid_get_pid(inferior_ptid), + 0, core + 1)); + gdb_assert(current); + + current->task_struct = task; + } + else + { + /* update the thread's tid in thread_list if it exists and wasn't scheduled + * so that tid makes sense for both the gdbserver and infrun.c + **/ + PTID_OF (current).tid = core + 1; + } + + current->core = core; /* was CORE_INVAL */ + running_process[core] = current; + } // task + } // running_process[core] + + return running_process[core]; +} + +/* Return 1 if this is a current task (or 0)*/ +int +lkd_proc_is_curr_task (process_t * ps) +{ + return (ps && (ps == lkd_proc_get_running (ps->core))); +} + +static CORE_ADDR get_rq_idle (int core); /* forward decl. */ + +/* Helper function that iterates the task list in the kernel + memory which are stored in a tree like structure. From sched.h: + + #define do_each_thread(g, t) \ + for (g = t = &init_task ; (g = t = next_task(g)) != &init_task ; ) do + + #define while_each_thread(g, t) \ + while ((t = next_thread(t)) != g) +*/ +static CORE_ADDR +_next_task (CORE_ADDR p) +{ + CORE_ADDR cur_entry = read_unsigned_embedded_field (p, task_struct, tasks, list_head, next); + + if (!cur_entry) + { + warning ("kernel task list contains NULL pointer"); + return 0; + } + + return container_of (cur_entry, task_struct, tasks); +} + +static CORE_ADDR +_next_thread (CORE_ADDR p) +{ + CORE_ADDR cur_entry = read_unsigned_embedded_field (p, task_struct, thread_group, list_head, next); + + if (!cur_entry) + { + DEBUG (TASK, 3, "kernel thread group list contains NULL pointer\n"); + return 0; + } + + return container_of (cur_entry, task_struct, thread_group); +} + +static process_t ** +get_list_helper (process_t ** ps) +{ + CORE_ADDR g, t, init_task_addr; + int core; + + init_task_addr = ADDR (init_task); + g = init_task_addr; + core = 0; + + do + { + t = g; + do + { + if (!linux_awareness_ops->lo_is_kernel_address (t)) + { + warning ("parsing of task list stopped because of invalid address %s", phex (t, 4)); + break; + } + + get_task_info (t, ps, core /*zero-based */ ); + core = CORE_INVAL; + + if (ptid_get_lwp (PTID_OF (*ps)) == 0) + { + /* this is init_task, let's insert the other cores swapper now */ + int i; + for (i = 1; i < max_cores; i++) + { + CORE_ADDR idle; + ps = &((*ps)->next); + idle = get_rq_idle (i); + get_task_info (idle, ps, i); + } + } + + DEBUG (TASK, 2, "Got task info for %s (%li)\n", + (*ps)->comm, ptid_get_lwp (PTID_OF (*ps))); + + ps = &((*ps)->next); + + /* mark end of chain and remove those threads + * that disappeared from the thread_list + * to avoid any_thread_of_process() to select a ghost. + **/ + if (*ps) + (*ps)->valid = 0; + + t = _next_thread (t); + } while (t && (t != g)); + + g = _next_task (g); + } while (g && (g != init_task_addr)); + + return ps; +} + + +/*----------------------------------------------------------------------------------------------*/ + +/* This function returns a the list of 'process_t' corresponding + to the tasks in the kernel's task list. */ +process_t * +lkd_proc_get_list (void) +{ + /* Return the cached copy if there's one, + * or rebuild it. + **/ + if (process_list && process_list->valid) + return process_list; + + gdb_assert(lkd_private.proc_list_invalid); + + get_list_helper (&process_list); + + lkd_private.proc_list_invalid = 0; + + return process_list; +} + +/* Returns a valid 'process_t' corresponding to + * the passed ptid or NULL if not found. + */ +process_t * +lkd_proc_get_by_ptid (ptid_t ptid) +{ + struct thread_info *tp; + long lwp = ptid_get_lwp(ptid); + process_t *ps; + + gdb_assert(!lkd_private.proc_list_invalid); + + if (lwp) + /*non-swapper, ignore TID */ + tp = iterate_over_threads (find_thread_lwp, &lwp); + else + /*swapper, TID gives the core, lwp = 0 is not unique */ + tp = find_thread_ptid(ptid); + + ps = (process_t *)tp->priv; + + /* Prune the gdb-thread is the process is not valid + * meaning is was no longer found in the task list. */ + return ps; +} + +/* invalidate the gdb thread is the linux ps has died.*/ +static int +thread_clear_info (struct thread_info *tp, void *ignored) +{ + tp->priv = NULL; + return 0; +} + +/* invalidate the cached task list. */ +void +lkd_proc_invalidate_list (void) +{ + process_t *ps = process_list; + process_t *cur; + while (ps) + { + cur = ps; + ps = ps->next; + cur->valid = 0; + } + + /* We invalidate the processes attached to the gdb_thread + * setting tp->private to null tells if the thread can + * be deleted or not. */ + iterate_over_threads (thread_clear_info, NULL); + + lkd_private.proc_list_invalid = 1; +} + +void +lkd_proc_free_list (void) +{ + process_t *ps = process_list; + process_t *cur; + while (ps) + { + cur = ps; + ps = ps->next; + // xfree does check for null pointers. + xfree (cur->comm); + xfree (cur); + } + process_list = NULL; +} + +/* Return the processor core that thread PTID was last seen on. + This information is updated only when: + - update_thread_list is called + - thread stops + If the core cannot be determined -- either for the specified thread, or + right now, or in this debug session, or for this target -- return -1. */ +int +lkd_proc_core_of_thread (ptid_t ptid) +{ + int i = 0; + process_t *ps; + + ps = lkd_proc_get_by_ptid (ptid); + + if (!ps || (ps != lkd_proc_get_running (ps->core))) + return CORE_INVAL; + else + return ps->core; +} + +CORE_ADDR +lkd_proc_get_runqueues (int reset) +{ + CORE_ADDR swapper = 0; + process_t *test_ps; + + runqueues_addr = 0; + + if (HAS_ADDR (runqueues)) + { + runqueues_addr = ADDR (runqueues); + } + else + { + runqueues_addr = ADDR (per_cpu__runqueues); + } + /* check validity */ + + DBG_IF (TASK) + if (HAS_FIELD (raw_spinlock, magic)) + { + + CORE_ADDR lock_magic = ADDR (runqueues) + + (CORE_ADDR) per_cpu_offset[0] + + F_OFFSET (rq, lock) + F_OFFSET (raw_spinlock, + magic); + + if ((read_memory_unsigned_integer (lock_magic, 4 /*uint32 */ , + LKD_BYTE_ORDER) & 0xdead0000) + != 0xdead0000) + error ("accessing the core runqueues seems to be compromised."); + } + else + printf_filtered ("runqueues access validated OK."); + DBG_ENDIF (TASK) + + return runqueues_addr; +} + +/*attempt getting the runqueue address for a core*/ +CORE_ADDR +lkd_proc_get_rq_curr (int core) +{ + + if (!rq_curr[core]) + { + CORE_ADDR curr_addr = lkd_proc_get_runqueues (0); + if (!curr_addr) + return 0; + curr_addr = + curr_addr + (CORE_ADDR) per_cpu_offset[core] + F_OFFSET (rq, curr); + rq_curr[core] = read_memory_unsigned_integer (curr_addr, 4 /*uint32 */ , + LKD_BYTE_ORDER); + } + return rq_curr[core]; +}; + +/*attempt getting the idle task for a core*/ +static CORE_ADDR +get_rq_idle (int core) +{ + CORE_ADDR curr_addr = lkd_proc_get_runqueues (0); + + if (!curr_addr || !HAS_FIELD (rq, idle)) + return 0; + + if (!rq_idle[core]) + { + curr_addr += (CORE_ADDR) per_cpu_offset[core] + F_OFFSET (rq, idle); + + rq_idle[core] = read_memory_unsigned_integer (curr_addr, 4 /*uint32 */ , + LKD_BYTE_ORDER); + } + return rq_idle[core]; +}; + +static int +get_process_count (int core) +{ + CORE_ADDR curr_addr = (CORE_ADDR) per_cpu_offset[core]; + int proc_cnt; + static int warned = 0; + + /* curr_addr can be null on UNI systems + * */ + if (HAS_ADDR (process_counts)) + curr_addr += ADDR (process_counts); + else if (HAS_ADDR (per_cpu__process_counts)) + curr_addr += ADDR (per_cpu__process_counts); + else + { + /* return a fake, changing value + * at lest the list will be refreshed, but in a less optimal way.*/ + if (!warned) + printf_filtered ("this kernel does not support `process_counts`\n"); + + if (!lkd_stepping) + warned++; + + return warned; + } + + proc_cnt = read_memory_unsigned_integer (curr_addr, 4 /*uint32 */ , + LKD_BYTE_ORDER); + + return proc_cnt; +}; + +static int +get_last_pid (void) +{ + int new_last_pid = 0; + + if (HAS_ADDR (init_pid_ns)) + { + /* Since STLinux 2.3 (2.6.23) */ + new_last_pid = read_signed_field (ADDR (init_pid_ns), + pid_namespace, last_pid); + } + else + printf_filtered ("this kernel does not support `init_pid_ns`\n"); + + return new_last_pid; +}; + +void +lkd_proc_init (void) +{ + int i = MAX_CORES; + struct thread_info *th = NULL; + struct cleanup *cleanup; + + memset (per_cpu_offset, 0, MAX_CORES * sizeof (uint32_t)); + memset (mmu_info, 0, MAX_CORES * sizeof (struct mmu_infos)); + + /* ensure thread list from beneath target is up to date */ + cleanup = make_cleanup_restore_integer (&print_thread_events); + print_thread_events = 0; + update_thread_list (); + do_cleanups (cleanup); + + /* count the h/w threads + */ + max_cores = thread_count (); + gdb_assert (max_cores); + + if (HAS_ADDR (__per_cpu_offset)) + { + int core = max_cores; + + read_memory (ADDR (__per_cpu_offset), + (gdb_byte *) (per_cpu_offset), + max_cores * sizeof (uint32_t)); + + while (--core) + if (!per_cpu_offset[core]) + { + warning ("Suspicious null per-cpu offsets," + " or wrong number of detected cores:\n" + "ADDR (__per_cpu_offset) = %s\nmax_cores = %d", + phex (ADDR (__per_cpu_offset),4), max_cores); + break; + } + } + else + { + DEBUG (D_INIT, 1, "Assuming non-SMP kernel.\n"); + } + + if (!lkd_proc_get_runqueues (1 /*reset */ ) && (max_cores > 1)) + printf_filtered ("\nCould not find the address of cpu runqueues:" + "\ncurrent context information maybe less precise\n."); +} + +/* still useful with non-smp systems + **/ +CORE_ADDR current_task_struct[MAX_CORES]; +CORE_ADDR current_thread_info[MAX_CORES]; + +int +lkd_proc_refresh_info (int cur_core) +{ + int i = max_cores; + int new_last_pid; + process_t *ps; + int do_invalidate = 0; + + memset (running_process, 0, max_cores * sizeof (process_t *)); + memset (current_thread_info, 0, max_cores * (sizeof (CORE_ADDR))); + memset (current_task_struct, 0, max_cores * (sizeof (CORE_ADDR))); + memset (rq_curr, 0, max_cores * sizeof (CORE_ADDR)); + + DEBUG (TASK, 1, "WAS: last_pid=%d, pcount[0]=%d, pcount[1]=%d\n", + last_pid, process_counts[0], process_counts[1]); + + new_last_pid = get_last_pid (); + if (new_last_pid != last_pid) + { + do_invalidate = 1; + last_pid = new_last_pid; + } + + /* check if a process exited */ + for (i = 0; i < max_cores; i++) + { + int new_pcount = get_process_count (i); + if (new_pcount != process_counts[i]) + { + process_counts[i] = new_pcount; + do_invalidate = 1; + } + } + + DEBUG (TASK, 1, "NEW: last_pid=%d, pcount[0]=%d, pcount[1]=%d\n", + last_pid, process_counts[0], process_counts[1]); + + if (do_invalidate) + lkd_proc_invalidate_list (); + + /* Update the process_list now, so that init_task is in there. */ + (void) lkd_proc_get_list (); + + /* Call update to prune gdb_thread no longer linked to a linux task.*/ + if (lkd_private.loaded == LKD_LOADED) + update_thread_list(); + + /* Set the running process + * + * we now have a thread_list looking like this: + * [1] = { 42000, 0, 1 } + * [2] = { 42000, 0, 2 } + * [3] = { 42000, 1, -1 } + * .... + * [N] = { 42000, PID_N, -1 } + * + * Now set the tid according to the running core, + * */ + for (i = 0; i < max_cores; i++) + lkd_proc_get_running (i); + + wait_process = lkd_proc_get_running (cur_core); + + if (!wait_process) + return 0; + + DEBUG (TASK, 1, "wait_process: lwp = %ld\n", ptid_get_lwp(PTID_OF (wait_process))); + DEBUG (TASK, 1, "wait_process: pid = %d\n", ptid_get_pid(PTID_OF (wait_process))); + DEBUG (TASK, 1, "wait_process: tid = %ld\n", ptid_get_tid(PTID_OF (wait_process))); + + gdb_assert(wait_process->gdb_thread); + gdb_assert((process_t *) wait_process->gdb_thread->priv == wait_process); + + /* Notify ptid changed. */ + ps = process_list; + while (ps && ps->valid) + { + if (ptid_get_tid(ps->old_ptid) != ptid_get_tid(PTID_OF (ps))) + { + observer_notify_thread_ptid_changed (ps->old_ptid, PTID_OF (ps)); + ps->old_ptid.tid = ptid_get_tid(PTID_OF (ps)); + } + ps = ps->next; + } + + switch_to_thread(PTID_OF (wait_process)); + gdb_assert(lkd_proc_get_by_ptid(inferior_ptid) == wait_process); + + return 1; +} + +/* Setup the symbols to reflect the namespace of the passed + process. GDB doesn't really support this. We hack this support by + appending the list of objfiles containing the debug information + for the process to the list of objfiles that contain the debug + info for the kernel. When we switch to another process, we remove + the objfiles for the old process and replace it with the objfiles + for the new one. The pointers to the objfiles for a given process + are stored in the associated process_t. */ +void +lkd_proc_set_symfile (void) +{ + process_t *ps; + + + ps = lkd_proc_get_by_ptid (inferior_ptid); + if (ps && ps->main_objfile) + symfile_objfile = ps->main_objfile; +} + + +/* Selected the task that is really running on the CPU. */ +void +running_task_command (char *args, int from_tty) +{ + ptid_t ptid; + char *thread_id; + + if (!wait_process) + return; + + ptid = PTID_OF (wait_process); + thread_id = xstrprintf ("%d", ptid_to_global_thread_id (ptid)); + + /* switch_to_thread() sets inferior_ptid to ptid */ + gdb_thread_select (current_uiout, thread_id, NULL); + + xfree (thread_id); +} diff --git a/gdb/lkd/lkd-process.h b/gdb/lkd/lkd-process.h new file mode 100644 index 000000000000..15dbe9a5df6f --- /dev/null +++ b/gdb/lkd/lkd-process.h @@ -0,0 +1,63 @@ +/* + Linux Awareness extension target (Linux Kernel Debugger) + Copyright 2011-2013 STMicroelectronics. +*/ + +#ifndef __LKD_PROCESS_H__ +#define __LKD_PROCESS_H__ + +/* The list of tasks as cached in the debugger. */ +typedef struct process_t_ +{ + struct process_t_ *next; + CORE_ADDR task_struct; + CORE_ADDR mm; + CORE_ADDR active_mm; + CORE_ADDR pgd; /* FIXME: this should be arch specific, needs fix for SH4 */ + + ptid_t old_ptid; + + int core; /*this is the "dynamic" core info */ + + int tgid; + unsigned int prio; + char *comm; + int valid; + + struct thread_info *gdb_thread; + + /* user process info */ + struct objfile *objfiles; + struct objfile *main_objfile; +} process_t; + +#define VM_EXECUTABLE 0x00001000 +#define VM_EXEC 0x00000004 + +#define PTID_OF(ps) ((ps)->gdb_thread->ptid) + +extern process_t *running_process[]; +extern process_t *selected_process; +extern process_t *wait_process; +extern uint32_t per_cpu_offset[]; + + +/* API */ +void lkd_proc_init (void); +int lkd_proc_core_of_thread (ptid_t ptid); +void lkd_proc_invalidate_list (void); +void lkd_proc_free_list (void); +int lkd_proc_refresh_info (int core); +void lkd_proc_set_symfile (void); +process_t *lkd_proc_get_list (void); +process_t *lkd_proc_get_by_ptid (ptid_t ptid); +process_t *lkd_proc_get_by_task_struct (CORE_ADDR task); +process_t *lkd_proc_get_running (int core); +CORE_ADDR lkd_proc_get_runqueues (int reset); +CORE_ADDR lkd_proc_get_rq_curr (int core); +int lkd_proc_is_curr_task (process_t * ps); + +/*commands*/ +void running_task_command (char *args, int from_tty); + +#endif /*__LKD_PROCESS_H__*/ diff --git a/gdb/lkd/lkd.h b/gdb/lkd/lkd.h new file mode 100644 index 000000000000..91cc8632111e --- /dev/null +++ b/gdb/lkd/lkd.h @@ -0,0 +1,486 @@ +/* + Linux Awareness extension target (Linux Kernel Debugger) + + Copyright 2010-2013 STMicroelectronics. + + The Linux Awareness layer allows GDB to debug a Linux kernel + through a H/W link (presumably a JTAG) as if it were a normal + executable. One major issue with debugging the kernel through a + JTAG link is the handling of virtual memory. + + Virtual memory raises some issues while debugging through the JTAG + because the processor needs to have been correctly setup to be able + to access such memory. Usually, the TLB needs to be loaded with the + correct virtual memory translation so that the debugger may access + it. When some code running above (or inside) Linuux accesses virtual + memory, the processor raises an exception that will trigger + handlers in the kernel that in turn will setup everything needed + for the access to succeed. When the user wants to access such + memory from the debugger, the processor is basically 'stalled'. No + handlers will be called in case of exception and the access will + (sometimes silently) fail. This means the debugger has to setup + everything correctly *before* trying any virtual memory access. + + Once the debugger knows how to handle virtual memory, it can try to + debug modules. In most Linux ports, kernel modules are loaded in + kernel virtual memory (as returned by vmalloc()). This layer tries + to expose kernel modules as shared libraries to the rest of + GDB. This is done by registering a 'struct target_so_ops' that + implements a shared library handling that know how to deal with + kernel modules. Indeed, kernel modules are different from shared + libraries in some fundamental ways. First, the kernel module binary + files are relocatable files, they haven't undergone a link + step. This means eg. that the files might contain multiple sections + at the same address. This isn't handled nicely by GDB. The solution + implemented is to mimic the little linker that does the relocation + job in the kernel and do a real link that generates a fully + relocated file. This file can then be handled like a stantdard + shared library. There's another major difference between modules + and shared libraries: part of the module code and data might be + unmapped after the module's init step is over. GDB isn't designed + to allow part of debug information to disappear, thus we have to + work around that. It's important to handle that case, because the + freed memory will be reused by subsequent module loads, leading to + a big mess of overlapping informations if the information from the + unloaded code isn't somehow handled. As said above, GDB can't + forget part of the debug information. The workaround use is to + 'destroy' the debug information relating to the unloaded code by + pretending that every object that disappeared is located at + 0xFFFFFFFF (there is a repeating ~(CORE_ADDR)0 pattern in the + file). The detection of module load, unloads and end of init events + is done by installing breakpoints at carefully chosen points. + + The other major thing done in this file, is to expose all the + running tasks of the Linux kernel as threads of the debugged + application to GDB. To make abstraction more natural from an UI + point of view, the command 'info tasks' is introduced as an alias + for 'info threads'. + + Most of the above is achieved by inserting the 'struct target_ops + linux_aware_ops' on the top of the target stack. The target stack + is the way GDB talks to the debugged target. It exposes an API + defined in 'struct target_ops'. Most of the functions have explicit + names, like 'fetch_register', 'resume', 'xfer_memory'. This is a + stack because it is constructed by piling up layers that provide + certain functionality. For example, when you first open an + executable file, a layer (or stratum in GDB speak) that knows how + to read the memory from the file is on the target stack. Then you + run the executable and a layer that knows how to access the runtime + memory and control the execution of the process is pushed on the + stack. Then it might be that this is a threaded application, and + the thread awareness is pushed as another layer on the target + stack. Each stratum has the ability to overload the functions + defined by the target_ops structure and to access the layers below + its level. The code in target.c constructs the current_target + structure each time the target stack is modified by a call to + (push|unpush)_target (). In this file we push 'linux_aware_ops' at + the very top of the target stack, and thus it's able to intercept + every communication between GDB and the debugged processor. For + example we trap virtual memory accesses this way and do the + required setup before passing the access to the lower layer of the + target stack that will really perform the access. + + A last thing that is implemented in this file is a few generic data + display commands like 'dmesg', 'proc_iomem', 'proc_ioports', ... + */ +#ifndef __LINUX_AWARENESS_H__ +#define __LINUX_AWARENESS_H__ + +#define LKD_VERSION_STRING "7.11-development" + +#ifndef NULL +#define NULL ((void*)0) +#endif + + +/********** from breakpoint.c **********************/ + +extern struct breakpoint *breakpoint_chain; +extern struct bp_location **bp_location; +extern int breakpoint_count; +extern unsigned bp_location_count; + +#define ALL_BREAKPOINTS(B) for (B = breakpoint_chain; B; B = B->next) + +#define ALL_BREAKPOINTS_SAFE(B,TMP) \ + for (B = breakpoint_chain; \ + B ? (TMP=B->next, 1): 0; \ + B = TMP) + +#define ALL_BP_LOCATIONS(B,BP_TMP) \ + for (BP_TMP = bp_location; \ + BP_TMP < bp_location + bp_location_count && (B = *BP_TMP); \ + BP_TMP++) + +/*************************************************/ + +#define LKD_BYTE_ORDER BFD_ENDIAN_LITTLE + +/* target stack ordering */ +#define LKD_STRATUM_LINUX (thread_stratum + 10) + + +typedef enum +{ + LKD_NOTLOADED = 0, + LKD_LOADED = 1, /*must be one */ + LKD_LOADING = 2, +} lkd_load_states_t; + +/** + * LKD user parameters. + */ +struct linux_awareness_params +{ + + /* Global flag set by 'set linux-awareness enabled'. The user + shouldn't have to use that as the enablementlinux_awareness_check should be triggered + automatically by the kernel autodetection routines. */ + int enabled; + + /* Flag indicating that the kernel has been loaded. This is set by the + 'load' command, but it might be necessary to set it by hand ('set + linux-awareness loaded 1'), eg. when attaching to a running target + with another command than the standard 'attach'. */ + lkd_load_states_t loaded; + + /* For debugging. One can disable the task handling + issuing 'set linux-awareness enable_task_awareness 0' */ + int enable_task_awareness; + + /* A user might disable the automatic load of the Linux awareness + layer by issuing 'set linux-awareness auto_activate 0' before + loading the kernel binary. (This might be usefull in a .(sh)gdbinit + file. */ + int auto_activate; + + /* The current global loglevel as set by 'set linux-awareness + debug-all' */ + int loglevel; + + /* skip the schedule() frame in the backtrace */ + int skip_schedule_frame; + + /* disable thread info coloring */ + int no_colors; +}; + +extern struct linux_awareness_params lkd_params; + +/* use scratch area for messing around with strings + * to avoid static arrays and dispersed mallocs and frees + **/ +struct lkd_private_data +{ + lkd_load_states_t loaded; + int connected; + int keep_do_exit_event; + + unsigned char *string_buf; + int string_buf_size; + + char *banner_file; /* string for the banner as read from vmlinx */ + int banner_file_size; /* max size allocated */ + int banner_file_valid; /* valid or to refresh */ + + int proc_list_invalid; + + char *banner_mem; /* string for the banner as read from vmlinx */ + int banner_mem_size; /* max size allocated */ + int banner_mem_valid; /* valid or to refresh */ + + /* The UTS name as extracted from the file or the memory. This serves + to build the path that points to the depmod cache. */ + char *utsname_release; + int utsname_release_size; /* max size allocated */ + int utsname_release_valid; /* valid or to refresh */ + + struct type *target_pointer_type; + + uint32_t kflags; +}; + +extern struct lkd_private_data lkd_private; + +void lkd_loaded_set (char *arg, int from_tty, struct cmd_list_element *c); +void lkd_enabled_set (char *args, int from_tty, struct cmd_list_element *c); +void lkd_reset_thread_list (void); +long lkd_eval_long (char *string); + +/* lkd-irqs.c*/ +void interrupts_command (char *args, int from_tty); + +/* Mimic kernel macros */ +#define container_of(ptr, struc, field) ((ptr) - F_OFFSET(struc, field)) + +extern int max_cores; +#define MAX_CORES 5 +#define CORE_INVAL (-1) /* 0 = name on the inferior, cannot be used */ + +extern const struct objfile_data *linux_uprocess_objfile_data_key; + +extern int stopped_core; +extern int lkd_stepping; + +struct process_t_; + +struct linux_awareness_ops +{ + const char *name; + + /* Check if the current application is a compatible Linux kernel. */ + int (*lo_check_kernel) (); + + int (*lo_check_mem_rdy) (); /*must implement. */ + + /* Called at the beginning of a debugging session. */ + int (*lo_init) (); + /* Called at the end of a debugging session. Can be NULL. */ + void (*lo_close) (); + /* Called before detaching from the targe. Can be NULL. */ + int (*lo_pre_detach) (char *prog, int fromtty); + /* Called before the load of the kernel. Can be NULL. */ + void (*lo_pre_load) (char *prog, int fromtty); + /* Called after the load of the kernel. Can be NULL. */ + void (*lo_post_load) (char *prog, int fromtty); + /* Called before the processor is resumed. Can be NULL. */ + void (*lo_pre_exec_start) (); + + + /* Returns wether the page containing addr in the context of task_struct + is mapped as writable. */ + int (*lo_can_write) (CORE_ADDR addr, CORE_ADDR task_struct); + /* Returns wether the passed address is a userspace address. */ + int (*lo_is_user_address) (CORE_ADDR addr); + /* Returns wether the passed address is a kernelspace address. */ + int (*lo_is_kernel_address) (CORE_ADDR addr); + /* Returns wether the passed handler lies inside a TLB miss + handler. This is used to singlestep through code in virtual + memory: when this code produces an exception, the awareness + layer will hide from the user (by silently singlstepping + through) all the code where that callback returns true. */ + int (*lo_is_tlb_miss_handler) (CORE_ADDR addr); + /* Flush the processor cache lines corresponding to the virtual + address virtaddr and physical addr physaddr (as returned by + lo_translate_memory_address() above). + Called with virtaddr == physaddr, the aim is to flush the + aliased line that might have been introduced by directly + accessing the physical address. + + Precondition : + [virtaddr..virtaddr+len[ lies on the same physical page. + */ + void (*lo_flush_cache) (CORE_ADDR virtaddr, CORE_ADDR physaddr, + int len, int write); + /* If the target has to use software singlestepping, this callback + is called and returns the instruction that'll be executed after + the one at pc. Can be NULL. */ + CORE_ADDR (*lo_single_step_destination) (CORE_ADDR pc); + /* This callback is called when the execution is resumed and we + might switch task. It's different from lo_pre_exec_start() that + will be called systematically. For example, cached virtual + memory translations can be discarded here. When this called + back isn't called, the debugger stays in the same task, thus + the cached translations should stay valid. */ + void (*lo_clear_cache) (); + + /* Should supply the current regcache with the value of register + regno in the context of task_struct. This will only be called + for tasks that are stopped in the scheduler. The way the task + state is stored on context switch is target specific. */ + int (*lo_fetch_context_register) (int regno, CORE_ADDR task_struct); + /* Should store in the target the value of register regno from the + current regcache in the context of task_struct. This will only + be called for tasks that are stopped in the scheduler. The way + the task state is stored on context switch is target specific. */ + int (*lo_store_context_register) (int regno, CORE_ADDR task_struct); + + int kernel_offset; +}; + +extern struct target_ops linux_aware_ops; +extern struct linux_awareness_ops *linux_awareness_ops; + +extern bfd *cur_bfd; + +struct type; +struct cmd_list_element; + +extern struct cmd_list_element *set_linux_awareness_cmd_list; +extern struct cmd_list_element *show_linux_awareness_cmd_list; + +struct addr_info +{ + char *name; + struct bound_minimal_symbol bmsym; + struct addr_info *next; +}; + +struct field_info +{ + char *struct_name; + char *field_name; + struct symbol *type; + int offset; + int size; + struct field_info *next; +}; + +#define FIELD_INFO(s_name, field) _FIELD_##s_name##__##field + +#define DECLARE_FIELD(s_name, field) \ + static struct field_info FIELD_INFO(s_name, field) \ + = { .struct_name = #s_name, .field_name = #field, 0 } + +#define F_OFFSET(struct, field) \ + linux_get_field_offset (&FIELD_INFO(struct, field)) +#define F_SIZE(struct, field) \ + linux_get_field_size (&FIELD_INFO(struct, field)) +#define HAS_FIELD(struct, field) \ + (FIELD_INFO(struct, field).type != NULL \ + || (linux_init_field(&FIELD_INFO(struct, field), 1), \ + FIELD_INFO(struct, field).type != NULL)) + +#define ADDR_INFO(symb) _ADDR_##symb + +#define DECLARE_ADDR(symb) \ + static struct addr_info ADDR_INFO(symb) = { .name = #symb, .bmsym = {NULL, NULL} } + +#define HAS_ADDR(symb) \ + (ADDR_INFO(symb).bmsym.minsym != NULL \ + || (linux_init_addr(&ADDR_INFO(symb), 1), ADDR_INFO(symb).bmsym.minsym != NULL)) + +#define ADDR(sym) linux_get_address (&ADDR_INFO(sym)) + +#define read_unsigned_field(base, struct, field) \ + read_memory_unsigned_integer (base + F_OFFSET (struct, field), \ + F_SIZE (struct, field), LKD_BYTE_ORDER) + +#define read_signed_field(base, struct, field) \ + read_memory_integer (base + F_OFFSET (struct, field), \ + F_SIZE (struct, field), LKD_BYTE_ORDER) + +#define read_pointer_field(base, struct, field) \ + read_memory_typed_address (base + F_OFFSET (struct, field), \ + builtin_type (target_gdbarch ())->builtin_data_ptr) + +#define read_unsigned_embedded_field(base, struct, field, emb_str, emb_field) \ + read_memory_unsigned_integer (base + F_OFFSET (struct, field) \ + + F_OFFSET (emb_str, emb_field), \ + F_SIZE (emb_str, emb_field), LKD_BYTE_ORDER) + +#define read_signed_embedded_field(base, struct, field, emb_str, emb_field) \ + read_memory_integer (base + F_OFFSET (struct, field) \ + + F_OFFSET (emb_str, emb_field), \ + F_SIZE (emb_str, emb_field), LKD_BYTE_ORDER) + +#define read_pointer_embedded_field(base, struct, field, emb_str, emb_field) \ + read_memory_typed_address (base + F_OFFSET (struct, field) \ + + F_OFFSET (emb_str, emb_field), \ + builtin_type (target_gdbarch ())->builtin_data_ptr) + +#define extract_unsigned_field(base, struct, field) \ + extract_unsigned_integer(base + F_OFFSET (struct, field), \ + F_SIZE (struct, field), LKD_BYTE_ORDER) + +#define extract_signed_field(base, struct, field) \ + extract_signed_integer (base + F_OFFSET (struct, field), \ + F_SIZE (struct, field), LKD_BYTE_ORDER) + +#define extract_pointer_field(base, struct, field) \ + extract_typed_address (base + F_OFFSET (struct, field), \ + builtin_type(target_gdbarch ())->builtin_data_ptr) + +/*cleanup macro to compare an address to a bp loc value*/ +#define IS_LOC(pc,evnt) ((evnt != NULL) && (pc == evnt->loc->address)) + +enum page_status +{ + PAGE_PRESENT, + PAGE_SWAPPED, + PAGE_NOTMAPPED, + PAGE_NOPAGE, + PAGE_UNKNOWN +}; + +/*kversion building macros */ +#define KERNEL_VERSION(MM,mm,bb) (((MM) << 16) + ((mm) << 8) + (bb)) + +/* last known (validated) STLinux kernel version for this LKD. + * if we're beyond this version, of not on STLinux, issue a warning + **/ +#define KFLAG_NOLINUX 0x00000000 /* not linux */ +#define KFLAG_LINUX 0x00000001 /* is linux */ +#define KFLAG_STLINUX 0x00000002 /* STMicroelectronics linux distro */ +#define KFLAG_DBGINFO 0x00000004 /* has debug information */ + +/* kernel release string */ +extern char g_utsname_release[]; + +struct debug_domain +{ + const char *name; + int level; +}; + +extern struct debug_domain linux_aware_debug_domains_info[]; + +enum linux_aware_debug_domain +{ + TASK, + TARGET, + D_INIT, + KEEP_LAST +}; + +#define DEBUG(domain, l, ...) \ + ({if (domain < KEEP_LAST \ + && linux_aware_debug_domains_info[domain].level >= l) \ + fprintf_filtered(gdb_stdlog, "linux: " __VA_ARGS__);}) + +/*do thing a different way in debug mode.*/ +#define DBG_IF(domain) if (linux_aware_debug_domains_info[domain].level) { +#define DBG_ELSE } else { +#define DBG_ENDIF(domain) } + +#define LA_NOT_LOADED_STRING "Please type \"set linux-awareness loaded\".\n" + +int linux_init_addr (struct addr_info *field, int check); +int linux_init_field (struct field_info *field, int check); + +static inline CORE_ADDR +linux_get_address (struct addr_info *addr) +{ + if (addr->bmsym.minsym == NULL) + linux_init_addr (addr, 0); + + return BMSYMBOL_VALUE_ADDRESS (addr->bmsym); +} + +static inline unsigned int +linux_get_field_offset (struct field_info *field) +{ + if (field->type == NULL) + linux_init_field (field, 0); + + return field->offset; +} + +static inline unsigned int +linux_get_field_size (struct field_info *field) +{ + if (field->type == NULL) + linux_init_field (field, 0); + + return field->size; +} + +int lkd_try_push_target (void); +void lkd_uninstall_do_exit_event (void); + +void sanitize_path (char *path); +char *linux_aware_get_target_root_prefix (void); + +int linux_aware_target_core (void); + +#endif /*__LINUX_AWARENESS_H__*/