Home | History | Annotate | Line # | Download | only in gdb.base
      1 # Copyright (C) 2021-2024 Free Software Foundation, Inc.
      2 
      3 # This program is free software; you can redistribute it and/or modify
      4 # it under the terms of the GNU General Public License as published by
      5 # the Free Software Foundation; either version 3 of the License, or
      6 # (at your option) any later version.
      7 #
      8 # This program is distributed in the hope that it will be useful,
      9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11 # GNU General Public License for more details.
     12 #
     13 # You should have received a copy of the GNU General Public License
     14 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15 
     16 import gdb
     17 from gdb.unwinder import Unwinder
     18 
     19 # Set this to the stack level the backtrace should be corrupted at.
     20 # This will only work for frame 1, 3, or 5 in the test this unwinder
     21 # was written for.
     22 stop_at_level = None
     23 
     24 # Set this to the stack frame size of frames 1, 3, and 5.  These
     25 # frames will all have the same stack frame size as they are the same
     26 # function called recursively.
     27 stack_adjust = None
     28 
     29 
     30 class FrameId(object):
     31     def __init__(self, sp, pc):
     32         self._sp = sp
     33         self._pc = pc
     34 
     35     @property
     36     def sp(self):
     37         return self._sp
     38 
     39     @property
     40     def pc(self):
     41         return self._pc
     42 
     43 
     44 class TestUnwinder(Unwinder):
     45     def __init__(self):
     46         Unwinder.__init__(self, "stop at level")
     47 
     48     def __call__(self, pending_frame):
     49         global stop_at_level
     50         global stack_adjust
     51 
     52         if stop_at_level is None or pending_frame.level() != stop_at_level:
     53             return None
     54 
     55         if stack_adjust is None:
     56             raise gdb.GdbError("invalid stack_adjust")
     57 
     58         if not stop_at_level in [1, 3, 5]:
     59             raise gdb.GdbError("invalid stop_at_level")
     60 
     61         sp_desc = pending_frame.architecture().registers().find("sp")
     62         sp = pending_frame.read_register(sp_desc) + stack_adjust
     63         pc = (gdb.lookup_symbol("normal_func"))[0].value().address
     64         unwinder = pending_frame.create_unwind_info(FrameId(sp, pc))
     65 
     66         for reg in pending_frame.architecture().registers("general"):
     67             val = pending_frame.read_register(reg)
     68             unwinder.add_saved_register(reg, val)
     69         return unwinder
     70 
     71 
     72 gdb.unwinder.register_unwinder(None, TestUnwinder(), True)
     73 
     74 # When loaded, it is expected that the stack looks like:
     75 #
     76 #   main -> normal_func -> inline_func -> normal_func -> inline_func -> normal_func -> inline_func
     77 #
     78 # Compute the stack frame size of normal_func, which has inline_func
     79 # inlined within it.
     80 f0 = gdb.newest_frame()
     81 f1 = f0.older()
     82 f2 = f1.older()
     83 f0_sp = f0.read_register("sp")
     84 f2_sp = f2.read_register("sp")
     85 stack_adjust = f2_sp - f0_sp
     86