Occasionally I see questions about how to
import gdb from the ordinary Python interpreter. This turns out to be surprisingly easy to implement.
First, a detour into PIE and symbol visibility.
“PIE” stands for “Position Independent Executable”. It uses essentially the same approach as a shared library, except it can be applied to the executable. You can easily build a PIE by compiling the objects with the
-fPIE flag, and then linking the resulting executable with
-pie. Normally PIEs are used as a security feature, but in our case we’re going to compile gdb this way so we can have Python dlopen it, following the usual Python approach: we install it as
_gdb.so and add a a module initialization function,
init_gdb. (We actually name the module “
_gdb“, because that is what the gdb C code creates; the “
gdb” module itself is already plain Python that happens to “
Why install the PIE rather than make a true shared library? It is just more convenient — it doesn’t require a lot of configure and Makefile hacking, and it doesn’t slow down the build by forcing us to link gdb against a new library.
Next, what about all those functions in gdb? There are thousands of them… won’t they possibly cause conflicts at
dlopen time? Why yes… but that’s why we have symbol visibility. Symbol visibility is an ELF feature that lets us hide all of gdb’s symbols from any
dlopen caller. In fact, I found out during this process that you can even hide
ld.so seems to ignore visibility bits for this function.
Making this work is as simple as adding
-fvisibility=hidden to our
CFLAGS, and then marking our Python module initialization function with
__attribute__((visibility("default"))). Two notes here. First, it’s odd that “default” means “public”; just one of those mysterious details. Second, Python’s
PyMODINIT_FUNC macro ought to do this already, but it doesn’t; there’s a Python bug.
Those are the low-level mechanics. At this point gdb is a library, albeit an unusual one that has a single entry point. After this I needed a few tweaks to gdb’s startup process in order to make it work smoothly. This too was no big deal. Now I can write scripts from Python to do gdb things:
#!/usr/bin/python import gdb gdb.execute('file ./install/bin/gdb') print 'sizeof = %d' % gdb.lookup_type('struct minimal_symbol').sizeof
$ python zz.py 72
Soon I’ll polish all the patches and submit this upstream.