· 6 years ago · Oct 07, 2019, 01:04 AM
1python
2
3# GDB dashboard - Modular visual interface for GDB in Python.
4#
5# https://github.com/cyrus-and/gdb-dashboard
6
7import ast
8import math
9import os
10import re
11import struct
12import traceback
13
14# Common attributes ------------------------------------------------------------
15
16class R():
17
18 @staticmethod
19 def attributes():
20 return {
21 # miscellaneous
22 'ansi': {
23 'doc': 'Control the ANSI output of the dashboard.',
24 'default': True,
25 'type': bool
26 },
27 'syntax_highlighting': {
28 'doc': '''Pygments style to use for syntax highlighting.
29
30Using an empty string (or a name not in the list) disables this feature. The
31list of all the available styles can be obtained with (from GDB itself):
32
33 python from pygments.styles import *
34 python for style in get_all_styles(): print(style)''',
35 'default': 'monokai'
36 },
37 'discard_scrollback': {
38 'doc': '''Discard the scrollback buffer at each redraw.
39
40This makes scrolling less confusing by discarding the previously printed
41dashboards but only works with certain terminals.''',
42 'default': True,
43 'type': bool
44 },
45 # values formatting
46 'compact_values': {
47 'doc': 'Display complex objects in a single line.',
48 'default': True,
49 'type': bool
50 },
51 'max_value_length': {
52 'doc': 'Maximum length of displayed values before truncation.',
53 'default': 100,
54 'type': int
55 },
56 'value_truncation_string': {
57 'doc': 'String to use to mark value truncation.',
58 'default': '…',
59 },
60 'dereference': {
61 'doc': 'Annotate pointers with the pointed value.',
62 'default': True,
63 'type': bool
64 },
65 # prompt
66 'prompt': {
67 'doc': '''GDB prompt.
68
69This value is used as a Python format string where `{status}` is expanded with
70the substitution of either `prompt_running` or `prompt_not_running` attributes,
71according to the target program status. The resulting string must be a valid GDB
72prompt, see the command `python print(gdb.prompt.prompt_help())`''',
73 'default': '{status}'
74 },
75 'prompt_running': {
76 'doc': '''Define the value of `{status}` when the target program is running.
77
78See the `prompt` attribute. This value is used as a Python format string where
79`{pid}` is expanded with the process identifier of the target program.''',
80 'default': '\[\e[1;35m\]>>>\[\e[0m\]'
81 },
82 'prompt_not_running': {
83 'doc': '''Define the value of `{status}` when the target program is running.
84
85See the `prompt` attribute. This value is used as a Python format string.''',
86 'default': '\[\e[1;30m\]>>>\[\e[0m\]'
87 },
88 # divider
89 'divider_fill_char_primary': {
90 'doc': 'Filler around the label for primary dividers',
91 'default': '─'
92 },
93 'divider_fill_char_secondary': {
94 'doc': 'Filler around the label for secondary dividers',
95 'default': '─'
96 },
97 'divider_fill_style_primary': {
98 'doc': 'Style for `divider_fill_char_primary`',
99 'default': '36'
100 },
101 'divider_fill_style_secondary': {
102 'doc': 'Style for `divider_fill_char_secondary`',
103 'default': '1;30'
104 },
105 'divider_label_style_on_primary': {
106 'doc': 'Label style for non-empty primary dividers',
107 'default': '1;33'
108 },
109 'divider_label_style_on_secondary': {
110 'doc': 'Label style for non-empty secondary dividers',
111 'default': '1;37'
112 },
113 'divider_label_style_off_primary': {
114 'doc': 'Label style for empty primary dividers',
115 'default': '33'
116 },
117 'divider_label_style_off_secondary': {
118 'doc': 'Label style for empty secondary dividers',
119 'default': '1;30'
120 },
121 'divider_label_skip': {
122 'doc': 'Gap between the aligning border and the label.',
123 'default': 3,
124 'type': int,
125 'check': check_ge_zero
126 },
127 'divider_label_margin': {
128 'doc': 'Number of spaces around the label.',
129 'default': 1,
130 'type': int,
131 'check': check_ge_zero
132 },
133 'divider_label_align_right': {
134 'doc': 'Label alignment flag.',
135 'default': False,
136 'type': bool
137 },
138 # common styles
139 'style_selected_1': {
140 'default': '1;32'
141 },
142 'style_selected_2': {
143 'default': '32'
144 },
145 'style_low': {
146 'default': '1;30'
147 },
148 'style_high': {
149 'default': '1;37'
150 },
151 'style_error': {
152 'default': '31'
153 },
154 'style_critical': {
155 'default': '0;41'
156 }
157 }
158
159# Common -----------------------------------------------------------------------
160
161class Beautifier():
162
163 def __init__(self, hint, tab_size=4):
164 self.tab_spaces = ' ' * tab_size
165 self.active = False
166 if not R.ansi or not R.syntax_highlighting:
167 return
168 # attempt to set up Pygments
169 try:
170 import pygments
171 from pygments.lexers import GasLexer, NasmLexer
172 from pygments.formatters import Terminal256Formatter
173 if hint == 'att':
174 self.lexer = GasLexer()
175 elif hint == 'intel':
176 self.lexer = NasmLexer()
177 else:
178 from pygments.lexers import get_lexer_for_filename
179 self.lexer = get_lexer_for_filename(hint, stripnl=False)
180 self.formatter = Terminal256Formatter(style=R.syntax_highlighting)
181 self.active = True
182 except ImportError:
183 # Pygments not available
184 pass
185 except pygments.util.ClassNotFound:
186 # no lexer for this file or invalid style
187 pass
188
189 def process(self, source):
190 # convert tabs anyway
191 source = source.replace('\t', self.tab_spaces)
192 if self.active:
193 import pygments
194 source = pygments.highlight(source, self.lexer, self.formatter)
195 return source.rstrip('\n')
196
197def run(command):
198 return gdb.execute(command, to_string=True)
199
200def ansi(string, style):
201 if R.ansi:
202 return '\x1b[{}m{}\x1b[0m'.format(style, string)
203 else:
204 return string
205
206def divider(width, label='', primary=False, active=True):
207 if primary:
208 divider_fill_style = R.divider_fill_style_primary
209 divider_fill_char = R.divider_fill_char_primary
210 divider_label_style_on = R.divider_label_style_on_primary
211 divider_label_style_off = R.divider_label_style_off_primary
212 else:
213 divider_fill_style = R.divider_fill_style_secondary
214 divider_fill_char = R.divider_fill_char_secondary
215 divider_label_style_on = R.divider_label_style_on_secondary
216 divider_label_style_off = R.divider_label_style_off_secondary
217 if label:
218 if active:
219 divider_label_style = divider_label_style_on
220 else:
221 divider_label_style = divider_label_style_off
222 skip = R.divider_label_skip
223 margin = R.divider_label_margin
224 before = ansi(divider_fill_char * skip, divider_fill_style)
225 middle = ansi(label, divider_label_style)
226 after_length = width - len(label) - skip - 2 * margin
227 after = ansi(divider_fill_char * after_length, divider_fill_style)
228 if R.divider_label_align_right:
229 before, after = after, before
230 return ''.join([before, ' ' * margin, middle, ' ' * margin, after])
231 else:
232 return ansi(divider_fill_char * width, divider_fill_style)
233
234def check_gt_zero(x):
235 return x > 0
236
237def check_ge_zero(x):
238 return x >= 0
239
240def to_unsigned(value, size=8):
241 # values from GDB can be used transparently but are not suitable for
242 # being printed as unsigned integers, so a conversion is needed
243 mask = (2 ** (size * 8)) - 1
244 return int(value.cast(gdb.Value(mask).type)) & mask
245
246def to_string(value):
247 # attempt to convert an inferior value to string; OK when (Python 3 ||
248 # simple ASCII); otherwise (Python 2.7 && not ASCII) encode the string as
249 # utf8
250 try:
251 value_string = str(value)
252 except UnicodeEncodeError:
253 value_string = unicode(value).encode('utf8')
254 except gdb.error as e:
255 value_string = ansi(e, R.style_error)
256 return value_string
257
258def format_address(address):
259 pointer_size = gdb.parse_and_eval('$pc').type.sizeof
260 return ('0x{{:0{}x}}').format(pointer_size * 2).format(address)
261
262def format_value(value, compact=None):
263 # format references as referenced values
264 # (TYPE_CODE_RVALUE_REF is not supported by old GDB)
265 if value.type.code in (getattr(gdb, 'TYPE_CODE_REF', None),
266 getattr(gdb, 'TYPE_CODE_RVALUE_REF', None)):
267 value = value.referenced_value()
268 # format the value
269 out = to_string(value)
270 # dereference up to the actual value if requested
271 if R.dereference and value.type.code == gdb.TYPE_CODE_PTR:
272 while value.type.code == gdb.TYPE_CODE_PTR:
273 try:
274 value = value.dereference()
275 except gdb.error as e:
276 break
277 else:
278 formatted = to_string(value)
279 out += '{} {}'.format(ansi(':', R.style_low), formatted)
280 # compact the value
281 if compact is not None and compact or R.compact_values:
282 out = re.sub(r'$\s*', '', out, flags=re.MULTILINE)
283 # truncate the value
284 if R.max_value_length > 0 and len(out) > R.max_value_length:
285 out = out[0:R.max_value_length] + ansi(R.value_truncation_string, R.style_critical)
286 return out
287
288# XXX parsing the output of `info breakpoints` is apparently the best option
289# right now, see: https://sourceware.org/bugzilla/show_bug.cgi?id=18385
290# XXX GDB version 7.11 (quire recent) does not have the pending field, so
291# fall back to the parsed information
292def fetch_breakpoints(watchpoints=False, pending=False):
293 # fetch breakpoints addresses
294 parsed_breakpoints = dict()
295 for line in run('info breakpoints').split('\n'):
296 # just keep numbered lines
297 if not line or not line[0].isdigit():
298 continue
299 # extract breakpoint number, address and pending status
300 fields = line.split()
301 number = int(fields[0], 16)
302 is_pending = fields[4] == '<PENDING>'
303 try:
304 address = int(fields[4], 16) if len(fields) >= 5 and fields[1] == 'breakpoint' else None
305 except ValueError:
306 address = None
307 parsed_breakpoints[number] = address, is_pending
308 # fetch breakpoints from the API and complement with address and source
309 # information
310 breakpoints = []
311 # XXX in older versions gdb.breakpoints() returns None
312 for gdb_breakpoint in gdb.breakpoints() or []:
313 address, is_pending = parsed_breakpoints[gdb_breakpoint.number]
314 is_pending = getattr(gdb_breakpoint, 'pending', is_pending)
315 if not pending and is_pending:
316 continue
317 if not watchpoints and gdb_breakpoint.type != gdb.BP_BREAKPOINT:
318 continue
319 # add useful fields to the object
320 breakpoint = dict()
321 breakpoint['number'] = gdb_breakpoint.number
322 breakpoint['type'] = gdb_breakpoint.type
323 breakpoint['enabled'] = gdb_breakpoint.enabled
324 breakpoint['location'] = gdb_breakpoint.location
325 breakpoint['expression'] = gdb_breakpoint.expression
326 breakpoint['condition'] = gdb_breakpoint.condition
327 breakpoint['temporary'] = gdb_breakpoint.temporary
328 breakpoint['hit_count'] = gdb_breakpoint.hit_count
329 breakpoint['pending'] = is_pending
330 # add address and source information
331 if address:
332 sal = gdb.find_pc_line(address)
333 breakpoint['address'] = address
334 breakpoint['file_name'] = sal.symtab.filename if sal.symtab else None
335 breakpoint['file_line'] = sal.line
336 breakpoints.append(breakpoint)
337 return breakpoints
338
339# Dashboard --------------------------------------------------------------------
340
341class Dashboard(gdb.Command):
342 '''Redisplay the dashboard.'''
343
344 def __init__(self):
345 gdb.Command.__init__(self, 'dashboard', gdb.COMMAND_USER, gdb.COMPLETE_NONE, True)
346 # setup subcommands
347 Dashboard.ConfigurationCommand(self)
348 Dashboard.OutputCommand(self)
349 Dashboard.EnabledCommand(self)
350 Dashboard.LayoutCommand(self)
351 # setup style commands
352 Dashboard.StyleCommand(self, 'dashboard', R, R.attributes())
353 # main terminal
354 self.output = None
355 # used to inhibit redisplays during init parsing
356 self.inhibited = None
357 # enabled by default
358 self.enabled = None
359 self.enable()
360
361 def on_continue(self, _):
362 # try to contain the GDB messages in a specified area unless the
363 # dashboard is printed to a separate file (dashboard -output ...)
364 if self.is_running() and not self.output:
365 width, _ = Dashboard.get_term_size()
366 gdb.write(Dashboard.clear_screen())
367 gdb.write(divider(width, 'Output/messages', True))
368 gdb.write('\n')
369 gdb.flush()
370
371 def on_stop(self, _):
372 if self.is_running():
373 self.render(clear_screen=False)
374
375 def on_exit(self, _):
376 if not self.is_running():
377 return
378 # collect all the outputs
379 outputs = set()
380 outputs.add(self.output)
381 outputs.update(module.output for module in self.modules)
382 outputs.remove(None)
383 # reset the terminal status
384 for output in outputs:
385 try:
386 with open(output, 'w') as fs:
387 fs.write(Dashboard.reset_terminal())
388 except:
389 # skip cleanup for invalid outputs
390 pass
391
392 def enable(self):
393 if self.enabled:
394 return
395 self.enabled = True
396 # setup events
397 gdb.events.cont.connect(self.on_continue)
398 gdb.events.stop.connect(self.on_stop)
399 gdb.events.exited.connect(self.on_exit)
400
401 def disable(self):
402 if not self.enabled:
403 return
404 self.enabled = False
405 # setup events
406 gdb.events.cont.disconnect(self.on_continue)
407 gdb.events.stop.disconnect(self.on_stop)
408 gdb.events.exited.disconnect(self.on_exit)
409
410 def load_modules(self, modules):
411 self.modules = []
412 for module in modules:
413 info = Dashboard.ModuleInfo(self, module)
414 self.modules.append(info)
415
416 def redisplay(self, style_changed=False):
417 # manually redisplay the dashboard
418 if self.is_running() and not self.inhibited:
419 self.render(True, style_changed)
420
421 def inferior_pid(self):
422 return gdb.selected_inferior().pid
423
424 def is_running(self):
425 return self.inferior_pid() != 0
426
427 def render(self, clear_screen, style_changed=False):
428 # fetch module content and info
429 all_disabled = True
430 display_map = dict()
431 for module in self.modules:
432 # fall back to the global value
433 output = module.output or self.output
434 # add the instance or None if disabled
435 if module.enabled:
436 all_disabled = False
437 instance = module.instance
438 else:
439 instance = None
440 display_map.setdefault(output, []).append(instance)
441 # process each display info
442 for output, instances in display_map.items():
443 try:
444 buf = ''
445 # use GDB stream by default
446 fs = None
447 if output:
448 fs = open(output, 'w')
449 fd = fs.fileno()
450 fs.write(Dashboard.setup_terminal())
451 else:
452 fs = gdb
453 fd = 1 # stdout
454 # get the terminal size (default main terminal if either the
455 # output is not a file)
456 try:
457 width, height = Dashboard.get_term_size(fd)
458 except:
459 width, height = Dashboard.get_term_size()
460 # clear the "screen" if requested for the main terminal,
461 # auxiliary terminals are always cleared
462 if fs is not gdb or clear_screen:
463 buf += Dashboard.clear_screen()
464 # show message if all the modules in this output are disabled
465 if not any(instances):
466 # write the error message
467 buf += divider(width, 'Warning', True)
468 buf += '\n'
469 if self.modules:
470 buf += 'No module to display (see `dashboard -layout`)'
471 else:
472 buf += 'No module loaded'
473 # write the terminator only in the main terminal
474 buf += '\n'
475 if fs is gdb:
476 buf += divider(width, primary=True)
477 buf += '\n'
478 fs.write(buf)
479 continue
480 # process all the modules for that output
481 for n, instance in enumerate(instances, 1):
482 # skip disabled modules
483 if not instance:
484 continue
485 try:
486 # ask the module to generate the content
487 lines = instance.lines(width, height, style_changed)
488 except Exception as e:
489 # allow to continue on exceptions in modules
490 stacktrace = traceback.format_exc().strip()
491 lines = [ansi(stacktrace, R.style_error)]
492 # create the divider accordingly
493 div = divider(width, instance.label(), True, lines)
494 # write the data
495 buf += '\n'.join([div] + lines)
496 # write the newline for all but last unless main terminal
497 if n != len(instances) or fs is gdb:
498 buf += '\n'
499 # write the final newline and the terminator only if it is the
500 # main terminal to allow the prompt to display correctly (unless
501 # there are no modules to display)
502 if fs is gdb and not all_disabled:
503 buf += divider(width, primary=True)
504 buf += '\n'
505 fs.write(buf)
506 except Exception as e:
507 cause = traceback.format_exc().strip()
508 Dashboard.err('Cannot write the dashboard\n{}'.format(cause))
509 finally:
510 # don't close gdb stream
511 if fs and fs is not gdb:
512 fs.close()
513
514# Utility methods --------------------------------------------------------------
515
516 @staticmethod
517 def start():
518 # initialize the dashboard
519 dashboard = Dashboard()
520 Dashboard.set_custom_prompt(dashboard)
521 # parse Python inits, load modules then parse GDB inits
522 dashboard.inhibited = True
523 Dashboard.parse_inits(True)
524 modules = Dashboard.get_modules()
525 dashboard.load_modules(modules)
526 Dashboard.parse_inits(False)
527 dashboard.inhibited = False
528 # GDB overrides
529 run('set pagination off')
530 # display if possible (program running and not explicitly disabled by
531 # some configuration file)
532 if dashboard.enabled:
533 dashboard.redisplay()
534
535 @staticmethod
536 def get_term_size(fd=1): # defaults to the main terminal
537 if sys.platform == 'win32':
538 try:
539 import curses
540 # XXX always neglects the fd parameter
541 height, width = curses.initscr().getmaxyx()
542 curses.endwin()
543 return int(width), int(height)
544 except ImportError:
545 return 80, 24 # hardcoded fallback value
546 else:
547 import termios
548 import fcntl
549 # first 2 shorts (4 byte) of struct winsize
550 raw = fcntl.ioctl(fd, termios.TIOCGWINSZ, ' ' * 4)
551 height, width = struct.unpack('hh', raw)
552 return int(width), int(height)
553
554 @staticmethod
555 def set_custom_prompt(dashboard):
556 def custom_prompt(_):
557 # render thread status indicator
558 if dashboard.is_running():
559 pid = dashboard.inferior_pid()
560 status = R.prompt_running.format(pid=pid)
561 else:
562 status = R.prompt_not_running
563 # build prompt
564 prompt = R.prompt.format(status=status)
565 prompt = gdb.prompt.substitute_prompt(prompt)
566 return prompt + ' ' # force trailing space
567 gdb.prompt_hook = custom_prompt
568
569 @staticmethod
570 def parse_inits(python):
571 for root, dirs, files in os.walk(os.path.expanduser('~/.gdbinit.d/')):
572 dirs.sort()
573 for init in sorted(files):
574 path = os.path.join(root, init)
575 _, ext = os.path.splitext(path)
576 # either load Python files or GDB
577 if python == (ext == '.py'):
578 gdb.execute('source ' + path)
579
580 @staticmethod
581 def get_modules():
582 # scan the scope for modules
583 modules = []
584 for name in globals():
585 obj = globals()[name]
586 try:
587 if issubclass(obj, Dashboard.Module):
588 modules.append(obj)
589 except TypeError:
590 continue
591 # sort modules alphabetically
592 modules.sort(key=lambda x: x.__name__)
593 return modules
594
595 @staticmethod
596 def create_command(name, invoke, doc, is_prefix, complete=None):
597 Class = type('', (gdb.Command,), {'invoke': invoke, '__doc__': doc})
598 Class(name, gdb.COMMAND_USER, complete or gdb.COMPLETE_NONE, is_prefix)
599
600 @staticmethod
601 def err(string):
602 print(ansi(string, R.style_error))
603
604 @staticmethod
605 def complete(word, candidates):
606 return filter(lambda candidate: candidate.startswith(word), candidates)
607
608 @staticmethod
609 def parse_arg(arg):
610 # encode unicode GDB command arguments as utf8 in Python 2.7
611 if type(arg) is not str:
612 arg = arg.encode('utf8')
613 return arg
614
615 @staticmethod
616 def clear_screen():
617 # ANSI: move the cursor to top-left corner and clear the screen
618 # (optionally also clear the scrollback buffer if supported by the
619 # terminal)
620 return '\x1b[H\x1b[J' + '\x1b[3J' if R.discard_scrollback else ''
621
622 @staticmethod
623 def setup_terminal():
624 # ANSI: enable alternative screen buffer and hide cursor
625 return '\x1b[?1049h\x1b[?25l'
626
627 @staticmethod
628 def reset_terminal():
629 # ANSI: disable alternative screen buffer and show cursor
630 return '\x1b[?1049l\x1b[?25h'
631
632# Module descriptor ------------------------------------------------------------
633
634 class ModuleInfo:
635
636 def __init__(self, dashboard, module):
637 self.name = module.__name__.lower() # from class to module name
638 self.enabled = True
639 self.output = None # value from the dashboard by default
640 self.instance = module()
641 self.doc = self.instance.__doc__ or '(no documentation)'
642 self.prefix = 'dashboard {}'.format(self.name)
643 # add GDB commands
644 self.add_main_command(dashboard)
645 self.add_output_command(dashboard)
646 self.add_style_command(dashboard)
647 self.add_subcommands(dashboard)
648
649 def add_main_command(self, dashboard):
650 module = self
651 def invoke(self, arg, from_tty, info=self):
652 arg = Dashboard.parse_arg(arg)
653 if arg == '':
654 info.enabled ^= True
655 if dashboard.is_running():
656 dashboard.redisplay()
657 else:
658 status = 'enabled' if info.enabled else 'disabled'
659 print('{} module {}'.format(module.name, status))
660 else:
661 Dashboard.err('Wrong argument "{}"'.format(arg))
662 doc_brief = 'Configure the {} module, with no arguments toggles its visibility.'.format(self.name)
663 doc = '{}\n\n{}'.format(doc_brief, self.doc)
664 Dashboard.create_command(self.prefix, invoke, doc, True)
665
666 def add_output_command(self, dashboard):
667 Dashboard.OutputCommand(dashboard, self.prefix, self)
668
669 def add_style_command(self, dashboard):
670 Dashboard.StyleCommand(dashboard, self.prefix, self.instance, self.instance.attributes())
671
672 def add_subcommands(self, dashboard):
673 for name, command in self.instance.commands().items():
674 self.add_subcommand(dashboard, name, command)
675
676 def add_subcommand(self, dashboard, name, command):
677 action = command['action']
678 doc = command['doc']
679 complete = command.get('complete')
680 def invoke(self, arg, from_tty, info=self):
681 arg = Dashboard.parse_arg(arg)
682 if info.enabled:
683 try:
684 action(arg)
685 except Exception as e:
686 Dashboard.err(e)
687 return
688 # don't catch redisplay errors
689 dashboard.redisplay()
690 else:
691 Dashboard.err('Module disabled')
692 prefix = '{} {}'.format(self.prefix, name)
693 Dashboard.create_command(prefix, invoke, doc, False, complete)
694
695# GDB commands -----------------------------------------------------------------
696
697 # handler for the `dashboard` command itself
698 def invoke(self, arg, from_tty):
699 arg = Dashboard.parse_arg(arg)
700 # show messages for checks in redisplay
701 if arg != '':
702 Dashboard.err('Wrong argument "{}"'.format(arg))
703 elif not self.is_running():
704 Dashboard.err('Is the target program running?')
705 else:
706 self.redisplay()
707
708 class ConfigurationCommand(gdb.Command):
709 '''Dump or save the dashboard configuration.
710
711With an optional argument the configuration will be written to the specified
712file.
713
714This command allows to configure the dashboard live then make the changes
715permanent, for example:
716
717 dashboard -configuration ~/.gdbinit.d/init
718
719At startup the `~/.gdbinit.d/` directory tree is walked and files are evaluated
720in alphabetical order but giving priority to Python files. This is where user
721configuration files must be placed.'''
722
723 def __init__(self, dashboard):
724 gdb.Command.__init__(self, 'dashboard -configuration',
725 gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
726 self.dashboard = dashboard
727
728 def invoke(self, arg, from_tty):
729 arg = Dashboard.parse_arg(arg)
730 if arg:
731 with open(os.path.expanduser(arg), 'w') as fs:
732 fs.write('# auto generated by GDB dashboard\n\n')
733 self.dump(fs)
734 self.dump(gdb)
735
736 def dump(self, fs):
737 # dump layout
738 self.dump_layout(fs)
739 # dump styles
740 self.dump_style(fs, R)
741 for module in self.dashboard.modules:
742 self.dump_style(fs, module.instance, module.prefix)
743 # dump outputs
744 self.dump_output(fs, self.dashboard)
745 for module in self.dashboard.modules:
746 self.dump_output(fs, module, module.prefix)
747
748 def dump_layout(self, fs):
749 layout = ['dashboard -layout']
750 for module in self.dashboard.modules:
751 mark = '' if module.enabled else '!'
752 layout.append('{}{}'.format(mark, module.name))
753 fs.write(' '.join(layout))
754 fs.write('\n')
755
756 def dump_style(self, fs, obj, prefix='dashboard'):
757 attributes = getattr(obj, 'attributes', lambda: dict())()
758 for name, attribute in attributes.items():
759 real_name = attribute.get('name', name)
760 default = attribute.get('default')
761 value = getattr(obj, real_name)
762 if value != default:
763 fs.write('{} -style {} {!r}\n'.format(prefix, name, value))
764
765 def dump_output(self, fs, obj, prefix='dashboard'):
766 output = getattr(obj, 'output')
767 if output:
768 fs.write('{} -output {}\n'.format(prefix, output))
769
770 class OutputCommand(gdb.Command):
771 '''Set the output file/TTY for the whole dashboard or single modules.
772
773The dashboard/module will be written to the specified file, which will be
774created if it does not exist. If the specified file identifies a terminal then
775its geometry will be used, otherwise it falls back to the geometry of the main
776GDB terminal.
777
778When invoked without argument on the dashboard, the output/messages and modules
779which do not specify an output themselves will be printed on standard output
780(default).
781
782When invoked without argument on a module, it will be printed where the
783dashboard will be printed.
784
785An overview of all the outputs can be obtained with the `dashboard -layout`
786command.'''
787
788 def __init__(self, dashboard, prefix=None, obj=None):
789 if not prefix:
790 prefix = 'dashboard'
791 if not obj:
792 obj = dashboard
793 prefix = prefix + ' -output'
794 gdb.Command.__init__(self, prefix, gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
795 self.dashboard = dashboard
796 self.obj = obj # None means the dashboard itself
797
798 def invoke(self, arg, from_tty):
799 arg = Dashboard.parse_arg(arg)
800 # reset the terminal status
801 if self.obj.output:
802 try:
803 with open(self.obj.output, 'w') as fs:
804 fs.write(Dashboard.reset_terminal())
805 except:
806 # just do nothing if the file is not writable
807 pass
808 # set or open the output file
809 if arg == '':
810 self.obj.output = None
811 else:
812 self.obj.output = arg
813 # redisplay the dashboard in the new output
814 self.dashboard.redisplay()
815
816 class EnabledCommand(gdb.Command):
817 '''Enable or disable the dashboard.
818
819The current status is printed if no argument is present.'''
820
821 def __init__(self, dashboard):
822 gdb.Command.__init__(self, 'dashboard -enabled', gdb.COMMAND_USER)
823 self.dashboard = dashboard
824
825 def invoke(self, arg, from_tty):
826 arg = Dashboard.parse_arg(arg)
827 if arg == '':
828 status = 'enabled' if self.dashboard.enabled else 'disabled'
829 print('The dashboard is {}'.format(status))
830 elif arg == 'on':
831 self.dashboard.enable()
832 self.dashboard.redisplay()
833 elif arg == 'off':
834 self.dashboard.disable()
835 else:
836 msg = 'Wrong argument "{}"; expecting "on" or "off"'
837 Dashboard.err(msg.format(arg))
838
839 def complete(self, text, word):
840 return Dashboard.complete(word, ['on', 'off'])
841
842 class LayoutCommand(gdb.Command):
843 '''Set or show the dashboard layout.
844
845Accepts a space-separated list of directive. Each directive is in the form
846"[!]<module>". Modules in the list are placed in the dashboard in the same order
847as they appear and those prefixed by "!" are disabled by default. Omitted
848modules are hidden and placed at the bottom in alphabetical order.
849
850Without arguments the current layout is shown where the first line uses the same
851form expected by the input while the remaining depict the current status of
852output files.
853
854Passing `!` as a single argument resets the dashboard original layout.'''
855
856 def __init__(self, dashboard):
857 gdb.Command.__init__(self, 'dashboard -layout', gdb.COMMAND_USER)
858 self.dashboard = dashboard
859
860 def invoke(self, arg, from_tty):
861 arg = Dashboard.parse_arg(arg)
862 directives = str(arg).split()
863 if directives:
864 # apply the layout
865 if directives == ['!']:
866 self.reset()
867 else:
868 if not self.layout(directives):
869 return # in case of errors
870 # redisplay or otherwise notify
871 if from_tty:
872 if self.dashboard.is_running():
873 self.dashboard.redisplay()
874 else:
875 self.show()
876 else:
877 self.show()
878
879 def reset(self):
880 modules = self.dashboard.modules
881 modules.sort(key=lambda module: module.name)
882 for module in modules:
883 module.enabled = True
884
885 def show(self):
886 global_str = 'Dashboard'
887 default = '(default TTY)'
888 max_name_len = max(len(module.name) for module in self.dashboard.modules)
889 max_name_len = max(max_name_len, len(global_str))
890 fmt = '{{}}{{:{}s}}{{}}'.format(max_name_len + 2)
891 print((fmt + '\n').format(' ', global_str, self.dashboard.output or default))
892 for module in self.dashboard.modules:
893 mark = ' ' if module.enabled else '!'
894 style = R.style_high if module.enabled else R.style_low
895 line = fmt.format(mark, module.name, module.output or default)
896 print(ansi(line, style))
897
898 def layout(self, directives):
899 modules = self.dashboard.modules
900 # parse and check directives
901 parsed_directives = []
902 selected_modules = set()
903 for directive in directives:
904 enabled = (directive[0] != '!')
905 name = directive[not enabled:]
906 if name in selected_modules:
907 Dashboard.err('Module "{}" already set'.format(name))
908 return False
909 if next((False for module in modules if module.name == name), True):
910 Dashboard.err('Cannot find module "{}"'.format(name))
911 return False
912 parsed_directives.append((name, enabled))
913 selected_modules.add(name)
914 # reset visibility
915 for module in modules:
916 module.enabled = False
917 # move and enable the selected modules on top
918 last = 0
919 for name, enabled in parsed_directives:
920 todo = enumerate(modules[last:], start=last)
921 index = next(index for index, module in todo if name == module.name)
922 modules[index].enabled = enabled
923 modules.insert(last, modules.pop(index))
924 last += 1
925 return True
926
927 def complete(self, text, word):
928 all_modules = (m.name for m in self.dashboard.modules)
929 return Dashboard.complete(word, all_modules)
930
931 class StyleCommand(gdb.Command):
932 '''Access the stylable attributes.
933
934Without arguments print all the stylable attributes.
935
936When only the name is specified show the current value.
937
938With name and value set the stylable attribute. Values are parsed as Python
939literals and converted to the proper type. '''
940
941 def __init__(self, dashboard, prefix, obj, attributes):
942 self.prefix = prefix + ' -style'
943 gdb.Command.__init__(self, self.prefix, gdb.COMMAND_USER, gdb.COMPLETE_NONE, True)
944 self.dashboard = dashboard
945 self.obj = obj
946 self.attributes = attributes
947 self.add_styles()
948
949 def add_styles(self):
950 this = self
951 for name, attribute in self.attributes.items():
952 # fetch fields
953 attr_name = attribute.get('name', name)
954 attr_type = attribute.get('type', str)
955 attr_check = attribute.get('check', lambda _: True)
956 attr_default = attribute['default']
957 # set the default value (coerced to the type)
958 value = attr_type(attr_default)
959 setattr(self.obj, attr_name, value)
960 # create the command
961 def invoke(self, arg, from_tty,
962 name=name,
963 attr_name=attr_name,
964 attr_type=attr_type,
965 attr_check=attr_check):
966 new_value = Dashboard.parse_arg(arg)
967 if new_value == '':
968 # print the current value
969 value = getattr(this.obj, attr_name)
970 print('{} = {!r}'.format(name, value))
971 else:
972 try:
973 # convert and check the new value
974 parsed = ast.literal_eval(new_value)
975 value = attr_type(parsed)
976 if not attr_check(value):
977 msg = 'Invalid value "{}" for "{}"'
978 raise Exception(msg.format(new_value, name))
979 except Exception as e:
980 Dashboard.err(e)
981 else:
982 # set and redisplay
983 setattr(this.obj, attr_name, value)
984 this.dashboard.redisplay(True)
985 prefix = self.prefix + ' ' + name
986 doc = attribute.get('doc', 'This style is self-documenting')
987 Dashboard.create_command(prefix, invoke, doc, False)
988
989 def invoke(self, arg, from_tty):
990 # an argument here means that the provided attribute is invalid
991 if arg:
992 Dashboard.err('Invalid argument "{}"'.format(arg))
993 return
994 # print all the pairs
995 for name, attribute in self.attributes.items():
996 attr_name = attribute.get('name', name)
997 value = getattr(self.obj, attr_name)
998 print('{} = {!r}'.format(name, value))
999
1000# Base module ------------------------------------------------------------------
1001
1002 # just a tag
1003 class Module():
1004 '''Base class for GDB dashboard modules.
1005
1006 Modules are instantiated once at initialization time and kept during the
1007 whole the GDB session.
1008
1009 The name of a module is automatically obtained by the class name.
1010
1011 Optionally, a module may include a description which will appear in the
1012 GDB help system by specifying a Python docstring for the class. By
1013 convention the first line should contain a brief description.'''
1014
1015 def label(self):
1016 '''Return the module label which will appear in the divider.'''
1017 pass
1018
1019 def lines(self, term_width, term_height, style_changed):
1020 '''Return a list of strings which will form the module content.
1021
1022 When a module is temporarily unable to produce its content, it
1023 should return an empty list; its divider will then use the styles
1024 with the "off" qualifier.
1025
1026 term_width and term_height are the dimension of the terminal where
1027 this module will be displayed. If `style_changed` is `True` then
1028 some attributes have changed since the last time so the
1029 implementation may want to update its status.'''
1030 pass
1031
1032 def attributes(self):
1033 '''Return the dictionary of available attributes.
1034
1035 The key is the attribute name and the value is another dictionary
1036 with items:
1037
1038 - `default` is the initial value for this attribute;
1039
1040 - `doc` is the optional documentation of this attribute which will
1041 appear in the GDB help system;
1042
1043 - `name` is the name of the attribute of the Python object (defaults
1044 to the key value);
1045
1046 - `type` is the Python type of this attribute defaulting to the
1047 `str` type, it is used to coerce the value passed as an argument
1048 to the proper type, or raise an exception;
1049
1050 - `check` is an optional control callback which accept the coerced
1051 value and returns `True` if the value satisfies the constraint and
1052 `False` otherwise.
1053
1054 Those attributes can be accessed from the implementation using
1055 instance variables named `name`.'''
1056 return {}
1057
1058 def commands(self):
1059 '''Return the dictionary of available commands.
1060
1061 The key is the attribute name and the value is another dictionary
1062 with items:
1063
1064 - `action` is the callback to be executed which accepts the raw
1065 input string from the GDB prompt, exceptions in these functions
1066 will be shown automatically to the user;
1067
1068 - `doc` is the documentation of this command which will appear in
1069 the GDB help system;
1070
1071 - `completion` is the optional completion policy, one of the
1072 `gdb.COMPLETE_*` constants defined in the GDB reference manual
1073 (https://sourceware.org/gdb/onlinedocs/gdb/Commands-In-Python.html).'''
1074 return {}
1075
1076# Default modules --------------------------------------------------------------
1077
1078class Source(Dashboard.Module):
1079 '''Show the program source code, if available.'''
1080
1081 def __init__(self):
1082 self.file_name = None
1083 self.source_lines = []
1084 self.ts = None
1085 self.highlighted = False
1086 self.offset = 0
1087
1088 def label(self):
1089 return 'Source'
1090
1091 def lines(self, term_width, term_height, style_changed):
1092 # skip if the current thread is not stopped
1093 if not gdb.selected_thread().is_stopped():
1094 return []
1095 # try to fetch the current line (skip if no line information)
1096 sal = gdb.selected_frame().find_sal()
1097 current_line = sal.line
1098 if current_line == 0:
1099 return []
1100 # reload the source file if changed
1101 file_name = sal.symtab.filename
1102 ts = None
1103 try:
1104 ts = os.path.getmtime(file_name)
1105 except:
1106 pass # delay error check to open()
1107 # style changed, different file name or file modified in the meanwhile
1108 if style_changed or file_name != self.file_name or ts and ts > self.ts:
1109 try:
1110 with open(file_name) as source_file:
1111 highlighter = Beautifier(file_name, self.tab_size)
1112 self.highlighted = highlighter.active
1113 source = highlighter.process(source_file.read())
1114 self.source_lines = source.split('\n')
1115 # store file name and timestamp only if success to have
1116 # persistent errors
1117 self.file_name = file_name
1118 self.ts = ts
1119 except IOError as e:
1120 msg = 'Cannot display "{}"'.format(file_name)
1121 return [ansi(msg, R.style_error)]
1122 # compute the line range
1123 height = self.height or (term_height - 1)
1124 start = current_line - 1 - int(height / 2) + self.offset
1125 end = start + height
1126 # extra at start
1127 extra_start = 0
1128 if start < 0:
1129 extra_start = min(-start, height)
1130 start = 0
1131 # extra at end
1132 extra_end = 0
1133 if end > len(self.source_lines):
1134 extra_end = min(end - len(self.source_lines), height)
1135 end = len(self.source_lines)
1136 else:
1137 end = max(end, 0)
1138 # find the breakpoints for teh current file
1139 breakpoints = list(filter(lambda x: x.get('file_name') == file_name, fetch_breakpoints()))
1140 # return the source code listing
1141 out = []
1142 number_format = '{{:>{}}}'.format(len(str(end)))
1143 for number, line in enumerate(self.source_lines[start:end], start + 1):
1144 # properly handle UTF-8 source files
1145 line = to_string(line)
1146 if int(number) == current_line:
1147 # the current line has a different style without ANSI
1148 if R.ansi:
1149 if self.highlighted:
1150 line_format = '{}' + ansi(number_format, R.style_selected_1) + ' {}'
1151 else:
1152 line_format = '{}' + ansi(number_format + ' {}', R.style_selected_1)
1153 else:
1154 # just show a plain text indicator
1155 line_format = '{}' + number_format + '> {}'
1156 else:
1157 line_format = '{}' + ansi(number_format, R.style_low) + ' {}'
1158 # check for breakpoint presence
1159 enabled = None
1160 for breakpoint in breakpoints:
1161 if breakpoint['file_line'] == number:
1162 enabled = enabled or breakpoint['enabled']
1163 if enabled is None:
1164 breakpoint = ' '
1165 else:
1166 breakpoint = ansi('!', R.style_critical) if enabled else ansi('-', R.style_low)
1167 out.append(line_format.format(breakpoint, number, line.rstrip('\n')))
1168 # return the output along with scroll indicators
1169 if len(out) <= height:
1170 extra = [ansi('~', R.style_low)]
1171 return extra_start * extra + out + extra_end * extra
1172 else:
1173 return out
1174
1175 def commands(self):
1176 return {
1177 'scroll': {
1178 'action': self.scroll,
1179 'doc': 'Scroll by relative steps or reset if invoked without argument.'
1180 }
1181 }
1182
1183 def attributes(self):
1184 return {
1185 'height': {
1186 'doc': '''Height of the module.
1187
1188A value of 0 uses the whole height.''',
1189 'default': 10,
1190 'type': int,
1191 'check': check_ge_zero
1192 },
1193 'tab-size': {
1194 'doc': 'Number of spaces used to display the tab character.',
1195 'default': 4,
1196 'name': 'tab_size',
1197 'type': int,
1198 'check': check_gt_zero
1199 }
1200 }
1201
1202 def scroll(self, arg):
1203 if arg:
1204 self.offset += int(arg)
1205 else:
1206 self.offset = 0
1207
1208class Assembly(Dashboard.Module):
1209 '''Show the disassembled code surrounding the program counter.
1210
1211The instructions constituting the current statement are marked, if available.'''
1212
1213 def __init__(self):
1214 self.offset = 0
1215 self.cache_key = None
1216 self.cache_asm = None
1217
1218 def label(self):
1219 return 'Assembly'
1220
1221 def lines(self, term_width, term_height, style_changed):
1222 # skip if the current thread is not stopped
1223 if not gdb.selected_thread().is_stopped():
1224 return []
1225 # flush the cache if the style is changed
1226 if style_changed:
1227 self.cache_key = None
1228 # prepare the highlighter
1229 try:
1230 flavor = gdb.parameter('disassembly-flavor')
1231 except:
1232 flavor = 'att' # not always defined (see #36)
1233 highlighter = Beautifier(flavor)
1234 # fetch the assembly code
1235 line_info = None
1236 frame = gdb.selected_frame() # PC is here
1237 height = self.height or (term_height - 1)
1238 try:
1239 # disassemble the current block (if function information is
1240 # available then try to obtain the boundaries by looking at the
1241 # superblocks)
1242 block = frame.block()
1243 if frame.function():
1244 while block and (not block.function or block.function.name != frame.function().name):
1245 block = block.superblock
1246 block = block or frame.block()
1247 asm_start = block.start
1248 asm_end = block.end - 1
1249 asm = self.fetch_asm(asm_start, asm_end, False, highlighter)
1250 # find the location of the PC
1251 pc_index = next(index for index, instr in enumerate(asm)
1252 if instr['addr'] == frame.pc())
1253 # compute the instruction range
1254 start = pc_index - int(height / 2) + self.offset
1255 end = start + height
1256 # extra at start
1257 extra_start = 0
1258 if start < 0:
1259 extra_start = min(-start, height)
1260 start = 0
1261 # extra at end
1262 extra_end = 0
1263 if end > len(asm):
1264 extra_end = min(end - len(asm), height)
1265 end = len(asm)
1266 else:
1267 end = max(end, 0)
1268 # fetch actual interval
1269 asm = asm[start:end]
1270 # if there are line information then use it, it may be that
1271 # line_info is not None but line_info.last is None
1272 line_info = gdb.find_pc_line(frame.pc())
1273 line_info = line_info if line_info.last else None
1274 except (gdb.error, RuntimeError, StopIteration):
1275 # if it is not possible (stripped binary or the PC is not present in
1276 # the output of `disassemble` as per issue #31) start from PC
1277 try:
1278 extra_start = 0
1279 extra_end = 0
1280 asm = self.fetch_asm(frame.pc(), height, True, highlighter)
1281 except gdb.error as e:
1282 msg = '{}'.format(e)
1283 return [ansi(msg, R.style_error)]
1284 # fetch function start if available (e.g., not with @plt)
1285 func_start = None
1286 if self.show_function and frame.function():
1287 func_start = to_unsigned(frame.function().value())
1288 # compute the maximum offset size
1289 if asm and func_start:
1290 max_offset = max(len(str(abs(asm[0]['addr'] - func_start))),
1291 len(str(abs(asm[-1]['addr'] - func_start))))
1292 # return the machine code
1293 breakpoints = fetch_breakpoints()
1294 max_length = max(instr['length'] for instr in asm) if asm else 0
1295 inferior = gdb.selected_inferior()
1296 out = []
1297 for index, instr in enumerate(asm):
1298 addr = instr['addr']
1299 length = instr['length']
1300 text = instr['asm']
1301 addr_str = format_address(addr)
1302 if self.show_opcodes:
1303 # fetch and format opcode
1304 region = inferior.read_memory(addr, length)
1305 opcodes = (' '.join('{:02x}'.format(ord(byte)) for byte in region))
1306 opcodes += (max_length - len(region)) * 3 * ' ' + ' '
1307 else:
1308 opcodes = ''
1309 # compute the offset if available
1310 if self.show_function:
1311 if func_start:
1312 offset = '{:+d}'.format(addr - func_start)
1313 offset = offset.ljust(max_offset + 1) # sign
1314 func_info = '{}{}'.format(frame.function(), offset)
1315 else:
1316 func_info = '?'
1317 else:
1318 func_info = ''
1319 format_string = '{}{}{}{}{}{}'
1320 indicator = ' '
1321 text = ' ' + text
1322 if addr == frame.pc():
1323 if not R.ansi:
1324 indicator = '> '
1325 addr_str = ansi(addr_str, R.style_selected_1)
1326 indicator = ansi(indicator, R.style_selected_1)
1327 opcodes = ansi(opcodes, R.style_selected_1)
1328 func_info = ansi(func_info, R.style_selected_1)
1329 if not highlighter.active:
1330 text = ansi(text, R.style_selected_1)
1331 elif line_info and line_info.pc <= addr < line_info.last:
1332 if not R.ansi:
1333 indicator = ': '
1334 addr_str = ansi(addr_str, R.style_selected_2)
1335 indicator = ansi(indicator, R.style_selected_2)
1336 opcodes = ansi(opcodes, R.style_selected_2)
1337 func_info = ansi(func_info, R.style_selected_2)
1338 if not highlighter.active:
1339 text = ansi(text, R.style_selected_2)
1340 else:
1341 addr_str = ansi(addr_str, R.style_low)
1342 func_info = ansi(func_info, R.style_low)
1343 # check for breakpoint presence
1344 enabled = None
1345 for breakpoint in breakpoints:
1346 if breakpoint.get('address') == addr:
1347 enabled = enabled or breakpoint['enabled']
1348 if enabled is None:
1349 breakpoint = ' '
1350 else:
1351 breakpoint = ansi('!', R.style_critical) if enabled else ansi('-', R.style_low)
1352 out.append(format_string.format(breakpoint, addr_str, indicator, opcodes, func_info, text))
1353 # return the output along with scroll indicators
1354 if len(out) <= height:
1355 extra = [ansi('~', R.style_low)]
1356 return extra_start * extra + out + extra_end * extra
1357 else:
1358 return out
1359
1360 def commands(self):
1361 return {
1362 'scroll': {
1363 'action': self.scroll,
1364 'doc': 'Scroll by relative steps or reset if invoked without argument.'
1365 }
1366 }
1367
1368 def attributes(self):
1369 return {
1370 'height': {
1371 'doc': '''Height of the module.
1372
1373A value of 0 uses the whole height.''',
1374 'default': 10,
1375 'type': int,
1376 'check': check_ge_zero
1377 },
1378 'opcodes': {
1379 'doc': 'Opcodes visibility flag.',
1380 'default': False,
1381 'name': 'show_opcodes',
1382 'type': bool
1383 },
1384 'function': {
1385 'doc': 'Function information visibility flag.',
1386 'default': True,
1387 'name': 'show_function',
1388 'type': bool
1389 }
1390 }
1391
1392 def scroll(self, arg):
1393 if arg:
1394 self.offset += int(arg)
1395 else:
1396 self.offset = 0
1397
1398 def fetch_asm(self, start, end_or_count, relative, highlighter):
1399 # fetch asm from cache or disassemble
1400 if self.cache_key == (start, end_or_count):
1401 asm = self.cache_asm
1402 else:
1403 kwargs = {
1404 'start_pc': start,
1405 'count' if relative else 'end_pc': end_or_count
1406 }
1407 asm = gdb.selected_frame().architecture().disassemble(**kwargs)
1408 self.cache_key = (start, end_or_count)
1409 self.cache_asm = asm
1410 # syntax highlight the cached entry
1411 for instr in asm:
1412 instr['asm'] = highlighter.process(instr['asm'])
1413 return asm
1414
1415class Variables(Dashboard.Module):
1416 '''Show arguments and locals of the selected frame.'''
1417
1418 def label(self):
1419 return 'Variables'
1420
1421 def lines(self, term_width, term_height, style_changed):
1422 return Variables.format_frame(
1423 gdb.selected_frame(), self.show_arguments, self.show_locals, self.compact)
1424
1425 def attributes(self):
1426 return {
1427 'arguments': {
1428 'doc': 'Frame arguments visibility flag.',
1429 'default': True,
1430 'name': 'show_arguments',
1431 'type': bool
1432 },
1433 'locals': {
1434 'doc': 'Frame locals visibility flag.',
1435 'default': True,
1436 'name': 'show_locals',
1437 'type': bool
1438 },
1439 'compact': {
1440 'doc': 'Single-line display flag.',
1441 'default': True,
1442 'type': bool
1443 }
1444 }
1445
1446 @staticmethod
1447 def format_frame(frame, show_arguments, show_locals, compact):
1448 out = []
1449 # fetch frame arguments and locals
1450 decorator = gdb.FrameDecorator.FrameDecorator(frame)
1451 separator = ansi(', ', R.style_low)
1452 if show_arguments:
1453 def prefix(line):
1454 return Stack.format_line('arg', line)
1455 frame_args = decorator.frame_args()
1456 args_lines = Variables.fetch(frame, frame_args, compact)
1457 if args_lines:
1458 if compact:
1459 args_line = separator.join(args_lines)
1460 single_line = prefix(args_line)
1461 out.append(single_line)
1462 else:
1463 out.extend(map(prefix, args_lines))
1464 if show_locals:
1465 def prefix(line):
1466 return Stack.format_line('loc', line)
1467 frame_locals = decorator.frame_locals()
1468 locals_lines = Variables.fetch(frame, frame_locals, compact)
1469 if locals_lines:
1470 if compact:
1471 locals_line = separator.join(locals_lines)
1472 single_line = prefix(locals_line)
1473 out.append(single_line)
1474 else:
1475 out.extend(map(prefix, locals_lines))
1476 return out
1477
1478 @staticmethod
1479 def fetch(frame, data, compact):
1480 lines = []
1481 for elem in data or []:
1482 name = ansi(elem.sym, R.style_high)
1483 equal = ansi('=', R.style_low)
1484 value = format_value(elem.sym.value(frame), compact)
1485 lines.append('{} {} {}'.format(name, equal, value))
1486 return lines
1487
1488class Stack(Dashboard.Module):
1489 '''Show the current stack trace including the function name and the file location, if available.
1490
1491Optionally list the frame arguments and locals too.'''
1492
1493 def label(self):
1494 return 'Stack'
1495
1496 def lines(self, term_width, term_height, style_changed):
1497 # skip if the current thread is not stopped
1498 if not gdb.selected_thread().is_stopped():
1499 return []
1500 # find the selected frame (i.e., the first to display)
1501 selected_index = 0
1502 frame = gdb.newest_frame()
1503 while frame:
1504 if frame == gdb.selected_frame():
1505 break
1506 frame = frame.older()
1507 selected_index += 1
1508 # format up to "limit" frames
1509 frames = []
1510 number = selected_index
1511 more = False
1512 while frame:
1513 # the first is the selected one
1514 selected = (len(frames) == 0)
1515 # fetch frame info
1516 style = R.style_selected_1 if selected else R.style_selected_2
1517 frame_id = ansi(str(number), style)
1518 info = Stack.get_pc_line(frame, style)
1519 frame_lines = []
1520 frame_lines.append('[{}] {}'.format(frame_id, info))
1521 # add frame arguments and locals
1522 variables = Variables.format_frame(frame, self.show_arguments, self.show_locals, self.compact)
1523 frame_lines.extend(variables)
1524 # add frame
1525 frames.append(frame_lines)
1526 # next
1527 frame = frame.older()
1528 number += 1
1529 # check finished according to the limit
1530 if self.limit and len(frames) == self.limit:
1531 # more frames to show but limited
1532 if frame:
1533 more = True
1534 break
1535 # format the output
1536 lines = []
1537 for frame_lines in frames:
1538 lines.extend(frame_lines)
1539 # add the placeholder
1540 if more:
1541 lines.append('[{}]'.format(ansi('+', R.style_selected_2)))
1542 return lines
1543
1544 def attributes(self):
1545 return {
1546 'limit': {
1547 'doc': 'Maximum number of displayed frames (0 means no limit).',
1548 'default': 10,
1549 'type': int,
1550 'check': check_ge_zero
1551 },
1552 'arguments': {
1553 'doc': 'Frame arguments visibility flag.',
1554 'default': False,
1555 'name': 'show_arguments',
1556 'type': bool
1557 },
1558 'locals': {
1559 'doc': 'Frame locals visibility flag.',
1560 'default': False,
1561 'name': 'show_locals',
1562 'type': bool
1563 },
1564 'compact': {
1565 'doc': 'Single-line display flag.',
1566 'default': False,
1567 'type': bool
1568 }
1569 }
1570
1571 @staticmethod
1572 def format_line(prefix, line):
1573 prefix = ansi(prefix, R.style_low)
1574 return '{} {}'.format(prefix, line)
1575
1576 @staticmethod
1577 def get_pc_line(frame, style):
1578 frame_pc = ansi(format_address(frame.pc()), style)
1579 info = 'from {}'.format(frame_pc)
1580 if frame.function():
1581 name = ansi(frame.function(), style)
1582 func_start = to_unsigned(frame.function().value())
1583 offset = ansi(str(frame.pc() - func_start), style)
1584 info += ' in {}+{}'.format(name, offset)
1585 sal = frame.find_sal()
1586 if sal and sal.symtab:
1587 file_name = ansi(sal.symtab.filename, style)
1588 file_line = ansi(str(sal.line), style)
1589 info += ' at {}:{}'.format(file_name, file_line)
1590 return info
1591
1592class History(Dashboard.Module):
1593 '''List the last entries of the value history.'''
1594
1595 def label(self):
1596 return 'History'
1597
1598 def lines(self, term_width, term_height, style_changed):
1599 out = []
1600 # fetch last entries
1601 for i in range(-self.limit + 1, 1):
1602 try:
1603 value = format_value(gdb.history(i))
1604 value_id = ansi('$${}', R.style_high).format(abs(i))
1605 equal = ansi('=', R.style_low)
1606 line = '{} {} {}'.format(value_id, equal, value)
1607 out.append(line)
1608 except gdb.error:
1609 continue
1610 return out
1611
1612 def attributes(self):
1613 return {
1614 'limit': {
1615 'doc': 'Maximum number of values to show.',
1616 'default': 3,
1617 'type': int,
1618 'check': check_gt_zero
1619 }
1620 }
1621
1622class Memory(Dashboard.Module):
1623 '''Allow to inspect memory regions.'''
1624
1625 DEFAULT_LENGTH = 16
1626
1627 class Region():
1628 def __init__(self, expression, length, module):
1629 self.expression = expression
1630 self.length = length
1631 self.module = module
1632 self.original = None
1633 self.latest = None
1634
1635 def reset(self):
1636 self.original = None
1637 self.latest = None
1638
1639 def format(self, per_line):
1640 # fetch the memory content
1641 try:
1642 address = Memory.parse_as_address(self.expression)
1643 inferior = gdb.selected_inferior()
1644 memory = inferior.read_memory(address, self.length)
1645 # set the original memory snapshot if needed
1646 if not self.original:
1647 self.original = memory
1648 except gdb.error as e:
1649 msg = 'Cannot access {} bytes starting at {}: {}'
1650 msg = msg.format(self.length, self.expression, e)
1651 return [ansi(msg, R.style_error)]
1652 # format the memory content
1653 out = []
1654 for i in range(0, len(memory), per_line):
1655 region = memory[i:i + per_line]
1656 pad = per_line - len(region)
1657 address_str = format_address(address + i)
1658 # compute changes
1659 hexa = []
1660 text = []
1661 for j in range(len(region)):
1662 rel = i + j
1663 byte = memory[rel]
1664 hexa_byte = '{:02x}'.format(ord(byte))
1665 text_byte = self.module.format_byte(byte)
1666 # differences against the latest have the highest priority
1667 if self.latest and memory[rel] != self.latest[rel]:
1668 hexa_byte = ansi(hexa_byte, R.style_selected_1)
1669 text_byte = ansi(text_byte, R.style_selected_1)
1670 # cumulative changes if enabled
1671 elif self.module.cumulative and memory[rel] != self.original[rel]:
1672 hexa_byte = ansi(hexa_byte, R.style_selected_2)
1673 text_byte = ansi(text_byte, R.style_selected_2)
1674 # format the text differently for clarity
1675 else:
1676 text_byte = ansi(text_byte, R.style_high)
1677 hexa.append(hexa_byte)
1678 text.append(text_byte)
1679 # output the formatted line
1680 hexa_placeholder = ' {}'.format(self.module.placeholder[0] * 2)
1681 text_placeholder = self.module.placeholder[0]
1682 out.append('{} {}{} {}{}'.format(
1683 ansi(address_str, R.style_low),
1684 ' '.join(hexa), ansi(pad * hexa_placeholder, R.style_low),
1685 ''.join(text), ansi(pad * text_placeholder, R.style_low)))
1686 # update the latest memory snapshot
1687 self.latest = memory
1688 return out
1689
1690 def __init__(self):
1691 self.table = {}
1692
1693 def label(self):
1694 return 'Memory'
1695
1696 def lines(self, term_width, term_height, style_changed):
1697 out = []
1698 for expression, region in self.table.items():
1699 out.append(divider(term_width, expression))
1700 out.extend(region.format(self.get_per_line(term_width)))
1701 return out
1702
1703 def commands(self):
1704 return {
1705 'watch': {
1706 'action': self.watch,
1707 'doc': '''Watch a memory region by expression and length.
1708
1709The length defaults to 16 bytes.''',
1710 'complete': gdb.COMPLETE_EXPRESSION
1711 },
1712 'unwatch': {
1713 'action': self.unwatch,
1714 'doc': 'Stop watching a memory region by expression.',
1715 'complete': gdb.COMPLETE_EXPRESSION
1716 },
1717 'clear': {
1718 'action': self.clear,
1719 'doc': 'Clear all the watched regions.'
1720 }
1721 }
1722
1723 def attributes(self):
1724 return {
1725 'cumulative': {
1726 'doc': 'Highlight changes cumulatively, watch again to reset.',
1727 'default': False,
1728 'type': bool
1729 },
1730 'full': {
1731 'doc': 'Take the whole horizontal space.',
1732 'default': False,
1733 'type': bool
1734 },
1735 'placeholder': {
1736 'doc': 'Placeholder used for missing items and unprintable characters.',
1737 'default': '·'
1738 }
1739 }
1740
1741 def watch(self, arg):
1742 if arg:
1743 expression, _, length_str = arg.partition(' ')
1744 length = Memory.parse_as_address(length_str) if length_str else Memory.DEFAULT_LENGTH
1745 # keep the length when the memory is watched to reset the changes
1746 region = self.table.get(expression)
1747 if region and not length_str:
1748 region.reset()
1749 else:
1750 self.table[expression] = Memory.Region(expression, length, self)
1751 else:
1752 raise Exception('Specify a memory location')
1753
1754 def unwatch(self, arg):
1755 if arg:
1756 try:
1757 del self.table[arg]
1758 except KeyError:
1759 raise Exception('Memory expression not watched')
1760 else:
1761 raise Exception('Specify a matched memory expression')
1762
1763 def clear(self, arg):
1764 self.table.clear()
1765
1766 def format_byte(self, byte):
1767 # `type(byte) is bytes` in Python 3
1768 if 0x20 < ord(byte) < 0x7f:
1769 return chr(ord(byte))
1770 else:
1771 return self.placeholder[0]
1772
1773 def get_per_line(self, term_width):
1774 if self.full:
1775 padding = 3 # two double spaces separator (one is part of below)
1776 elem_size = 4 # HH + 1 space + T
1777 address_length = gdb.parse_and_eval('$pc').type.sizeof * 2 + 2 # 0x
1778 return max(int((term_width - address_length - padding) / elem_size), 1)
1779 else:
1780 return Memory.DEFAULT_LENGTH
1781
1782 @staticmethod
1783 def parse_as_address(expression):
1784 value = gdb.parse_and_eval(expression)
1785 return to_unsigned(value)
1786
1787class Registers(Dashboard.Module):
1788 '''Show the CPU registers and their values.'''
1789
1790 def __init__(self):
1791 self.table = {}
1792
1793 def label(self):
1794 return 'Registers'
1795
1796 def lines(self, term_width, term_height, style_changed):
1797 # skip if the current thread is not stopped
1798 if not gdb.selected_thread().is_stopped():
1799 return []
1800 # obtain the registers to display
1801 if style_changed:
1802 self.table = {}
1803 if self.register_list:
1804 register_list = self.register_list.split()
1805 else:
1806 register_list = list(map(lambda line: line.split(None, 1)[0],
1807 run('info registers').strip().split('\n')))
1808 # fetch registers status
1809 registers = []
1810 for name in register_list:
1811 # Exclude registers with a dot '.' or parse_and_eval() will fail
1812 if '.' in name:
1813 continue
1814 value = gdb.parse_and_eval('${}'.format(name))
1815 string_value = Registers.format_value(value)
1816 changed = self.table and (self.table.get(name, '') != string_value)
1817 self.table[name] = string_value
1818 registers.append((name, string_value, changed))
1819 # compute lengths considering an extra space between and around the
1820 # entries (hence the +2 and term_width - 1)
1821 max_name = max(len(name) for name, _, _ in registers)
1822 max_value = max(len(value) for _, value, _ in registers)
1823 max_width = max_name + max_value + 2
1824 columns = min(int((term_width - 1) / max_width) or 1, len(registers))
1825 rows = int(math.ceil(float(len(registers)) / columns))
1826 # build the registers matrix
1827 if self.column_major:
1828 matrix = list(registers[i:i + rows] for i in range(0, len(registers), rows))
1829 else:
1830 matrix = list(registers[i::columns] for i in range(columns))
1831 # compute the lengths column wise
1832 max_names_column = list(max(len(name) for name, _, _ in column) for column in matrix)
1833 max_values_column = list(max(len(value) for _, value, _ in column) for column in matrix)
1834 line_length = sum(max_names_column) + columns + sum(max_values_column)
1835 extra = term_width - line_length
1836 # compute padding as if there were one more column
1837 base_padding = int(extra / (columns + 1))
1838 padding_column = [base_padding] * columns
1839 # distribute the remainder among columns giving the precedence to
1840 # internal padding
1841 rest = extra % (columns + 1)
1842 while rest:
1843 padding_column[rest % columns] += 1
1844 rest -= 1
1845 # format the registers
1846 out = [''] * rows
1847 for i, column in enumerate(matrix):
1848 max_name = max_names_column[i]
1849 max_value = max_values_column[i]
1850 for j, (name, value, changed) in enumerate(column):
1851 name = ' ' * (max_name - len(name)) + ansi(name, R.style_low)
1852 style = R.style_selected_1 if changed else ''
1853 value = ansi(value, style) + ' ' * (max_value - len(value))
1854 padding = ' ' * padding_column[i]
1855 item = '{}{} {}'.format(padding, name, value)
1856 out[j] += item
1857 return out
1858
1859 def attributes(self):
1860 return {
1861 'column-major': {
1862 'doc': 'Show registers in columns instead of rows.',
1863 'default': False,
1864 'name': 'column_major',
1865 'type': bool
1866 },
1867 'list': {
1868 'doc': '''String of space-separated register names to display.
1869
1870The empty list (default) causes to show all the available registers.''',
1871 'default': '',
1872 'name': 'register_list',
1873 }
1874 }
1875
1876 @staticmethod
1877 def format_value(value):
1878 try:
1879 if value.type.code in [gdb.TYPE_CODE_INT, gdb.TYPE_CODE_PTR]:
1880 int_value = to_unsigned(value, value.type.sizeof)
1881 value_format = '0x{{:0{}x}}'.format(2 * value.type.sizeof)
1882 return value_format.format(int_value)
1883 except (gdb.error, ValueError):
1884 # convert to unsigned but preserve code and flags information
1885 pass
1886 return str(value)
1887
1888class Threads(Dashboard.Module):
1889 '''List the currently available threads.'''
1890
1891 def label(self):
1892 return 'Threads'
1893
1894 def lines(self, term_width, term_height, style_changed):
1895 out = []
1896 selected_thread = gdb.selected_thread()
1897 # do not restore the selected frame if the thread is not stopped
1898 restore_frame = gdb.selected_thread().is_stopped()
1899 if restore_frame:
1900 selected_frame = gdb.selected_frame()
1901 # fetch the thread list
1902 threads = []
1903 for inferior in gdb.inferiors():
1904 if self.all_inferiors or inferior == gdb.selected_inferior():
1905 threads += gdb.Inferior.threads(inferior)
1906 for thread in threads:
1907 # skip running threads if requested
1908 if self.skip_running and thread.is_running():
1909 continue
1910 is_selected = (thread.ptid == selected_thread.ptid)
1911 style = R.style_selected_1 if is_selected else R.style_selected_2
1912 if self.all_inferiors:
1913 number = '{}.{}'.format(thread.inferior.num, thread.num)
1914 else:
1915 number = str(thread.num)
1916 number = ansi(number, style)
1917 tid = ansi(str(thread.ptid[1] or thread.ptid[2]), style)
1918 info = '[{}] id {}'.format(number, tid)
1919 if thread.name:
1920 info += ' name {}'.format(ansi(thread.name, style))
1921 # switch thread to fetch info (unless is running in non-stop mode)
1922 try:
1923 thread.switch()
1924 frame = gdb.newest_frame()
1925 info += ' ' + Stack.get_pc_line(frame, style)
1926 except gdb.error:
1927 info += ' (running)'
1928 out.append(info)
1929 # restore thread and frame
1930 selected_thread.switch()
1931 if restore_frame:
1932 selected_frame.select()
1933 return out
1934
1935 def attributes(self):
1936 return {
1937 'skip-running': {
1938 'doc': 'Skip running threads.',
1939 'default': False,
1940 'name': 'skip_running',
1941 'type': bool
1942 },
1943 'all-inferiors': {
1944 'doc': 'Show threads from all inferiors.',
1945 'default': False,
1946 'name': 'all_inferiors',
1947 'type': bool
1948 },
1949 }
1950
1951class Expressions(Dashboard.Module):
1952 '''Watch user expressions.'''
1953
1954 def __init__(self):
1955 self.table = set()
1956
1957 def label(self):
1958 return 'Expressions'
1959
1960 def lines(self, term_width, term_height, style_changed):
1961 out = []
1962 default_radix = Expressions.get_default_radix()
1963 for expression in self.table:
1964 label = expression
1965 match = re.match('^/(\d+) +(.+)$', expression)
1966 try:
1967 if match:
1968 radix, expression = match.groups()
1969 run('set output-radix {}'.format(radix))
1970 value = format_value(gdb.parse_and_eval(expression))
1971 except gdb.error as e:
1972 value = ansi(e, R.style_error)
1973 finally:
1974 if match:
1975 run('set output-radix {}'.format(default_radix))
1976 expression = ansi(label, R.style_high)
1977 equal = ansi('=', R.style_low)
1978 out.append('{} {} {}'.format(expression, equal, value))
1979 return out
1980
1981 def commands(self):
1982 return {
1983 'watch': {
1984 'action': self.watch,
1985 'doc': 'Watch an expression using the format `[/<radix>] <expression>`.',
1986 'complete': gdb.COMPLETE_EXPRESSION
1987 },
1988 'unwatch': {
1989 'action': self.unwatch,
1990 'doc': 'Stop watching an expression.',
1991 'complete': gdb.COMPLETE_EXPRESSION
1992 },
1993 'clear': {
1994 'action': self.clear,
1995 'doc': 'Clear all the watched expressions.'
1996 }
1997 }
1998
1999 def watch(self, arg):
2000 if arg:
2001 self.table.add(arg)
2002 else:
2003 raise Exception('Specify an expression')
2004
2005 def unwatch(self, arg):
2006 if arg:
2007 try:
2008 self.table.remove(arg)
2009 except:
2010 raise Exception('Expression not watched')
2011 else:
2012 raise Exception('Specify an expression')
2013
2014 def clear(self, arg):
2015 self.table.clear()
2016
2017 @staticmethod
2018 def get_default_radix():
2019 try:
2020 return gdb.parameter('output-radix')
2021 except RuntimeError:
2022 # XXX this is a fix for GDB <8.1.x see #161
2023 message = run('show output-radix')
2024 match = re.match('^Default output radix for printing of values is (\d+)\.$', message)
2025 return match.groups()[0] if match else 10 # fallback
2026
2027class Breakpoints(Dashboard.Module):
2028 '''Display the breakpoints list.'''
2029
2030 NAMES = {
2031 gdb.BP_BREAKPOINT: 'break',
2032 gdb.BP_WATCHPOINT: 'watch',
2033 gdb.BP_HARDWARE_WATCHPOINT: 'write watch',
2034 gdb.BP_READ_WATCHPOINT: 'read watch',
2035 gdb.BP_ACCESS_WATCHPOINT: 'access watch'
2036 }
2037
2038 def label(self):
2039 return 'Breakpoints'
2040
2041 def lines(self, term_width, term_height, style_changed):
2042 out = []
2043 breakpoints = fetch_breakpoints(watchpoints=True, pending=self.show_pending)
2044 for breakpoint in breakpoints:
2045 # format common information
2046 style = R.style_selected_1 if breakpoint['enabled'] else R.style_selected_2
2047 number = ansi(breakpoint['number'], style)
2048 bp_type = ansi(Breakpoints.NAMES[breakpoint['type']], style)
2049 if breakpoint['temporary']:
2050 bp_type = bp_type + ' {}'.format(ansi('once', style))
2051 if not R.ansi and breakpoint['enabled']:
2052 bp_type = 'disabled ' + bp_type
2053 line = '[{}] {}'.format(number, bp_type)
2054 if breakpoint['type'] == gdb.BP_BREAKPOINT:
2055 # format memory address
2056 address = breakpoint.get('address')
2057 if address:
2058 line += ' at {}'.format(ansi(format_address(address), style))
2059 # format source information
2060 file_name = breakpoint.get('file_name')
2061 file_line = breakpoint.get('file_line')
2062 if file_name and file_line:
2063 file_name = ansi(file_name, style)
2064 file_line = ansi(file_line, style)
2065 line += ' in {}:{}'.format(file_name, file_line)
2066 # format user location
2067 location = breakpoint['location']
2068 line += ' for {}'.format(ansi(location, style))
2069 else:
2070 # format user expression
2071 expression = breakpoint['expression']
2072 line += ' for {}'.format(ansi(expression, style))
2073 # format condition
2074 condition = breakpoint['condition']
2075 if condition:
2076 line += ' if {}'.format(ansi(condition, style))
2077 # format hit count
2078 hit_count = breakpoint['hit_count']
2079 if hit_count:
2080 word = 'time{}'.format('s' if hit_count > 1 else '')
2081 line += ' hit {} {}'.format(ansi(breakpoint['hit_count'], style), word)
2082 out.append(line)
2083 return out
2084
2085 def attributes(self):
2086 return {
2087 'pending': {
2088 'doc': 'Also show pending breakpoints.',
2089 'default': True,
2090 'name': 'show_pending',
2091 'type': bool
2092 }
2093 }
2094
2095# XXX traceback line numbers in this Python block must be increased by 1
2096end
2097
2098# Better GDB defaults ----------------------------------------------------------
2099
2100set history save
2101set verbose off
2102set print pretty on
2103set print array off
2104set print array-indexes on
2105set python print-stack full
2106
2107# Start ------------------------------------------------------------------------
2108
2109python Dashboard.start()
2110
2111# ------------------------------------------------------------------------------
2112# Copyright (c) 2015-2019 Andrea Cardaci <cyrus.and@gmail.com>
2113#
2114# Permission is hereby granted, free of charge, to any person obtaining a copy
2115# of this software and associated documentation files (the "Software"), to deal
2116# in the Software without restriction, including without limitation the rights
2117# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2118# copies of the Software, and to permit persons to whom the Software is
2119# furnished to do so, subject to the following conditions:
2120#
2121# The above copyright notice and this permission notice shall be included in all
2122# copies or substantial portions of the Software.
2123#
2124# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2125# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2126# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2127# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2128# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2129# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2130# SOFTWARE.
2131# ------------------------------------------------------------------------------
2132# vim: filetype=python
2133# Local Variables:
2134# mode: python
2135# End:# INSTALL INSTRUCTIONS: save as ~/.gdbinit
2136#
2137# DESCRIPTION: A user-friendly gdb configuration file, for x86/x86_64 and ARM platforms.
2138#
2139# REVISION : 8.0 (13/04/2012)
2140#
2141# CONTRIBUTORS: mammon_, elaine, pusillus, mong, zhang le, l0kit,
2142# truthix the cyberpunk, fG!, gln
2143#
2144# FEEDBACK: http://reverse.put.as - reverser@put.as
2145#
2146# NOTES: 'help user' in gdb will list the commands/descriptions in this file
2147# 'context on' now enables auto-display of context screen
2148#
2149# MAC OS X NOTES: If you are using this on Mac OS X, you must either attach gdb to a process
2150# or launch gdb without any options and then load the binary file you want to analyse with "exec-file" option
2151# If you load the binary from the command line, like $gdb binary-name, this will not work as it should
2152# For more information, read it here http://reverse.put.as/2008/11/28/apples-gdb-bug/
2153#
2154# UPDATE: This bug can be fixed in gdb source. Refer to http://reverse.put.as/2009/08/10/fix-for-apples-gdb-bug-or-why-apple-forks-are-bad/
2155# and http://reverse.put.as/2009/08/26/gdb-patches/ (if you want the fixed binary for i386)
2156#
2157# An updated version of the patch and binary is available at http://reverse.put.as/2011/02/21/update-to-gdb-patches-fix-a-new-bug/
2158#
2159# iOS NOTES: iOS gdb from Cydia (and Apple's) suffer from the same OS X bug.
2160# If you are using this on Mac OS X or iOS, you must either attach gdb to a process
2161# or launch gdb without any options and then load the binary file you want to analyse with "exec-file" option
2162# If you load the binary from the command line, like $gdb binary-name, this will not work as it should
2163# For more information, read it here http://reverse.put.as/2008/11/28/apples-gdb-bug/
2164#
2165# CHANGELOG: (older changes at the end of the file)
2166#
2167# Version 8.0 (13/04/2012)
2168# - Merged x86/x64 and ARM versions
2169# - Added commands intelsyntax and attsyntax to switch between x86 disassembly flavors
2170# - Added new configuration variables ARM, ARMOPCODES, and X86FLAVOR
2171# - Code cleanups and fixes to the indentation
2172# - Bug fixes to some ARM related code
2173# - Added the dumpmacho command to memory dump the mach-o header to a file
2174#
2175# TODO:
2176#
2177
2178# __________________gdb options_________________
2179
2180# set to 1 to have ARM target debugging as default, use the "arm" command to switch inside gdb
2181set $ARM = 0
2182# set to 1 to enable 64bits target by default (32bits is the default)
2183set $64BITS = 0
2184# set to 0 if you have problems with the colorized prompt - reported by Plouj with Ubuntu gdb 7.2
2185set $COLOUREDPROMPT = 1
2186# Colour the first line of the disassembly - default is green, if you want to change it search for
2187# SETCOLOUR1STLINE and modify it :-)
2188set $SETCOLOUR1STLINE = 1
2189# set to 0 to remove display of objectivec messages (default is 1)
2190set $SHOWOBJECTIVEC = 1
2191# set to 0 to remove display of cpu registers (default is 1)
2192set $SHOWCPUREGISTERS = 1
2193# set to 1 to enable display of stack (default is 0)
2194set $SHOWSTACK = 0
2195# set to 1 to enable display of data window (default is 0)
2196set $SHOWDATAWIN = 0
2197# set to 0 to disable coloured display of changed registers
2198set $SHOWREGCHANGES = 1
2199# set to 1 so skip command to execute the instruction at the new location
2200# by default it EIP/RIP will be modified and update the new context but not execute the instruction
2201set $SKIPEXECUTE = 0
2202# if $SKIPEXECUTE is 1 configure the type of execution
2203# 1 = use stepo (do not get into calls), 0 = use stepi (step into calls)
2204set $SKIPSTEP = 1
2205# show the ARM opcodes - change to 0 if you don't want such thing (in x/i command)
2206set $ARMOPCODES = 1
2207# x86 disassembly flavor: 0 for Intel, 1 for AT&T
2208set $X86FLAVOR = 1
2209
2210set confirm off
2211set verbose off
2212
2213if $COLOUREDPROMPT == 1
2214 set prompt \033[31mgdb$ \033[0m
2215end
2216
2217set output-radix 0x10
2218set input-radix 0x10
2219
2220# These make gdb never pause in its output
2221set height 0
2222set width 0
2223
2224set $SHOW_CONTEXT = 1
2225set $SHOW_NEST_INSN = 0
2226
2227set $CONTEXTSIZE_STACK = 6
2228set $CONTEXTSIZE_DATA = 8
2229set $CONTEXTSIZE_CODE = 8
2230
2231# __________________end gdb options_________________
2232
2233# Initialize these variables else comparisons will fail for colouring
2234# we must initialize all of them at once, 32 and 64 bits, and ARM.
2235set $oldrax = 0
2236set $oldrbx = 0
2237set $oldrcx = 0
2238set $oldrdx = 0
2239set $oldrsi = 0
2240set $oldrdi = 0
2241set $oldrbp = 0
2242set $oldrsp = 0
2243set $oldr8 = 0
2244set $oldr9 = 0
2245set $oldr10 = 0
2246set $oldr11 = 0
2247set $oldr12 = 0
2248set $oldr13 = 0
2249set $oldr14 = 0
2250set $oldr15 = 0
2251set $oldeax = 0
2252set $oldebx = 0
2253set $oldecx = 0
2254set $oldedx = 0
2255set $oldesi = 0
2256set $oldedi = 0
2257set $oldebp = 0
2258set $oldesp = 0
2259set $oldr0 = 0
2260set $oldr1 = 0
2261set $oldr2 = 0
2262set $oldr3 = 0
2263set $oldr4 = 0
2264set $oldr5 = 0
2265set $oldr6 = 0
2266set $oldr7 = 0
2267set $oldsp = 0
2268set $oldlr = 0
2269
2270# used by ptraceme/rptraceme
2271set $ptrace_bpnum = 0
2272
2273# ______________window size control___________
2274define contextsize-stack
2275 if $argc != 1
2276 help contextsize-stack
2277 else
2278 set $CONTEXTSIZE_STACK = $arg0
2279 end
2280end
2281document contextsize-stack
2282Set stack dump window size to NUM lines.
2283Usage: contextsize-stack NUM
2284end
2285
2286
2287define contextsize-data
2288 if $argc != 1
2289 help contextsize-data
2290 else
2291 set $CONTEXTSIZE_DATA = $arg0
2292 end
2293end
2294document contextsize-data
2295Set data dump window size to NUM lines.
2296Usage: contextsize-data NUM
2297end
2298
2299
2300define contextsize-code
2301 if $argc != 1
2302 help contextsize-code
2303 else
2304 set $CONTEXTSIZE_CODE = $arg0
2305 end
2306end
2307document contextsize-code
2308Set code window size to NUM lines.
2309Usage: contextsize-code NUM
2310end
2311
2312
2313# _____________breakpoint aliases_____________
2314define bpl
2315 info breakpoints
2316end
2317document bpl
2318List all breakpoints.
2319end
2320
2321
2322define bp
2323 if $argc != 1
2324 help bp
2325 else
2326 break $arg0
2327 end
2328end
2329document bp
2330Set breakpoint.
2331Usage: bp LOCATION
2332LOCATION may be a line number, function name, or "*" and an address.
2333To break on a symbol you must enclose symbol name inside "".
2334Example:
2335bp "[NSControl stringValue]"
2336Or else you can use directly the break command (break [NSControl stringValue])
2337end
2338
2339
2340define bpc
2341 if $argc != 1
2342 help bpc
2343 else
2344 clear $arg0
2345 end
2346end
2347document bpc
2348Clear breakpoint.
2349Usage: bpc LOCATION
2350LOCATION may be a line number, function name, or "*" and an address.
2351end
2352
2353
2354define bpe
2355 if $argc != 1
2356 help bpe
2357 else
2358 enable $arg0
2359 end
2360end
2361document bpe
2362Enable breakpoint with number NUM.
2363Usage: bpe NUM
2364end
2365
2366
2367define bpd
2368 if $argc != 1
2369 help bpd
2370 else
2371 disable $arg0
2372 end
2373end
2374document bpd
2375Disable breakpoint with number NUM.
2376Usage: bpd NUM
2377end
2378
2379
2380define bpt
2381 if $argc != 1
2382 help bpt
2383 else
2384 tbreak $arg0
2385 end
2386end
2387document bpt
2388Set a temporary breakpoint.
2389This breakpoint will be automatically deleted when hit!.
2390Usage: bpt LOCATION
2391LOCATION may be a line number, function name, or "*" and an address.
2392end
2393
2394
2395define bpm
2396 if $argc != 1
2397 help bpm
2398 else
2399 awatch $arg0
2400 end
2401end
2402document bpm
2403Set a read/write breakpoint on EXPRESSION, e.g. *address.
2404Usage: bpm EXPRESSION
2405end
2406
2407
2408define bhb
2409 if $argc != 1
2410 help bhb
2411 else
2412 hb $arg0
2413 end
2414end
2415document bhb
2416Set hardware assisted breakpoint.
2417Usage: bhb LOCATION
2418LOCATION may be a line number, function name, or "*" and an address.
2419end
2420
2421
2422define bht
2423 if $argc != 1
2424 help bht
2425 else
2426 thbreak $arg0
2427 end
2428end
2429document bht
2430Set a temporary hardware breakpoint.
2431This breakpoint will be automatically deleted when hit!
2432Usage: bht LOCATION
2433LOCATION may be a line number, function name, or "*" and an address.
2434end
2435
2436
2437# ______________process information____________
2438define argv
2439 show args
2440end
2441document argv
2442Print program arguments.
2443end
2444
2445
2446define stack
2447 if $argc == 0
2448 info stack
2449 end
2450 if $argc == 1
2451 info stack $arg0
2452 end
2453 if $argc > 1
2454 help stack
2455 end
2456end
2457document stack
2458Print backtrace of the call stack, or innermost COUNT frames.
2459Usage: stack <COUNT>
2460end
2461
2462
2463define frame
2464 info frame
2465 info args
2466 info locals
2467end
2468document frame
2469Print stack frame.
2470end
2471
2472
2473define flagsarm
2474# conditional flags are
2475# negative/less than (N), bit 31 of CPSR
2476# zero (Z), bit 30
2477# Carry/Borrow/Extend (C), bit 29
2478# Overflow (V), bit 28
2479 # negative/less than (N), bit 31 of CPSR
2480 if ($cpsr->n & 1)
2481 printf "N "
2482 set $_n_flag = 1
2483 else
2484 printf "n "
2485 set $_n_flag = 0
2486 end
2487 # zero (Z), bit 30
2488 if ($cpsr->z & 1)
2489 printf "Z "
2490 set $_z_flag = 1
2491 else
2492 printf "z "
2493 set $_z_flag = 0
2494 end
2495 # Carry/Borrow/Extend (C), bit 29
2496 if ($cpsr->c & 1)
2497 printf "C "
2498 set $_c_flag = 1
2499 else
2500 printf "c "
2501 set $_c_flag = 0
2502 end
2503 # Overflow (V), bit 28
2504 if ($cpsr->v & 1)
2505 printf "V "
2506 set $_v_flag = 1
2507 else
2508 printf "v "
2509 set $_v_flag = 0
2510 end
2511 # Sticky overflow (Q), bit 27
2512 if ($cpsr->q & 1)
2513 printf "Q "
2514 set $_q_flag = 1
2515 else
2516 printf "q "
2517 set $_q_flag = 0
2518 end
2519 # Java state bit (J), bit 24
2520 # When T=1:
2521 # J = 0 The processor is in Thumb state.
2522 # J = 1 The processor is in ThumbEE state.
2523 if ($cpsr->j & 1)
2524 printf "J "
2525 set $_j_flag = 1
2526 else
2527 printf "j "
2528 set $_j_flag = 0
2529 end
2530 # Data endianness bit (E), bit 9
2531 if ($cpsr->e & 1)
2532 printf "E "
2533 set $_e_flag = 1
2534 else
2535 printf "e "
2536 set $_e_flag = 0
2537 end
2538 # Imprecise abort disable bit (A), bit 8
2539 # The A bit is set to 1 automatically. It is used to disable imprecise data aborts.
2540 # It might not be writable in the Nonsecure state if the AW bit in the SCR register is reset.
2541 if ($cpsr->a & 1)
2542 printf "A "
2543 set $_a_flag = 1
2544 else
2545 printf "a "
2546 set $_a_flag = 0
2547 end
2548 # IRQ disable bit (I), bit 7
2549 # When the I bit is set to 1, IRQ interrupts are disabled.
2550 if ($cpsr->i & 1)
2551 printf "I "
2552 set $_i_flag = 1
2553 else
2554 printf "i "
2555 set $_i_flag = 0
2556 end
2557 # FIQ disable bit (F), bit 6
2558 # When the F bit is set to 1, FIQ interrupts are disabled.
2559 # FIQ can be nonmaskable in the Nonsecure state if the FW bit in SCR register is reset.
2560 if ($cpsr->f & 1)
2561 printf "F "
2562 set $_f_flag = 1
2563 else
2564 printf "f "
2565 set $_f_flag = 0
2566 end
2567 # Thumb state bit (F), bit 5
2568 # if 1 then the processor is executing in Thumb state or ThumbEE state depending on the J bit
2569 if ($cpsr->t & 1)
2570 printf "T "
2571 set $_t_flag = 1
2572 else
2573 printf "t "
2574 set $_t_flag = 0
2575 end
2576 # TODO: GE bit ?
2577end
2578document flagsarm
2579Auxiliary function to set ARM cpu flags.
2580end
2581
2582
2583define flagsx86
2584 # OF (overflow) flag
2585 if (($eflags >> 0xB) & 1)
2586 printf "O "
2587 set $_of_flag = 1
2588 else
2589 printf "o "
2590 set $_of_flag = 0
2591 end
2592 # DF (direction) flag
2593 if (($eflags >> 0xA) & 1)
2594 printf "D "
2595 else
2596 printf "d "
2597 end
2598 # IF (interrupt enable) flag
2599 if (($eflags >> 9) & 1)
2600 printf "I "
2601 else
2602 printf "i "
2603 end
2604 # TF (trap) flag
2605 if (($eflags >> 8) & 1)
2606 printf "T "
2607 else
2608 printf "t "
2609 end
2610 # SF (sign) flag
2611 if (($eflags >> 7) & 1)
2612 printf "S "
2613 set $_sf_flag = 1
2614 else
2615 printf "s "
2616 set $_sf_flag = 0
2617 end
2618 # ZF (zero) flag
2619 if (($eflags >> 6) & 1)
2620 printf "Z "
2621 set $_zf_flag = 1
2622 else
2623 printf "z "
2624 set $_zf_flag = 0
2625 end
2626 # AF (adjust) flag
2627 if (($eflags >> 4) & 1)
2628 printf "A "
2629 else
2630 printf "a "
2631 end
2632 # PF (parity) flag
2633 if (($eflags >> 2) & 1)
2634 printf "P "
2635 set $_pf_flag = 1
2636 else
2637 printf "p "
2638 set $_pf_flag = 0
2639 end
2640 # CF (carry) flag
2641 if ($eflags & 1)
2642 printf "C "
2643 set $_cf_flag = 1
2644 else
2645 printf "c "
2646 set $_cf_flag = 0
2647 end
2648 printf "\n"
2649end
2650document flagsx86
2651Auxiliary function to set X86/X64 cpu flags.
2652end
2653
2654
2655define flags
2656 # call the auxiliary functions based on target cpu
2657 if $ARM == 1
2658 flagsarm
2659 else
2660 flagsx86
2661 end
2662end
2663document flags
2664Print flags register.
2665end
2666
2667
2668define eflags
2669 if $ARM == 1
2670 printf " N <%d> Z <%d> C <%d> V <%d>",\
2671 ($cpsr->n & 1), ($cpsr->z & 1), \
2672 ($cpsr->c & 1), ($cpsr->v & 1)
2673 printf " Q <%d> J <%d> GE <%d> E <%d> A <%d>",\
2674 ($cpsr->q & 1), ($cpsr->j & 1),\
2675 ($cpsr->ge), ($cpsr->e & 1), ($cpsr->a & 1)
2676 printf " I <%d> F <%d> T <%d> \n",\
2677 ($cpsr->i & 1), ($cpsr->f & 1), \
2678 ($cpsr->t & 1)
2679 else
2680 printf " OF <%d> DF <%d> IF <%d> TF <%d>",\
2681 (($eflags >> 0xB) & 1), (($eflags >> 0xA) & 1), \
2682 (($eflags >> 9) & 1), (($eflags >> 8) & 1)
2683 printf " SF <%d> ZF <%d> AF <%d> PF <%d> CF <%d>\n",\
2684 (($eflags >> 7) & 1), (($eflags >> 6) & 1),\
2685 (($eflags >> 4) & 1), (($eflags >> 2) & 1), ($eflags & 1)
2686 printf " ID <%d> VIP <%d> VIF <%d> AC <%d>",\
2687 (($eflags >> 0x15) & 1), (($eflags >> 0x14) & 1), \
2688 (($eflags >> 0x13) & 1), (($eflags >> 0x12) & 1)
2689 printf " VM <%d> RF <%d> NT <%d> IOPL <%d>\n",\
2690 (($eflags >> 0x11) & 1), (($eflags >> 0x10) & 1),\
2691 (($eflags >> 0xE) & 1), (($eflags >> 0xC) & 3)
2692 end
2693end
2694document eflags
2695Print eflags register.
2696end
2697
2698
2699define cpsr
2700 eflags
2701end
2702document cpsr
2703Print cpsr register.
2704end
2705
2706define regarm
2707 printf " "
2708 echo \033[32m
2709 printf "R0:"
2710 if ($r0 != $oldr0 && $SHOWREGCHANGES == 1)
2711 echo \033[31m
2712 else
2713 echo \033[0m
2714 end
2715 printf " 0x%08X ", $r0
2716
2717 echo \033[32m
2718 printf "R1:"
2719 if ($r1 != $oldr1 && $SHOWREGCHANGES == 1)
2720 echo \033[31m
2721 else
2722 echo \033[0m
2723 end
2724 printf " 0x%08X ", $r1
2725
2726 echo \033[32m
2727 printf "R2:"
2728 if ($r2 != $oldr2 && $SHOWREGCHANGES == 1)
2729 echo \033[31m
2730 else
2731 echo \033[0m
2732 end
2733 printf " 0x%08X ", $r2
2734
2735 echo \033[32m
2736 printf "R3:"
2737 if ($r3 != $oldr3 && $SHOWREGCHANGES == 1)
2738 echo \033[31m
2739 else
2740 echo \033[0m
2741 end
2742 printf " 0x%08X\n", $r3
2743 printf " "
2744
2745 echo \033[32m
2746 printf "R4:"
2747 if ($r4 != $oldr4 && $SHOWREGCHANGES == 1)
2748 echo \033[31m
2749 else
2750 echo \033[0m
2751 end
2752 printf " 0x%08X ", $r4
2753
2754 echo \033[32m
2755 printf "R5:"
2756 if ($r5 != $oldr5 && $SHOWREGCHANGES == 1)
2757 echo \033[31m
2758 else
2759 echo \033[0m
2760 end
2761 printf " 0x%08X ", $r5
2762
2763 echo \033[32m
2764 printf "R6:"
2765 if ($r6 != $oldr6 && $SHOWREGCHANGES == 1)
2766 echo \033[31m
2767 else
2768 echo \033[0m
2769 end
2770 printf " 0x%08X ", $r6
2771
2772 echo \033[32m
2773 printf "R7:"
2774 if ($r7 != $oldr7 && $SHOWREGCHANGES == 1)
2775 echo \033[31m
2776 else
2777 echo \033[0m
2778 end
2779 printf " 0x%08X\n", $r7
2780 printf " "
2781
2782 echo \033[32m
2783 printf "R8:"
2784 if ($r8 != $oldr8 && $SHOWREGCHANGES == 1)
2785 echo \033[31m
2786 else
2787 echo \033[0m
2788 end
2789 printf " 0x%08X ", $r8
2790
2791 echo \033[32m
2792 printf "R9:"
2793 if ($r9 != $oldr9 && $SHOWREGCHANGES == 1)
2794 echo \033[31m
2795 else
2796 echo \033[0m
2797 end
2798 printf " 0x%08X ", $r9
2799
2800 echo \033[32m
2801 printf "R10:"
2802 if ($r10 != $oldr10 && $SHOWREGCHANGES == 1)
2803 echo \033[31m
2804 else
2805 echo \033[0m
2806 end
2807 printf " 0x%08X ", $r10
2808
2809 echo \033[32m
2810 printf "R11:"
2811 if ($r11 != $oldr11 && $SHOWREGCHANGES == 1)
2812 echo \033[31m
2813 else
2814 echo \033[0m
2815 end
2816 printf " 0x%08X ", $r11
2817 dumpjump
2818 printf "\n"
2819 echo \033[32m
2820 printf " R12:"
2821 if ($r12 != $oldr12 && $SHOWREGCHANGES == 1)
2822 echo \033[31m
2823 else
2824 echo \033[0m
2825 end
2826 printf " 0x%08X", $r12
2827 printf " "
2828 echo \033[32m
2829 printf "SP:"
2830 if ($sp != $oldsp && $SHOWREGCHANGES == 1)
2831 echo \033[31m
2832 else
2833 echo \033[0m
2834 end
2835 printf " 0x%08X ", $sp
2836
2837 echo \033[32m
2838 printf "LR:"
2839 if ($lr != $oldlr && $SHOWREGCHANGES == 1)
2840 echo \033[31m
2841 else
2842 echo \033[0m
2843 end
2844 printf " 0x%08X ", $lr
2845
2846 echo \033[32m
2847 printf "PC:"
2848 echo \033[0m
2849 printf " 0x%08X ", $pc
2850 echo \033[1m\033[4m\033[31m
2851 flags
2852 echo \033[0m
2853 printf "\n"
2854end
2855document regarm
2856Auxiliary function to display ARM registers.
2857end
2858
2859define regx64
2860 # 64bits stuff
2861 printf " "
2862 # RAX
2863 echo \033[32m
2864 printf "RAX:"
2865 if ($rax != $oldrax && $SHOWREGCHANGES == 1)
2866 echo \033[31m
2867 else
2868 echo \033[0m
2869 end
2870 printf " 0x%016lX ", $rax
2871 # RBX
2872 echo \033[32m
2873 printf "RBX:"
2874 if ($rbx != $oldrbx && $SHOWREGCHANGES == 1)
2875 echo \033[31m
2876 else
2877 echo \033[0m
2878 end
2879 printf " 0x%016lX ", $rbx
2880 # RCX
2881 echo \033[32m
2882 printf "RCX:"
2883 if ($rcx != $oldrcx && $SHOWREGCHANGES == 1)
2884 echo \033[31m
2885 else
2886 echo \033[0m
2887 end
2888 printf " 0x%016lX ", $rcx
2889 # RDX
2890 echo \033[32m
2891 printf "RDX:"
2892 if ($rdx != $oldrdx && $SHOWREGCHANGES == 1)
2893 echo \033[31m
2894 else
2895 echo \033[0m
2896 end
2897 printf " 0x%016lX ", $rdx
2898 echo \033[1m\033[4m\033[31m
2899 flags
2900 echo \033[0m
2901 printf " "
2902 # RSI
2903 echo \033[32m
2904 printf "RSI:"
2905 if ($rsi != $oldrsi && $SHOWREGCHANGES == 1)
2906 echo \033[31m
2907 else
2908 echo \033[0m
2909 end
2910 printf " 0x%016lX ", $rsi
2911 # RDI
2912 echo \033[32m
2913 printf "RDI:"
2914 if ($rdi != $oldrdi && $SHOWREGCHANGES == 1)
2915 echo \033[31m
2916 else
2917 echo \033[0m
2918 end
2919 printf " 0x%016lX ", $rdi
2920 # RBP
2921 echo \033[32m
2922 printf "RBP:"
2923 if ($rbp != $oldrbp && $SHOWREGCHANGES == 1)
2924 echo \033[31m
2925 else
2926 echo \033[0m
2927 end
2928 printf " 0x%016lX ", $rbp
2929 # RSP
2930 echo \033[32m
2931 printf "RSP:"
2932 if ($rsp != $oldrsp && $SHOWREGCHANGES == 1)
2933 echo \033[31m
2934 else
2935 echo \033[0m
2936 end
2937 printf " 0x%016lX ", $rsp
2938 echo \033[32m
2939 printf "RIP:"
2940 echo \033[0m
2941 printf " 0x%016lX\n ", $rip
2942 # R8
2943 echo \033[32m
2944 printf "R8 :"
2945 if ($r8 != $oldr8 && $SHOWREGCHANGES == 1)
2946 echo \033[31m
2947 else
2948 echo \033[0m
2949 end
2950 printf " 0x%016lX ", $r8
2951 # R9
2952 echo \033[32m
2953 printf "R9 :"
2954 if ($r9 != $oldr9 && $SHOWREGCHANGES == 1)
2955 echo \033[31m
2956 else
2957 echo \033[0m
2958 end
2959 printf " 0x%016lX ", $r9
2960 # R10
2961 echo \033[32m
2962 printf "R10:"
2963 if ($r10 != $oldr10 && $SHOWREGCHANGES == 1)
2964 echo \033[31m
2965 else
2966 echo \033[0m
2967 end
2968 printf " 0x%016lX ", $r10
2969 # R11
2970 echo \033[32m
2971 printf "R11:"
2972 if ($r11 != $oldr11 && $SHOWREGCHANGES == 1)
2973 echo \033[31m
2974 else
2975 echo \033[0m
2976 end
2977 printf " 0x%016lX ", $r11
2978 # R12
2979 echo \033[32m
2980 printf "R12:"
2981 if ($r12 != $oldr12 && $SHOWREGCHANGES == 1)
2982 echo \033[31m
2983 else
2984 echo \033[0m
2985 end
2986 printf " 0x%016lX\n ", $r12
2987 # R13
2988 echo \033[32m
2989 printf "R13:"
2990 if ($r13 != $oldr13 && $SHOWREGCHANGES == 1)
2991 echo \033[31m
2992 else
2993 echo \033[0m
2994 end
2995 printf " 0x%016lX ", $r13
2996 # R14
2997 echo \033[32m
2998 printf "R14:"
2999 if ($r14 != $oldr14 && $SHOWREGCHANGES == 1)
3000 echo \033[31m
3001 else
3002 echo \033[0m
3003 end
3004 printf " 0x%016lX ", $r14
3005 # R15
3006 echo \033[32m
3007 printf "R15:"
3008 if ($r15 != $oldr15 && $SHOWREGCHANGES == 1)
3009 echo \033[31m
3010 else
3011 echo \033[0m
3012 end
3013 printf " 0x%016lX\n ", $r15
3014 echo \033[32m
3015 printf "CS:"
3016 echo \033[0m
3017 printf " %04X ", $cs
3018 echo \033[32m
3019 printf "DS:"
3020 echo \033[0m
3021 printf " %04X ", $ds
3022 echo \033[32m
3023 printf "ES:"
3024 echo \033[0m
3025 printf " %04X ", $es
3026 echo \033[32m
3027 printf "FS:"
3028 echo \033[0m
3029 printf " %04X ", $fs
3030 echo \033[32m
3031 printf "GS:"
3032 echo \033[0m
3033 printf " %04X ", $gs
3034 echo \033[32m
3035 printf "SS:"
3036 echo \033[0m
3037 printf " %04X", $ss
3038 echo \033[0m
3039end
3040document regx64
3041Auxiliary function to display X86_64 registers.
3042end
3043
3044
3045define regx86
3046 printf " "
3047 # EAX
3048 echo \033[32m
3049 printf "EAX:"
3050 if ($eax != $oldeax && $SHOWREGCHANGES == 1)
3051 echo \033[31m
3052 else
3053 echo \033[0m
3054 end
3055 printf " 0x%08X ", $eax
3056 # EBX
3057 echo \033[32m
3058 printf "EBX:"
3059 if ($ebx != $oldebx && $SHOWREGCHANGES == 1)
3060 echo \033[31m
3061 else
3062 echo \033[0m
3063 end
3064 printf " 0x%08X ", $ebx
3065 # ECX
3066 echo \033[32m
3067 printf "ECX:"
3068 if ($ecx != $oldecx && $SHOWREGCHANGES == 1)
3069 echo \033[31m
3070 else
3071 echo \033[0m
3072 end
3073 printf " 0x%08X ", $ecx
3074 # EDX
3075 if ($edx != $oldedx && $SHOWREGCHANGES == 1)
3076 echo \033[32m
3077 printf "EDX:"
3078 echo \033[31m
3079 printf " 0x%08X ", $edx
3080 else
3081 echo \033[32m
3082 printf "EDX:"
3083 echo \033[0m
3084 printf " 0x%08X ", $edx
3085 end
3086 echo \033[1m\033[4m\033[31m
3087 flags
3088 echo \033[0m
3089 printf " "
3090 # ESI
3091 echo \033[32m
3092 printf "ESI:"
3093 if ($esi != $oldesi && $SHOWREGCHANGES == 1)
3094 echo \033[31m
3095 else
3096 echo \033[0m
3097 end
3098 printf " 0x%08X ", $esi
3099 # EDI
3100 echo \033[32m
3101 printf "EDI:"
3102 if ($edi != $oldedi && $SHOWREGCHANGES == 1)
3103 echo \033[31m
3104 else
3105 echo \033[0m
3106 end
3107 printf " 0x%08X ", $edi
3108 # EBP
3109 echo \033[32m
3110 printf "EBP:"
3111 if ($ebp != $oldebp && $SHOWREGCHANGES == 1)
3112 echo \033[31m
3113 else
3114 echo \033[0m
3115 end
3116 printf " 0x%08X ", $ebp
3117 # ESP
3118 echo \033[32m
3119 printf "ESP:"
3120 if ($esp != $oldesp && $SHOWREGCHANGES == 1)
3121 echo \033[31m
3122 else
3123 echo \033[0m
3124 end
3125 printf " 0x%08X ", $esp
3126 # EIP
3127 echo \033[32m
3128 printf "EIP:"
3129 echo \033[0m
3130 printf " 0x%08X\n ", $eip
3131 echo \033[32m
3132 printf "CS:"
3133 echo \033[0m
3134 printf " %04X ", $cs
3135 echo \033[32m
3136 printf "DS:"
3137 echo \033[0m
3138 printf " %04X ", $ds
3139 echo \033[32m
3140 printf "ES:"
3141 echo \033[0m
3142 printf " %04X ", $es
3143 echo \033[32m
3144 printf "FS:"
3145 echo \033[0m
3146 printf " %04X ", $fs
3147 echo \033[32m
3148 printf "GS:"
3149 echo \033[0m
3150 printf " %04X ", $gs
3151 echo \033[32m
3152 printf "SS:"
3153 echo \033[0m
3154 printf " %04X", $ss
3155 echo \033[0m
3156end
3157document regx86
3158Auxiliary function to display X86 registers.
3159end
3160
3161
3162define reg
3163 if $ARM == 1
3164 regarm
3165 if ($SHOWREGCHANGES == 1)
3166 set $oldr0 = $r0
3167 set $oldr1 = $r1
3168 set $oldr2 = $r2
3169 set $oldr3 = $r3
3170 set $oldr4 = $r4
3171 set $oldr5 = $r5
3172 set $oldr6 = $r6
3173 set $oldr7 = $r7
3174 set $oldr8 = $r8
3175 set $oldr9 = $r9
3176 set $oldr10 = $r10
3177 set $oldr11 = $r11
3178 set $oldr12 = $r12
3179 set $oldsp = $sp
3180 set $oldlr = $lr
3181 end
3182 else
3183 if ($64BITS == 1)
3184 regx64
3185 else
3186 regx86
3187 end
3188 # call smallregisters
3189 smallregisters
3190 # display conditional jump routine
3191 if ($64BITS == 1)
3192 printf "\t\t\t\t"
3193 end
3194 dumpjump
3195 printf "\n"
3196 if ($SHOWREGCHANGES == 1)
3197 if ($64BITS == 1)
3198 set $oldrax = $rax
3199 set $oldrbx = $rbx
3200 set $oldrcx = $rcx
3201 set $oldrdx = $rdx
3202 set $oldrsi = $rsi
3203 set $oldrdi = $rdi
3204 set $oldrbp = $rbp
3205 set $oldrsp = $rsp
3206 set $oldr8 = $r8
3207 set $oldr9 = $r9
3208 set $oldr10 = $r10
3209 set $oldr11 = $r11
3210 set $oldr12 = $r12
3211 set $oldr13 = $r13
3212 set $oldr14 = $r14
3213 set $oldr15 = $r15
3214 else
3215 set $oldeax = $eax
3216 set $oldebx = $ebx
3217 set $oldecx = $ecx
3218 set $oldedx = $edx
3219 set $oldesi = $esi
3220 set $oldedi = $edi
3221 set $oldebp = $ebp
3222 set $oldesp = $esp
3223 end
3224 end
3225 end
3226end
3227document reg
3228Print CPU registers.
3229end
3230
3231
3232define smallregisters
3233 if ($64BITS == 1)
3234 #64bits stuff
3235 # from rax
3236 set $eax = $rax & 0xffffffff
3237 set $ax = $rax & 0xffff
3238 set $al = $ax & 0xff
3239 set $ah = $ax >> 8
3240 # from rbx
3241 set $ebx = $rbx & 0xffffffff
3242 set $bx = $rbx & 0xffff
3243 set $bl = $bx & 0xff
3244 set $bh = $bx >> 8
3245 # from rcx
3246 set $ecx = $rcx & 0xffffffff
3247 set $cx = $rcx & 0xffff
3248 set $cl = $cx & 0xff
3249 set $ch = $cx >> 8
3250 # from rdx
3251 set $edx = $rdx & 0xffffffff
3252 set $dx = $rdx & 0xffff
3253 set $dl = $dx & 0xff
3254 set $dh = $dx >> 8
3255 # from rsi
3256 set $esi = $rsi & 0xffffffff
3257 set $si = $rsi & 0xffff
3258 # from rdi
3259 set $edi = $rdi & 0xffffffff
3260 set $di = $rdi & 0xffff
3261 #32 bits stuff
3262 else
3263 # from eax
3264 set $ax = $eax & 0xffff
3265 set $al = $ax & 0xff
3266 set $ah = $ax >> 8
3267 # from ebx
3268 set $bx = $ebx & 0xffff
3269 set $bl = $bx & 0xff
3270 set $bh = $bx >> 8
3271 # from ecx
3272 set $cx = $ecx & 0xffff
3273 set $cl = $cx & 0xff
3274 set $ch = $cx >> 8
3275 # from edx
3276 set $dx = $edx & 0xffff
3277 set $dl = $dx & 0xff
3278 set $dh = $dx >> 8
3279 # from esi
3280 set $si = $esi & 0xffff
3281 # from edi
3282 set $di = $edi & 0xffff
3283 end
3284end
3285document smallregisters
3286Create the 16 and 8 bit cpu registers (gdb doesn't have them by default).
3287And 32bits if we are dealing with 64bits binaries.
3288end
3289
3290
3291define func
3292 if $argc == 0
3293 info functions
3294 end
3295 if $argc == 1
3296 info functions $arg0
3297 end
3298 if $argc > 1
3299 help func
3300 end
3301end
3302document func
3303Print all function names in target, or those matching REGEXP.
3304Usage: func <REGEXP>
3305end
3306
3307
3308define var
3309 if $argc == 0
3310 info variables
3311 end
3312 if $argc == 1
3313 info variables $arg0
3314 end
3315 if $argc > 1
3316 help var
3317 end
3318end
3319document var
3320Print all global and static variable names (symbols), or those matching REGEXP.
3321Usage: var <REGEXP>
3322end
3323
3324
3325define lib
3326 info sharedlibrary
3327end
3328document lib
3329Print shared libraries linked to target.
3330end
3331
3332
3333define sig
3334 if $argc == 0
3335 info signals
3336 end
3337 if $argc == 1
3338 info signals $arg0
3339 end
3340 if $argc > 1
3341 help sig
3342 end
3343end
3344document sig
3345Print what debugger does when program gets various signals.
3346Specify a SIGNAL as argument to print info on that signal only.
3347Usage: sig <SIGNAL>
3348end
3349
3350
3351define threads
3352 info threads
3353end
3354document threads
3355Print threads in target.
3356end
3357
3358
3359define dis
3360 if $argc == 0
3361 disassemble
3362 end
3363 if $argc == 1
3364 disassemble $arg0
3365 end
3366 if $argc == 2
3367 disassemble $arg0 $arg1
3368 end
3369 if $argc > 2
3370 help dis
3371 end
3372end
3373document dis
3374Disassemble a specified section of memory.
3375Default is to disassemble the function surrounding the PC (program counter) of selected frame.
3376With one argument, ADDR1, the function surrounding this address is dumped.
3377Two arguments are taken as a range of memory to dump.
3378Usage: dis <ADDR1> <ADDR2>
3379end
3380
3381
3382# __________hex/ascii dump an address_________
3383define ascii_char
3384 if $argc != 1
3385 help ascii_char
3386 else
3387 # thanks elaine :)
3388 set $_c = *(unsigned char *)($arg0)
3389 if ($_c < 0x20 || $_c > 0x7E)
3390 printf "."
3391 else
3392 printf "%c", $_c
3393 end
3394 end
3395end
3396document ascii_char
3397Print ASCII value of byte at address ADDR.
3398Print "." if the value is unprintable.
3399Usage: ascii_char ADDR
3400end
3401
3402
3403define hex_quad
3404 if $argc != 1
3405 help hex_quad
3406 else
3407 printf "%02X %02X %02X %02X %02X %02X %02X %02X", \
3408 *(unsigned char*)($arg0), *(unsigned char*)($arg0 + 1), \
3409 *(unsigned char*)($arg0 + 2), *(unsigned char*)($arg0 + 3), \
3410 *(unsigned char*)($arg0 + 4), *(unsigned char*)($arg0 + 5), \
3411 *(unsigned char*)($arg0 + 6), *(unsigned char*)($arg0 + 7)
3412 end
3413end
3414document hex_quad
3415Print eight hexadecimal bytes starting at address ADDR.
3416Usage: hex_quad ADDR
3417end
3418
3419
3420define hexdump
3421 if $argc == 1
3422 hexdump_aux $arg0
3423 else
3424 if $argc == 2
3425 set $_count = 0
3426 while ($_count < $arg1)
3427 set $_i = ($_count * 0x10)
3428 hexdump_aux $data_addr+$_i
3429 set $_count++
3430 end
3431 else
3432 help hexdump
3433 end
3434 end
3435end
3436document hexdump
3437Display a 16-byte hex/ASCII dump of memory starting at address ADDR.
3438Optional parameter is the number of lines to display if you want more than one.
3439Usage: hexdump ADDR [nr lines]
3440end
3441
3442
3443define hexdump_aux
3444 if $argc != 1
3445 help hexdump_aux
3446 else
3447 echo \033[1m
3448 if ($64BITS == 1)
3449 printf "0x%016lX : ", $arg0
3450 else
3451 printf "0x%08X : ", $arg0
3452 end
3453 echo \033[0m
3454 hex_quad $arg0
3455 echo \033[1m
3456 printf " - "
3457 echo \033[0m
3458 hex_quad $arg0+8
3459 printf " "
3460 echo \033[1m
3461 ascii_char $arg0+0x0
3462 ascii_char $arg0+0x1
3463 ascii_char $arg0+0x2
3464 ascii_char $arg0+0x3
3465 ascii_char $arg0+0x4
3466 ascii_char $arg0+0x5
3467 ascii_char $arg0+0x6
3468 ascii_char $arg0+0x7
3469 ascii_char $arg0+0x8
3470 ascii_char $arg0+0x9
3471 ascii_char $arg0+0xA
3472 ascii_char $arg0+0xB
3473 ascii_char $arg0+0xC
3474 ascii_char $arg0+0xD
3475 ascii_char $arg0+0xE
3476 ascii_char $arg0+0xF
3477 echo \033[0m
3478 printf "\n"
3479 end
3480end
3481document hexdump_aux
3482Display a 16-byte hex/ASCII dump of memory at address ADDR.
3483Usage: hexdump_aux ADDR
3484end
3485
3486
3487# _______________data window__________________
3488define ddump
3489 if $argc != 1
3490 help ddump
3491 else
3492 echo \033[34m
3493 if ($64BITS == 1)
3494 printf "[0x%04X:0x%016lX]", $ds, $data_addr
3495 else
3496 printf "[0x%04X:0x%08X]", $ds, $data_addr
3497 end
3498 echo \033[34m
3499 printf "------------------------"
3500 printf "-------------------------------"
3501 if ($64BITS == 1)
3502 printf "-------------------------------------"
3503 end
3504 echo \033[1;34m
3505 printf "[data]\n"
3506 echo \033[0m
3507 set $_count = 0
3508 while ($_count < $arg0)
3509 set $_i = ($_count * 0x10)
3510 hexdump $data_addr+$_i
3511 set $_count++
3512 end
3513 end
3514end
3515document ddump
3516Display NUM lines of hexdump for address in $data_addr global variable.
3517Usage: ddump NUM
3518end
3519
3520
3521define dd
3522 if $argc != 1
3523 help dd
3524 else
3525 set $data_addr = $arg0
3526 ddump 0x10
3527 end
3528end
3529document dd
3530Display 16 lines of a hex dump of address starting at ADDR.
3531Usage: dd ADDR
3532end
3533
3534
3535define datawin
3536 if $ARM == 1
3537 if ((($r0 >> 0x18) == 0x40) || (($r0 >> 0x18) == 0x08) || (($r0 >> 0x18) == 0xBF))
3538 set $data_addr = $r0
3539 else
3540 if ((($r1 >> 0x18) == 0x40) || (($r1 >> 0x18) == 0x08) || (($r1 >> 0x18) == 0xBF))
3541 set $data_addr = $r1
3542 else
3543 if ((($r2 >> 0x18) == 0x40) || (($r2 >> 0x18) == 0x08) || (($r2 >> 0x18) == 0xBF))
3544 set $data_addr = $r2
3545 else
3546 set $data_addr = $sp
3547 end
3548 end
3549 end
3550################################# X86
3551 else
3552 if ($64BITS == 1)
3553 if ((($rsi >> 0x18) == 0x40) || (($rsi >> 0x18) == 0x08) || (($rsi >> 0x18) == 0xBF))
3554 set $data_addr = $rsi
3555 else
3556 if ((($rdi >> 0x18) == 0x40) || (($rdi >> 0x18) == 0x08) || (($rdi >> 0x18) == 0xBF))
3557 set $data_addr = $rdi
3558 else
3559 if ((($rax >> 0x18) == 0x40) || (($rax >> 0x18) == 0x08) || (($rax >> 0x18) == 0xBF))
3560 set $data_addr = $rax
3561 else
3562 set $data_addr = $rsp
3563 end
3564 end
3565 end
3566 else
3567 if ((($esi >> 0x18) == 0x40) || (($esi >> 0x18) == 0x08) || (($esi >> 0x18) == 0xBF))
3568 set $data_addr = $esi
3569 else
3570 if ((($edi >> 0x18) == 0x40) || (($edi >> 0x18) == 0x08) || (($edi >> 0x18) == 0xBF))
3571 set $data_addr = $edi
3572 else
3573 if ((($eax >> 0x18) == 0x40) || (($eax >> 0x18) == 0x08) || (($eax >> 0x18) == 0xBF))
3574 set $data_addr = $eax
3575 else
3576 set $data_addr = $esp
3577 end
3578 end
3579 end
3580 end
3581 end
3582 ddump $CONTEXTSIZE_DATA
3583end
3584document datawin
3585Display valid address from one register in data window.
3586Registers to choose are: esi, edi, eax, or esp.
3587end
3588
3589
3590################################
3591##### ALERT ALERT ALERT ########
3592################################
3593# Huge mess going here :) HAHA #
3594################################
3595define dumpjump
3596 if $ARM == 1
3597 ## Most ARM and Thumb instructions are conditional!
3598 # each instruction is 32 bits long
3599 # 4 bits are for condition codes (16 in total) (bits 31:28 in ARM contain the condition or 1111 if instruction is unconditional)
3600 # 2x4 bits for destination and first operand registers
3601 # one for the set-status flag
3602 # an assorted number for other stuff
3603 # 12 bits for any immediate value
3604 # $_t_flag == 0 => ARM mode
3605 # $_t_flag == 1 => Thumb or ThumbEE
3606 if ($cpsr->t & 1)
3607 set $_t_flag = 1
3608 else
3609 set $_t_flag = 0
3610 end
3611
3612 if $_t_flag == 0
3613 set $_lastbyte = *(unsigned char *)($pc+3)
3614 #set $_bit31 = ($_lastbyte >> 7) & 1
3615 #set $_bit30 = ($_lastbyte >> 6) & 1
3616 #set $_bit29 = ($_lastbyte >> 5) & 1
3617 #set $_bit28 = ($_lastbyte >> 4) & 1
3618 set $_conditional = $_lastbyte >> 4
3619 dumpjumphelper
3620 else
3621 # if bits 15-12 (opcode in Thumb instructions) are equal to 1 1 0 1 (0xD) then we have a conditional branch
3622 # bits 11-8 for the conditional execution code (check ARMv7 manual A8.3)
3623 if ( (*(unsigned char *)($pc+1) >> 4) == 0xD )
3624 set $_conditional = *(unsigned char *)($pc+1) ^ 0xD0
3625 dumpjumphelper
3626 end
3627 end
3628##################### X86
3629 else
3630 ## grab the first two bytes from the instruction so we can determine the jump instruction
3631 set $_byte1 = *(unsigned char *)$pc
3632 set $_byte2 = *(unsigned char *)($pc+1)
3633 ## and now check what kind of jump we have (in case it's a jump instruction)
3634 ## I changed the flags routine to save the flag into a variable, so we don't need to repeat the process :) (search for "define flags")
3635
3636 ## opcode 0x77: JA, JNBE (jump if CF=0 and ZF=0)
3637 ## opcode 0x0F87: JNBE, JA
3638 if ( ($_byte1 == 0x77) || ($_byte1 == 0x0F && $_byte2 == 0x87) )
3639 # cf=0 and zf=0
3640 if ($_cf_flag == 0 && $_zf_flag == 0)
3641 echo \033[31m
3642 printf " Jump is taken (c=0 and z=0)"
3643 else
3644 # cf != 0 or zf != 0
3645 echo \033[31m
3646 printf " Jump is NOT taken (c!=0 or z!=0)"
3647 end
3648 end
3649 ## opcode 0x73: JAE, JNB, JNC (jump if CF=0)
3650 ## opcode 0x0F83: JNC, JNB, JAE (jump if CF=0)
3651 if ( ($_byte1 == 0x73) || ($_byte1 == 0x0F && $_byte2 == 0x83) )
3652 # cf=0
3653 if ($_cf_flag == 0)
3654 echo \033[31m
3655 printf " Jump is taken (c=0)"
3656 else
3657 # cf != 0
3658 echo \033[31m
3659 printf " Jump is NOT taken (c!=0)"
3660 end
3661 end
3662 ## opcode 0x72: JB, JC, JNAE (jump if CF=1)
3663 ## opcode 0x0F82: JNAE, JB, JC
3664 if ( ($_byte1 == 0x72) || ($_byte1 == 0x0F && $_byte2 == 0x82) )
3665 # cf=1
3666 if ($_cf_flag == 1)
3667 echo \033[31m
3668 printf " Jump is taken (c=1)"
3669 else
3670 # cf != 1
3671 echo \033[31m
3672 printf " Jump is NOT taken (c!=1)"
3673 end
3674 end
3675 ## opcode 0x76: JBE, JNA (jump if CF=1 or ZF=1)
3676 ## opcode 0x0F86: JBE, JNA
3677 if ( ($_byte1 == 0x76) || ($_byte1 == 0x0F && $_byte2 == 0x86) )
3678 # cf=1 or zf=1
3679 if (($_cf_flag == 1) || ($_zf_flag == 1))
3680 echo \033[31m
3681 printf " Jump is taken (c=1 or z=1)"
3682 else
3683 # cf != 1 or zf != 1
3684 echo \033[31m
3685 printf " Jump is NOT taken (c!=1 or z!=1)"
3686 end
3687 end
3688 ## opcode 0xE3: JCXZ, JECXZ, JRCXZ (jump if CX=0 or ECX=0 or RCX=0)
3689 if ($_byte1 == 0xE3)
3690 # cx=0 or ecx=0
3691 if (($ecx == 0) || ($cx == 0))
3692 echo \033[31m
3693 printf " Jump is taken (cx=0 or ecx=0)"
3694 else
3695 echo \033[31m
3696 printf " Jump is NOT taken (cx!=0 or ecx!=0)"
3697 end
3698 end
3699 ## opcode 0x74: JE, JZ (jump if ZF=1)
3700 ## opcode 0x0F84: JZ, JE, JZ (jump if ZF=1)
3701 if ( ($_byte1 == 0x74) || ($_byte1 == 0x0F && $_byte2 == 0x84) )
3702 # ZF = 1
3703 if ($_zf_flag == 1)
3704 echo \033[31m
3705 printf " Jump is taken (z=1)"
3706 else
3707 # ZF = 0
3708 echo \033[31m
3709 printf " Jump is NOT taken (z!=1)"
3710 end
3711 end
3712 ## opcode 0x7F: JG, JNLE (jump if ZF=0 and SF=OF)
3713 ## opcode 0x0F8F: JNLE, JG (jump if ZF=0 and SF=OF)
3714 if ( ($_byte1 == 0x7F) || ($_byte1 == 0x0F && $_byte2 == 0x8F) )
3715 # zf = 0 and sf = of
3716 if (($_zf_flag == 0) && ($_sf_flag == $_of_flag))
3717 echo \033[31m
3718 printf " Jump is taken (z=0 and s=o)"
3719 else
3720 echo \033[31m
3721 printf " Jump is NOT taken (z!=0 or s!=o)"
3722 end
3723 end
3724 ## opcode 0x7D: JGE, JNL (jump if SF=OF)
3725 ## opcode 0x0F8D: JNL, JGE (jump if SF=OF)
3726 if ( ($_byte1 == 0x7D) || ($_byte1 == 0x0F && $_byte2 == 0x8D) )
3727 # sf = of
3728 if ($_sf_flag == $_of_flag)
3729 echo \033[31m
3730 printf " Jump is taken (s=o)"
3731 else
3732 echo \033[31m
3733 printf " Jump is NOT taken (s!=o)"
3734 end
3735 end
3736 ## opcode: 0x7C: JL, JNGE (jump if SF != OF)
3737 ## opcode: 0x0F8C: JNGE, JL (jump if SF != OF)
3738 if ( ($_byte1 == 0x7C) || ($_byte1 == 0x0F && $_byte2 == 0x8C) )
3739 # sf != of
3740 if ($_sf_flag != $_of_flag)
3741 echo \033[31m
3742 printf " Jump is taken (s!=o)"
3743 else
3744 echo \033[31m
3745 printf " Jump is NOT taken (s=o)"
3746 end
3747 end
3748 ## opcode 0x7E: JLE, JNG (jump if ZF = 1 or SF != OF)
3749 ## opcode 0x0F8E: JNG, JLE (jump if ZF = 1 or SF != OF)
3750 if ( ($_byte1 == 0x7E) || ($_byte1 == 0x0F && $_byte2 == 0x8E) )
3751 # zf = 1 or sf != of
3752 if (($_zf_flag == 1) || ($_sf_flag != $_of_flag))
3753 echo \033[31m
3754 printf " Jump is taken (zf=1 or sf!=of)"
3755 else
3756 echo \033[31m
3757 printf " Jump is NOT taken (zf!=1 or sf=of)"
3758 end
3759 end
3760 ## opcode 0x75: JNE, JNZ (jump if ZF = 0)
3761 ## opcode 0x0F85: JNE, JNZ (jump if ZF = 0)
3762 if ( ($_byte1 == 0x75) || ($_byte1 == 0x0F && $_byte2 == 0x85) )
3763 # ZF = 0
3764 if ($_zf_flag == 0)
3765 echo \033[31m
3766 printf " Jump is taken (z=0)"
3767 else
3768 # ZF = 1
3769 echo \033[31m
3770 printf " Jump is NOT taken (z!=0)"
3771 end
3772 end
3773 ## opcode 0x71: JNO (OF = 0)
3774 ## opcode 0x0F81: JNO (OF = 0)
3775 if ( ($_byte1 == 0x71) || ($_byte1 == 0x0F && $_byte2 == 0x81) )
3776 # OF = 0
3777 if ($_of_flag == 0)
3778 echo \033[31m
3779 printf " Jump is taken (o=0)"
3780 else
3781 # OF != 0
3782 echo \033[31m
3783 printf " Jump is NOT taken (o!=0)"
3784 end
3785 end
3786 ## opcode 0x7B: JNP, JPO (jump if PF = 0)
3787 ## opcode 0x0F8B: JPO (jump if PF = 0)
3788 if ( ($_byte1 == 0x7B) || ($_byte1 == 0x0F && $_byte2 == 0x8B) )
3789 # PF = 0
3790 if ($_pf_flag == 0)
3791 echo \033[31m
3792 printf " Jump is NOT taken (p=0)"
3793 else
3794 # PF != 0
3795 echo \033[31m
3796 printf " Jump is taken (p!=0)"
3797 end
3798 end
3799 ## opcode 0x79: JNS (jump if SF = 0)
3800 ## opcode 0x0F89: JNS (jump if SF = 0)
3801 if ( ($_byte1 == 0x79) || ($_byte1 == 0x0F && $_byte2 == 0x89) )
3802 # SF = 0
3803 if ($_sf_flag == 0)
3804 echo \033[31m
3805 printf " Jump is taken (s=0)"
3806 else
3807 # SF != 0
3808 echo \033[31m
3809 printf " Jump is NOT taken (s!=0)"
3810 end
3811 end
3812 ## opcode 0x70: JO (jump if OF=1)
3813 ## opcode 0x0F80: JO (jump if OF=1)
3814 if ( ($_byte1 == 0x70) || ($_byte1 == 0x0F && $_byte2 == 0x80) )
3815 # OF = 1
3816 if ($_of_flag == 1)
3817 echo \033[31m
3818 printf " Jump is taken (o=1)"
3819 else
3820 # OF != 1
3821 echo \033[31m
3822 printf " Jump is NOT taken (o!=1)"
3823 end
3824 end
3825 ## opcode 0x7A: JP, JPE (jump if PF=1)
3826 ## opcode 0x0F8A: JP, JPE (jump if PF=1)
3827 if ( ($_byte1 == 0x7A) || ($_byte1 == 0x0F && $_byte2 == 0x8A) )
3828 # PF = 1
3829 if ($_pf_flag == 1)
3830 echo \033[31m
3831 printf " Jump is taken (p=1)"
3832 else
3833 # PF = 0
3834 echo \033[31m
3835 printf " Jump is NOT taken (p!=1)"
3836 end
3837 end
3838 ## opcode 0x78: JS (jump if SF=1)
3839 ## opcode 0x0F88: JS (jump if SF=1)
3840 if ( ($_byte1 == 0x78) || ($_byte1 == 0x0F && $_byte2 == 0x88) )
3841 # SF = 1
3842 if ($_sf_flag == 1)
3843 echo \033[31m
3844 printf " Jump is taken (s=1)"
3845 else
3846 # SF != 1
3847 echo \033[31m
3848 printf " Jump is NOT taken (s!=1)"
3849 end
3850 end
3851 end
3852end
3853document dumpjump
3854Display if conditional jump will be taken or not.
3855end
3856
3857define dumpjumphelper
3858 # 0000 - EQ: Z == 1
3859 if ($_conditional == 0x0)
3860 if ($_z_flag == 1)
3861 echo \033[31m
3862 printf " Jump is taken (z==1)"
3863 else
3864 echo \033[31m
3865 printf " Jump is NOT taken (z!=1)"
3866 end
3867 end
3868 # 0001 - NE: Z == 0
3869 if ($_conditional == 0x1)
3870 if ($_z_flag == 0)
3871 echo \033[31m
3872 printf " Jump is taken (z==0)"
3873 else
3874 echo \033[31m
3875 printf " Jump is NOT taken (z!=0)"
3876 end
3877 end
3878 # 0010 - CS: C == 1
3879 if ($_conditional == 0x2)
3880 if ($_c_flag == 1)
3881 echo \033[31m
3882 printf " Jump is taken (c==1)"
3883 else
3884 echo \033[31m
3885 printf " Jump is NOT taken (c!=1)"
3886 end
3887 end
3888 # 0011 - CC: C == 0
3889 if ($_conditional == 0x3)
3890 if ($_c_flag == 0)
3891 echo \033[31m
3892 printf " Jump is taken (c==0)"
3893 else
3894 echo \033[31m
3895 printf " Jump is NOT taken (c!=0)"
3896 end
3897 end
3898 # 0100 - MI: N == 1
3899 if ($_conditional == 0x4)
3900 if ($_n_flag == 1)
3901 echo \033[31m
3902 printf " Jump is taken (n==1)"
3903 else
3904 echo \033[31m
3905 printf " Jump is NOT taken (n!=1)"
3906 end
3907 end
3908 # 0101 - PL: N == 0
3909 if ($_conditional == 0x5)
3910 if ($_n_flag == 0)
3911 echo \033[31m
3912 printf " Jump is taken (n==0)"
3913 else
3914 echo \033[31m
3915 printf " Jump is NOT taken (n!=0)"
3916 end
3917 end
3918 # 0110 - VS: V == 1
3919 if ($_conditional == 0x6)
3920 if ($_v_flag == 1)
3921 echo \033[31m
3922 printf " Jump is taken (v==1)"
3923 else
3924 echo \033[31m
3925 printf " Jump is NOT taken (v!=1)"
3926 end
3927 end
3928 # 0111 - VC: V == 0
3929 if ($_conditional == 0x7)
3930 if ($_v_flag == 0)
3931 echo \033[31m
3932 printf " Jump is taken (v==0)"
3933 else
3934 echo \033[31m
3935 printf " Jump is NOT taken (v!=0)"
3936 end
3937 end
3938 # 1000 - HI: C == 1 and Z == 0
3939 if ($_conditional == 0x8)
3940 if ($_c_flag == 1 && $_z_flag == 0)
3941 echo \033[31m
3942 printf " Jump is taken (c==1 and z==0)"
3943 else
3944 echo \033[31m
3945 printf " Jump is NOT taken (c!=1 or z!=0)"
3946 end
3947 end
3948 # 1001 - LS: C == 0 or Z == 1
3949 if ($_conditional == 0x9)
3950 if ($_c_flag == 0 || $_z_flag == 1)
3951 echo \033[31m
3952 printf " Jump is taken (c==0 or z==1)"
3953 else
3954 echo \033[31m
3955 printf " Jump is NOT taken (c!=0 or z!=1)"
3956 end
3957 end
3958 # 1010 - GE: N == V
3959 if ($_conditional == 0xA)
3960 if ($_n_flag == $_v_flag)
3961 echo \033[31m
3962 printf " Jump is taken (n==v)"
3963 else
3964 echo \033[31m
3965 printf " Jump is NOT taken (n!=v)"
3966 end
3967 end
3968 # 1011 - LT: N != V
3969 if ($_conditional == 0xB)
3970 if ($_n_flag != $_v_flag)
3971 echo \033[31m
3972 printf " Jump is taken (n!=v)"
3973 else
3974 echo \033[31m
3975 printf " Jump is NOT taken (n==v)"
3976 end
3977 end
3978 # 1100 - GT: Z == 0 and N == V
3979 if ($_conditional == 0xC)
3980 if ($_z_flag == 0 && $_n_flag == $_v_flag)
3981 echo \033[31m
3982 printf " Jump is taken (z==0 and n==v)"
3983 else
3984 echo \033[31m
3985 printf " Jump is NOT taken (z!=0 or n!=v)"
3986 end
3987 end
3988 # 1101 - LE: Z == 1 or N != V
3989 if ($_conditional == 0xD)
3990 if ($_z_flag == 1 || $_n_flag != $_v_flag)
3991 echo \033[31m
3992 printf " Jump is taken (z==1 or n!=v)"
3993 else
3994 echo \033[31m
3995 printf " Jump is NOT taken (z!=1 or n==v)"
3996 end
3997 end
3998end
3999document dumpjumphelper
4000Helper function to decide if conditional jump will be taken or not, for ARM and Thumb.
4001end
4002
4003
4004# _______________process context______________
4005# initialize variable
4006set $displayobjectivec = 0
4007
4008define context
4009 echo \033[34m
4010 if $SHOWCPUREGISTERS == 1
4011 printf "----------------------------------------"
4012 printf "----------------------------------"
4013 if ($64BITS == 1)
4014 printf "---------------------------------------------"
4015 end
4016 echo \033[34m\033[1m
4017 printf "[regs]\n"
4018 echo \033[0m
4019 reg
4020 echo \033[36m
4021 end
4022 if $SHOWSTACK == 1
4023 echo \033[34m
4024 if ($64BITS == 1)
4025 printf "[0x%04X:0x%016lX]", $ss, $rsp
4026 else
4027 printf "[0x%04X:0x%08X]", $ss, $esp
4028 end
4029 echo \033[34m
4030 printf "-------------------------"
4031 printf "-----------------------------"
4032 if ($64BITS == 1)
4033 printf "-------------------------------------"
4034 end
4035 echo \033[34m\033[1m
4036 printf "[stack]\n"
4037 echo \033[0m
4038 set $context_i = $CONTEXTSIZE_STACK
4039 while ($context_i > 0)
4040 set $context_t = $sp + 0x10 * ($context_i - 1)
4041 hexdump $context_t
4042 set $context_i--
4043 end
4044 end
4045 # show the objective C message being passed to msgSend
4046 if $SHOWOBJECTIVEC == 1
4047 #FIXME: X64 and ARM
4048 # What a piece of crap that's going on here :)
4049 # detect if it's the correct opcode we are searching for
4050 if $ARM == 0
4051 set $__byte1 = *(unsigned char *)$pc
4052 set $__byte = *(int *)$pc
4053 if ($__byte == 0x4244489)
4054 set $objectivec = $eax
4055 set $displayobjectivec = 1
4056 end
4057
4058 if ($__byte == 0x4245489)
4059 set $objectivec = $edx
4060 set $displayobjectivec = 1
4061 end
4062
4063 if ($__byte == 0x4244c89)
4064 set $objectivec = $edx
4065 set $displayobjectivec = 1
4066 end
4067 else
4068 set $__byte1 = 0
4069 end
4070 # and now display it or not (we have no interest in having the info displayed after the call)
4071 if $__byte1 == 0xE8
4072 if $displayobjectivec == 1
4073 echo \033[34m
4074 printf "--------------------------------------------------------------------"
4075 if ($64BITS == 1)
4076 printf "---------------------------------------------"
4077 end
4078 echo \033[34m\033[1m
4079 printf "[ObjectiveC]\n"
4080 echo \033[0m\033[30m
4081 x/s $objectivec
4082 end
4083 set $displayobjectivec = 0
4084 end
4085 if $displayobjectivec == 1
4086 echo \033[34m
4087 printf "--------------------------------------------------------------------"
4088 if ($64BITS == 1)
4089 printf "---------------------------------------------"
4090 end
4091 echo \033[34m\033[1m
4092 printf "[ObjectiveC]\n"
4093 echo \033[0m\033[30m
4094 x/s $objectivec
4095 end
4096 end
4097 echo \033[0m
4098# and this is the end of this little crap
4099
4100 if $SHOWDATAWIN == 1
4101 datawin
4102 end
4103
4104 echo \033[34m
4105 printf "--------------------------------------------------------------------------"
4106 if ($64BITS == 1)
4107 printf "---------------------------------------------"
4108 end
4109 echo \033[34m\033[1m
4110 printf "[code]\n"
4111 echo \033[0m
4112 set $context_i = $CONTEXTSIZE_CODE
4113 if ($context_i > 0)
4114 if ($SETCOLOUR1STLINE == 1)
4115 echo \033[32m
4116 x /i $pc
4117 echo \033[0m
4118 else
4119 x /i $pc
4120 end
4121 set $context_i--
4122 end
4123 while ($context_i > 0)
4124 x /i
4125 set $context_i--
4126 end
4127 echo \033[34m
4128 printf "----------------------------------------"
4129 printf "----------------------------------------"
4130 if ($64BITS == 1)
4131 printf "---------------------------------------------\n"
4132 else
4133 printf "\n"
4134 end
4135 echo \033[0m
4136end
4137document context
4138Print context window, i.e. regs, stack, ds:esi and disassemble cs:eip.
4139end
4140
4141
4142define context-on
4143 set $SHOW_CONTEXT = 1
4144 printf "Displaying of context is now ON\n"
4145end
4146document context-on
4147Enable display of context on every program break.
4148end
4149
4150
4151define context-off
4152 set $SHOW_CONTEXT = 0
4153 printf "Displaying of context is now OFF\n"
4154end
4155document context-off
4156Disable display of context on every program break.
4157end
4158
4159
4160# _______________process control______________
4161define n
4162 if $argc == 0
4163 nexti
4164 end
4165 if $argc == 1
4166 nexti $arg0
4167 end
4168 if $argc > 1
4169 help n
4170 end
4171end
4172document n
4173Step one instruction, but proceed through subroutine calls.
4174If NUM is given, then repeat it NUM times or till program stops.
4175This is alias for nexti.
4176Usage: n <NUM>
4177end
4178
4179
4180define go
4181 if $argc == 0
4182 stepi
4183 end
4184 if $argc == 1
4185 stepi $arg0
4186 end
4187 if $argc > 1
4188 help go
4189 end
4190end
4191document go
4192Step one instruction exactly.
4193If NUM is given, then repeat it NUM times or till program stops.
4194This is alias for stepi.
4195Usage: go <NUM>
4196end
4197
4198
4199define pret
4200 finish
4201end
4202document pret
4203Execute until selected stack frame returns (step out of current call).
4204Upon return, the value returned is printed and put in the value history.
4205end
4206
4207
4208define init
4209 set $SHOW_NEST_INSN = 0
4210 tbreak _init
4211 r
4212end
4213document init
4214Run program and break on _init().
4215end
4216
4217
4218define start
4219 set $SHOW_NEST_INSN = 0
4220 tbreak _start
4221 r
4222end
4223document start
4224Run program and break on _start().
4225end
4226
4227
4228define sstart
4229 set $SHOW_NEST_INSN = 0
4230 tbreak __libc_start_main
4231 r
4232end
4233document sstart
4234Run program and break on __libc_start_main().
4235Useful for stripped executables.
4236end
4237
4238
4239define main
4240 set $SHOW_NEST_INSN = 0
4241 tbreak main
4242 r
4243end
4244document main
4245Run program and break on main().
4246end
4247
4248
4249# FIXME64
4250#### WARNING ! WARNING !!
4251#### More more messy stuff starting !!!
4252#### I was thinking about how to do this and then it ocurred me that it could be as simple as this ! :)
4253define stepoframework
4254 if $ARM == 1
4255 # bl and bx opcodes
4256 # bx Rn => ARM bits 27-20: 0 0 0 1 0 0 1 0 , bits 7-4: 0 0 0 1 ; Thumb bits: 15-7: 0 1 0 0 0 1 1 1 0
4257 # blx Rn => ARM bits 27-20: 0 0 0 1 0 0 1 0 , bits 7-4: 0 0 1 1 ; Thumb bits: 15-7: 0 1 0 0 0 1 1 1 1
4258 # bl # => ARM bits 27-24: 1 0 1 1 ; Thumb bits: 15-11: 1 1 1 1 0
4259 # blx # => ARM bits 31-25: 1 1 1 1 1 0 1 ; Thumb bits: 15-11: 1 1 1 1 0
4260 set $_nextaddress = 0
4261
4262 # ARM Mode
4263 if ($_t_flag == 0)
4264 set $_branchesint = *(unsigned int*)$pc
4265 set $_bit31 = ($_branchesint >> 0x1F) & 1
4266 set $_bit30 = ($_branchesint >> 0x1E) & 1
4267 set $_bit29 = ($_branchesint >> 0x1D) & 1
4268 set $_bit28 = ($_branchesint >> 0x1C) & 1
4269 set $_bit27 = ($_branchesint >> 0x1B) & 1
4270 set $_bit26 = ($_branchesint >> 0x1A) & 1
4271 set $_bit25 = ($_branchesint >> 0x19) & 1
4272 set $_bit24 = ($_branchesint >> 0x18) & 1
4273 set $_bit23 = ($_branchesint >> 0x17) & 1
4274 set $_bit22 = ($_branchesint >> 0x16) & 1
4275 set $_bit21 = ($_branchesint >> 0x15) & 1
4276 set $_bit20 = ($_branchesint >> 0x14) & 1
4277 set $_bit7 = ($_branchesint >> 0x7) & 1
4278 set $_bit6 = ($_branchesint >> 0x6) & 1
4279 set $_bit5 = ($_branchesint >> 0x5) & 1
4280 set $_bit4 = ($_branchesint >> 0x4) & 1
4281
4282 # set $_lastbyte = *(unsigned char *)($pc+3)
4283 # set $_bits2724 = $_lastbyte & 0x1
4284 # set $_bits3128 = $_lastbyte >> 4
4285 # if ($_bits3128 == 0xF)
4286 # set $_bits2724 = $_lastbyte & 0xA
4287 # set $_bits2724 = $_bits2724 >> 1
4288 # end
4289 # set $_previousbyte = *(unsigned char *)($pc+2)
4290 # set $_bits2320 = $_previousbyte >> 4
4291 # printf "bits2724: %x bits2320: %x\n", $_bits2724, $_bits2320
4292
4293 if ($_bit27 == 0 && $_bit26 == 0 && $_bit25 == 0 && $_bit24 == 1 && $_bit23 == 0 && $_bit22 == 0 && $_bit21 == 1 && $_bit20 == 0 && $_bit7 == 0 && $_bit6 == 0 && $_bit5 == 0 && $_bit4 == 1)
4294 printf "Found a bx Rn\n"
4295 set $_nextaddress = $pc+0x4
4296 end
4297 if ($_bit27 == 0 && $_bit26 == 0 && $_bit25 == 0 && $_bit24 == 1 && $_bit23 == 0 && $_bit22 == 0 && $_bit21 == 1 && $_bit20 == 0 && $_bit7 == 0 && $_bit6 == 0 && $_bit5 == 1 && $_bit4 == 1)
4298 printf "Found a blx Rn\n"
4299 set $_nextaddress = $pc+0x4
4300 end
4301 if ($_bit27 == 1 && $_bit26 == 0 && $_bit25 == 1 && $_bit24 == 1)
4302 printf "Found a bl #\n"
4303 set $_nextaddress = $pc+0x4
4304 end
4305 if ($_bit31 == 1 && $_bit30 == 1 && $_bit29 == 1 && $_bit28 == 1 && $_bit27 == 1 && $_bit26 == 0 && $_bit25 == 1)
4306 printf "Found a blx #\n"
4307 set $_nextaddress = $pc+0x4
4308 end
4309 # Thumb Mode
4310 else
4311 # 32 bits instructions in Thumb are divided into two half words
4312 set $_hw1 = *(unsigned short*)($pc)
4313 set $_hw2 = *(unsigned short*)($pc+2)
4314
4315 # bl/blx (immediate)
4316 # hw1: bits 15-11: 1 1 1 1 0
4317 # hw2: bits 15-14: 1 1 ; BL bit 12: 1 ; BLX bit 12: 0
4318 if ( ($_hw1 >> 0xC) == 0xF && (($_hw1 >> 0xB) & 1) == 0)
4319 if ( ((($_hw2 >> 0xF) & 1) == 1) && ((($_hw2 >> 0xE) & 1) == 1) )
4320 set $_nextaddress = $pc+0x4
4321 end
4322 end
4323 end
4324 # if we have found a call to bypass we set a temporary breakpoint on next instruction and continue
4325 if ($_nextaddress != 0)
4326 tbreak *$_nextaddress
4327 continue
4328 printf "[StepO] Next address will be %x\n", $_nextaddress
4329 # else we just single step
4330 else
4331 nexti
4332 end
4333###################################### X86
4334 else
4335 ## we know that an opcode starting by 0xE8 has a fixed length
4336 ## for the 0xFF opcodes, we can enumerate what is possible to have
4337 # first we grab the first 3 bytes from the current program counter
4338 set $_byte1 = *(unsigned char *)$pc
4339 set $_byte2 = *(unsigned char *)($pc+1)
4340 set $_byte3 = *(unsigned char *)($pc+2)
4341 # and start the fun
4342 # if it's a 0xE8 opcode, the total instruction size will be 5 bytes
4343 # so we can simply calculate the next address and use a temporary breakpoint ! Voila :)
4344 set $_nextaddress = 0
4345 # this one is the must useful for us !!!
4346 if ($_byte1 == 0xE8)
4347 set $_nextaddress = $pc + 0x5
4348 else
4349 # just other cases we might be interested in... maybe this should be removed since the 0xE8 opcode is the one we will use more
4350 # this is a big fucking mess and can be improved for sure :) I don't like the way it is ehehehe
4351 if ($_byte1 == 0xFF)
4352 # call *%eax (0xFFD0) || call *%edx (0xFFD2) || call *(%ecx) (0xFFD1) || call (%eax) (0xFF10) || call *%esi (0xFFD6) || call *%ebx (0xFFD3) || call DWORD PTR [edx] (0xFF12)
4353 if ($_byte2 == 0xD0 || $_byte2 == 0xD1 || $_byte2 == 0xD2 || $_byte2 == 0xD3 || $_byte2 == 0xD6 || $_byte2 == 0x10 || $_byte2 == 0x11 || $_byte2 == 0xD7 || $_byte2 == 0x12)
4354 set $_nextaddress = $pc + 0x2
4355 end
4356 # call *0x??(%ebp) (0xFF55??) || call *0x??(%esi) (0xFF56??) || call *0x??(%edi) (0xFF5F??) || call *0x??(%ebx)
4357 # call *0x??(%edx) (0xFF52??) || call *0x??(%ecx) (0xFF51??) || call *0x??(%edi) (0xFF57??) || call *0x??(%eax) (0xFF50??)
4358 if ($_byte2 == 0x55 || $_byte2 == 0x56 || $_byte2 == 0x5F || $_byte2 == 0x53 || $_byte2 == 0x52 || $_byte2 == 0x51 || $_byte2 == 0x57 || $_byte2 == 0x50)
4359 set $_nextaddress = $pc + 0x3
4360 end
4361 # call *0x????????(%ebx) (0xFF93????????) ||
4362 if ($_byte2 == 0x93 || $_byte2 == 0x94 || $_byte2 == 0x90 || $_byte2 == 0x92)
4363 set $_nextaddress = $pc + 6
4364 end
4365 # call *0x????????(%ebx,%eax,4) (0xFF94??????????)
4366 if ($_byte2 == 0x94)
4367 set $_nextaddress = $pc + 7
4368 end
4369 end
4370 end
4371 # if we have found a call to bypass we set a temporary breakpoint on next instruction and continue
4372 if ($_nextaddress != 0)
4373 if ($arg0 == 1)
4374 thbreak *$_nextaddress
4375 else
4376 tbreak *$_nextaddress
4377 end
4378 continue
4379 # else we just single step
4380 else
4381 nexti
4382 end
4383 end
4384end
4385document stepoframework
4386Auxiliary function to stepo command.
4387end
4388
4389define stepo
4390 stepoframework 0
4391end
4392document stepo
4393Step over calls (interesting to bypass the ones to msgSend).
4394This function will set a temporary breakpoint on next instruction after the call so the call will be bypassed.
4395You can safely use it instead nexti or n since it will single step code if it's not a call instruction (unless you want to go into the call function).
4396end
4397
4398
4399define stepoh
4400 stepoframework 1
4401end
4402document stepoh
4403Same as stepo command but uses temporary hardware breakpoints.
4404end
4405
4406
4407# FIXME: ARM
4408define skip
4409 x/2i $pc
4410 set $instruction_size = (int)($_ - $pc)
4411 set $pc = $pc + $instruction_size
4412 if ($SKIPEXECUTE == 1)
4413 if ($SKIPSTEP == 1)
4414 stepo
4415 else
4416 stepi
4417 end
4418 else
4419 context
4420 end
4421end
4422document skip
4423Skip over the instruction located at EIP/RIP. By default, the instruction will not be executed!
4424Some configurable options are available on top of gdbinit to override this.
4425end
4426
4427
4428# _______________eflags commands______________
4429# conditional flags are
4430# negative/less than (N), bit 31 of CPSR
4431# zero (Z), bit 30
4432# Carry/Borrow/Extend (C), bit 29
4433# Overflow (V), bit 28
4434
4435# negative/less than (N), bit 31 of CPSR
4436define cfn
4437 if $ARM == 1
4438 set $tempflag = $cpsr->n
4439 if ($tempflag & 1)
4440 set $cpsr->n = $tempflag&~0x1
4441 else
4442 set $cpsr->n = $tempflag|0x1
4443 end
4444 end
4445end
4446document cfn
4447Change Negative/Less Than Flag.
4448end
4449
4450
4451define cfc
4452# Carry/Borrow/Extend (C), bit 29
4453 if $ARM == 1
4454 set $tempflag = $cpsr->c
4455 if ($tempflag & 1)
4456 set $cpsr->c = $tempflag&~0x1
4457 else
4458 set $cpsr->c = $tempflag|0x1
4459 end
4460 else
4461 if ($eflags & 1)
4462 set $eflags = $eflags&~0x1
4463 else
4464 set $eflags = $eflags|0x1
4465 end
4466 end
4467end
4468document cfc
4469Change Carry Flag.
4470end
4471
4472
4473define cfp
4474 if (($eflags >> 2) & 1)
4475 set $eflags = $eflags&~0x4
4476 else
4477 set $eflags = $eflags|0x4
4478 end
4479end
4480document cfp
4481Change Parity Flag.
4482end
4483
4484
4485define cfa
4486 if (($eflags >> 4) & 1)
4487 set $eflags = $eflags&~0x10
4488 else
4489 set $eflags = $eflags|0x10
4490 end
4491end
4492document cfa
4493Change Auxiliary Carry Flag.
4494end
4495
4496
4497define cfz
4498# zero (Z), bit 30
4499 if $ARM == 1
4500 set $tempflag = $cpsr->z
4501 if ($tempflag & 1)
4502 set $cpsr->z = $tempflag&~0x1
4503 else
4504 set $cpsr->z = $tempflag|0x1
4505 end
4506 else
4507 if (($eflags >> 6) & 1)
4508 set $eflags = $eflags&~0x40
4509 else
4510 set $eflags = $eflags|0x40
4511 end
4512 end
4513end
4514document cfz
4515Change Zero Flag.
4516end
4517
4518
4519define cfs
4520 if (($eflags >> 7) & 1)
4521 set $eflags = $eflags&~0x80
4522 else
4523 set $eflags = $eflags|0x80
4524 end
4525end
4526document cfs
4527Change Sign Flag.
4528end
4529
4530
4531define cft
4532 if (($eflags >>8) & 1)
4533 set $eflags = $eflags&~0x100
4534 else
4535 set $eflags = $eflags|0x100
4536 end
4537end
4538document cft
4539Change Trap Flag.
4540end
4541
4542
4543define cfi
4544 if (($eflags >> 9) & 1)
4545 set $eflags = $eflags&~0x200
4546 else
4547 set $eflags = $eflags|0x200
4548 end
4549end
4550document cfi
4551Change Interrupt Flag.
4552Only privileged applications (usually the OS kernel) may modify IF.
4553This only applies to protected mode (real mode code may always modify IF).
4554end
4555
4556
4557define cfd
4558 if (($eflags >>0xA) & 1)
4559 set $eflags = $eflags&~0x400
4560 else
4561 set $eflags = $eflags|0x400
4562 end
4563end
4564document cfd
4565Change Direction Flag.
4566end
4567
4568
4569define cfo
4570 if (($eflags >> 0xB) & 1)
4571 set $eflags = $eflags&~0x800
4572 else
4573 set $eflags = $eflags|0x800
4574 end
4575end
4576document cfo
4577Change Overflow Flag.
4578end
4579
4580
4581# Overflow (V), bit 28
4582define cfv
4583 if $ARM == 1
4584 set $tempflag = $cpsr->v
4585 if ($tempflag & 1)
4586 set $cpsr->v = $tempflag&~0x1
4587 else
4588 set $cpsr->v = $tempflag|0x1
4589 end
4590 end
4591end
4592document cfv
4593Change Overflow Flag.
4594end
4595
4596
4597# ____________________patch___________________
4598# the usual nops are mov r0,r0 for arm (0xe1a00000)
4599# and mov r8,r8 in Thumb (0x46c0)
4600# armv7 has other nops
4601# FIXME: make sure that the interval fits the 32bits address for arm and 16bits for thumb
4602# status: works, fixme
4603define nop
4604 if ($argc > 2 || $argc == 0)
4605 help nop
4606 end
4607
4608 if $ARM == 1
4609 if ($argc == 1)
4610 if ($cpsr->t &1)
4611 # thumb
4612 set *(short *)$arg0 = 0x46c0
4613 else
4614 # arm
4615 set *(int *)$arg0 = 0xe1a00000
4616 end
4617 else
4618 set $addr = $arg0
4619 if ($cpsr->t & 1)
4620 # thumb
4621 while ($addr < $arg1)
4622 set *(short *)$addr = 0x46c0
4623 set $addr = $addr + 2
4624 end
4625 else
4626 # arm
4627 while ($addr < $arg1)
4628 set *(int *)$addr = 0xe1a00000
4629 set $addr = $addr + 4
4630 end
4631 end
4632 end
4633 else
4634 if ($argc == 1)
4635 set *(unsigned char *)$arg0 = 0x90
4636 else
4637 set $addr = $arg0
4638 while ($addr < $arg1)
4639 set *(unsigned char *)$addr = 0x90
4640 set $addr = $addr + 1
4641 end
4642 end
4643 end
4644end
4645document nop
4646Usage: nop ADDR1 [ADDR2]
4647Patch a single byte at address ADDR1, or a series of bytes between ADDR1 and ADDR2 to a NOP (0x90) instruction.
4648ARM or Thumb code will be patched accordingly.
4649end
4650
4651
4652define null
4653 if ( $argc >2 || $argc == 0)
4654 help null
4655 end
4656
4657 if ($argc == 1)
4658 set *(unsigned char *)$arg0 = 0
4659 else
4660 set $addr = $arg0
4661 while ($addr < $arg1)
4662 set *(unsigned char *)$addr = 0
4663 set $addr = $addr +1
4664 end
4665 end
4666end
4667document null
4668Usage: null ADDR1 [ADDR2]
4669Patch a single byte at address ADDR1 to NULL (0x00), or a series of bytes between ADDR1 and ADDR2.
4670end
4671
4672# FIXME: thumb breakpoint ?
4673define int3
4674 if $argc != 1
4675 help int3
4676 else
4677 if $ARM == 1
4678 set $ORIGINAL_INT3 = *(unsigned int *)$arg0
4679 set $ORIGINAL_INT3ADDRESS = $arg0
4680 set *(unsigned int*)$arg0 = 0xe7ffdefe
4681 else
4682 # save original bytes and address
4683 set $ORIGINAL_INT3 = *(unsigned char *)$arg0
4684 set $ORIGINAL_INT3ADDRESS = $arg0
4685 # patch
4686 set *(unsigned char *)$arg0 = 0xCC
4687 end
4688 end
4689end
4690document int3
4691Patch byte at address ADDR to an INT3 (0xCC) instruction or the equivalent software breakpoint for ARM.
4692Usage: int3 ADDR
4693end
4694
4695
4696define rint3
4697 if $ARM == 1
4698 set *(unsigned int *)$ORIGINAL_INT3ADDRESS = $ORIGINAL_INT3
4699 set $pc = $ORIGINAL_INT3ADDRESS
4700 else
4701 set *(unsigned char *)$ORIGINAL_INT3ADDRESS = $ORIGINAL_INT3
4702 if $64BITS == 1
4703 set $rip = $ORIGINAL_INT3ADDRESS
4704 else
4705 set $eip = $ORIGINAL_INT3ADDRESS
4706 end
4707 end
4708end
4709document rint3
4710Restore the original byte previous to int3 patch issued with "int3" command.
4711end
4712
4713
4714# ____________________cflow___________________
4715define print_insn_type
4716 if $argc != 1
4717 help print_insn_type
4718 else
4719 if ($arg0 < 0 || $arg0 > 5)
4720 printf "UNDEFINED/WRONG VALUE"
4721 end
4722 if ($arg0 == 0)
4723 printf "UNKNOWN"
4724 end
4725 if ($arg0 == 1)
4726 printf "JMP"
4727 end
4728 if ($arg0 == 2)
4729 printf "JCC"
4730 end
4731 if ($arg0 == 3)
4732 printf "CALL"
4733 end
4734 if ($arg0 == 4)
4735 printf "RET"
4736 end
4737 if ($arg0 == 5)
4738 printf "INT"
4739 end
4740 end
4741end
4742document print_insn_type
4743Print human-readable mnemonic for the instruction type (usually $INSN_TYPE).
4744Usage: print_insn_type INSN_TYPE_NUMBER
4745end
4746
4747
4748define get_insn_type
4749 if $argc != 1
4750 help get_insn_type
4751 else
4752 set $INSN_TYPE = 0
4753 set $_byte1 = *(unsigned char *)$arg0
4754 if ($_byte1 == 0x9A || $_byte1 == 0xE8)
4755 # "call"
4756 set $INSN_TYPE = 3
4757 end
4758 if ($_byte1 >= 0xE9 && $_byte1 <= 0xEB)
4759 # "jmp"
4760 set $INSN_TYPE = 1
4761 end
4762 if ($_byte1 >= 0x70 && $_byte1 <= 0x7F)
4763 # "jcc"
4764 set $INSN_TYPE = 2
4765 end
4766 if ($_byte1 >= 0xE0 && $_byte1 <= 0xE3 )
4767 # "jcc"
4768 set $INSN_TYPE = 2
4769 end
4770 if ($_byte1 == 0xC2 || $_byte1 == 0xC3 || $_byte1 == 0xCA || \
4771 $_byte1 == 0xCB || $_byte1 == 0xCF)
4772 # "ret"
4773 set $INSN_TYPE = 4
4774 end
4775 if ($_byte1 >= 0xCC && $_byte1 <= 0xCE)
4776 # "int"
4777 set $INSN_TYPE = 5
4778 end
4779 if ($_byte1 == 0x0F )
4780 # two-byte opcode
4781 set $_byte2 = *(unsigned char *)($arg0 + 1)
4782 if ($_byte2 >= 0x80 && $_byte2 <= 0x8F)
4783 # "jcc"
4784 set $INSN_TYPE = 2
4785 end
4786 end
4787 if ($_byte1 == 0xFF)
4788 # opcode extension
4789 set $_byte2 = *(unsigned char *)($arg0 + 1)
4790 set $_opext = ($_byte2 & 0x38)
4791 if ($_opext == 0x10 || $_opext == 0x18)
4792 # "call"
4793 set $INSN_TYPE = 3
4794 end
4795 if ($_opext == 0x20 || $_opext == 0x28)
4796 # "jmp"
4797 set $INSN_TYPE = 1
4798 end
4799 end
4800 end
4801end
4802document get_insn_type
4803Recognize instruction type at address ADDR.
4804Take address ADDR and set the global $INSN_TYPE variable to
48050, 1, 2, 3, 4, 5 if the instruction at that address is
4806unknown, a jump, a conditional jump, a call, a return, or an interrupt.
4807Usage: get_insn_type ADDR
4808end
4809
4810
4811define step_to_call
4812 set $_saved_ctx = $SHOW_CONTEXT
4813 set $SHOW_CONTEXT = 0
4814 set $SHOW_NEST_INSN = 0
4815
4816 set logging file /dev/null
4817 set logging redirect on
4818 set logging on
4819
4820 set $_cont = 1
4821 while ($_cont > 0)
4822 stepi
4823 get_insn_type $pc
4824 if ($INSN_TYPE == 3)
4825 set $_cont = 0
4826 end
4827 end
4828
4829 set logging off
4830
4831 if ($_saved_ctx > 0)
4832 context
4833 end
4834
4835 set $SHOW_CONTEXT = $_saved_ctx
4836 set $SHOW_NEST_INSN = 0
4837
4838 set logging file ~/gdb.txt
4839 set logging redirect off
4840 set logging on
4841
4842 printf "step_to_call command stopped at:\n "
4843 x/i $pc
4844 printf "\n"
4845 set logging off
4846
4847end
4848document step_to_call
4849Single step until a call instruction is found.
4850Stop before the call is taken.
4851Log is written into the file ~/gdb.txt.
4852end
4853
4854
4855define trace_calls
4856
4857 printf "Tracing...please wait...\n"
4858
4859 set $_saved_ctx = $SHOW_CONTEXT
4860 set $SHOW_CONTEXT = 0
4861 set $SHOW_NEST_INSN = 0
4862 set $_nest = 1
4863 set listsize 0
4864
4865 set logging overwrite on
4866 set logging file ~/gdb_trace_calls.txt
4867 set logging on
4868 set logging off
4869 set logging overwrite off
4870
4871 while ($_nest > 0)
4872 get_insn_type $pc
4873 # handle nesting
4874 if ($INSN_TYPE == 3)
4875 set $_nest = $_nest + 1
4876 else
4877 if ($INSN_TYPE == 4)
4878 set $_nest = $_nest - 1
4879 end
4880 end
4881 # if a call, print it
4882 if ($INSN_TYPE == 3)
4883 set logging file ~/gdb_trace_calls.txt
4884 set logging redirect off
4885 set logging on
4886
4887 set $x = $_nest - 2
4888 while ($x > 0)
4889 printf "\t"
4890 set $x = $x - 1
4891 end
4892 x/i $pc
4893 end
4894
4895 set logging off
4896 set logging file /dev/null
4897 set logging redirect on
4898 set logging on
4899 stepi
4900 set logging redirect off
4901 set logging off
4902 end
4903
4904 set $SHOW_CONTEXT = $_saved_ctx
4905 set $SHOW_NEST_INSN = 0
4906
4907 printf "Done, check ~/gdb_trace_calls.txt\n"
4908end
4909document trace_calls
4910Create a runtime trace of the calls made by target.
4911Log overwrites(!) the file ~/gdb_trace_calls.txt.
4912end
4913
4914
4915define trace_run
4916
4917 printf "Tracing...please wait...\n"
4918
4919 set $_saved_ctx = $SHOW_CONTEXT
4920 set $SHOW_CONTEXT = 0
4921 set $SHOW_NEST_INSN = 1
4922 set logging overwrite on
4923 set logging file ~/gdb_trace_run.txt
4924 set logging redirect on
4925 set logging on
4926 set $_nest = 1
4927
4928 while ( $_nest > 0 )
4929
4930 get_insn_type $pc
4931 # jmp, jcc, or cll
4932 if ($INSN_TYPE == 3)
4933 set $_nest = $_nest + 1
4934 else
4935 # ret
4936 if ($INSN_TYPE == 4)
4937 set $_nest = $_nest - 1
4938 end
4939 end
4940 stepi
4941 end
4942
4943 printf "\n"
4944
4945 set $SHOW_CONTEXT = $_saved_ctx
4946 set $SHOW_NEST_INSN = 0
4947 set logging redirect off
4948 set logging off
4949
4950 # clean up trace file
4951 shell grep -v ' at ' ~/gdb_trace_run.txt > ~/gdb_trace_run.1
4952 shell grep -v ' in ' ~/gdb_trace_run.1 > ~/gdb_trace_run.txt
4953 shell rm -f ~/gdb_trace_run.1
4954 printf "Done, check ~/gdb_trace_run.txt\n"
4955end
4956document trace_run
4957Create a runtime trace of target.
4958Log overwrites(!) the file ~/gdb_trace_run.txt.
4959end
4960
4961#define ptraceme
4962# catch syscall ptrace
4963# commands
4964# if ($64BITS == 0)
4965# if ($ebx == 0)
4966# set $eax = 0
4967# continue
4968# end
4969# else
4970# if ($rdi == 0)
4971# set $rax = 0
4972# continue
4973# end
4974# end
4975# end
4976# set $ptrace_bpnum = $bpnum
4977#end
4978#document ptraceme
4979#Hook ptrace to bypass PTRACE_TRACEME anti debugging technique
4980#end
4981
4982define rptraceme
4983 if ($ptrace_bpnum != 0)
4984 delete $ptrace_bpnum
4985 set $ptrace_bpnum = 0
4986 end
4987end
4988document rptraceme
4989Remove ptrace hook.
4990end
4991
4992
4993# ____________________misc____________________
4994define hook-stop
4995# Display instructions formats
4996 if $ARM == 1
4997 if $ARMOPCODES == 1
4998 set arm show-opcode-bytes 1
4999 else
5000 set arm show-opcode-bytes 1
5001 end
5002 else
5003 if $X86FLAVOR == 0
5004 set disassembly-flavor intel
5005 else
5006 set disassembly-flavor att
5007 end
5008 end
5009
5010 # this makes 'context' be called at every BP/step
5011 if ($SHOW_CONTEXT > 0)
5012 context
5013 end
5014 if ($SHOW_NEST_INSN > 0)
5015 set $x = $_nest
5016 while ($x > 0)
5017 printf "\t"
5018 set $x = $x - 1
5019 end
5020 end
5021end
5022document hook-stop
5023!!! FOR INTERNAL USE ONLY - DO NOT CALL !!!
5024end
5025
5026
5027# original by Tavis Ormandy (http://my.opera.com/taviso/blog/index.dml/tag/gdb) (great fix!)
5028# modified to work with Mac OS X by fG!
5029# seems nasm shipping with Mac OS X has problems accepting input from stdin or heredoc
5030# input is read into a variable and sent to a temporary file which nasm can read
5031define assemble
5032 # dont enter routine again if user hits enter
5033 dont-repeat
5034 if ($argc)
5035 if (*$arg0 = *$arg0)
5036 # check if we have a valid address by dereferencing it,
5037 # if we havnt, this will cause the routine to exit.
5038 end
5039 printf "Instructions will be written to %#x.\n", $arg0
5040 else
5041 printf "Instructions will be written to stdout.\n"
5042 end
5043 printf "Type instructions, one per line."
5044 echo \033[1m
5045 printf " Do not forget to use NASM assembler syntax!\n"
5046 echo \033[0m
5047 printf "End with a line saying just \"end\".\n"
5048
5049 if ($argc)
5050 if ($64BITS == 1)
5051 # argument specified, assemble instructions into memory at address specified.
5052 shell ASMOPCODE="$(while read -ep '>' r && test "$r" != end ; do echo -E "$r"; done)" ; GDBASMFILENAME=$RANDOM; \
5053 echo -e "BITS 64\n$ASMOPCODE" >/tmp/$GDBASMFILENAME ; /usr/local/bin/nasm -f bin -o /dev/stdout /tmp/$GDBASMFILENAME | /usr/bin/hexdump -ve '1/1 "set *((unsigned char *) $arg0 + %#2_ax) = %#02x\n"' >/tmp/gdbassemble ; /bin/rm -f /tmp/$GDBASMFILENAME
5054 source /tmp/gdbassemble
5055 # all done. clean the temporary file
5056 shell /bin/rm -f /tmp/gdbassemble
5057 else
5058 # argument specified, assemble instructions into memory at address specified.
5059 shell ASMOPCODE="$(while read -ep '>' r && test "$r" != end ; do echo -E "$r"; done)" ; GDBASMFILENAME=$RANDOM; \
5060 echo -e "BITS 32\n$ASMOPCODE" >/tmp/$GDBASMFILENAME ; /usr/bin/nasm -f bin -o /dev/stdout /tmp/$GDBASMFILENAME | /usr/bin/hexdump -ve '1/1 "set *((unsigned char *) $arg0 + %#2_ax) = %#02x\n"' >/tmp/gdbassemble ; /bin/rm -f /tmp/$GDBASMFILENAME
5061 source /tmp/gdbassemble
5062 # all done. clean the temporary file
5063 shell /bin/rm -f /tmp/gdbassemble
5064 end
5065 else
5066 if ($64BITS == 1)
5067 # no argument, assemble instructions to stdout
5068 shell ASMOPCODE="$(while read -ep '>' r && test "$r" != end ; do echo -E "$r"; done)" ; GDBASMFILENAME=$RANDOM; \
5069 echo -e "BITS 64\n$ASMOPCODE" >/tmp/$GDBASMFILENAME ; /usr/local/bin/nasm -f bin -o /dev/stdout /tmp/$GDBASMFILENAME | /usr/local/bin/ndisasm -i -b64 /dev/stdin ; \
5070 /bin/rm -f /tmp/$GDBASMFILENAME
5071 else
5072 # no argument, assemble instructions to stdout
5073 shell ASMOPCODE="$(while read -ep '>' r && test "$r" != end ; do echo -E "$r"; done)" ; GDBASMFILENAME=$RANDOM; \
5074 echo -e "BITS 32\n$ASMOPCODE" >/tmp/$GDBASMFILENAME ; /usr/bin/nasm -f bin -o /dev/stdout /tmp/$GDBASMFILENAME | /usr/bin/ndisasm -i -b32 /dev/stdin ; \
5075 /bin/rm -f /tmp/$GDBASMFILENAME
5076 end
5077 end
5078end
5079document assemble
5080Assemble instructions using nasm.
5081Type a line containing "end" to indicate the end.
5082If an address is specified, insert/modify instructions at that address.
5083If no address is specified, assembled instructions are printed to stdout.
5084Use the pseudo instruction "org ADDR" to set the base address.
5085end
5086
5087
5088define asm
5089 if $argc == 1
5090 assemble $arg0
5091 else
5092 assemble
5093 end
5094end
5095document asm
5096Shortcut to the asssemble command.
5097end
5098
5099
5100define assemble_gas
5101 printf "\nType code to assemble and hit Ctrl-D when finished.\n"
5102 printf "You must use GNU assembler (AT&T) syntax.\n"
5103
5104 shell filename=$(mktemp); \
5105 binfilename=$(mktemp); \
5106 echo -e "Writing into: ${filename}\n"; \
5107 cat > $filename; echo ""; \
5108 as -o $binfilename < $filename; \
5109 objdump -d -j .text $binfilename; \
5110 rm -f $binfilename; \
5111 rm -f $filename; \
5112 echo -e "temporaly files deleted.\n"
5113end
5114document assemble_gas
5115Assemble instructions to binary opcodes. Uses GNU as and objdump.
5116Usage: assemble_gas
5117end
5118
5119
5120define dump_hexfile
5121 dump ihex memory $arg0 $arg1 $arg2
5122end
5123document dump_hexfile
5124Write a range of memory to a file in Intel ihex (hexdump) format.
5125The range is specified by ADDR1 and ADDR2 addresses.
5126Usage: dump_hexfile FILENAME ADDR1 ADDR2
5127end
5128
5129
5130define dump_binfile
5131 dump memory $arg0 $arg1 $arg2
5132end
5133document dump_binfile
5134Write a range of memory to a binary file.
5135The range is specified by ADDR1 and ADDR2 addresses.
5136Usage: dump_binfile FILENAME ADDR1 ADDR2
5137end
5138
5139
5140define dumpmacho
5141 if $argc != 2
5142 help dumpmacho
5143 end
5144 set $headermagic = *$arg0
5145 # the || operator isn't working as it should, wtf!!!
5146 if $headermagic != 0xfeedface
5147 if $headermagic != 0xfeedfacf
5148 printf "[Error] Target address doesn't contain a valid Mach-O binary!\n"
5149 help dumpmacho
5150 end
5151 end
5152 set $headerdumpsize = *($arg0+0x14)
5153 if $headermagic == 0xfeedface
5154 dump memory $arg1 $arg0 ($arg0+0x1c+$headerdumpsize)
5155 end
5156 if $headermagic == 0xfeedfacf
5157 dump memory $arg1 $arg0 ($arg0+0x20+$headerdumpsize)
5158 end
5159end
5160document dumpmacho
5161Dump the Mach-O header to a file.
5162You need to input the start address (use info shared command to find it).
5163Usage: dumpmacho STARTADDRESS FILENAME
5164end
5165
5166
5167define cls
5168 shell clear
5169end
5170document cls
5171Clear screen.
5172end
5173
5174
5175define search
5176 set $start = (char *) $arg0
5177 set $end = (char *) $arg1
5178 set $pattern = (short) $arg2
5179 set $p = $start
5180 while $p < $end
5181 if (*(short *) $p) == $pattern
5182 printf "pattern 0x%hx found at 0x%x\n", $pattern, $p
5183 end
5184 set $p++
5185 end
5186end
5187document search
5188Search for the given pattern beetween $start and $end address.
5189Usage: search <start> <end> <pattern>
5190end
5191
5192
5193# _________________user tips_________________
5194# The 'tips' command is used to provide tutorial-like info to the user
5195define tips
5196 printf "Tip Topic Commands:\n"
5197 printf "\ttip_display : Automatically display values on each break\n"
5198 printf "\ttip_patch : Patching binaries\n"
5199 printf "\ttip_strip : Dealing with stripped binaries\n"
5200 printf "\ttip_syntax : AT&T vs Intel syntax\n"
5201end
5202document tips
5203Provide a list of tips from users on various topics.
5204end
5205
5206
5207define tip_patch
5208 printf "\n"
5209 printf " PATCHING MEMORY\n"
5210 printf "Any address can be patched using the 'set' command:\n"
5211 printf "\t`set ADDR = VALUE` \te.g. `set *0x8049D6E = 0x90`\n"
5212 printf "\n"
5213 printf " PATCHING BINARY FILES\n"
5214 printf "Use `set write` in order to patch the target executable\n"
5215 printf "directly, instead of just patching memory\n"
5216 printf "\t`set write on` \t`set write off`\n"
5217 printf "Note that this means any patches to the code or data segments\n"
5218 printf "will be written to the executable file\n"
5219 printf "When either of these commands has been issued,\n"
5220 printf "the file must be reloaded.\n"
5221 printf "\n"
5222end
5223document tip_patch
5224Tips on patching memory and binary files.
5225end
5226
5227
5228define tip_strip
5229 printf "\n"
5230 printf " STOPPING BINARIES AT ENTRY POINT\n"
5231 printf "Stripped binaries have no symbols, and are therefore tough to\n"
5232 printf "start automatically. To debug a stripped binary, use\n"
5233 printf "\tinfo file\n"
5234 printf "to get the entry point of the file\n"
5235 printf "The first few lines of output will look like this:\n"
5236 printf "\tSymbols from '/tmp/a.out'\n"
5237 printf "\tLocal exec file:\n"
5238 printf "\t `/tmp/a.out', file type elf32-i386.\n"
5239 printf "\t Entry point: 0x80482e0\n"
5240 printf "Use this entry point to set an entry point:\n"
5241 printf "\t`tbreak *0x80482e0`\n"
5242 printf "The breakpoint will delete itself after the program stops as\n"
5243 printf "the entry point\n"
5244 printf "\n"
5245end
5246document tip_strip
5247Tips on dealing with stripped binaries.
5248end
5249
5250
5251define tip_syntax
5252 printf "\n"
5253 printf "\t INTEL SYNTAX AT&T SYNTAX\n"
5254 printf "\tmnemonic dest, src, imm mnemonic src, dest, imm\n"
5255 printf "\t[base+index*scale+disp] disp(base, index, scale)\n"
5256 printf "\tregister: eax register: %%eax\n"
5257 printf "\timmediate: 0xFF immediate: $0xFF\n"
5258 printf "\tdereference: [addr] dereference: addr(,1)\n"
5259 printf "\tabsolute addr: addr absolute addr: *addr\n"
5260 printf "\tbyte insn: mov byte ptr byte insn: movb\n"
5261 printf "\tword insn: mov word ptr word insn: movw\n"
5262 printf "\tdword insn: mov dword ptr dword insn: movd\n"
5263 printf "\tfar call: call far far call: lcall\n"
5264 printf "\tfar jump: jmp far far jump: ljmp\n"
5265 printf "\n"
5266 printf "Note that order of operands in reversed, and that AT&T syntax\n"
5267 printf "requires that all instructions referencing memory operands \n"
5268 printf "use an operand size suffix (b, w, d, q)\n"
5269 printf "\n"
5270end
5271document tip_syntax
5272Summary of Intel and AT&T syntax differences.
5273end
5274
5275
5276define tip_display
5277 printf "\n"
5278 printf "Any expression can be set to automatically be displayed every time\n"
5279 printf "the target stops. The commands for this are:\n"
5280 printf "\t`display expr' : automatically display expression 'expr'\n"
5281 printf "\t`display' : show all displayed expressions\n"
5282 printf "\t`undisplay num' : turn off autodisplay for expression # 'num'\n"
5283 printf "Examples:\n"
5284 printf "\t`display/x *(int *)$esp` : print top of stack\n"
5285 printf "\t`display/x *(int *)($ebp+8)` : print first parameter\n"
5286 printf "\t`display (char *)$esi` : print source string\n"
5287 printf "\t`display (char *)$edi` : print destination string\n"
5288 printf "\n"
5289end
5290document tip_display
5291Tips on automatically displaying values when a program stops.
5292end
5293
5294# bunch of semi-useless commands
5295
5296# enable and disable shortcuts for stop-on-solib-events fantastic trick!
5297define enablesolib
5298 set stop-on-solib-events 1
5299 printf "Stop-on-solib-events is enabled!\n"
5300end
5301document enablesolib
5302Shortcut to enable stop-on-solib-events trick.
5303end
5304
5305
5306define disablesolib
5307 set stop-on-solib-events 0
5308 printf "Stop-on-solib-events is disabled!\n"
5309end
5310document disablesolib
5311Shortcut to disable stop-on-solib-events trick.
5312end
5313
5314
5315# enable commands for different displays
5316define enableobjectivec
5317 set $SHOWOBJECTIVEC = 1
5318end
5319document enableobjectivec
5320Enable display of objective-c information in the context window.
5321end
5322
5323
5324define enablecpuregisters
5325 set $SHOWCPUREGISTERS = 1
5326end
5327document enablecpuregisters
5328Enable display of cpu registers in the context window.
5329end
5330
5331
5332define enablestack
5333 set $SHOWSTACK = 1
5334end
5335document enablestack
5336Enable display of stack in the context window.
5337end
5338
5339
5340define enabledatawin
5341 set $SHOWDATAWIN = 1
5342end
5343document enabledatawin
5344Enable display of data window in the context window.
5345end
5346
5347
5348# disable commands for different displays
5349define disableobjectivec
5350 set $SHOWOBJECTIVEC = 0
5351end
5352document disableobjectivec
5353Disable display of objective-c information in the context window.
5354end
5355
5356
5357define disablecpuregisters
5358 set $SHOWCPUREGISTERS = 0
5359end
5360document disablecpuregisters
5361Disable display of cpu registers in the context window.
5362end
5363
5364
5365define disablestack
5366 set $SHOWSTACK = 0
5367end
5368document disablestack
5369Disable display of stack information in the context window.
5370end
5371
5372
5373define disabledatawin
5374 set $SHOWDATAWIN = 0
5375end
5376document disabledatawin
5377Disable display of data window in the context window.
5378end
5379
5380
5381define 32bits
5382 set $64BITS = 0
5383 if $X86FLAVOR == 0
5384 set disassembly-flavor intel
5385 else
5386 set disassembly-flavor att
5387 end
5388end
5389document 32bits
5390Set gdb to work with 32bits binaries.
5391end
5392
5393
5394define 64bits
5395 set $64BITS = 1
5396 if $X86FLAVOR == 0
5397 set disassembly-flavor intel
5398 else
5399 set disassembly-flavor att
5400 end
5401end
5402document 64bits
5403Set gdb to work with 64bits binaries.
5404end
5405
5406
5407define arm
5408 if $ARMOPCODES == 1
5409 set arm show-opcode-bytes 1
5410 else
5411 set arm show-opcode-bytes 1
5412 end
5413 set $ARM = 1
5414 set $64BITS = 0
5415end
5416document arm
5417Set gdb to work with ARM binaries.
5418end
5419
5420
5421define enablelib
5422 set stop-on-solib-events 1
5423end
5424document enablelib
5425Activate stop-on-solib-events.
5426end
5427
5428
5429define disablelib
5430 set stop-on-solib-events 0
5431end
5432document disablelib
5433Deactivate stop-on-solib-events.
5434end
5435
5436
5437define intelsyntax
5438 if $ARM == 0
5439 set disassembly-flavor intel
5440 end
5441end
5442document intelsyntax
5443Change disassembly syntax to intel flavor.
5444end
5445
5446
5447define attsyntax
5448 if $ARM == 0
5449 set disassembly-flavor att
5450 end
5451end
5452document attsyntax
5453Change disassembly syntax to at&t flavor.
5454end
5455
5456#EOF
5457
5458# Older change logs:
5459#
5460# Version 7.4.4 (02/01/2012)
5461# - Added the "skip" command. This will jump to the next instruction after EIP/RIP without executing the current one.
5462# Thanks to @bSr43 for the tip to retrieve the current instruction size.
5463#
5464# Version 7.4.3 (04/11/2011)
5465# - Modified "hexdump" command to support a variable number of lines (optional parameter)
5466# - Removed restrictions on type of addresses in the "dd" command - Thanks to Plouj for the warning :-)
5467# I don't know what was the original thinking behind those :-)
5468# - Modified the assemble command to support 64bits - You will need to recompile nasm since the version shipped with OS X doesn't supports 64bits (www.nasm.us).
5469# Assumes that the new binary is installed at /usr/local/bin - modify the variable at the top if you need so.
5470# It will assemble based on the target arch being debugged. If you want to use gdb for a quick asm just use the 32bits or 64bits commands to set your target.
5471# Thanks to snare for the warning and original patch :-)
5472# - Added "asm" command - it's a shortcut to the "assemble" command.
5473# - Added configuration variable for colorized prompt. Plouj reported some issues with Ubuntu's gdb 7.2 if prompt is colorized.
5474#
5475# Version 7.4.2 (11/08/2011)
5476# Small fix to a weird bug happening on FreeBSD 8.2. It doesn't like a "if(" instruction, needs to be "if (". Weird!
5477# Many thanks to Evan for reporting and sending the patch :-)
5478# Added the ptraceme/rptraceme commands to bypass PTRACE_TRACME anti-debugging technique.
5479# Grabbed this from http://falken.tuxfamily.org/?p=171
5480# It's commented out due to a gdb problem in OS X (refer to http://reverse.put.as/2011/08/20/another-patch-for-apples-gdb-the-definecommands-problem/ )
5481# Just uncomment it if you want to use in ptrace enabled systems.
5482#
5483# Version 7.4.1 (21/06/2011) - fG!
5484# Added patch sent by sbz, more than 1 year ago, which I forgot to add :-/
5485# This will allow to search for a given pattern between start and end address.
5486# On sbz words: "It's usefull to find call, ret or everything like that." :-)
5487# New command is "search"
5488#
5489# Version 7.4 (20/06/2011) - fG!
5490# When registers change between instructions the colour will change to red (like it happens in OllyDBG)
5491# This is the default behavior, if you don't like it, modify the variable SHOWREGCHANGES
5492# Added patch sent by Philippe Langlois
5493# Colour the first disassembly line - change the setting below on SETCOLOUR1STLINE - by default it's disabled
5494#
5495# Version 7.3.2 (21/02/2011) - fG!
5496# Added the command rint3 and modified the int3 command. The new command will restore the byte in previous int3 patch.
5497#
5498# Version 7.3.1 (29/06/2010) - fG!
5499# Added enablelib/disablelib command to quickly set the stop-on-solib-events trick
5500# Implemented the stepoh command equivalent to the stepo but using hardware breakpoints
5501# More fixes to stepo
5502#
5503# Version 7.3 (16/04/2010) - fG!
5504# Support for 64bits targets. Default is 32bits, you should modify the variable or use the 32bits or 64bits to choose the mode.
5505# I couldn't find another way to recognize the type of binary… Testing the register doesn't work that well.
5506# TODO: fix objectivec messages and stepo for 64bits
5507# Version 7.2.1 (24/11/2009) - fG!
5508# Another fix to stepo (0xFF92 missing)
5509#
5510# Version 7.2 (11/10/2009) - fG!
5511# Added the smallregisters function to create 16 and 8 bit versions from the registers EAX, EBX, ECX, EDX
5512# Revised and fixed all the dumpjump stuff, following Intel manuals. There were some errors (thx to rev who pointed the jle problem).
5513# Small fix to stepo command (missed a few call types)
5514#
5515# Version 7.1.7 - fG!
5516# Added the possibility to modify what's displayed with the context window. You can change default options at the gdb options part. For example, kernel debugging is much slower if the stack display is enabled...
5517# New commands enableobjectivec, enablecpuregisters, enablestack, enabledatawin and their disable equivalents (to support realtime change of default options)
5518# Fixed problem with the assemble command. I was calling /bin/echo which doesn't support the -e option ! DUH ! Should have used bash internal version.
5519# Small fixes to colours...
5520# New commands enablesolib and disablesolib . Just shortcuts for the stop-on-solib-events fantastic trick ! Hey... I'm lazy ;)
5521# Fixed this: Possible removal of "u" command, info udot is missing in gdb 6.8-debian . Doesn't exist on OS X so bye bye !!!
5522# Displays affected flags in jump decisions
5523#
5524# Version 7.1.6 - fG!
5525# Added modified assemble command from Tavis Ormandy (further modified to work with Mac OS X) (shell commands used use full path name, working for Leopard, modify for others if necessary)
5526# Renamed thread command to threads because thread is an internal gdb command that allows to move between program threads
5527#
5528# Version 7.1.5 (04/01/2009) - fG!
5529# Fixed crash on Leopard ! There was a If Else condition where the else had no code and that made gdb crash on Leopard (CRAZY!!!!)
5530# Better code indention
5531#
5532# Version 7.1.4 (02/01/2009) - fG!
5533# Bug in show objective c messages with Leopard ???
5534# Nop routine support for single address or range (contribution from gln [ghalen at hack.se])
5535# Used the same code from nop to null routine
5536#
5537# Version 7.1.3 (31/12/2008) - fG!
5538# Added a new command 'stepo'. This command will step a temporary breakpoint on next instruction after the call, so you can skip over
5539# the call. Did this because normal commands not always skip over (mainly with objc_msgSend)
5540#
5541# Version 7.1.2 (31/12/2008) - fG!
5542# Support for the jump decision (will display if a conditional jump will be taken or not)
5543#
5544# Version 7.1.1 (29/12/2008) - fG!
5545# Moved gdb options to the beginning (makes more sense)
5546# Added support to dump message being sent to msgSend (easier to understand what's going on)
5547#
5548# Version 7.1
5549# Fixed serious (and old) bug in dd and datawin, causing dereference of
5550# obviously invalid address. See below:
5551# gdb$ dd 0xffffffff
5552# FFFFFFFF : Cannot access memory at address 0xffffffff
5553#
5554# Version 7.0
5555# Added cls command.
5556# Improved documentation of many commands.
5557# Removed bp_alloc, was neither portable nor usefull.
5558# Checking of passed argument(s) in these commands:
5559# contextsize-stack, contextsize-data, contextsize-code
5560# bp, bpc, bpe, bpd, bpt, bpm, bhb,...
5561# Fixed bp and bhb inconsistencies, look at * signs in Version 6.2
5562# Bugfix in bhb command, changed "break" to "hb" command body
5563# Removed $SHOW_CONTEXT=1 from several commands, this variable
5564# should only be controlled globally with context-on and context-off
5565# Improved stack, func, var and sig, dis, n, go,...
5566# they take optional argument(s) now
5567# Fixed wrong $SHOW_CONTEXT assignment in context-off
5568# Fixed serious bug in cft command, forgotten ~ sign
5569# Fixed these bugs in step_to_call:
5570# 1) the correct logging sequence is:
5571# set logging file > set logging redirect > set logging on
5572# 2) $SHOW_CONTEXT is now correctly restored from $_saved_ctx
5573# Fixed these bugs in trace_calls:
5574# 1) the correct logging sequence is:
5575# set logging file > set logging overwrite >
5576# set logging redirect > set logging on
5577# 2) removed the "clean up trace file" part, which is not needed now,
5578# stepi output is properly redirected to /dev/null
5579# 3) $SHOW_CONTEXT is now correctly restored from $_saved_ctx
5580# Fixed bug in trace_run:
5581# 1) $SHOW_CONTEXT is now correctly restored from $_saved_ctx
5582# Fixed print_insn_type -- removed invalid semicolons!, wrong value checking,
5583# Added TODO entry regarding the "u" command
5584# Changed name from gas_assemble to assemble_gas due to consistency
5585# Output from assemble and assemble_gas is now similar, because i made
5586# both of them to use objdump, with respect to output format (AT&T|Intel).
5587# Whole code was checked and made more consistent, readable/maintainable.
5588#
5589# Version 6.2
5590# Add global variables to allow user to control stack, data and code window sizes
5591# Increase readability for registers
5592# Some corrections (hexdump, ddump, context, cfp, assemble, gas_asm, tips, prompt)
5593#
5594# Version 6.1-color-user
5595# Took the Gentoo route and ran sed s/user/user/g
5596#
5597# Version 6.1-color
5598# Added color fixes from
5599# http://gnurbs.blogsome.com/2006/12/22/colorizing-mamons-gdbinit/
5600#
5601# Version 6.1
5602# Fixed filename in step_to_call so it points to /dev/null
5603# Changed location of logfiles from /tmp to ~
5604#
5605# Version 6
5606# Added print_insn_type, get_insn_type, context-on, context-off commands
5607# Added trace_calls, trace_run, step_to_call commands
5608# Changed hook-stop so it checks $SHOW_CONTEXT variable
5609#
5610# Version 5
5611# Added bpm, dump_bin, dump_hex, bp_alloc commands
5612# Added 'assemble' by elaine, 'gas_asm' by mong
5613# Added Tip Topics for aspiring users ;)
5614#
5615# Version 4
5616# Added eflags-changing insns by pusillus
5617# Added bp, nop, null, and int3 patch commands, also hook-stop
5618#
5619# Version 3
5620# Incorporated elaine's if/else goodness into the hex/ascii dump
5621#
5622# Version 2
5623# Radix bugfix by elaine