Monday, April 25, 2011

GDB Python Script To Get The Start Address Of A Loaded Library


The following GDB Python convenience function (as defined by the script) will search through the output of (gdb) info proc mappings for the library you've specified in the function parameter and return the start address of the .text segment of that library or executable.

Here is an example output of (gdb) info proc mapping with the entry for libc.so highlighted in red.  The Start Addr is what we'll have the convenience function fetch:
   Start Addr        End Addr       Size     Offset objfile
     0x100000        0x103000     0x3000   0x100000
    0x8000000       0x8029000    0x29000          0   /home/user/glibc64_power6/elf/ld.so
    0x8039000       0x803a000     0x1000    0x29000   /home/user/glibc64_power6/elf/ld.so
    0x803a000       0x803e000     0x4000    0x2a000   /home/user/glibc64_power6/elf/ld.so
   0x10000000      0x10002000     0x2000          0   /home/user/libdfp64_power6/test-isinf
   0x10011000      0x10012000     0x1000     0x1000   /home/user/libdfp64_power6/test-isinf
0x40000000000   0x40000002000     0x2000          0
0x40000002000   0x40000195000   0x193000          0   /home/user/glibc64_power6/libc.so
0x40000195000   0x400001a4000     0xf000   0x193000   /home/user/glibc64_power6/libc.so
0x400001a4000   0x400001a8000     0x4000   0x192000   /home/user/glibc64_power6/libc.so
0x400001a8000   0x400001bf000    0x17000   0x196000   /home/user/glibc64_power6/libc.so
0x400001bf000   0x400001c3000     0x4000   0x1bf000
0x400001c3000   0x4000026b000    0xa8000          0   /home/user/glibc64_power6/math/libm.so
0x4000026b000   0x4000027a000     0xf000    0xa8000   /home/user/glibc64_power6/math/libm.so
0x4000027a000   0x4000027b000     0x1000    0xa7000   /home/user/glibc64_power6/math/libm.so
0x4000027b000   0x40000285000     0xa000    0xa8000   /home/user/glibc64_power6/math/libm.so
0x40000285000   0x40000286000     0x1000   0x285000
0x40000286000   0x400002a3000    0x1d000          0   /home/user/glibc64_power6/nptl/libpthread.so
0x400002a3000   0x400002b2000     0xf000    0x1d000   /home/user/glibc64_power6/nptl/libpthread.so
0x400002b2000   0x400002b3000     0x1000    0x1c000   /home/user/glibc64_power6/nptl/libpthread.so
0x400002b3000   0x400002b5000     0x2000    0x1d000   /home/user/glibc64_power6/nptl/libpthread.so
0x400002b5000   0x400002ba000     0x5000   0x2b5000
0x400002ba000   0x4000031a000    0x60000          0   /home/user/libdfp64_power6/libdfp-1.0.7.so
0x4000031a000   0x40000329000     0xf000    0x60000   /home/user/libdfp64_power6/libdfp-1.0.7.so
0x40000329000   0x40000332000     0x9000    0x5f000   /home/user/libdfp64_power6/libdfp-1.0.7.so
0x40000332000   0x40000334000     0x2000   0x332000
0xffffff9d000   0xffffffb3000    0x16000 0xfff9d000               [stack]

Add the following to your ~.gdbinit file, or source it directly in your .gdb script:
source /path/to/gdb_start_address.py
gdb_start_address.py:
# gdb_start_address.py created by Ryan S. Arnold, April 2011.  No
# attribution required or necessary to use/reuse/copy/modify this
# function/script.

import re
class StartAddress(gdb.Function):
    """Returns the start address of a library or executable from info
    proc mappings."""

    def __init__(self):
        super (StartAddress, self).__init__ ("start_address")

    def invoke(self, library):
        mappings = gdb.execute("info proc mappings", to_string=True)
        lines = mappings.split('\n')
        to_mappings = ""
        for line in lines:
            if (to_mappings != "true"):
                if (re.search("Start Addr", line)):
                    to_mappings = "true"
                continue
            else:

                # The first match is the .text segment. Make sure to
                # match on "/libdfp-1.0.7.so" when passed "libdfp" and
                # not on "/libdfp" in the following example:
                # /home/ryanarn/libdfp/build64_power6/libdfp-1.0.7.so
                if (re.search("/" + library.string() + "[^/]*$", line)):
                    chunks = line.split()
                    return int(str(chunks[0]),16)
        return 0x0

StartAddress ()
There is currently a bug in GDB which crashes GDB after a direct assignment from this user function, e.g., (gdb) set $foo = $start_address("libc"). To use the function to set a variable do the following:
(gdb) p $start_address("libc")
$3 = 4398046760960
(gdb) set $foo = $
(gdb) p $foo
$4 = 4398046760960
I created this function to use in the Libdfp Makefile.gdb GNU make script.  Libdfp's Make system will automatically generate .gdb scripts for each of the test cases in the test-suite.  I use this function to find the load address of the library so that I can programmaticaly load the symbol file in the correct location, as in the following snippet of test-isinf.gdb:
source /home/ryanarn/eglibc/eglibc/libdfp/trunk/tests/gdb_start_address.py
...
p/x $start_address("libdfp")
set $libdfp_start = $
set $libdfp_text = 0x0af40
set $libdfp_addr = $libdfp_start + $libdfp_text
add-symbol-file ./libdfp.so.1 $libdfp_addr

1 comment: