From 12b50bd85bdd7649ccd365a9ed378978aec246fc Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Fri, 30 Oct 2020 11:48:54 -0400 Subject: [PATCH 1/3] Add gdb scripts for memquery and drsymload Adds two gdb python scripts I've developed that may be useful to others: 1) drsymload: loads DR symbols regardless of gdb's current state, which may include having DR symbols at the wrong address. It does this by reading /proc/self/maps and running objdump on libdynamorio.so. Ideally this would be integrated into a revived libdynamorio.so-gdb.py: that's part of #2100. 2) memquery: prints the /proc/self/maps line for a given address. I'm still shocked gdb doesn't provide such a command natively. Issue: #2100 --- tools/gdb-scripts/gdb-drsymload.py | 84 ++++++++++++++++++++++++++++++ tools/gdb-scripts/gdb-memquery.py | 68 ++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 tools/gdb-scripts/gdb-drsymload.py create mode 100644 tools/gdb-scripts/gdb-memquery.py diff --git a/tools/gdb-scripts/gdb-drsymload.py b/tools/gdb-scripts/gdb-drsymload.py new file mode 100644 index 00000000000..33d1a9d0b82 --- /dev/null +++ b/tools/gdb-scripts/gdb-drsymload.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +# ********************************************************** +# Copyright (c) 2020 Google, Inc. All rights reserved. +# ********************************************************** + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of Google, Inc. nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. + +# Loads symbols for libdynamorio.so by looking in /proc/self/maps. +# This clears the symbol table, so run it early/first. +# TODO i#2100: Integrate with a revived libdynamorio.so-gdb.py. +# Usage: +# 1) Place this line into your ~/.gdbinit: +# source ~/bin/gdb-drsymload.py +# 2) Execute the command in gdb: +# (gdb) drsymload + +try: + import gdb +except ImportError as e: + raise ImportError("This script must be run in GDB: ", str(e)) +import re +import subprocess +from pathlib import Path +import os.path + +class DRSymLoadCommand (gdb.Command): + """Loads symbols for libdynamorio.so""" + def __init__ (self): + super (DRSymLoadCommand, self).__init__ ("drsymload", + gdb.COMMAND_DATA, + gdb.COMPLETE_FILENAME) + def invoke(self, arg, from_tty): + pid = int(gdb.selected_inferior().pid) + exefile = "/proc/%d/exe" % pid + drfile = str(Path(exefile).resolve()) + base = 0 + map_name = "/proc/%d/maps" % pid + with open(map_name,'r') as map: + for line in map: + line = line.rstrip() + if not line: continue + match = re.match(r'^([^-]*)-.*libdynamorio.so', line) + if match: + base = int(match.group(1), 16) + print("libdynamorio base is 0x%x" % base) + break + p = subprocess.Popen(["objdump", "-h", drfile], stdout=subprocess.PIPE) + stdout, _ = p.communicate() + for line in iter(stdout.splitlines()): + match = re.match(r'.*\.text *\w+ *\w+ *\w+ *(\w+)', str(line)) + if match: + offs = match.group(1) + cmd = "add-symbol-file %s 0x%x+0x%s" % (drfile, base, offs) + # First clear the symbols (sometimes gdb has DR syms at the wrong + # load address). + gdb.execute("symbol-file") + print("Running %s" % cmd) + gdb.execute(cmd) +DRSymLoadCommand() diff --git a/tools/gdb-scripts/gdb-memquery.py b/tools/gdb-scripts/gdb-memquery.py new file mode 100644 index 00000000000..345b1e917b4 --- /dev/null +++ b/tools/gdb-scripts/gdb-memquery.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +# ********************************************************** +# Copyright (c) 2020 Google, Inc. All rights reserved. +# ********************************************************** + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of Google, Inc. nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. + +# Searches /proc/self/maps for the line containing a given address. +# Usage: +# 1) Place this line into your ~/.gdbinit: +# source ~/bin/gdb-memquery.py +# 2) Execute the command in gdb: +# (gdb) memquery $rsp +# 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] + +try: + import gdb +except ImportError as e: + raise ImportError("This script must be run in GDB: ", str(e)) +import re +from subprocess import check_output + +class MemQueryCommand (gdb.Command): + """Prints memory address properties""" + def __init__ (self): + super (MemQueryCommand, self).__init__ ("memquery", + gdb.COMMAND_DATA, + gdb.COMPLETE_FILENAME) + def invoke(self, arg, from_tty): + addr = gdb.parse_and_eval(arg) + pid = int(gdb.selected_inferior().pid) + map_name = "/proc/%d/maps" % pid + with open(map_name,'r') as map: + for line in map: + line = line.rstrip() + if not line: continue + match = re.match(r'^(\w+)-(\w+)', line) + if match: + start = int(match.group(1), 16) + end = int(match.group(2), 16) + if addr >= start and addr < end: + print(line) +MemQueryCommand() From f34a8aa049f788373af832de029bbeec938511a7 Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Fri, 30 Oct 2020 13:28:48 -0400 Subject: [PATCH 2/3] Rename unused args with unused_ prefix --- tools/gdb-scripts/gdb-drsymload.py | 2 +- tools/gdb-scripts/gdb-memquery.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/gdb-scripts/gdb-drsymload.py b/tools/gdb-scripts/gdb-drsymload.py index 33d1a9d0b82..9d6f9f2eba3 100644 --- a/tools/gdb-scripts/gdb-drsymload.py +++ b/tools/gdb-scripts/gdb-drsymload.py @@ -54,7 +54,7 @@ def __init__ (self): super (DRSymLoadCommand, self).__init__ ("drsymload", gdb.COMMAND_DATA, gdb.COMPLETE_FILENAME) - def invoke(self, arg, from_tty): + def invoke(self, unused_arg, unused_from_tty): pid = int(gdb.selected_inferior().pid) exefile = "/proc/%d/exe" % pid drfile = str(Path(exefile).resolve()) diff --git a/tools/gdb-scripts/gdb-memquery.py b/tools/gdb-scripts/gdb-memquery.py index 345b1e917b4..2b9533a1da7 100644 --- a/tools/gdb-scripts/gdb-memquery.py +++ b/tools/gdb-scripts/gdb-memquery.py @@ -51,7 +51,7 @@ def __init__ (self): super (MemQueryCommand, self).__init__ ("memquery", gdb.COMMAND_DATA, gdb.COMPLETE_FILENAME) - def invoke(self, arg, from_tty): + def invoke(self, arg, unused_from_tty): addr = gdb.parse_and_eval(arg) pid = int(gdb.selected_inferior().pid) map_name = "/proc/%d/maps" % pid From d6f0964c838334304a6482d15d90efc6074b37af Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Fri, 30 Oct 2020 13:30:41 -0400 Subject: [PATCH 3/3] Make path in comment for sourcing more generic --- tools/gdb-scripts/gdb-drsymload.py | 2 +- tools/gdb-scripts/gdb-memquery.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/gdb-scripts/gdb-drsymload.py b/tools/gdb-scripts/gdb-drsymload.py index 9d6f9f2eba3..e2ad979f989 100644 --- a/tools/gdb-scripts/gdb-drsymload.py +++ b/tools/gdb-scripts/gdb-drsymload.py @@ -35,7 +35,7 @@ # TODO i#2100: Integrate with a revived libdynamorio.so-gdb.py. # Usage: # 1) Place this line into your ~/.gdbinit: -# source ~/bin/gdb-drsymload.py +# source /gdb-drsymload.py # 2) Execute the command in gdb: # (gdb) drsymload diff --git a/tools/gdb-scripts/gdb-memquery.py b/tools/gdb-scripts/gdb-memquery.py index 2b9533a1da7..55f42740bad 100644 --- a/tools/gdb-scripts/gdb-memquery.py +++ b/tools/gdb-scripts/gdb-memquery.py @@ -33,7 +33,7 @@ # Searches /proc/self/maps for the line containing a given address. # Usage: # 1) Place this line into your ~/.gdbinit: -# source ~/bin/gdb-memquery.py +# source /gdb-memquery.py # 2) Execute the command in gdb: # (gdb) memquery $rsp # 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]