tor-browser

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

unwind.py (23933B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 # You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 # mozilla/unwind.py --- unwinder and frame filter for SpiderMonkey
      6 
      7 import platform
      8 
      9 import gdb
     10 import gdb.types
     11 from gdb.FrameDecorator import FrameDecorator
     12 
     13 from mozilla.JSObject import get_function_name, get_function_script
     14 from mozilla.prettyprinters import TypeCache
     15 
     16 # For ease of use in Python 2, we use "long" instead of "int"
     17 # everywhere.
     18 try:
     19    long
     20 except NameError:
     21    long = int
     22 
     23 # The Python 3 |map| built-in works lazily, but in Python 2 we need
     24 # itertools.imap to get this.
     25 try:
     26    from itertools import imap
     27 except ImportError:
     28    imap = map
     29 
     30 _have_unwinder = True
     31 try:
     32    from gdb.unwinder import Unwinder
     33 except ImportError:
     34    _have_unwinder = False
     35    # We need something here; it doesn't matter what as no unwinder
     36    # will ever be instantiated.
     37    Unwinder = object
     38 
     39 
     40 def debug(something):
     41    # print("@@ " + something)
     42    pass
     43 
     44 
     45 # Maps frametype enum base names to corresponding class.
     46 SizeOfFramePrefix = {
     47    "FrameType::IonJS": "ExitFrameLayout",
     48    "FrameType::BaselineJS": "JitFrameLayout",
     49    "FrameType::BaselineStub": "BaselineStubFrameLayout",
     50    "FrameType::CppToJSJit": "JitFrameLayout",
     51    "FrameType::WasmToJSJit": "JitFrameLayout",
     52    "FrameType::Rectifier": "RectifierFrameLayout",
     53    "FrameType::IonAccessorIC": "IonAccessorICFrameLayout",
     54    "FrameType::IonICCall": "IonICCallFrameLayout",
     55    "FrameType::Exit": "ExitFrameLayout",
     56    "FrameType::Bailout": "JitFrameLayout",
     57 }
     58 
     59 
     60 # We cannot have semi-colon as identifier names, so use a colon instead,
     61 # and forward the name resolution to the type cache class.
     62 class UnwinderTypeCacheFrameType:
     63    def __init__(self, tc):
     64        self.tc = tc
     65 
     66    def __getattr__(self, name):
     67        return self.tc.__getattr__("FrameType::" + name)
     68 
     69 
     70 class UnwinderTypeCache(TypeCache):
     71    # All types and symbols that we need are attached to an object that we
     72    # can dispose of as needed.
     73 
     74    def __init__(self):
     75        self.d = None
     76        self.frame_enum_names = {}
     77        self.frame_class_types = {}
     78        super().__init__(None)
     79 
     80    # We take this bizarre approach to defer trying to look up any
     81    # symbols until absolutely needed.  Without this, the loading
     82    # approach taken by the gdb-tests would cause spurious exceptions.
     83    def __getattr__(self, name):
     84        if self.d is None:
     85            self.initialize()
     86        if name == "frame_type":
     87            return UnwinderTypeCacheFrameType(self)
     88        if name not in self.d:
     89            return None
     90        return self.d[name]
     91 
     92    def value(self, name):
     93        return long(gdb.lookup_symbol(name)[0].value())
     94 
     95    def jit_value(self, name):
     96        return self.value("js::jit::" + name)
     97 
     98    def initialize(self):
     99        self.d = {}
    100        self.d["FRAMETYPE_MASK"] = self.jit_value("FrameDescriptor::TypeMask")
    101        self.d["FRAMESIZE_SHIFT"] = self.jit_value("FRAMESIZE_SHIFT")
    102        self.d["FRAME_HEADER_SIZE_SHIFT"] = self.jit_value("FRAME_HEADER_SIZE_SHIFT")
    103        self.d["FRAME_HEADER_SIZE_MASK"] = self.jit_value("FRAME_HEADER_SIZE_MASK")
    104 
    105        self.compute_frame_info()
    106        commonFrameLayout = gdb.lookup_type("js::jit::CommonFrameLayout")
    107        self.d["typeCommonFrameLayout"] = commonFrameLayout
    108        self.d["typeCommonFrameLayoutPointer"] = commonFrameLayout.pointer()
    109        self.d["per_tls_context"] = gdb.lookup_global_symbol("js::TlsContext")
    110 
    111        self.d["void_starstar"] = gdb.lookup_type("void").pointer().pointer()
    112 
    113        jitframe = gdb.lookup_type("js::jit::JitFrameLayout")
    114        self.d["jitFrameLayoutPointer"] = jitframe.pointer()
    115 
    116        self.d["CalleeToken_Function"] = self.jit_value("CalleeToken_Function")
    117        self.d["CalleeToken_FunctionConstructing"] = self.jit_value(
    118            "CalleeToken_FunctionConstructing"
    119        )
    120        self.d["CalleeToken_Script"] = self.jit_value("CalleeToken_Script")
    121        self.d["JSScript"] = gdb.lookup_type("JSScript").pointer()
    122        self.d["Value"] = gdb.lookup_type("JS::Value")
    123 
    124        self.d["SOURCE_SLOT"] = self.value("js::ScriptSourceObject::SOURCE_SLOT")
    125        self.d["NativeObject"] = gdb.lookup_type("js::NativeObject").pointer()
    126        self.d["HeapSlot"] = gdb.lookup_type("js::HeapSlot").pointer()
    127        self.d["ScriptSource"] = gdb.lookup_type("js::ScriptSource").pointer()
    128 
    129        # ProcessExecutableMemory, used to identify if a pc is in the section
    130        # pre-allocated by the JIT.
    131        self.d["MaxCodeBytesPerProcess"] = self.jit_value("MaxCodeBytesPerProcess")
    132        self.d["execMemory"] = gdb.lookup_symbol("::execMemory")[0].value()
    133 
    134    # Compute maps related to jit frames.
    135    def compute_frame_info(self):
    136        t = gdb.lookup_type("enum js::jit::FrameType")
    137        for field in t.fields():
    138            # Strip off "js::jit::", remains: "FrameType::*".
    139            name = field.name[9:]
    140            enumval = long(field.enumval)
    141            self.d[name] = enumval
    142            self.frame_enum_names[enumval] = name
    143            class_type = gdb.lookup_type("js::jit::" + SizeOfFramePrefix[name])
    144            self.frame_class_types[enumval] = class_type.pointer()
    145 
    146 
    147 class FrameSymbol:
    148    "A symbol/value pair as expected from gdb frame decorators."
    149 
    150    def __init__(self, sym, val):
    151        self.sym = sym
    152        self.val = val
    153 
    154    def symbol(self):
    155        return self.sym
    156 
    157    def value(self):
    158        return self.val
    159 
    160 
    161 class JitFrameDecorator(FrameDecorator):
    162    """This represents a single JIT frame for the purposes of display.
    163    That is, the frame filter creates instances of this when it sees a
    164    JIT frame in the stack."""
    165 
    166    def __init__(self, base, info, cache):
    167        super().__init__(base)
    168        self.info = info
    169        self.cache = cache
    170 
    171    def _decode_jitframe(self, this_frame):
    172        calleetoken = long(this_frame["calleeToken_"])
    173        tag = calleetoken & 3
    174        calleetoken = calleetoken ^ tag
    175        function = None
    176        script = None
    177        if tag in {
    178            self.cache.CalleeToken_Function,
    179            self.cache.CalleeToken_FunctionConstructing,
    180        }:
    181            value = gdb.Value(calleetoken)
    182            function = get_function_name(value, self.cache)
    183            script = get_function_script(value, self.cache)
    184        elif tag == self.cache.CalleeToken_Script:
    185            script = gdb.Value(calleetoken).cast(self.cache.JSScript)
    186        return {"function": function, "script": script}
    187 
    188    def function(self):
    189        if self.info["name"] is None:
    190            return FrameDecorator.function(self)
    191        name = self.info["name"]
    192        result = "<<" + name
    193        # If we have a frame, we can extract the callee information
    194        # from it for display here.
    195        this_frame = self.info["this_frame"]
    196        if this_frame is not None:
    197            if gdb.types.has_field(this_frame.type.target(), "calleeToken_"):
    198                function = self._decode_jitframe(this_frame)["function"]
    199                if function is not None:
    200                    result = result + " " + function
    201        return result + ">>"
    202 
    203    def filename(self):
    204        this_frame = self.info["this_frame"]
    205        if this_frame is not None:
    206            if gdb.types.has_field(this_frame.type.target(), "calleeToken_"):
    207                script = self._decode_jitframe(this_frame)["script"]
    208                if script is not None:
    209                    obj = script["sourceObject_"]["value"]
    210                    # Verify that this is a ScriptSource object.
    211                    # FIXME should also deal with wrappers here.
    212                    nativeobj = obj.cast(self.cache.NativeObject)
    213                    # See bug 987069 and despair.  At least this
    214                    # approach won't give exceptions.
    215                    class_name = nativeobj["group_"]["value"]["clasp_"]["name"].string(
    216                        "ISO-8859-1"
    217                    )
    218                    if class_name != "ScriptSource":
    219                        return FrameDecorator.filename(self)
    220                    scriptsourceobj = (nativeobj + 1).cast(self.cache.HeapSlot)[
    221                        self.cache.SOURCE_SLOT
    222                    ]
    223                    scriptsource = scriptsourceobj["value"]["asBits_"] << 1
    224                    scriptsource = scriptsource.cast(self.cache.ScriptSource)
    225                    return scriptsource["filename_"]["mTuple"]["mFirstA"].string()
    226        return FrameDecorator.filename(self)
    227 
    228    def frame_args(self):
    229        this_frame = self.info["this_frame"]
    230        if this_frame is None:
    231            return FrameDecorator.frame_args(self)
    232        if not gdb.types.has_field(this_frame.type.target(), "numActualArgs_"):
    233            return FrameDecorator.frame_args(self)
    234        # See if this is a function call.
    235        if self._decode_jitframe(this_frame)["function"] is None:
    236            return FrameDecorator.frame_args(self)
    237        # Construct and return an iterable of all the arguments.
    238        result = []
    239        num_args = long(this_frame["numActualArgs_"])
    240        # Sometimes we see very large values here, so truncate it to
    241        # bypass the damage.
    242        num_args = min(num_args, 10)
    243        args_ptr = (this_frame + 1).cast(self.cache.Value.pointer())
    244        for i in range(num_args + 1):
    245            # Synthesize names, since there doesn't seem to be
    246            # anything better to do.
    247            if i == 0:
    248                name = "this"
    249            else:
    250                name = "arg%d" % i
    251            result.append(FrameSymbol(name, args_ptr[i]))
    252        return result
    253 
    254 
    255 class SpiderMonkeyFrameFilter:
    256    "A frame filter for SpiderMonkey."
    257 
    258    # |state_holder| is either None, or an instance of
    259    # SpiderMonkeyUnwinder.  If the latter, then this class will
    260    # reference the |unwinder_state| attribute to find the current
    261    # unwinder state.
    262    def __init__(self, cache, state_holder):
    263        self.name = "SpiderMonkey"
    264        self.enabled = True
    265        self.priority = 100
    266        self.state_holder = state_holder
    267        self.cache = cache
    268 
    269    def maybe_wrap_frame(self, frame):
    270        if self.state_holder is None or self.state_holder.unwinder_state is None:
    271            return frame
    272        base = frame.inferior_frame()
    273        info = self.state_holder.unwinder_state.get_frame(base)
    274        if info is None:
    275            return frame
    276        return JitFrameDecorator(frame, info, self.cache)
    277 
    278    def filter(self, frame_iter):
    279        return imap(self.maybe_wrap_frame, frame_iter)
    280 
    281 
    282 class SpiderMonkeyFrameId:
    283    "A frame id class, as specified by the gdb unwinder API."
    284 
    285    def __init__(self, sp, pc):
    286        self.sp = sp
    287        self.pc = pc
    288 
    289 
    290 class UnwinderState:
    291    """This holds all the state needed during a given unwind.  Each time a
    292    new unwind is done, a new instance of this class is created.  It
    293    keeps track of all the state needed to unwind JIT frames.  Note that
    294    this class is not directly instantiated.
    295 
    296    This is a base class, and must be specialized for each target
    297    architecture, both because we need to use arch-specific register
    298    names, and because entry frame unwinding is arch-specific.
    299    See https://sourceware.org/bugzilla/show_bug.cgi?id=19286 for info
    300    about the register name issue.
    301 
    302    Each subclass must define SP_REGISTER, PC_REGISTER, and
    303    SENTINEL_REGISTER (see x64UnwinderState for info); and implement
    304    unwind_entry_frame_registers."""
    305 
    306    def __init__(self, typecache):
    307        self.next_sp = None
    308        self.next_type = None
    309        self.activation = None
    310        # An unwinder instance is specific to a thread.  Record the
    311        # selected thread for later verification.
    312        self.thread = gdb.selected_thread()
    313        self.frame_map = {}
    314        self.typecache = typecache
    315 
    316    # If the given gdb.Frame was created by this unwinder, return the
    317    # corresponding informational dictionary for the frame.
    318    # Otherwise, return None.  This is used by the frame filter to
    319    # display extra information about the frame.
    320    def get_frame(self, frame):
    321        sp = long(frame.read_register(self.SP_REGISTER))
    322        if sp in self.frame_map:
    323            return self.frame_map[sp]
    324        return None
    325 
    326    # Add information about a frame to the frame map.  This map is
    327    # queried by |self.get_frame|.  |sp| is the frame's stack pointer,
    328    # and |name| the frame's type as a string, e.g. "FrameType::Exit".
    329    def add_frame(self, sp, name=None, this_frame=None):
    330        self.frame_map[long(sp)] = {"name": name, "this_frame": this_frame}
    331 
    332    # See whether |pc| is claimed by the Jit.
    333    def is_jit_address(self, pc):
    334        execMem = self.typecache.execMemory
    335        base = long(execMem["base_"])
    336        length = self.typecache.MaxCodeBytesPerProcess
    337 
    338        # If the base pointer is null, then no memory got allocated yet.
    339        if long(base) == 0:
    340            return False
    341 
    342        # If allocated, then we allocated MaxCodeBytesPerProcess.
    343        return base <= pc < base + length
    344 
    345    # Check whether |self| is valid for the selected thread.
    346    def check(self):
    347        return gdb.selected_thread() is self.thread
    348 
    349    # Essentially js::TlsContext.get().
    350    def get_tls_context(self):
    351        return self.typecache.per_tls_context.value()["mValue"]
    352 
    353    # |common| is a pointer to a CommonFrameLayout object.  Return a
    354    # tuple (local_size, header_size, frame_type), where |size| is the
    355    # integer size of the previous frame's locals; |header_size| is
    356    # the size of this frame's header; and |frame_type| is an integer
    357    # representing the previous frame's type.
    358    def unpack_descriptor(self, common):
    359        value = long(common["descriptor_"])
    360        local_size = value >> self.typecache.FRAMESIZE_SHIFT
    361        header_size = (
    362            value >> self.typecache.FRAME_HEADER_SIZE_SHIFT
    363        ) & self.typecache.FRAME_HEADER_SIZE_MASK
    364        header_size = header_size * self.typecache.void_starstar.sizeof
    365        frame_type = long(value & self.typecache.FRAMETYPE_MASK)
    366        if frame_type == self.typecache.frame_type.CppToJSJit:
    367            # Trampoline-x64.cpp pushes a JitFrameLayout object, but
    368            # the stack pointer is actually adjusted as if a
    369            # CommonFrameLayout object was pushed.
    370            header_size = self.typecache.typeCommonFrameLayout.sizeof
    371        return (local_size, header_size, frame_type)
    372 
    373    # Create a new frame for gdb.  This makes a new unwind info object
    374    # and fills it in, then returns it.  It also registers any
    375    # pertinent information with the frame filter for later display.
    376    #
    377    # |pc| is the PC from the pending frame
    378    # |sp| is the stack pointer to use
    379    # |frame| points to the CommonFrameLayout object
    380    # |frame_type| is a integer, one of the |enum FrameType| values,
    381    # describing the current frame.
    382    # |pending_frame| is the pending frame (see the gdb unwinder
    383    # documentation).
    384    def create_frame(self, pc, sp, frame, frame_type, pending_frame):
    385        # Make a frame_id that claims that |frame| is sort of like a
    386        # frame pointer for this frame.
    387        frame_id = SpiderMonkeyFrameId(frame, pc)
    388 
    389        # Read the frame layout object to find the next such object.
    390        # This lets us unwind the necessary registers for the next
    391        # frame, and also update our internal state to match.
    392        common = frame.cast(self.typecache.typeCommonFrameLayoutPointer)
    393        next_pc = common["returnAddress_"]
    394        (local_size, header_size, next_type) = self.unpack_descriptor(common)
    395        next_sp = frame + header_size + local_size
    396 
    397        # Compute the type of the next oldest frame's descriptor.
    398        this_class_type = self.typecache.frame_class_types[frame_type]
    399        this_frame = frame.cast(this_class_type)
    400 
    401        # Register this frame so the frame filter can find it.  This
    402        # is registered using SP because we don't have any other good
    403        # approach -- you can't get the frame id from a gdb.Frame.
    404        # https://sourceware.org/bugzilla/show_bug.cgi?id=19800
    405        frame_name = self.typecache.frame_enum_names[frame_type]
    406        self.add_frame(sp, name=frame_name, this_frame=this_frame)
    407 
    408        # Update internal state for the next unwind.
    409        self.next_sp = next_sp
    410        self.next_type = next_type
    411 
    412        unwind_info = pending_frame.create_unwind_info(frame_id)
    413        unwind_info.add_saved_register(self.PC_REGISTER, next_pc)
    414        unwind_info.add_saved_register(self.SP_REGISTER, next_sp)
    415        # FIXME it would be great to unwind any other registers here.
    416        return unwind_info
    417 
    418    # Unwind an "ordinary" JIT frame.  This is used for JIT frames
    419    # other than enter and exit frames.  Returns the newly-created
    420    # unwind info for gdb.
    421    def unwind_ordinary(self, pc, pending_frame):
    422        return self.create_frame(
    423            pc, self.next_sp, self.next_sp, self.next_type, pending_frame
    424        )
    425 
    426    # Unwind an exit frame.  Returns None if this cannot be done;
    427    # otherwise returns the newly-created unwind info for gdb.
    428    def unwind_exit_frame(self, pc, pending_frame):
    429        if self.activation == 0:
    430            # Reached the end of the list.
    431            return None
    432        elif self.activation is None:
    433            cx = self.get_tls_context()
    434            self.activation = cx["jitActivation"]["value"]
    435        else:
    436            self.activation = self.activation["prevJitActivation_"]
    437 
    438        packedExitFP = self.activation["packedExitFP_"]
    439        if packedExitFP == 0:
    440            return None
    441 
    442        exit_sp = pending_frame.read_register(self.SP_REGISTER)
    443        frame_type = self.typecache.frame_type.Exit
    444        return self.create_frame(pc, exit_sp, packedExitFP, frame_type, pending_frame)
    445 
    446    # A wrapper for unwind_entry_frame_registers that handles
    447    # architecture-independent boilerplate.
    448    def unwind_entry_frame(self, pc, pending_frame):
    449        sp = self.next_sp
    450        # Notify the frame filter.
    451        self.add_frame(sp, name="FrameType::CppToJSJit")
    452        # Make an unwind_info for the per-architecture code to fill in.
    453        frame_id = SpiderMonkeyFrameId(sp, pc)
    454        unwind_info = pending_frame.create_unwind_info(frame_id)
    455        self.unwind_entry_frame_registers(sp, unwind_info)
    456        self.next_sp = None
    457        self.next_type = None
    458        return unwind_info
    459 
    460    # The main entry point that is called to try to unwind a JIT frame
    461    # of any type.  Returns None if this cannot be done; otherwise
    462    # returns the newly-created unwind info for gdb.
    463    def unwind(self, pending_frame):
    464        pc = pending_frame.read_register(self.PC_REGISTER)
    465 
    466        # If the jit does not claim this address, bail.  GDB defers to our
    467        # unwinder by default, but we don't really want that kind of power.
    468        if not self.is_jit_address(long(pc)):
    469            return None
    470 
    471        if self.next_sp is not None:
    472            if self.next_type == self.typecache.frame_type.CppToJSJit:
    473                return self.unwind_entry_frame(pc, pending_frame)
    474            return self.unwind_ordinary(pc, pending_frame)
    475        # Maybe we've found an exit frame.  FIXME I currently don't
    476        # know how to identify these precisely, so we'll just hope for
    477        # the time being.
    478        return self.unwind_exit_frame(pc, pending_frame)
    479 
    480 
    481 class x64UnwinderState(UnwinderState):
    482    "The UnwinderState subclass for x86-64."
    483 
    484    SP_REGISTER = "rsp"
    485    PC_REGISTER = "rip"
    486 
    487    # A register unique to this architecture, that is also likely to
    488    # have been saved in any frame.  The best thing to use here is
    489    # some arch-specific name for PC or SP.
    490    SENTINEL_REGISTER = "rip"
    491 
    492    # Must be in sync with Trampoline-x64.cpp:generateEnterJIT.  Note
    493    # that rip isn't pushed there explicitly, but rather by the
    494    # previous function's call.
    495    PUSHED_REGS = ["r15", "r14", "r13", "r12", "rbx", "rbp", "rip"]
    496 
    497    # Fill in the unwound registers for an entry frame.
    498    def unwind_entry_frame_registers(self, sp, unwind_info):
    499        sp = sp.cast(self.typecache.void_starstar)
    500        # Skip the "result" push.
    501        sp = sp + 1
    502        for reg in self.PUSHED_REGS:
    503            data = sp.dereference()
    504            sp = sp + 1
    505            unwind_info.add_saved_register(reg, data)
    506            if reg == "rbp":
    507                unwind_info.add_saved_register(self.SP_REGISTER, sp)
    508 
    509 
    510 class SpiderMonkeyUnwinder(Unwinder):
    511    """The unwinder object.  This provides the "user interface" to the JIT
    512    unwinder, and also handles constructing or destroying UnwinderState
    513    objects as needed."""
    514 
    515    # A list of all the possible unwinders.  See |self.make_unwinder|.
    516    UNWINDERS = [x64UnwinderState]
    517 
    518    def __init__(self, typecache):
    519        super().__init__("SpiderMonkey")
    520        self.typecache = typecache
    521        self.unwinder_state = None
    522 
    523        # Disabled by default until we figure out issues in gdb.
    524        self.enabled = False
    525        gdb.write(
    526            "SpiderMonkey unwinder is disabled by default, to enable it type:\n"
    527            + "\tenable unwinder .* SpiderMonkey\n"
    528        )
    529        # Some versions of gdb did not flush the internal frame cache
    530        # when enabling or disabling an unwinder.  This was fixed in
    531        # the same release of gdb that added the breakpoint_created
    532        # event.
    533        if not hasattr(gdb.events, "breakpoint_created"):
    534            gdb.write("\tflushregs\n")
    535 
    536        # We need to invalidate the unwinder state whenever the
    537        # inferior starts executing.  This avoids having a stale
    538        # cache.
    539        gdb.events.cont.connect(self.invalidate_unwinder_state)
    540        assert self.test_sentinels()
    541 
    542    def test_sentinels(self):
    543        # Self-check.
    544        regs = {}
    545        for unwinder in self.UNWINDERS:
    546            if unwinder.SENTINEL_REGISTER in regs:
    547                return False
    548            regs[unwinder.SENTINEL_REGISTER] = 1
    549        return True
    550 
    551    def make_unwinder(self, pending_frame):
    552        # gdb doesn't provide a good way to find the architecture.
    553        # See https://sourceware.org/bugzilla/show_bug.cgi?id=19399
    554        # So, we look at each known architecture and see if the
    555        # corresponding "unique register" is known.
    556        for unwinder in self.UNWINDERS:
    557            try:
    558                pending_frame.read_register(unwinder.SENTINEL_REGISTER)
    559            except Exception:
    560                # Failed to read the register, so let's keep going.
    561                # This is more fragile than it might seem, because it
    562                # fails if the sentinel register wasn't saved in the
    563                # previous frame.
    564                continue
    565            return unwinder(self.typecache)
    566        return None
    567 
    568    def __call__(self, pending_frame):
    569        if self.unwinder_state is None or not self.unwinder_state.check():
    570            self.unwinder_state = self.make_unwinder(pending_frame)
    571        if not self.unwinder_state:
    572            return None
    573        return self.unwinder_state.unwind(pending_frame)
    574 
    575    def invalidate_unwinder_state(self, *args, **kwargs):
    576        self.unwinder_state = None
    577 
    578 
    579 def register_unwinder(objfile):
    580    """Register the unwinder and frame filter with |objfile|.  If |objfile|
    581    is None, register them globally."""
    582 
    583    type_cache = UnwinderTypeCache()
    584    unwinder = None
    585    # This currently only works on Linux, due to parse_proc_maps.
    586    if _have_unwinder and platform.system() == "Linux":
    587        unwinder = SpiderMonkeyUnwinder(type_cache)
    588        gdb.unwinder.register_unwinder(objfile, unwinder, replace=True)
    589    # We unconditionally register the frame filter, because at some
    590    # point we'll add interpreter frame filtering.
    591    filt = SpiderMonkeyFrameFilter(type_cache, unwinder)
    592    if objfile is None:
    593        objfile = gdb
    594    objfile.frame_filters[filt.name] = filt