Created
April 5, 2013 00:40
-
-
Save scottt/5315727 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/gdb --python | |
# vim: set filetype=python: | |
import sys | |
import optparse | |
import struct | |
import logging | |
import signal | |
import elftools.elf.elffile | |
import gdb | |
# gdbcall type: must match gdbcall.h | |
(GDBCALL_GDB, | |
GDBCALL_PYTHON, | |
GDBCALL_PYTHON_VA, | |
) = ( | |
0xdeadbeef, | |
0xdead5a5a, | |
0xdead8877, | |
) | |
GDBCALL_TYPES = set( v for (k, v) in globals().iteritems() if k.startswith('GDBCALL_') ) | |
def _parse_gdbcall_tlv(elffile, section): | |
(f, s) = (elffile, section) | |
if f.little_endian: | |
endian_fmt = '<' | |
else: | |
endian_fmt = '>' | |
int32_fmt = '%sI' % (endian_fmt,) | |
(offset, d) = (0, s.data()) | |
while offset < len(d): | |
t = struct.unpack_from(int32_fmt, d, offset)[0]; offset += 4 | |
l = struct.unpack_from(int32_fmt, d, offset)[0]; offset += 4 | |
yield (t, (d, offset, l)); offset += l | |
def _parse_gdbcalls(filename): | |
'-> (type, value) ...' | |
f = elftools.elf.elffile.ELFFile(open(filename)) | |
s = f.get_section_by_name('.note.gdbcall') | |
if s is None: # no section with the name | |
return [] | |
if f.little_endian: | |
endian_fmt = '<' | |
else: | |
endian_fmt = '>' | |
if f.elfclass == 64: | |
(addr_fmt, addr_size) = ('Q', 8) | |
elif f.elfclass == 32: | |
(addr_fmt, addr_size) = ('I', 4) | |
addr_fmt = '%s%s' % (endian_fmt, addr_fmt) | |
type_with_addr = GDBCALL_TYPES | |
out = [] | |
for (t, (d, offset, l)) in _parse_gdbcall_tlv(f, s): | |
if t not in type_with_addr: | |
logging.error(str((t, d, offset, l))) | |
assert(t in type_with_addr) | |
addr = struct.unpack_from(addr_fmt, d, offset)[0] | |
cmd = struct.unpack_from('%ds' % (l - addr_size,), d, offset + addr_size)[0] | |
cmd = ','.join(x for x in cmd.split('\x00') if x) | |
logging.debug('0x%x, 0x%08x, 0x%x, %d, %r' % (t, addr, offset, l, cmd)) | |
out.append((t, addr, cmd)) | |
return out | |
breakpoint_handler = {} | |
def _on_breakpoint_hit(e): | |
if not isinstance(e, gdb.BreakpointEvent): | |
return True | |
for b in e.breakpoints: | |
f = breakpoint_handler[b.number] | |
f() | |
gdb.execute('continue') | |
return True | |
def _gdbcall_setup_breakpoint(t, addr, cmd, options): | |
global breakpoint_handler | |
# parse addr, cmd from type | |
def run_command(): | |
gdb.execute(cmd) | |
def run_python(): | |
eval(cmd_code, globals()) | |
bp = gdb.Breakpoint('*0x%x' % (addr,), internal=True) | |
if options.debug > 1: | |
bp.silent = False | |
else: | |
bp.silent = True | |
if t == GDBCALL_GDB: | |
breakpoint_handler[bp.number] = run_command | |
elif t == GDBCALL_PYTHON: | |
# unescape string, e.g. "f()" -> f() | |
cmd0 = eval(cmd) | |
cmd_code = compile(cmd0, '<gdbcall>', 'single') | |
breakpoint_handler[bp.number] = run_python | |
elif t == GDBCALL_PYTHON_VA: | |
cmd_code = compile(cmd, '<gdbcall>', 'single') | |
breakpoint_handler[bp.number] = run_python | |
else: | |
logging.info('gdbcall-load: unknown type: 0x%x, addr: 0x%x, cmd: %r' % (t, addr, cmd)) | |
def _gdbcall_setup(filename, options): | |
gdb.events.stop.connect(_on_breakpoint_hit) | |
for (t, addr, cmd) in _parse_gdbcalls(filename): | |
_gdbcall_setup_breakpoint(t, addr, cmd, options) | |
def gdb_fix_argv(): | |
''' | |
gdb breaks sys.argv[0] in the way it embeds Python | |
./gdb-python-script -> sys.argv: [''] | |
when argv should be: [ './gdb-python-script' ] | |
./gdb-python-script 0 1 2 -> sys.argv: ['0', '1', '2'] | |
when argv should be should be: [ './gdb-python-script', '0', '1', '2' ] | |
''' | |
try: | |
f = open('/proc/%d/cmdline' % (os.getpid(),)) | |
except IOError: | |
return | |
l = f.readline() | |
f.close() | |
cmdline = l.split('\x00') | |
if cmdline[-1] == '': | |
cmdline = cmdline[:-1] | |
if cmdline[0].endswith('gdb') and cmdline[1] == '--python': | |
sys.argv = cmdline[2:] | |
def on_inferior_exit(filename): | |
exitcode = gdb.parse_and_eval('$_exitcode') | |
if exitcode.type.code == gdb.TYPE_CODE_INT: | |
sys.exit(int(exitcode)) | |
else: | |
siginfo = gdb.parse_and_eval('$_siginfo') | |
if siginfo.type.code != gdb.TYPE_CODE_VOID: | |
signal_names = dict( (getattr(signal, x), x) | |
for x in dir(signal) if x.startswith('SIG') ) | |
sig = siginfo['si_signo'] | |
msg = [ '%s terminated by signal %d' % (filename, sig,) ] | |
signame = signal_names.get(int(sig)) | |
if signame is not None: | |
msg.append(' (%s)' % (signame,)) | |
if sig == signal.SIGSEGV: | |
# $_siginfo._sifields._sigfault.si_addr | |
try: | |
addr = siginfo['_sifields']['_sigfault']['si_addr'] | |
except gdb.error: | |
pass | |
if addr is not None: | |
msg.append(' while accessing address: %s' % (addr,)) | |
logging.error(''.join(msg)) | |
else: | |
logging.error('%s terminated abnormally\n' % (filename,)) | |
sys.exit(3) | |
def main(args): | |
op = optparse.OptionParser(option_list = [ | |
optparse.Option('-d', '--debug', type=int, default=0, help='debug level'), | |
]) | |
(options, args) = op.parse_args(args) | |
if options.debug: | |
level = logging.DEBUG | |
else: | |
level = logging.WARNING | |
logging.basicConfig(format='%(message)s', level=level) | |
filename = args[0] | |
gdb.execute('set python print-stack full') | |
gdb.execute('set print inferior-events off') | |
_gdbcall_setup(filename, options) | |
gdb.execute('file %s' % (filename,)) | |
sys.argv = args | |
gdb.execute('run') | |
on_inferior_exit(filename) | |
if __name__ == '__main__': | |
gdb_fix_argv() | |
main(sys.argv[1:]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef GDB_CALL_H | |
#define GDB_CALL_H | |
/* .note.gdbcall section format: TLV, | |
* Type (4 byte) | |
* Length (4 byte, number of bytes for the variable length value that follows. | |
* Does not include the 4 bytes for the length field itself.) | |
* Value (variable length) | |
* | |
* | |
* See: | |
* http://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html | |
* http://sourceware.org/binutils/docs/as/Symbol-Names.html#index-local-labels-217 | |
*/ | |
#define GDB(cmd, args...) \ | |
0: nop; \ | |
.pushsection .note.gdbcall,"?","note"; \ | |
.balign 4; \ | |
.4byte 0xdeadbeef; /* type */ \ | |
.4byte 2f-1f; /* length */ \ | |
1: .8byte 0b; \ | |
.asciz #cmd #args; \ | |
.balign 4; \ | |
2: .popsection; | |
#define GDBPY(stmt, args...) \ | |
0: nop; \ | |
.pushsection .note.gdbcall,"?","note"; \ | |
.balign 4; \ | |
.4byte 0xdead8877; /* type */ \ | |
.4byte 2f-1f; /* length */ \ | |
1: .8byte 0b; \ | |
.asciz #stmt #args; \ | |
.balign 4; \ | |
2: .popsection; | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/syscall.h> | |
#include "gdbcall.h" | |
.section .text | |
.global _start | |
_start: | |
movq $3, %rdi | |
movq $0, %rsi | |
movq $2, %rdx | |
GDBPY(import sys, gdb) | |
GDBPY(gdb.execute('set variable $rdi = %d' % (int(sys.argv[1]),))) | |
call hanoi | |
/* exit_group(0) */ | |
movq $SYS_exit_group, %rax | |
movq $0, %rdi | |
syscall | |
.type hanoi,STT_FUNC | |
hanoi: | |
cmp $1, %rdi | |
jg recurse | |
GDB(printf "move %d, %d, %d\n", $rdi - 1, $rsi, $rdx) | |
ret | |
recurse: | |
/* tmp = 0 + 1 + 2 - src - dst */ | |
push %rbx | |
movq $3, %rbx | |
sub %rsi, %rbx | |
sub %rdx, %rbx | |
/* hanoi(n-1, src, tmp) */ | |
push %rdi | |
dec %rdi | |
push %rsi | |
push %rdx | |
movq %rbx, %rdx | |
call hanoi | |
pop %rdx | |
pop %rsi | |
pop %rdi | |
GDB(printf "move %d, %d, %d\n", $rdi - 1, $rsi, $rdx) | |
/* hanoi(n-1, tmp, dst) */ | |
dec %rdi | |
movq %rbx, %rsi | |
call hanoi | |
pop %rbx | |
ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment