Re: [RFA] Add interface for registering JITed code

Reid Kleckner
2009-07-28T21:35:30Z

[-- Attachment #1: Type: text/plain, Size: 6860 bytes --]

On Tue, Jul 28, 2009 at 12:00 PM, Eli Zaretskii<eliz@gnu.org> wrote:
>> From: Reid Kleckner <rnk@mit.edu>
>> Date: Mon, 27 Jul 2009 13:40:02 -0700
>> Cc: Tom Tromey <tromey@redhat.com>, gdb-patches@sourceware.org,
>>       unladen-swallow@googlegroups.com
>>
>> I mentioned the manual chapter in the NEWS entry, since the
>> explanation of how it works is long.
>
> Thanks.  I have a few comments.
>
>> +* GDB now has an interface for JIT compilation.  Applications that
>> +dynamically generate code can create symbol files in memory and register
>> +them with GDB.  For users, the feature should work transparently, and
>> +for JIT developers, the interface is documented in the GDB manual.
>
> Please state the name of the chapter in the manual where this is
> documented.

Done.

>> +@node JIT Interface
>> +@chapter @value{GDBN}'s JIT Interface
>
> I think a better name is "JIT Compilation Interface".
>
> Please add pertinent index entries here.  I suggest these:
>
>  @cindex just-in-time compilation
>  @cindex JIT compilation interface

Done.

>> +This chapter documents @value{GDBN}'s just-in-time compiler (JIT) interface.
>
> Whenever you introduce new terminology, first usage should be in @dfn,
> to make the term stand out:
>
>   This chapter documents @value{GDBN}'s @dfn{just-in-time} (JIT)
>   compilation interface.
>
> Also, how about saying a few words here about JIT compiling, for those
> who maybe hear about that for the first time?  Just a sentence or two
> would be good enough.

Sure.

>> +Just-in-time compilers (JITs) are normally difficult to debug because they
>
> Are we talking about debugging a JIT compiler or about debugging a
> program that uses a JIT compiler?  I thought the latter, but it sounds
> like you are talking about the former here.

The line is somewhat blurry, since for our purposes we are trying to
debug Unladen Swallow, which is an interpreter for Python that uses
the LLVM JIT. It's somewhat appropriate to refer to it as a JIT for
Python, but for the purposes of the documentation it's better to be
clear.

>> +generate code at runtime, and GDB normally gets all of its symbols from object
>                                 ^^^
> "@value{GDBN}" (here and elsewhere).

Done.

>> +files.
>
> Does this rewording of the above sentence catch what you meant to say
> (I'm not sure I understood)?
>
>  Programs compiled with JIT compilers are normally difficult to debug
>  because portions of their code are generated at runtime, and not
>  written to object files, where @value{GDBN} normally finds the debug
>  information it needs.
>
> The rest of the text needs similar rewording, to make it more clear.
> In particular, you use "JIT" meaning "a JIT compiler" and "executables
> that use JIT" when you really mean "programs that use JIT
> compilation".

I've left the use of JIT as a noun in the later technical part of the
chapter where "JIT" means "the JIT compiler" and not the program using
the JIT. The audience for those sections should be JIT authors, not
JIT users. In other cases I've tried to use "program that uses JIT
compilation" or just "program".

> If you can rephrase the text along the above guidelines, that'd be
> great.  If not, go ahead and commit it with the few technical changes
> I mention here, and I will do the rest.
>
>> +    In order to debug executables that use JITs, GDB has an interface that
>> +allows the JIT to register in-memory symbol files with GDB at runtime.  If you
>> +are using GDB to debug a program using this interface, then it should just work
>> +so long as you have not stripped the binary.  If you are developing a JIT, then
>> +the interface is documented in the next section.  At this time, the only known
>> +client of this interface is the LLVM JIT.
>
> A question out of ignorance: are the special symbols you use to
> interface with the JIT generated code specific to LLVM, or are they
> part of some broader standard?  If the former, the instructions below
> are actually instructions to develop other JIT compilers that follow
> the LLVM conventions, and we are in effect committing GDB to force
> these conventions on developers of JIT compilers, don't we?

If only there were a standard. :) I talked with some GDB developers
here at Google a month and a half ago, and we decided that this would
be a decent interface for GDB, LLVM, and potentially other JIT
compilers out there. There's nothing intended to be LLVM specific
about it. Most of the constraints on the interface come from the GDB
side, since GDB thinks about symbols and debug info mostly in terms of
object files. The symbol names don't actually refer to LLVM or GDB
explicitly, and they are just __jit_debug_register_code and
__jit_debug_descriptor.

The only thing that that the interface forces on JIT authors is that
they need to link in some kind of object file generation like libelf.
LLVM happens to have code for writing ELFs, so it doesn't introduce an
external dependency, it only makes the binary using the JIT a bit
bigger.

If this restriction is a problem for somebody using this interface in
the future, then I think it could be extended to come up with some
kind of symbol "file" format, where the user specifies all of the
section headers, symbols, etc in some standard in memory structure,
and gives pointer/size pairs to the sections. If you've used libelf,
then you can imagine something similar to the in-memory structures
that you use to describe the ELF before writing it. That way they
only need one copy of the code and symbols. This would probably
require implementing a more interesting BFD iovector to read the
structures and follow the pointers, and then maybe even a custom BFD
filetype, which would be a bit of work.

So while I think that would be the ideal because it requires less
effort on the client side, I consider it future work.

>> +@section Using the JIT Interface
>
> Please make each @section also a @node.  (This will need a @menu be
> added to the parent chapter.)  I would like to avoid sections that are
> not nodes, because nodes make it easier to navigate the manual in a
> structural way.

Sure.

>> +If the JIT recompiles code throughout its lifetime without unregistering it,
>> +then GDB and the JIT will continue to use extra memory for these symbol files.
>
> Does this simply say "don't recompile without unregistering, or you
> will leak memory", or does it say something else?

Yeah, I didn't use the phrase "leak memory" because to some people it
means specifically that the pointers are lost and cannot be freed,
which is not the case. But on second thought, saying "leak memory" is
less confusing and less verbose.

Thanks for the review,
Reid

[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 18244 bytes --]

2009-07-28 Reid Kleckner <reid@kleckner.net>

Add interface for JIT code generation.
* NEWS: Announce JIT interface.
* doc/gdb.texinfo: Add chapter on JIT interface.
* Makefile.in (SFILES): Add jit.c.
(HFILES_NO_SRCDIR): Add jit.h.
(COMMON_OBS): Add jit.o.
* jit.c: New file.
* jit.h: New file.
* breakpoint.h:
(enum bptype): Add bp_jit_event to enum.
* breakpoint.c:
(update_breakpoints_after_exec): Delete jit breakpoints after exec.
(bpstat_what): Update event table for bp_jit_event.
(print_it_typical): Added case for bp_jit_event.
(print_one_breakpoint_location): Added case for bp_jit_event.
(allocate_bp_location): Added case for bp_jit_event.
(mention): Added case for bp_jit_event.
(delete_command): Added case for bp_jit_event.
(breakpoint_re_set_one): Added case for bp_jit_event.
(breakpoint_re_set): Added call to jit_inferior_created_hook.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
(follow_exec): Add call to jit_inferior_created_hook.
* objfiles.c (free_objfile): Fixed a memory leak.

Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1091
diff -u -r1.1091 Makefile.in
--- gdb/Makefile.in 3 Jul 2009 12:06:35 -0000 1.1091
+++ gdb/Makefile.in 28 Jul 2009 21:32:02 -0000
@@ -677,7 +677,8 @@
wrapper.c \
xml-tdesc.c xml-support.c \
inferior.c gdb_usleep.c \
- record.c
+ record.c \
+ jit.c

LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c

@@ -746,7 +747,7 @@
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
-gdb_usleep.h
+gdb_usleep.h jit.h

# Header files that already have srcdir in them, or which are in objdir.

@@ -828,7 +829,8 @@
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o
+ inferior.o osdata.o gdb_usleep.o record.o \
+ jit.o

TSOBS = inflow.o

Index: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.319
diff -u -r1.319 NEWS
--- gdb/NEWS 20 Jul 2009 18:51:41 -0000 1.319
+++ gdb/NEWS 28 Jul 2009 21:32:02 -0000
@@ -3,6 +3,12 @@

*** Changes since GDB 6.8

+* GDB now has an interface for JIT compilation. Applications that
+dynamically generate code can create symbol files in memory and register
+them with GDB. For users, the feature should work transparently, and
+for JIT developers, the interface is documented in the GDB manual in the
+"JIT Compilation Interface" chapter.
+
* Tracepoints may now be conditional. The syntax is as for
breakpoints; either an "if" clause appended to the "trace" command,
or the "condition" command is available. GDB sends the condition to
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.413
diff -u -r1.413 breakpoint.c
--- gdb/breakpoint.c 7 Jul 2009 22:21:09 -0000 1.413
+++ gdb/breakpoint.c 28 Jul 2009 21:32:02 -0000
@@ -59,6 +59,7 @@
#include "top.h"
#include "wrapper.h"
#include "valprint.h"
+#include "jit.h"

/* readline include files */
#include "readline/readline.h"
@@ -1575,6 +1576,13 @@
continue;
}

+ /* JIT breakpoints must be explicitly reset after an exec(). */
+ if (b->type == bp_jit_event)
+ {
+ delete_breakpoint (b);
+ continue;
+ }
+
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
@@ -2573,6 +2581,7 @@
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_tracepoint:
+ case bp_jit_event:
default:
result = PRINT_UNKNOWN;
break;
@@ -3298,6 +3307,9 @@
/* We hit the shared library event breakpoint. */
shlib_event,

+ /* We hit the jit event breakpoint. */
+ jit_event,
+
/* This is just used to count how many enums there are. */
class_last
};
@@ -3313,6 +3325,7 @@
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
#define sr BPSTAT_WHAT_STEP_RESUME
#define shl BPSTAT_WHAT_CHECK_SHLIBS
+#define jit BPSTAT_WHAT_CHECK_JIT

/* "Can't happen." Might want to print an error message.
abort() is not out of the question, but chances are GDB is just
@@ -3333,12 +3346,13 @@
back and decide something of a lower priority is better. The
ordering is:

- kc < clr sgl shl slr sn sr ss
- sgl < shl slr sn sr ss
- slr < err shl sn sr ss
- clr < err shl sn sr ss
- ss < shl sn sr
- sn < shl sr
+ kc < jit clr sgl shl slr sn sr ss
+ sgl < jit shl slr sn sr ss
+ slr < jit err shl sn sr ss
+ clr < jit err shl sn sr ss
+ ss < jit shl sn sr
+ sn < jit shl sr
+ jit < shl sr
shl < sr
sr <

@@ -3356,28 +3370,18 @@
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
{
/* old action */
- /* kc ss sn sgl slr clr sr shl
- */
-/*no_effect */
- {kc, ss, sn, sgl, slr, clr, sr, shl},
-/*wp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*wp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*bp_nostop */
- {sgl, ss, sn, sgl, slr, slr, sr, shl},
-/*bp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*bp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*long_jump */
- {slr, ss, sn, slr, slr, err, sr, shl},
-/*long_resume */
- {clr, ss, sn, err, err, err, sr, shl},
-/*step_resume */
- {sr, sr, sr, sr, sr, sr, sr, sr},
-/*shlib */
- {shl, shl, shl, shl, shl, shl, sr, shl}
+ /* kc ss sn sgl slr clr sr shl jit */
+/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
+/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
+/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
+/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
+/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
+/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
+/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
};

#undef kc
@@ -3390,6 +3394,7 @@
#undef sr
#undef ts
#undef shl
+#undef jit
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
struct bpstat_what retval;

@@ -3460,6 +3465,9 @@
case bp_shlib_event:
bs_class = shlib_event;
break;
+ case bp_jit_event:
+ bs_class = jit_event;
+ break;
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
@@ -3593,6 +3601,7 @@
{bp_longjmp_master, "longjmp master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};

static char bpenables[] = "nynny";
@@ -3721,6 +3730,7 @@
case bp_overlay_event:
case bp_longjmp_master:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4362,6 +4372,7 @@
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
@@ -4644,6 +4655,17 @@
int radix;
};

+/* Create a breakpoint for JIT code registration and unregistration. */
+
+struct breakpoint *
+create_jit_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (gdbarch, address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}

void
remove_solib_event_breakpoints (void)
@@ -5279,6 +5301,7 @@
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
break;
}
@@ -7585,6 +7608,7 @@
{
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
+ && b->type != bp_jit_event
&& b->type != bp_thread_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
@@ -7604,6 +7628,7 @@
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
&& b->type != bp_thread_event
+ && b->type != bp_jit_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->number >= 0)
@@ -7926,6 +7951,7 @@
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}

@@ -7954,6 +7980,8 @@
set_language (save_language);
input_radix = save_input_radix;

+ jit_inferior_created_hook ();
+
create_overlay_event_breakpoint ("_ovly_debug_event");
create_longjmp_master_breakpoint ("longjmp");
create_longjmp_master_breakpoint ("_longjmp");
Index: gdb/breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.95
diff -u -r1.95 breakpoint.h
--- gdb/breakpoint.h 2 Jul 2009 17:12:24 -0000 1.95
+++ gdb/breakpoint.h 28 Jul 2009 21:32:02 -0000
@@ -120,6 +120,9 @@
bp_catchpoint,

bp_tracepoint,
+
+ /* Event for JIT compiled code generation or deletion. */
+ bp_jit_event,
};

/* States of enablement of breakpoint. */
@@ -548,6 +551,9 @@
keep checking. */
BPSTAT_WHAT_CHECK_SHLIBS,

+ /* Check for new JITed code. */
+ BPSTAT_WHAT_CHECK_JIT,
+
/* This is just used to keep track of how many enums there are. */
BPSTAT_WHAT_LAST
};
@@ -841,6 +847,9 @@

extern void make_breakpoint_permanent (struct breakpoint *);

+extern struct breakpoint *create_jit_event_breakpoint (struct gdbarch *,
+ CORE_ADDR);
+
extern struct breakpoint *create_solib_event_breakpoint (struct gdbarch *,
CORE_ADDR);

Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.402
diff -u -r1.402 infrun.c
--- gdb/infrun.c 20 Jul 2009 15:05:12 -0000 1.402
+++ gdb/infrun.c 28 Jul 2009 21:32:03 -0000
@@ -50,6 +50,7 @@
#include "event-top.h"
#include "record.h"
#include "inline-frame.h"
+#include "jit.h"

/* Prototypes for local functions */

@@ -544,6 +545,8 @@
solib_create_inferior_hook ();
#endif

+ jit_inferior_created_hook ();
+
/* Reinsert all breakpoints. (Those which were symbolic have
been reset to the proper address in the new a.out, thanks
to symbol_file_command...) */
@@ -3529,6 +3532,22 @@
}
break;

+ case BPSTAT_WHAT_CHECK_JIT:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
+
+ /* Switch terminal for any messages produced by breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+
+ jit_event_handler ();
+
+ target_terminal_inferior ();
+
+ /* We want to step over this breakpoint, then keep going. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */

Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.87
diff -u -r1.87 objfiles.c
--- gdb/objfiles.c 22 Jul 2009 19:21:31 -0000 1.87
+++ gdb/objfiles.c 28 Jul 2009 21:32:03 -0000
@@ -25,6 +25,7 @@

#include "defs.h"
#include "bfd.h" /* Binary File Description */
+#include "libbfd.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
@@ -438,9 +439,19 @@
if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD))
{
char *name = bfd_get_filename (objfile->obfd);
+ struct bfd_in_memory *bim = NULL;
+ /* Hack to work around the fact that BFD does not take ownership of the
+ memory for files allocated in memory. */
+ if (objfile->obfd->flags & BFD_IN_MEMORY)
+ bim = (struct bfd_in_memory *) objfile->obfd->iostream;
if (!bfd_close (objfile->obfd))
warning (_("cannot close \"%s\": %s"),
name, bfd_errmsg (bfd_get_error ()));
+ if (bim != NULL)
+ {
+ xfree (bim->buffer);
+ xfree (bim);
+ }
xfree (name);
}

Index: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.608
diff -u -r1.608 gdb.texinfo
--- gdb/doc/gdb.texinfo 20 Jul 2009 18:51:41 -0000 1.608
+++ gdb/doc/gdb.texinfo 28 Jul 2009 21:32:05 -0000
@@ -159,6 +159,7 @@
* Emacs:: Using @value{GDBN} under @sc{gnu} Emacs
* GDB/MI:: @value{GDBN}'s Machine Interface.
* Annotations:: @value{GDBN}'s annotation interface.
+* JIT Interface:: Using the JIT debugging interface.

* GDB Bugs:: Reporting bugs in @value{GDBN}

@@ -25822,6 +25823,136 @@
followed by one or more lowercase hex digits (note that this does not
depend on the language).

+@node JIT Interface
+@chapter JIT Compilation Interface
+@cindex just-in-time compilation
+@cindex JIT compilation interface
+
+This chapter documents @value{GDBN}'s @dfn{just-in-time} (JIT) compilation
+interface. A JIT compiler is a program or library that generates native
+executable code at runtime and executes it, usually in order to achieve good
+performance while maintaining platform independence.
+
+Programs that use JIT compilation are normally difficult to debug because
+portions of their code are generated at runtime, instead of being loaded from
+object files, which is where @value{GDBN} normally finds the program's symbols
+and debug information. In order to debug programs that use JIT compilation,
+@value{GDBN} has an interface that allows the program to register in-memory
+symbol files with @value{GDBN} at runtime.
+
+If you are using @value{GDBN} to debug a program that uses this interface, then
+it should work transparently so long as you have not stripped the binary. If
+you are developing a JIT compiler, then the interface is documented in the rest
+of this chapter. At this time, the only known client of this interface is the
+LLVM JIT.
+
+Broadly speaking, the JIT interface mirrors the dynamic loader interface. The
+JIT compiler communicates with @value{GDBN} by writing data into a global
+variable and calling a fuction at a well-known symbol. When @value{GDBN}
+attaches, it reads a linked list of symbol files from the global variable to
+find existing code, and puts a breakpoint in the function so that it can find
+out about additional code.
+
+@menu
+* Declarations:: Relevant C struct declarations
+* Registering Code:: Steps to register code
+* Unregistering Code:: Steps to unregister code
+@end menu
+
+@node Declarations
+@section JIT Declarations
+
+These are the relevant struct declarations that a C program should include to
+implement the interface:
+
+@smallexample
+typedef enum
+@{
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+@} jit_actions_t;
+
+struct jit_code_entry
+@{
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+@};
+
+struct jit_descriptor
+@{
+ uint32_t version;
+ /* This type should be jit_actions_t, but we use uint32_t
+ to be explicit about the bitwidth. */
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+@};
+
+/* GDB puts a breakpoint in this function. */
+void __attribute__((noinline)) __jit_debug_register_code() @{ @};
+
+/* Make sure to specify the version statically, because the
+ debugger may check the version before we can set it. */
+struct jit_descriptor __jit_debug_descriptor = @{ 1, 0, 0, 0 @};
+@end smallexample
+
+If the JIT is multi-threaded, then it is important that the JIT synchronize any
+modifications to this global data properly, which can easily be done by putting
+a global mutex around modifications to these structures.
+
+@node Registering Code
+@section Registering Code
+
+To register code with @value{GDBN}, the JIT should follow this protocol:
+
+@itemize @bullet
+@item
+Generate an object file in memory with symbols and other desired debug
+information. The file must include the virtual addresses of the sections.
+
+@item
+Create a code entry for the file, which gives the start and size of the symbol
+file.
+
+@item
+Add it to the linked list in the JIT descriptor.
+
+@item
+Point the relevant_entry field of the descriptor at the entry.
+
+@item
+Set @code{action_flag} to @code{JIT_REGISTER} and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+When @value{GDBN} is attached and the breakpoint fires, @value{GDBN} uses the
+@code{relevant_entry} pointer so it doesn't have to walk the list looking for
+new code. However, the linked list must still be maintained in order to allow
+@value{GDBN} to attach to a running process and still find the symbol files.
+
+@node Unregistering Code
+@section Unregistering Code
+
+If code is freed, then the JIT should use the following protocol:
+
+@itemize @bullet
+@item
+Remove the code entry corresponding to the code from the linked list.
+
+@item
+Point the @code{relevant_entry} field of the descriptor at the code entry.
+
+@item
+Set @code{action_flag} to @code{JIT_UNREGISTER} and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+If the JIT frees or recompiles code without unregistering it, then @value{GDBN}
+and the JIT will leak the memory used for the associated symbol files.
+
@node GDB Bugs
@chapter Reporting Bugs in @value{GDBN}
@cindex bugs in @value{GDBN}