Differences between revisions 3 and 4
Revision 3 as of 2013-09-18 22:04:23
Size: 4317
Editor: DougEvans
Comment:
Revision 4 as of 2013-09-18 22:08:28
Size: 4615
Editor: DougEvans
Comment:
Deletions are marked like this. Additions are marked like this.
Line 158: Line 158:

== Important things to remember ==

 * We're calling {{{dlopen}}} at a possibly random point in the program. As long as the thread is not stopped inside the dynamic linker this should be ok.
 * {{{dlopen}}} needs to be available, so at a minimum your program must be linked with {{{-ldl}}}.

Loading code into an active GDB session

Sometimes one is in the middle of a debugging session and one would like to add some new code. For example, you may want to whip up a function that prints an object a certain way and it's just a temporary hack and it's just easier to do in C/C++. Or you may want some kind of complex test to use as the condition for the breakpoint, and you don't want to relink the program.

For situations such as these the following is an option to try. It involves calling dlopen from GDB to load the new code into the running inferior. ["inferior" is GDB parlance for the program you are debugging.] Once the code is loaded it is accessible from GDB.

Here is a simple and somewhat contrived example. [A better example is most welcome. :-)] This example is specific to Linux (and presumably other *nix Systems). If your system is not one of these perhaps this example can be adapted for it.

Alas, while it would be nice if this example Just Worked out of the box, it uses dlopen and thus requires libdl.so linked into your program. Heads up.

Here is the program we are debugging.

birthdays.h:

#include <map>
#include <vector>
#include <string>

// A table mapping birthdays 1-31 to names of people having                                                                                                      
// birthdays on that day.                                                                                                                                        
typedef std::map<int, std::vector<std::string> > birthdays_type;

birthdays.cc:

#include <iostream>
#include <cstdlib>
#include "birthdays.h"

using namespace std;

birthdays_type birthdays;

static void
init ()
{
  birthdays[8].push_back ("Ann");
  birthdays[8].push_back ("Claire");
  birthdays[16].push_back ("John");
}

static void
usage ()
{
  cerr << "Usage: birthdays number(1-31)\n";
  exit (1);
}

static void
print (const string& s)
{
  cout << s << "\n";
}

static void
print_matching (int day)
{
  const vector<string>& people = birthdays[day];

  for (string p : people)
    print (p);
}

int
main (int argc, char *argv[])
{
  if (argc != 2)
    usage ();

  int day = atoi (argv[1]);

  init ();

  print_matching (day);

  return 0;
}

Suppose we're in the middle of a GDB session and we want to stop when we're examining the entry for "Claire".

bash$ gdb a.out
(gdb) start 8
Temporary breakpoint 1 at 0x401447: file birthdays.cc, line 45.
Starting program: /home/dje/src/play/birthdays.x64 8

Temporary breakpoint 1, main (argc=2, argv=0x7fffffffe7f8) at birthdays.cc:45
45        if (argc != 2)

Since it's stored as a string class doing this test is perhaps not complex but certainly more effort than just a simple strcmp.

So we have the following helper function:

int
is_claire (string *s)
{
  return *s == "Claire";
}

Now we just need to add it to our session.

First, let's set up some helper convenience variables:

(gdb) set $dlopen = (void*(*)(char *, int)) dlopen
(gdb) set $dlsym = (void*(*)(void*, char *)) dlsym
(gdb) set $dlclose = (int(*)(void*)) dlclose

That may not work however. libdl.so exports these symbols as versioned symbols, e.g., dlopen@@GLIBC_2.2.5. So if the above doesn't work try one of these:

(gdb) set $dlopen = (void*(*)(char *, int)) 'dlopen@@GLIBC_2.2.5'
(gdb) set $dlsym = (void*(*)(void*, char *)) 'dlsym@@GLIBC_2.2.5'
(gdb) set $dlclose = (int(*)(void*)) 'dlclose@@GLIBC_2.2.5'

or

(gdb) set $dlopen = (void*(*)(char *, int)) __dlopen
(gdb) set $dlsym = (void*(*)(void*, char *)) __dlsym
(gdb) set $dlclose = (int(*)(void*)) __dlclose

The __ versions are "for internal use only", but there is Lefler's Law #36: You gotta go with what works. :-)

With those convenience variables in place we can do this:

(gdb) !g++ -shared -fpic helper.cc -o helper.so
(gdb) $mylib = $dlopen ("./helper.so", 1)       # 1 is RTLD_LAZY
(gdb) b print if is_claire (&s)
(gdb) c
Continuing.
Ann

Breakpoint 2, print (s="Claire") at birthdays.cc:30
30        cout << s << "\n";
(gdb) 

We are now stopped at the desired point.

Important things to remember

  • We're calling dlopen at a possibly random point in the program. As long as the thread is not stopped inside the dynamic linker this should be ok.

  • dlopen needs to be available, so at a minimum your program must be linked with -ldl.

None: LoadingCodeIntoActiveSession (last edited 2013-09-18 22:08:28 by DougEvans)

All content (C) 2008 Free Software Foundation. For terms of use, redistribution, and modification, please see the WikiLicense page.