tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

symbols.py (3942B)


      1 # Any copyright is dedicated to the Public Domain.
      2 # http://creativecommons.org/publicdomain/zero/1.0/
      3 #
      4 # A GDB Python script to fetch debug symbols from the Mozilla symbol server.
      5 #
      6 # To use, run `source /path/to/symbols.py` in GDB 7.9 or newer, or
      7 # put that in your ~/.gdbinit.
      8 
      9 
     10 import gzip
     11 import io
     12 import itertools
     13 import os
     14 import shutil
     15 
     16 try:
     17    from urllib.error import HTTPError, URLError
     18    from urllib.parse import quote, urljoin
     19    from urllib.request import urlopen
     20 except ImportError:
     21    from urllib import quote
     22 
     23    from urllib2 import urlopen
     24    from urlparse import urljoin
     25 
     26 # /try covers regular symbols as well, see https://github.com/mstange/reliost/pull/8
     27 SYMBOL_SERVER_URL = "https://symbols.mozilla.org/try/"
     28 
     29 debug_dir = os.path.join(os.environ["HOME"], ".cache", "gdb")
     30 cache_dir = os.path.join(debug_dir, ".build-id")
     31 
     32 
     33 def munge_build_id(build_id):
     34    """
     35    Breakpad stuffs the build id into a GUID struct so the bytes are
     36    flipped from the standard presentation.
     37    """
     38    b = list(map("".join, list(zip(*[iter(build_id.upper())] * 2))))
     39    return (
     40        "".join(
     41            itertools.chain(
     42                reversed(b[:4]), reversed(b[4:6]), reversed(b[6:8]), b[8:16]
     43            )
     44        )
     45        + "0"
     46    )
     47 
     48 
     49 def try_fetch_symbols(filename, build_id, destination):
     50    debug_file = os.path.join(destination, build_id[:2], build_id[2:] + ".debug")
     51    if os.path.exists(debug_file):
     52        return debug_file
     53    try:
     54        d = os.path.dirname(debug_file)
     55        if not os.path.isdir(d):
     56            os.makedirs(d)
     57    except OSError:
     58        pass
     59    path = os.path.join(filename, munge_build_id(build_id), filename + ".dbg.gz")
     60    url = urljoin(SYMBOL_SERVER_URL, quote(path))
     61    try:
     62        print(f"Trying to fetch symbols from {url}")
     63        u = urlopen(url)
     64        if u.getcode() != 200:
     65            return None
     66        with open(debug_file, "wb") as f, gzip.GzipFile(
     67            fileobj=io.BytesIO(u.read()), mode="r"
     68        ) as z:
     69            shutil.copyfileobj(z, f)
     70            print(f"Fetched symbols from {url}")
     71            return debug_file
     72    except (URLError, HTTPError):
     73        None
     74 
     75 
     76 def is_moz_binary(filename):
     77    """
     78    Try to determine if a file lives in a Firefox install dir, to save
     79    HTTP requests for things that aren't going to work.
     80    """
     81    # The linux-gate VDSO doesn't have a real filename.
     82    if not os.path.isfile(filename):
     83        return False
     84    while True:
     85        filename = os.path.dirname(filename)
     86        if filename == "/" or not filename:
     87            return False
     88        if os.path.isfile(os.path.join(filename, "application.ini")):
     89            return True
     90 
     91 
     92 def fetch_symbols_for(objfile):
     93    build_id = objfile.build_id if hasattr(objfile, "build_id") else None
     94    if getattr(objfile, "owner", None) is not None or any(
     95        o.owner == objfile for o in gdb.objfiles()
     96    ):
     97        # This is either a separate debug file or this file already
     98        # has symbols in a separate debug file.
     99        return
    100    if build_id and is_moz_binary(objfile.filename):
    101        debug_file = try_fetch_symbols(
    102            os.path.basename(objfile.filename), build_id, cache_dir
    103        )
    104        if debug_file:
    105            objfile.add_separate_debug_file(debug_file)
    106 
    107 
    108 def new_objfile(event):
    109    fetch_symbols_for(event.new_objfile)
    110 
    111 
    112 def fetch_symbols():
    113    """
    114    Try to fetch symbols for all loaded modules.
    115    """
    116    for objfile in gdb.objfiles():
    117        fetch_symbols_for(objfile)
    118 
    119 
    120 # Create our debug cache dir.
    121 try:
    122    if not os.path.isdir(cache_dir):
    123        os.makedirs(cache_dir)
    124 except OSError:
    125    pass
    126 
    127 # Set it as a debug-file-directory.
    128 try:
    129    dirs = gdb.parameter("debug-file-directory").split(":")
    130 except gdb.error:
    131    dirs = []
    132 if debug_dir not in dirs:
    133    dirs.append(debug_dir)
    134    gdb.execute("set debug-file-directory {}".format(":".join(dirs)))
    135 
    136 gdb.events.new_objfile.connect(new_objfile)