1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
# Copyright (C) 2021-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Disassembler related module."""
import _gdb.disassembler
# Re-export everything from the _gdb.disassembler module, which is
# defined within GDB's C++ code. Note that two indicators are needed
# here to silence flake8.
from _gdb.disassembler import * # noqa: F401,F403
import gdb
# Module global dictionary of gdb.disassembler.Disassembler objects.
# The keys of this dictionary are bfd architecture names, or the
# special value None.
#
# When a request to disassemble comes in we first lookup the bfd
# architecture name from the gdbarch, if that name exists in this
# dictionary then we use that Disassembler object.
#
# If there's no architecture specific disassembler then we look for
# the key None in this dictionary, and if that key exists, we use that
# disassembler.
#
# If none of the above checks found a suitable disassembler, then no
# disassembly is performed in Python.
_disassemblers_dict = {}
class Disassembler(object):
"""A base class from which all user implemented disassemblers must
inherit."""
def __init__(self, name):
"""Constructor. Takes a name, which should be a string, which can be
used to identify this disassembler in diagnostic messages."""
self.name = name
def __call__(self, info):
"""A default implementation of __call__. All sub-classes must
override this method. Calling this default implementation will throw
a NotImplementedError exception."""
raise NotImplementedError("Disassembler.__call__")
def register_disassembler(disassembler, architecture=None):
"""Register a disassembler. DISASSEMBLER is a sub-class of
gdb.disassembler.Disassembler. ARCHITECTURE is either None or a
string, the name of an architecture known to GDB.
DISASSEMBLER is registered as a disassembler for ARCHITECTURE, or
all architectures when ARCHITECTURE is None.
Returns the previous disassembler registered with this
ARCHITECTURE value.
"""
if not isinstance(disassembler, Disassembler) and disassembler is not None:
raise TypeError("disassembler should sub-class gdb.disassembler.Disassembler")
old = None
if architecture in _disassemblers_dict:
old = _disassemblers_dict[architecture]
del _disassemblers_dict[architecture]
if disassembler is not None:
_disassemblers_dict[architecture] = disassembler
# Call the private _set_enabled function within the
# _gdb.disassembler module. This function sets a global flag
# within GDB's C++ code that enables or disables the Python
# disassembler functionality, this improves performance of the
# disassembler by avoiding unneeded calls into Python when we know
# that no disassemblers are registered.
_gdb.disassembler._set_enabled(len(_disassemblers_dict) > 0)
return old
def _print_insn(info):
"""This function is called by GDB when it wants to disassemble an
instruction. INFO describes the instruction to be
disassembled."""
def lookup_disassembler(arch):
name = arch.name()
if name is None:
return None
if name in _disassemblers_dict:
return _disassemblers_dict[name]
if None in _disassemblers_dict:
return _disassemblers_dict[None]
return None
disassembler = lookup_disassembler(info.architecture)
if disassembler is None:
return None
return disassembler(info)
class maint_info_py_disassemblers_cmd(gdb.Command):
"""
List all registered Python disassemblers.
List the name of all registered Python disassemblers, next to the
name of the architecture for which the disassembler is registered.
The global Python disassembler is listed next to the string
'GLOBAL'.
The disassembler that matches the architecture of the currently
selected inferior will be marked, this is an indication of which
disassembler will be invoked if any disassembly is performed in
the current inferior.
"""
def __init__(self):
super().__init__("maintenance info python-disassemblers", gdb.COMMAND_USER)
def invoke(self, args, from_tty):
# If no disassemblers are registered, tell the user.
if len(_disassemblers_dict) == 0:
print("No Python disassemblers registered.")
return
# Figure out the longest architecture name, so we can
# correctly format the table of results.
longest_arch_name = 0
for architecture in _disassemblers_dict:
if architecture is not None:
name = _disassemblers_dict[architecture].name
if len(name) > longest_arch_name:
longest_arch_name = len(name)
# Figure out the name of the current architecture. There
# should always be a current inferior, but if, somehow, there
# isn't, then leave curr_arch as the empty string, which will
# not then match against any architecture in the dictionary.
curr_arch = ""
if gdb.selected_inferior() is not None:
curr_arch = gdb.selected_inferior().architecture().name()
# Now print the dictionary of registered disassemblers out to
# the user.
match_tag = "\t(Matches current architecture)"
fmt_len = max(longest_arch_name, len("Architecture"))
format_string = "{:" + str(fmt_len) + "s} {:s}"
print(format_string.format("Architecture", "Disassember Name"))
for architecture in _disassemblers_dict:
if architecture is not None:
name = _disassemblers_dict[architecture].name
if architecture == curr_arch:
name += match_tag
match_tag = ""
print(format_string.format(architecture, name))
if None in _disassemblers_dict:
name = _disassemblers_dict[None].name + match_tag
print(format_string.format("GLOBAL", name))
maint_info_py_disassemblers_cmd()
|