1# Copyright (C) 2015-2019 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 16import gdb 17from gdb.unwinder import Unwinder 18 19class FrameId(object): 20 21 def __init__(self, sp, pc): 22 self._sp = sp 23 self._pc = pc 24 25 @property 26 def sp(self): 27 return self._sp 28 29 @property 30 def pc(self): 31 return self._pc 32 33 34class TestUnwinder(Unwinder): 35 AMD64_RBP = 6 36 AMD64_RSP = 7 37 AMD64_RIP = 16 38 39 def __init__(self): 40 Unwinder.__init__(self, "test unwinder") 41 self.char_ptr_t = gdb.lookup_type("unsigned char").pointer() 42 self.char_ptr_ptr_t = self.char_ptr_t.pointer() 43 44 def _read_word(self, address): 45 return address.cast(self.char_ptr_ptr_t).dereference() 46 47 def __call__(self, pending_frame): 48 """Test unwinder written in Python. 49 50 This unwinder can unwind the frames that have been deliberately 51 corrupted in a specific way (functions in the accompanying 52 py-unwind.c file do that.) 53 This code is only on AMD64. 54 On AMD64 $RBP points to the innermost frame (unless the code 55 was compiled with -fomit-frame-pointer), which contains the 56 address of the previous frame at offset 0. The functions 57 deliberately corrupt their frames as follows: 58 Before After 59 Corruption: Corruption: 60 +--------------+ +--------------+ 61 RBP-8 | | | Previous RBP | 62 +--------------+ +--------------+ 63 RBP + Previous RBP | | RBP | 64 +--------------+ +--------------+ 65 RBP+8 | Return RIP | | Return RIP | 66 +--------------+ +--------------+ 67 Old SP | | | | 68 69 This unwinder recognizes the corrupt frames by checking that 70 *RBP == RBP, and restores previous RBP from the word above it. 71 """ 72 try: 73 # NOTE: the registers in Unwinder API can be referenced 74 # either by name or by number. The code below uses both 75 # to achieve more coverage. 76 bp = pending_frame.read_register("rbp").cast(self.char_ptr_t) 77 if self._read_word(bp) != bp: 78 return None 79 # Found the frame that the test program has corrupted for us. 80 # The correct BP for the outer frame has been saved one word 81 # above, previous IP and SP are at the expected places. 82 previous_bp = self._read_word(bp - 8) 83 previous_ip = self._read_word(bp + 8) 84 previous_sp = bp + 16 85 86 frame_id = FrameId( 87 pending_frame.read_register(TestUnwinder.AMD64_RSP), 88 pending_frame.read_register(TestUnwinder.AMD64_RIP)) 89 unwind_info = pending_frame.create_unwind_info(frame_id) 90 unwind_info.add_saved_register(TestUnwinder.AMD64_RBP, 91 previous_bp) 92 unwind_info.add_saved_register("rip", previous_ip) 93 unwind_info.add_saved_register("rsp", previous_sp) 94 return unwind_info 95 except (gdb.error, RuntimeError): 96 return None 97 98gdb.unwinder.register_unwinder(None, TestUnwinder(), True) 99print("Python script imported") 100