From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 9251 invoked by alias); 3 Nov 2011 10:38:22 -0000 Received: (qmail 9055 invoked by uid 22791); 3 Nov 2011 10:38:13 -0000 X-SWARE-Spam-Status: No, hits=2.6 required=5.0 tests=AWL,BAYES_50,KAM_STOCKTIP,RCVD_IN_DNSWL_LOW,SPF_SOFTFAIL,TW_BJ,TW_CP,TW_DB,TW_WH,TW_YM X-Spam-Check-By: sourceware.org Received: from mail-iy0-f169.google.com (HELO mail-iy0-f169.google.com) (209.85.210.169) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 03 Nov 2011 10:37:51 +0000 Received: by iagf6 with SMTP id f6so1576284iag.0 for ; Thu, 03 Nov 2011 03:37:50 -0700 (PDT) Received: by 10.42.135.69 with SMTP id o5mr7245777ict.34.1320316670123; Thu, 03 Nov 2011 03:37:50 -0700 (PDT) Received: from [127.0.0.1] ([203.110.240.205]) by mx.google.com with ESMTPS id p16sm7669917ibk.6.2011.11.03.03.37.44 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 03 Nov 2011 03:37:48 -0700 (PDT) Message-ID: <4EB26FF7.1070102@playingwithpointers.com> Date: Thu, 03 Nov 2011 10:38:00 -0000 From: Sanjoy Das User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.23) Gecko/20111010 Icedove/3.1.15 MIME-Version: 1.0 To: Tom Tromey CC: gdb-patches@sourceware.org Subject: Re: JIT Reader References: <4EA9095D.5070408@playingwithpointers.com> In-Reply-To: Content-Type: multipart/mixed; boundary="------------050308020203010507070800" X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-11/txt/msg00063.txt.bz2 This is a multi-part message in MIME format. --------------050308020203010507070800 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-length: 587 Hi! I've attached the new patches, with the necessary corrections. Importantly, instead of a throwing build time #error when compiling for platform on which GBD cannot load shared objects, now GDB simply hides the jit-reader-load and jit-reader-unload commands. So we get a reduced-functionality build instead of no build at all. Should this be specified in a #warning? I've added a small comment to the documentation on the functionality not being available on platforms which don't support loading shared objects at runtime. Thanks! -- Sanjoy Das http://playingwithpointers.com --------------050308020203010507070800 Content-Type: text/x-diff; name="0001-Introduce-jit-reader.in-and-modify-build-system.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0001-Introduce-jit-reader.in-and-modify-build-system.patch" Content-length: 21911 >From 9144218a00928583e4085f9d63f6413e73758218 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 25 Oct 2011 18:50:53 +0530 Subject: [PATCH 1/7] Introduce jit-reader.in and modify build system. jit-reader.in will host the interface to be implemented and the API to be used by the reader. The file needs to be processed by ./configure to produce `jit.reader.h'; so that GDB_CORE_ADDR is defined correctly. This commit arranges for `jit-reader.h' to be installed in the global include directory. gdb/ChangeLog: * gdb/Makefile.in: Add jit-reader.h as a header. Have it installed in $(includedir)/gdb. * gdb/configure.ac: Genereate a correct value for TARGET_PTR for jit-reader.h. Tell configure to generate jit-reader.h from jit-reader.in. * gdb/configure: Re-generated by autoconf. * gdb/jit-reader.in: New file. * gdb/jit.c: Include jit-reader.h. --- gdb/Makefile.in | 13 ++- gdb/configure | 124 +++++++++++++++++++ gdb/configure.ac | 22 ++++ gdb/jit-reader.in | 344 +++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/jit.c | 1 + 5 files changed, 500 insertions(+), 4 deletions(-) create mode 100644 gdb/jit-reader.in diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 7a7ff9f..ff5f246 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -833,7 +833,7 @@ common/linux-osdata.h # Header files that already have srcdir in them, or which are in objdir. -HFILES_WITH_SRCDIR = ../bfd/bfd.h +HFILES_WITH_SRCDIR = ../bfd/bfd.h jit-reader.h # GDB "info" files, which should be included in their entirety @@ -950,7 +950,7 @@ DISTSTUFF = $(YYFILES) # All generated files which can be included by another file. -generated_files = config.h observer.h observer.inc ada-lex.c \ +generated_files = config.h observer.h observer.inc ada-lex.c jit-reader.h \ $(GNULIB_H) $(NAT_GENERATED_FILES) .c.o: @@ -1035,7 +1035,9 @@ install-only: $(CONFIG_INSTALL) $(SHELL) $(srcdir)/../mkinstalldirs \ $(DESTDIR)$(man1dir) ; \ $(INSTALL_DATA) $(srcdir)/gdb.1 \ - $(DESTDIR)$(man1dir)/$$transformed_name.1 + $(DESTDIR)$(man1dir)/$$transformed_name.1 ; \ + $(SHELL) $(srcdir)/../mkinstalldirs $(DESTDIR)$(includedir)/gdb ; \ + $(INSTALL_DATA) jit-reader.h $(DESTDIR)$(includedir)/gdb/jit-reader.h @$(MAKE) DO=install "DODIRS=$(SUBDIRS)" $(FLAGS_TO_PASS) subdir_do .PHONY: install-tui install-tui: @@ -1263,7 +1265,7 @@ distclean: clean rm -f gdbserver/config.status gdbserver/config.log rm -f gdbserver/tm.h gdbserver/xm.h gdbserver/nm.h rm -f gdbserver/Makefile gdbserver/config.cache - rm -f nm.h config.status config.h stamp-h .gdbinit + rm -f nm.h config.status config.h stamp-h .gdbinit jit-reader.h rm -f y.output yacc.acts yacc.tmp y.tab.h rm -f config.log config.cache rm -f Makefile @@ -1329,6 +1331,9 @@ data-directory/Makefile: data-directory/Makefile.in config.status @frags@ CONFIG_LINKS= \ $(SHELL) config.status +jit-reader.h: $(srcdir)/jit-reader.in + $(SHELL) config.status $@ + config.h: stamp-h ; @true stamp-h: $(srcdir)/config.in config.status CONFIG_HEADERS=config.h:config.in \ diff --git a/gdb/configure b/gdb/configure index aec9d43..a206ee0 100755 --- a/gdb/configure +++ b/gdb/configure @@ -666,6 +666,7 @@ python_prog_path LTLIBEXPAT LIBEXPAT HAVE_LIBEXPAT +TARGET_PTR READLINE_TEXI_INCFLAG READLINE_CFLAGS READLINE_DEPS @@ -9884,6 +9885,128 @@ fi +# Generate jit-reader.h + +# This is typedeffed to GDB_CORE_ADDR in jit-reader.h +TARGET_PTR= + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long long" >&5 +$as_echo_n "checking size of unsigned long long... " >&6; } +if test "${ac_cv_sizeof_unsigned_long_long+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long long))" "ac_cv_sizeof_unsigned_long_long" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_long_long" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (unsigned long long) +See \`config.log' for more details." "$LINENO" 5; }; } + else + ac_cv_sizeof_unsigned_long_long=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long_long" >&5 +$as_echo "$ac_cv_sizeof_unsigned_long_long" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_LONG_LONG $ac_cv_sizeof_unsigned_long_long +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5 +$as_echo_n "checking size of unsigned long... " >&6; } +if test "${ac_cv_sizeof_unsigned_long+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_long" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (unsigned long) +See \`config.log' for more details." "$LINENO" 5; }; } + else + ac_cv_sizeof_unsigned_long=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5 +$as_echo "$ac_cv_sizeof_unsigned_long" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned __int128" >&5 +$as_echo_n "checking size of unsigned __int128... " >&6; } +if test "${ac_cv_sizeof_unsigned___int128+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned __int128))" "ac_cv_sizeof_unsigned___int128" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned___int128" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (unsigned __int128) +See \`config.log' for more details." "$LINENO" 5; }; } + else + ac_cv_sizeof_unsigned___int128=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned___int128" >&5 +$as_echo "$ac_cv_sizeof_unsigned___int128" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED___INT128 $ac_cv_sizeof_unsigned___int128 +_ACEOF + + + +if test "x${ac_cv_sizeof_unsigned_long}" = "x8"; then + TARGET_PTR="unsigned long" +elif test "x${ac_cv_sizeof_unsigned_long_long}" = "x8"; then + TARGET_PTR="unsigned long long" +elif test "x${ac_cv_sizeof_unsigned___int128}" = "x16"; then + TARGET_PTR="unsigned __int128" +else + TARGET_PTR="unsigned long" +fi + + +ac_config_files="$ac_config_files jit-reader.h:jit-reader.in" + + # Check whether --with-expat was given. if test "${with_expat+set}" = set; then : @@ -16794,6 +16917,7 @@ do "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h:config.in" ;; "depdir") CONFIG_COMMANDS="$CONFIG_COMMANDS depdir" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "jit-reader.h") CONFIG_FILES="$CONFIG_FILES jit-reader.h:jit-reader.in" ;; "$ac_config_links_1") CONFIG_LINKS="$CONFIG_LINKS $ac_config_links_1" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; ".gdbinit") CONFIG_FILES="$CONFIG_FILES .gdbinit:gdbinit.in" ;; diff --git a/gdb/configure.ac b/gdb/configure.ac index fbbb2de..ac8707a 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -605,6 +605,28 @@ AC_SUBST(READLINE_DEPS) AC_SUBST(READLINE_CFLAGS) AC_SUBST(READLINE_TEXI_INCFLAG) +# Generate jit-reader.h + +# This is typedeffed to GDB_CORE_ADDR in jit-reader.h +TARGET_PTR= + +AC_CHECK_SIZEOF(unsigned long long) +AC_CHECK_SIZEOF(unsigned long) +AC_CHECK_SIZEOF(unsigned __int128) + +if test "x${ac_cv_sizeof_unsigned_long}" = "x8"; then + TARGET_PTR="unsigned long" +elif test "x${ac_cv_sizeof_unsigned_long_long}" = "x8"; then + TARGET_PTR="unsigned long long" +elif test "x${ac_cv_sizeof_unsigned___int128}" = "x16"; then + TARGET_PTR="unsigned __int128" +else + TARGET_PTR="unsigned long" +fi + +AC_SUBST(TARGET_PTR) +AC_CONFIG_FILES([jit-reader.h:jit-reader.in]) + AC_ARG_WITH(expat, AS_HELP_STRING([--with-expat], [include expat support (auto/yes/no)]), [], [with_expat=auto]) diff --git a/gdb/jit-reader.in b/gdb/jit-reader.in new file mode 100644 index 0000000..3efe722 --- /dev/null +++ b/gdb/jit-reader.in @@ -0,0 +1,344 @@ +/* JIT declarations for GDB, the GNU Debugger. + + Copyright (C) 2011 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef GDB_JIT_READER_H +#define GDB_JIT_READER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Versioning information. See gdb_reader_funcs. */ + +#define GDB_READER_INTERFACE_VERSION 1 + +/* Readers must be released under a GPL compatible license. To + declare that the reader is indeed released under a GPL compatible + license, invoke the macro GDB_DECLARE_GPL_COMPATIBLE in a source + file. */ + +#ifdef __cplusplus +#define GDB_DECLARE_GPL_COMPATIBLE_READER \ + extern "C" { \ + extern int plugin_is_GPL_compatible (void) \ + { \ + return 0; \ + } \ + } + +#else + +#define GDB_DECLARE_GPL_COMPATIBLE_READER + extern int plugin_is_GPL_compatible (void) \ + { \ + return 0; \ + } + +#endif + +/* Represents an address on the target system. */ + +typedef @TARGET_PTR@ GDB_CORE_ADDR; + +/* Return status codes. */ + +enum gdb_status { + GDB_FAIL = 0, + GDB_SUCCESS = 1 +}; + +struct gdb_object; +struct gdb_symtab; +struct gdb_block; +struct gdb_symbol_callbacks; + +/* An array of these are used to represent a map from code addresses to line + numbers in the source file. */ + +struct gdb_line_mapping +{ + int line; + GDB_CORE_ADDR pc; +}; + +/* Create a new GDB code object. Each code object can have one or + more symbol tables, each representing a compiled source file. */ + +typedef struct gdb_object *(gdb_object_open) (struct gdb_symbol_callbacks *cb); + +/* The callback used to create new symbol table. CB is the + gdb_symbol_callbacks which the structure is part of. FILE_NAME is + an (optionally NULL) file name to associate with this new symbol + table. + + Returns a new instance to gdb_symtab that can later be passed to + gdb_block_new, gdb_symtab_add_line_mapping and gdb_symtab_close. */ + +typedef struct gdb_symtab *(gdb_symtab_open) (struct gdb_symbol_callbacks *cb, + struct gdb_object *obj, + const char *file_name); + +/* Creates a new block in a given symbol table. A symbol table is a + forest of blocks, each block representing an code address range and + a corresponding (optionally NULL) NAME. In case the block + corresponds to a function, the NAME passed should be the name of + the function. + + If the new block to be created is a child of (i.e. is nested in) + another block, the parent block can be passed in PARENT. SYMTAB is + the symbol table the new block is to belong in. BEGIN, END is the + code address range the block corresponds to. + + Returns a new instance of gdb_block, which, as of now, has no use. + Note that the gdb_block returned must not be freed by the + caller. */ + +typedef struct gdb_block *(gdb_block_open) (struct gdb_symbol_callbacks *cb, + struct gdb_symtab *symtab, + struct gdb_block *parent, + GDB_CORE_ADDR begin, + GDB_CORE_ADDR end, + const char *name); + +/* Adds a PC to line number mapping for the symbol table SYMTAB. + NLINES is the number of elements in LINES, each element + corresponding to one (PC, line) pair. */ + +typedef void (gdb_symtab_add_line_mapping) (struct gdb_symbol_callbacks *cb, + struct gdb_symtab *symtab, + int nlines, + struct gdb_line_mapping *lines); + +/* Close the symtab SYMTAB. This signals to GDB that no more blocks + will be opened on this symtab. */ + +typedef void (gdb_symtab_close) (struct gdb_symbol_callbacks *cb, + struct gdb_symtab *symtab); + + +/* Closes the gdb_object OBJ and adds the emitted information into + GDB's internal structures. Once this is done, the debug + information will be picked up and used; this will usually be the + last operation in gdb_read_debug_info. */ + +typedef void (gdb_object_close) (struct gdb_symbol_callbacks *cb, + struct gdb_object *obj); + +/* Reads LEN bytes from TARGET_MEM in the target's virtual address + space into GDB_BUF. + + Returns GDB_FAIL on failure, and GDB_SUCCESS on success. */ + +typedef enum gdb_status (gdb_target_read) (GDB_CORE_ADDR target_mem, + void *gdb_buf, int len); + +/* The list of callbacks that are passed to read. These callbacks are + to be used to construct the symbol table. The functions have been + described above. */ + +struct gdb_symbol_callbacks +{ + gdb_object_open *object_open; + gdb_symtab_open *symtab_open; + gdb_block_open *block_open; + gdb_symtab_close *symtab_close; + gdb_object_close *object_close; + + gdb_symtab_add_line_mapping *line_mapping_add; + gdb_target_read *target_read; + + /* For internal use by GDB. */ + void *priv_data; +}; + +/* Forward declaration. */ + +struct gdb_reg_value; + +/* A function of this type is used to free a gdb_reg_value. See the + comment on `free' in struct gdb_reg_value. */ + +typedef void (gdb_reg_value_free) (struct gdb_reg_value *); + +/* Denotes the value of a register. */ + +struct gdb_reg_value +{ + /* The size of the register in bytes. The reader need not set this + field. This will be set for (defined) register values being read + from GDB using reg_get. */ + int size; + + /* Set to non-zero if the value for the register is known. The + registers for which the reader does not call reg_set are also + assumed to be undefined */ + int defined; + + /* Since gdb_reg_value is a variable sized structure, it will + usually be allocated on the heap. This function is expected to + contain the corresponding "free" function. + + When a pointer to gdb_reg_value is being sent from GDB to the + reader (via gdb_unwind_reg_get), the reader is expected to call + this function (with the same gdb_reg_value as argument) once it + is done with the value. + + When the function sends the a gdb_reg_value to GDB (via + gdb_unwind_reg_set), it is expected to set this field to point to + an appropriate cleanup routine (or to NULL if no cleanup is + required). */ + gdb_reg_value_free *free; + + /* The value of the register. */ + unsigned char value[1]; +}; + +/* get_frame_id in gdb_reader_funcs is to return a gdb_frame_id + corresponding to the current frame. The registers corresponding to + the current frame can be read using reg_get. Calling get_frame_id + on a particular frame should return the same gdb_frame_id + throughout its lifetime (i.e. till before it gets unwound). One + way to do this is by having the CODE_ADDRESS point to the + function's first instruction and STACK_ADDRESS point to the value + of the stack pointer when entering the function. */ + +struct gdb_frame_id +{ + GDB_CORE_ADDR code_address; + GDB_CORE_ADDR stack_address; +}; + +/* Forward declaration. */ + +struct gdb_unwind_callbacks; + +/* Returns the value of a particular register in the current frame. + The current frame is the frame that needs to be unwound into the + outer (earlier) frame. + + CB is the struct gdb_unwind_callbacks * the callback belongs to. + REGNUM is the DWARF register number of the register that needs to + be unwound. + + Returns the gdb_reg_value corresponding to the register requested. + In case the value of the register has been optimized away or + otherwise unavailable, the defined flag in the returned + gdb_reg_value will be zero. */ + +typedef struct gdb_reg_value *(gdb_unwind_reg_get) + (struct gdb_unwind_callbacks *cb, int regnum); + +/* Sets the previous value of a particular register. REGNUM is the + (DWARF) register number whose value is to be set. VAL is the value + the register is to be set to. + + VAL is *not* copied, so the memory allocated to it cannot be + reused. Once GDB no longer needs the value, it is deallocated + using the FREE function (see gdb_reg_value). + + A register can also be "set" to an undefined value by setting the + defined in VAL to zero. */ + +typedef void (gdb_unwind_reg_set) (struct gdb_unwind_callbacks *cb, int regnum, + struct gdb_reg_value *val); + +/* This struct is passed to unwind in gdb_reader_funcs, and is to be + used to unwind the current frame (current being the frame whose + registers can be read using reg_get) into the earlier frame. The + functions have been described above. */ + +struct gdb_unwind_callbacks +{ + gdb_unwind_reg_get *reg_get; + gdb_unwind_reg_set *reg_set; + gdb_target_read *target_read; + + /* For internal use by GDB. */ + void *priv_data; +}; + +/* Forward declaration. */ + +struct gdb_reader_funcs; + +/* Parse the debug info off a block of memory, pointed to by MEMORY + (already copied to GDB's address space) and MEMORY_SZ bytes long. + The implementation has to use the functions in CB to actually emit + the parsed data into GDB. SELF is the same structure returned by + gdb_init_reader. + + Return GDB_FAIL on failure and GDB_SUCCESS on success. */ + +typedef enum gdb_status (gdb_read_debug_info) (struct gdb_reader_funcs *self, + struct gdb_symbol_callbacks *cb, + void *memory, long memory_sz); + +/* Unwind the current frame, CB is the set of unwind callbacks that + are to be used to do this. + + Return GDB_FAIL on failure and GDB_SUCCESS on success. */ + +typedef enum gdb_status (gdb_unwind_frame) (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb); + +/* Return the frame ID corresponding to the current frame, using C to + read the current register values. See the comment on struct + gdb_frame_id. */ + +typedef struct gdb_frame_id (gdb_get_frame_id) (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *c); + +/* Called when a reader is being unloaded. This function should also + free SELF, if required. */ + +typedef void (gdb_destroy_reader) (struct gdb_reader_funcs *self); + +/* Called when the reader is loaded. Must either return a properly + populated gdb_reader_funcs or NULL. The memory allocated for the + gdb_reader_funcs is to be managed by the reader itself (i.e. if it + is allocated from the heap, it must also be freed in + gdb_destroy_reader). */ + +extern struct gdb_reader_funcs *gdb_init_reader (void); + +/* Pointer to the functions which implement the reader's + functionality. The individual functions have been documented + above. + + None of the fields are optional. */ + +struct gdb_reader_funcs +{ + /* Must be set to GDB_READER_INTERFACE_VERSION. */ + int reader_version; + + /* For use by the reader. */ + void *priv_data; + + gdb_read_debug_info *read; + gdb_unwind_frame *unwind; + gdb_get_frame_id *get_frame_id; + gdb_destroy_reader *destroy; +}; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/gdb/jit.c b/gdb/jit.c index 954d297..85e40ac 100644 --- a/gdb/jit.c +++ b/gdb/jit.c @@ -20,6 +20,7 @@ #include "defs.h" #include "jit.h" +#include "jit-reader.h" #include "breakpoint.h" #include "command.h" #include "gdbcmd.h" -- 1.7.7 --------------050308020203010507070800 Content-Type: text/x-diff; name="0002-Relocatable-directory-for-loading-JIT-readers.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0002-Relocatable-directory-for-loading-JIT-readers.patch" Content-length: 4700 >From 42fd7d24efe4cc547cf4cdeac14eeed6ecab7df8 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 25 Oct 2011 18:50:53 +0530 Subject: [PATCH 2/7] Relocatable directory for loading JIT readers. Add a new directory `jit_reader_dir' to jit.c, which is relocated during initialization. The value of the directory can be set by using --with-jit-reader-dir on configure and defaults to `${libdir}/gdb'. gdb/ChangeLog: * gdb/config.in: Add new #defines: JIT_READER_DIR and JIT_READER_DIR_RELOCATABLE. * gdb/configure.ac: New GDB directory entry for jit-reader-dir. * gdb/configure: Re-generated by autoconf. * gdb/jit.c: New static variable: const char *jit_reader_dir. (_initialize_jit): Relocate jit_reader_dir. --- gdb/config.in | 6 ++++++ gdb/configure | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/configure.ac | 4 ++++ gdb/jit.c | 4 ++++ 4 files changed, 65 insertions(+), 0 deletions(-) diff --git a/gdb/config.in b/gdb/config.in index c1d7c68..27a4e2c 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -982,3 +982,9 @@ /* Define as `fork' if `vfork' does not work. */ #undef vfork + +/* The directory from which JIT readers should be loaded. */ +#undef JIT_READER_DIR + +/* Define if JIT_READER_DIR should be relocated when GDB is moved. */ +#undef JIT_READER_DIR_RELOCATABLE diff --git a/gdb/configure b/gdb/configure index a206ee0..78cd9e9 100755 --- a/gdb/configure +++ b/gdb/configure @@ -666,6 +666,7 @@ python_prog_path LTLIBEXPAT LIBEXPAT HAVE_LIBEXPAT +JIT_READER_DIR TARGET_PTR READLINE_TEXI_INCFLAG READLINE_CFLAGS @@ -966,6 +967,7 @@ with_zlib with_libiconv_prefix with_iconv_bin with_system_readline +with_jit_reader_dir with_expat with_gnu_ld enable_rpath @@ -1666,6 +1668,8 @@ Optional Packages: search for libiconv in DIR/include and DIR/lib --with-iconv-bin=PATH specify where to find the iconv program --with-system-readline use installed readline library + --with-jit-reader-dir=PATH + directory to load the JIT readers from --with-expat include expat support (auto/yes/no) --with-gnu-ld assume the C compiler uses GNU ld default=no --with-libexpat-prefix[=DIR] search for libexpat in DIR/include and DIR/lib @@ -10008,6 +10012,53 @@ ac_config_files="$ac_config_files jit-reader.h:jit-reader.in" + +# Check whether --with-jit-reader-dir was given. +if test "${with_jit_reader_dir+set}" = set; then : + withval=$with_jit_reader_dir; + JIT_READER_DIR=$withval +else + JIT_READER_DIR=${libdir}/gdb +fi + + + test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + ac_define_dir=`eval echo $JIT_READER_DIR` + ac_define_dir=`eval echo $ac_define_dir` + +cat >>confdefs.h <<_ACEOF +#define JIT_READER_DIR "$ac_define_dir" +_ACEOF + + + + + if test "x$exec_prefix" = xNONE || test "x$exec_prefix" = 'x${prefix}'; then + if test "x$prefix" = xNONE; then + test_prefix=/usr/local + else + test_prefix=$prefix + fi + else + test_prefix=$exec_prefix + fi + value=0 + case ${ac_define_dir} in + "${test_prefix}"|"${test_prefix}/"*|\ + '${exec_prefix}'|'${exec_prefix}/'*) + value=1 + ;; + esac + +cat >>confdefs.h <<_ACEOF +#define JIT_READER_DIR_RELOCATABLE $value +_ACEOF + + + + + # Check whether --with-expat was given. if test "${with_expat+set}" = set; then : withval=$with_expat; diff --git a/gdb/configure.ac b/gdb/configure.ac index ac8707a..4ecb5c9 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -627,6 +627,10 @@ fi AC_SUBST(TARGET_PTR) AC_CONFIG_FILES([jit-reader.h:jit-reader.in]) +GDB_AC_WITH_DIR([JIT_READER_DIR], [jit-reader-dir], + [directory to load the JIT readers from], + [${libdir}/gdb]) + AC_ARG_WITH(expat, AS_HELP_STRING([--with-expat], [include expat support (auto/yes/no)]), [], [with_expat=auto]) diff --git a/gdb/jit.c b/gdb/jit.c index 85e40ac..af33a91 100644 --- a/gdb/jit.c +++ b/gdb/jit.c @@ -33,6 +33,8 @@ #include "target.h" #include "gdb_stat.h" +static const char *jit_reader_dir = NULL; + static const struct objfile_data *jit_objfile_data; static const char *const jit_break_name = "__jit_debug_register_code"; @@ -558,6 +560,8 @@ extern void _initialize_jit (void); void _initialize_jit (void) { + jit_reader_dir = relocate_gdb_directory (JIT_READER_DIR, + JIT_READER_DIR_RELOCATABLE); add_setshow_zinteger_cmd ("jit", class_maintenance, &jit_debug, _("Set JIT debugging."), _("Show JIT debugging."), -- 1.7.7 --------------050308020203010507070800 Content-Type: text/x-diff; name="0003-Platform-agnostic-dynamic-loading-code.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Platform-agnostic-dynamic-loading-code.patch" Content-length: 10921 >From 9bcc2767e9862f6136116e33f5f3a2d6995fdceb Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 25 Oct 2011 18:50:53 +0530 Subject: [PATCH 3/7] Platform agnostic dynamic loading code. gdb-dlfcn.h and gdb-dlfcn.c are added, which implement the (cross platform) functions is_dl_available, gdb_dlopen, gdb_dlsym and gdb_dlclose. They should work correctly on POSIX and windows systems. gdb/ChangeLog: * gdb/Makefile.in: Add gdb-dlfcn.c and gdb-dlfcn.h to build system. * gdb/config.in: Add new #define HAVE_DLFCN_H. * gdb/configure.ac: Add check for dlopen and for dlfcn.h * gdb/configure: Re-generated by autoconf. --- gdb/Makefile.in | 6 +- gdb/config.in | 3 + gdb/configure | 60 +++++++++++++++++++++++- gdb/configure.ac | 5 ++- gdb/gdb-dlfcn.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/gdb-dlfcn.h | 51 ++++++++++++++++++++ 6 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 gdb/gdb-dlfcn.c create mode 100644 gdb/gdb-dlfcn.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index ff5f246..48221f2 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -748,7 +748,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ annotate.c common/signals.c copying.c dfp.c gdb.c inf-child.c \ regset.c sol-thread.c windows-termcap.c \ common/common-utils.c common/xml-utils.c \ - common/ptid.c common/buffer.c + common/ptid.c common/buffer.c gdb-dlfcn.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -829,7 +829,7 @@ solib-darwin.h solib-ia64-hpux.h solib-spu.h windows-nat.h xcoffread.h \ gnulib/extra/arg-nonnull.h gnulib/extra/c++defs.h gnulib/extra/warn-on-use.h \ gnulib/stddef.in.h inline-frame.h skip.h \ common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \ -common/linux-osdata.h +common/linux-osdata.h gdb-dlfcn.h # Header files that already have srcdir in them, or which are in objdir. @@ -916,7 +916,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \ inferior.o osdata.o gdb_usleep.o record.o gcore.o \ jit.o progspace.o skip.o \ - common-utils.o buffer.o ptid.o + common-utils.o buffer.o ptid.o gdb-dlfcn.o TSOBS = inflow.o diff --git a/gdb/config.in b/gdb/config.in index 27a4e2c..4af4e69 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -988,3 +988,6 @@ /* Define if JIT_READER_DIR should be relocated when GDB is moved. */ #undef JIT_READER_DIR_RELOCATABLE + +/* Define if the platform has dlfcn.h. */ +#undef HAVE_DLFCN_H diff --git a/gdb/configure b/gdb/configure index 78cd9e9..f8d038c 100755 --- a/gdb/configure +++ b/gdb/configure @@ -10011,6 +10011,63 @@ fi ac_config_files="$ac_config_files jit-reader.h:jit-reader.in" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5 +$as_echo_n "checking for library containing dlopen... " >&6; } +if test "${ac_cv_search_dlopen+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +for ac_lib in '' dl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_dlopen=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if test "${ac_cv_search_dlopen+set}" = set; then : + break +fi +done +if test "${ac_cv_search_dlopen+set}" = set; then : + +else + ac_cv_search_dlopen=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5 +$as_echo "$ac_cv_search_dlopen" >&6; } +ac_res=$ac_cv_search_dlopen +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + # Check whether --with-jit-reader-dir was given. @@ -11607,7 +11664,8 @@ for ac_header in nlist.h machine/reg.h poll.h sys/poll.h proc_service.h \ sys/resource.h sys/procfs.h sys/ptrace.h ptrace.h \ sys/reg.h sys/debugreg.h sys/select.h sys/syscall.h \ sys/types.h sys/wait.h wait.h termios.h termio.h \ - sgtty.h unistd.h elf_hp.h ctype.h time.h locale.h + sgtty.h unistd.h elf_hp.h ctype.h time.h locale.h \ + dlfcn.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" diff --git a/gdb/configure.ac b/gdb/configure.ac index 4ecb5c9..cbbf647 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -627,6 +627,8 @@ fi AC_SUBST(TARGET_PTR) AC_CONFIG_FILES([jit-reader.h:jit-reader.in]) +AC_SEARCH_LIBS(dlopen, dl) + GDB_AC_WITH_DIR([JIT_READER_DIR], [jit-reader-dir], [directory to load the JIT readers from], [${libdir}/gdb]) @@ -981,7 +983,8 @@ AC_CHECK_HEADERS([nlist.h machine/reg.h poll.h sys/poll.h proc_service.h \ sys/resource.h sys/procfs.h sys/ptrace.h ptrace.h \ sys/reg.h sys/debugreg.h sys/select.h sys/syscall.h \ sys/types.h sys/wait.h wait.h termios.h termio.h \ - sgtty.h unistd.h elf_hp.h ctype.h time.h locale.h]) + sgtty.h unistd.h elf_hp.h ctype.h time.h locale.h \ + dlfcn.h]) AC_CHECK_HEADERS(link.h, [], [], [#if HAVE_SYS_TYPES_H # include diff --git a/gdb/gdb-dlfcn.c b/gdb/gdb-dlfcn.c new file mode 100644 index 0000000..c808ec6 --- /dev/null +++ b/gdb/gdb-dlfcn.c @@ -0,0 +1,137 @@ +/* Platform independent shared object routines for GDB. + + Copyright (C) 2011 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" +#include "gdb_assert.h" + +#include "gdb-dlfcn.h" + +#ifdef HAVE_DLFCN_H +#include +#elif __MINGW32__ +#include +#else +/* Unsupported configuration. */ +#define NO_SHARED_LIB +#endif + +#ifdef NO_SHARED_LIB + +void *gdb_dlopen(const char *filename) +{ + gdb_assert_not_reached ("gdb_dlopen should not be called on this platform."); + return NULL; +} + +void *gdb_dlsym (void *handle, const char *symbol) +{ + gdb_assert_not_reached ("gdb_dlsym should not be called on this platform."); + return NULL; +} + +struct cleanup *make_cleanup_dlclose (void *handle) +{ + gdb_assert_not_reached ("make_cleanup_dlclose should not be called on this " + "platform."); + return NULL; +} + +int gdb_dlclose (void *handle) +{ + gdb_assert_not_reached ("gdb_dlclose should not be called on this platform."); + return 0; +} + +int is_dl_available(void) +{ + return 0; +} + +#else /* NO_SHARED_LIB */ + +void * +gdb_dlopen (const char *filename) +{ + void *result; +#ifdef HAVE_DLFCN_H + result = dlopen (filename, RTLD_NOW); +#elif __MINGW32__ + result = (void *) LoadLibrary (filename); +#endif + if (result != NULL) + return result; + +#ifdef HAVE_DLFCN_H + error (_("Could not load %s: %s"), filename, dlerror()); +#else + { + LPVOID buffer; + DWORD dw; + + dw = GetLastError(); + + FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &buffer + 0, NULL); + + error (_("Could not load %s: %s"), filename, (char *) buffer); + } +#endif +} + +void * +gdb_dlsym (void *handle, const char *symbol) +{ +#ifdef HAVE_DLFCN_H + return dlsym (handle, symbol); +#elif __MINGW32__ + return (void *) GetProcAddress (handle, symbol); +#endif +} + +int +gdb_dlclose (void *handle) +{ +#ifdef HAVE_DLFCN_H + return dlclose (handle); +#elif __MINGW32__ + return !((int) FreeLibrary (handle)); +#endif +} + +static void +do_dlclose_cleanup (void *handle) +{ + gdb_dlclose (handle); +} + +struct cleanup * +make_cleanup_dlclose (void *handle) +{ + return make_cleanup (do_dlclose_cleanup, handle); +} + +int is_dl_available(void) +{ + return 1; +} + +#endif /* NO_SHARED_LIB */ diff --git a/gdb/gdb-dlfcn.h b/gdb/gdb-dlfcn.h new file mode 100644 index 0000000..0a9d1eb --- /dev/null +++ b/gdb/gdb-dlfcn.h @@ -0,0 +1,51 @@ +/* Platform independent shared object routines for GDB. + + Copyright (C) 2011 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef GDB_DLFCN_H +#define GDB_DLFCN_H + +#include "defs.h" + +/* Load the dynamic library file named FILENAME, and return a handle + for that dynamic library. Return NULL if the loading fails for any + reason. */ + +void *gdb_dlopen (const char *filename); + +/* Return the address of the symbol named SYMBOL inside the shared + library whose handle is HANDLE. Return NULL when the symbol could + not be found. */ + +void *gdb_dlsym (void *handle, const char *symbol); + +/* Install a cleanup routine which closes the handle HANDLE. */ + +struct cleanup *make_cleanup_dlclose (void *handle); + +/* Cleanup the shared object pointed to by HANDLE. Return 0 on success + and nonzero on failure. */ + +int gdb_dlclose (void *handle); + +/* Return non-zero if the dynamic library functions are available on + this platform. */ + +int is_dl_available(void); + +#endif /* GDB_DLFCN_H */ -- 1.7.7 --------------050308020203010507070800 Content-Type: text/x-diff; name="0004-New-commands-for-loading-and-unloading-a-reader.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0004-New-commands-for-loading-and-unloading-a-reader.patch" Content-length: 4489 >From 6f054c456157455f51bf156f51887f88841e4778 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 25 Oct 2011 18:50:53 +0530 Subject: [PATCH 4/7] New commands for loading and unloading a reader. Introduces two new GDB commands - `load-jit-reader' and `unload-jit-reader'. The commands are not added if the platform does not support loading dynamic libraries. gdb/ChangeLog: * gdb/jit.c: Include gdb-dlfcn.h. (loaded_jit_reader, reader_init_fn_sym): New static variables. (jit_reader_load, jit_reader_load_command) (jit_reader_unload_command): New functions. (_initialize_jit): Add commands "jit-reader-load" and "jit-reader-unload". --- gdb/jit.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 100 insertions(+), 0 deletions(-) diff --git a/gdb/jit.c b/gdb/jit.c index af33a91..e7720b7 100644 --- a/gdb/jit.c +++ b/gdb/jit.c @@ -31,6 +31,7 @@ #include "symfile.h" #include "symtab.h" #include "target.h" +#include "gdb-dlfcn.h" #include "gdb_stat.h" static const char *jit_reader_dir = NULL; @@ -115,6 +116,92 @@ mem_bfd_iovec_stat (struct bfd *abfd, void *stream, struct stat *sb) return 0; } +/* One reader that has been loaded successfully, and can potentially be used to + parse debug info. */ + +static struct jit_reader +{ + struct gdb_reader_funcs *functions; + void *handle; +} *loaded_jit_reader = NULL; + +typedef struct gdb_reader_funcs * (reader_init_fn_type) (void); +static const char *reader_init_fn_sym = "gdb_init_reader"; + +/* Try to load FILE_NAME as a JIT debug info reader. */ + +static struct jit_reader * +jit_reader_load (const char *file_name) +{ + void *so; + reader_init_fn_type *init_fn; + struct jit_reader *new_reader = NULL; + struct gdb_reader_funcs *funcs = NULL; + struct cleanup *old_cleanups; + + if (jit_debug) + fprintf_unfiltered (gdb_stdlog, _("Opening shared object %s.\n"), + file_name); + so = gdb_dlopen (file_name); + old_cleanups = make_cleanup_dlclose (so); + + init_fn = gdb_dlsym (so, reader_init_fn_sym); + if (!init_fn) + error (_("Could not locate initialization function: %s."), + reader_init_fn_sym); + + if (gdb_dlsym (so, "plugin_is_GPL_compatible") == NULL) + error (_("Reader not GPL compatible.")); + + funcs = init_fn (); + if (funcs->reader_version != GDB_READER_INTERFACE_VERSION) + error (_("Reader version does not match GDB version.")); + + new_reader = XZALLOC (struct jit_reader); + new_reader->functions = funcs; + new_reader->handle = so; + + discard_cleanups (old_cleanups); + return new_reader; +} + +/* Provides the jit-reader-load command. */ + +static void +jit_reader_load_command (char *args, int from_tty) +{ + char *so_name; + int len; + struct cleanup *prev_cleanup; + + if (args == NULL) + error (_("No reader name provided.")); + + if (loaded_jit_reader != NULL) + error (_("JIT reader already loaded. Run jit-reader-unload first.")); + + so_name = xstrprintf ("%s/%s", jit_reader_dir, args); + prev_cleanup = make_cleanup (xfree, so_name); + + loaded_jit_reader = jit_reader_load (so_name); + do_cleanups (prev_cleanup); +} + +/* Provides the jit-reader-unload command. */ + +static void +jit_reader_unload_command (char *args, int from_tty) +{ + if (!loaded_jit_reader) + error (_("No JIT reader loaded.")); + + loaded_jit_reader->functions->destroy (loaded_jit_reader->functions); + + gdb_dlclose (loaded_jit_reader->handle); + xfree (loaded_jit_reader); + loaded_jit_reader = NULL; +} + /* Open a BFD from the target's memory. */ static struct bfd * @@ -576,4 +663,17 @@ _initialize_jit (void) jit_objfile_data = register_objfile_data (); jit_inferior_data = register_inferior_data_with_cleanup (jit_inferior_data_cleanup); + if (is_dl_available()) + { + add_com ("jit-reader-load", no_class, jit_reader_load_command, _("\ +Load FILE as debug info reader and unwinder for JIT compiled code.\n\ +Usage: jit-reader-load FILE\n\ +Try to load file FILE as a debug info reader (and unwinder) for\n\ +JIT compiled code. The file is loaded from " JIT_READER_DIR ",\n\ +relocated relative to the GDB executable if required.")); + add_com ("jit-reader-unload", no_class, jit_reader_unload_command, _("\ +Unload the currently loaded JIT debug info reader.\n\ +Usage: jit-reader-unload FILE\n\n\ +Do \"help jit-reader-load\" for info on loading debug info readers.")); + } } -- 1.7.7 --------------050308020203010507070800 Content-Type: text/x-diff; name="0005-Use-the-loaded-reader.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0005-Use-the-loaded-reader.patch" Content-length: 17920 >From 868d6cbe8d1edfe354318a1b7a67a97fc689857f Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 25 Oct 2011 18:50:53 +0530 Subject: [PATCH 5/7] Use the loaded reader. Invoke the loaded JIT debug info reader to parse the registered symbol files. gdb/ChangeLog: * gdb/jit.c: Include block.h, dictionary.h and frame-unwind.h. (add_objfile_entry, jit_target_read_impl, jit_object_open_impl) (jit_symtab_open_impl, compare_block, jit_block_open_impl) (jit_symtab_line_mapping_add_impl, jit_symtab_close_impl) (finalize_symtab, jit_object_close_impl) (jit_reader_try_read_symtab, jit_bfd_try_read_symtab) (free_objfile_data): New functions. (_initialize_jit): Register jit_objfile_data with a proper cleanup function. --- gdb/jit.c | 489 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 476 insertions(+), 13 deletions(-) diff --git a/gdb/jit.c b/gdb/jit.c index e7720b7..747477f 100644 --- a/gdb/jit.c +++ b/gdb/jit.c @@ -21,8 +21,11 @@ #include "jit.h" #include "jit-reader.h" +#include "block.h" #include "breakpoint.h" #include "command.h" +#include "dictionary.h" +#include "frame-unwind.h" #include "gdbcmd.h" #include "gdbcore.h" #include "inferior.h" @@ -33,6 +36,7 @@ #include "target.h" #include "gdb-dlfcn.h" #include "gdb_stat.h" +#include "exceptions.h" static const char *jit_reader_dir = NULL; @@ -228,6 +232,18 @@ struct jit_inferior_data CORE_ADDR descriptor_addr; /* &__jit_debug_descriptor */ }; +/* Remember a mapping from entry_addr to objfile. */ + +static void +add_objfile_entry (struct objfile *objfile, CORE_ADDR entry) +{ + CORE_ADDR *entry_addr_ptr; + + entry_addr_ptr = xmalloc (sizeof (CORE_ADDR)); + *entry_addr_ptr = entry; + set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr); +} + /* Return jit_inferior_data for current inferior. Allocate if not already present. */ @@ -330,14 +346,431 @@ jit_read_code_entry (struct gdbarch *gdbarch, extract_unsigned_integer (&entry_buf[off], 8, byte_order); } -/* This function registers code associated with a JIT code entry. It uses the - pointer and size pair in the entry to read the symbol file from the remote - and then calls symbol_file_add_from_local_memory to add it as though it were - a symbol file added by the user. */ +/* Proxy object for building a block. */ + +struct gdb_block +{ + /* gdb_blocks are linked into a tree structure. Next points to the + next node at the same depth as this block and parent to the + parent gdb_block. */ + struct gdb_block *next, *parent; + + /* Points to the "real" block that is being built out of this + instance. This block will be added to a blockvector, which will + then be added to a symtab. */ + struct block *real_block; + + /* The first and last code address corresponding to this block. */ + CORE_ADDR begin, end; + + /* The name of this block (if any). If this is non-NULL, the + FUNCTION symbol symbol is set to this value. */ + const char *name; +}; + +/* Proxy object for building a symtab. */ + +struct gdb_symtab +{ + /* The list of blocks in this symtab. These will eventually be + converted to real blocks. */ + struct gdb_block *blocks; + + /* The number of blocks inserted. */ + int nblocks; + + /* A mapping between line numbers to PC. */ + struct linetable *linetable; + + /* The source file for this symtab. */ + const char *file_name; + struct gdb_symtab *next; +}; + +/* Proxy object for building an object. */ + +struct gdb_object +{ + struct gdb_symtab *symtabs; +}; + +/* The type of the `private' data passed around by the callback + functions. */ + +typedef CORE_ADDR jit_dbg_reader_data; + +/* The reader calls into this function to read data off the targets + address space. */ + +static enum gdb_status +jit_target_read_impl (GDB_CORE_ADDR target_mem, void *gdb_buf, int len) +{ + int result = target_read_memory ((CORE_ADDR) target_mem, gdb_buf, len); + if (result == 0) + return GDB_SUCCESS; + else + return GDB_FAIL; +} + +/* The reader calls into this function to create a new gdb_object + which it can then pass around to the other callbacks. Right now, + all that is required is allocating the memory. */ + +static struct gdb_object * +jit_object_open_impl (struct gdb_symbol_callbacks *cb) +{ + /* CB is not required right now, but sometime in the future we might + need a handle to it, and we'd like to do that without breaking + the ABI. */ + return XZALLOC (struct gdb_object); +} + +/* Readers call into this function to open a new gdb_symtab, which, + again, is passed around to other callbacks. */ + +static struct gdb_symtab * +jit_symtab_open_impl (struct gdb_symbol_callbacks *cb, + struct gdb_object *object, + const char *file_name) +{ + struct gdb_symtab *ret; + + /* CB stays unused. See comment in jit_object_open_impl. */ + + ret = XZALLOC (struct gdb_symtab); + ret->file_name = file_name ? xstrdup (file_name) : xstrdup (""); + ret->next = object->symtabs; + object->symtabs = ret; + return ret; +} + +/* Returns true if the block corresponding to old should be placed + before the block corresponding to new in the final blockvector. */ + +static int +compare_block (const struct gdb_block *const old, + const struct gdb_block *const new) +{ + if (old == NULL) + return 1; + if (old->begin < new->begin) + return 1; + else if (old->begin == new->begin) + { + if (old->end > new->end) + return 1; + else + return 0; + } + else + return 0; +} + +/* Called by readers to open a new gdb_block. This function also + inserts the new gdb_block in the correct place in the corresponding + gdb_symtab. */ + +static struct gdb_block * +jit_block_open_impl (struct gdb_symbol_callbacks *cb, + struct gdb_symtab *symtab, struct gdb_block *parent, + GDB_CORE_ADDR begin, GDB_CORE_ADDR end, const char *name) +{ + struct gdb_block *block = XZALLOC (struct gdb_block); + + block->next = symtab->blocks; + block->begin = (CORE_ADDR) begin; + block->end = (CORE_ADDR) end; + block->name = name ? xstrdup (name) : NULL; + block->parent = parent; + + /* Ensure that the blocks are inserted in the correct (reverse of + the order expected by blockvector). */ + if (compare_block (symtab->blocks, block)) + { + symtab->blocks = block; + } + else + { + struct gdb_block *i = symtab->blocks; + + for (;; i = i->next) + { + /* Guaranteed to terminate, since compare_block (NULL, _) + returns 1. */ + if (compare_block (i->next, block)) + { + block->next = i->next; + i->next = block; + break; + } + } + } + symtab->nblocks++; + + return block; +} + +/* Readers call this to add a line mapping (from PC to line number) to + a gdb_symtab. */ static void -jit_register_code (struct gdbarch *gdbarch, - CORE_ADDR entry_addr, struct jit_code_entry *code_entry) +jit_symtab_line_mapping_add_impl (struct gdb_symbol_callbacks *cb, + struct gdb_symtab *stab, int nlines, + struct gdb_line_mapping *map) +{ + int i; + + if (nlines < 1) + return; + + stab->linetable = xmalloc (sizeof (struct linetable) + + (nlines - 1) * sizeof (struct linetable_entry)); + stab->linetable->nitems = nlines; + for (i = 0; i < nlines; i++) + { + stab->linetable->item[i].pc = (CORE_ADDR) map[i].pc; + stab->linetable->item[i].line = map[i].line; + } +} + +/* Called by readers to close a gdb_symtab. Does not need to do + anything as of now. */ + +static void +jit_symtab_close_impl (struct gdb_symbol_callbacks *cb, + struct gdb_symtab *stab) +{ + /* Right now nothing needs to be done here. We may need to do some + cleanup here in the future (again, without breaking the plugin + ABI). */ +} + +/* Transform STAB to a proper symtab, and add it it OBJFILE. */ + +static void +finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile) +{ + struct symtab *symtab; + struct gdb_block *gdb_block_iter, *gdb_block_iter_tmp; + struct block *block_iter; + int actual_nblocks, i, blockvector_size; + CORE_ADDR begin, end; + + actual_nblocks = FIRST_LOCAL_BLOCK + stab->nblocks; + + symtab = allocate_symtab (stab->file_name, objfile); + /* JIT compilers compile in memory. */ + symtab->dirname = NULL; + + /* Copy over the linetable entry if one was provided. */ + if (stab->linetable) + { + int size = ((stab->linetable->nitems - 1) + * sizeof (struct linetable_entry) + + sizeof (struct linetable)); + LINETABLE (symtab) = obstack_alloc (&objfile->objfile_obstack, size); + memcpy (LINETABLE (symtab), stab->linetable, size); + } + else + { + LINETABLE (symtab) = NULL; + } + + blockvector_size = (sizeof (struct blockvector) + + (actual_nblocks - 1) * sizeof (struct block *)); + symtab->blockvector = obstack_alloc (&objfile->objfile_obstack, + blockvector_size); + + /* (begin, end) will contain the PC range this entire blockvector + spans. */ + symtab->primary = 1; + BLOCKVECTOR_MAP (symtab->blockvector) = NULL; + begin = stab->blocks->begin; + end = stab->blocks->end; + BLOCKVECTOR_NBLOCKS (symtab->blockvector) = actual_nblocks; + + /* First run over all the gdb_block objects, creating a real block + object for each. Simultaneously, keep setting the real_block + fields. */ + for (i = (actual_nblocks - 1), gdb_block_iter = stab->blocks; + i >= FIRST_LOCAL_BLOCK; + i--, gdb_block_iter = gdb_block_iter->next) + { + struct block *new_block = allocate_block (&objfile->objfile_obstack); + struct symbol *block_name = obstack_alloc (&objfile->objfile_obstack, + sizeof (struct symbol)); + + BLOCK_DICT (new_block) = dict_create_linear (&objfile->objfile_obstack, + NULL); + /* The address range. */ + BLOCK_START (new_block) = (CORE_ADDR) gdb_block_iter->begin; + BLOCK_END (new_block) = (CORE_ADDR) gdb_block_iter->end; + + /* The name. */ + memset (block_name, 0, sizeof (struct symbol)); + SYMBOL_DOMAIN (block_name) = VAR_DOMAIN; + SYMBOL_CLASS (block_name) = LOC_BLOCK; + SYMBOL_SYMTAB (block_name) = symtab; + SYMBOL_BLOCK_VALUE (block_name) = new_block; + + block_name->ginfo.name = obsavestring (gdb_block_iter->name, + strlen (gdb_block_iter->name), + &objfile->objfile_obstack); + + BLOCK_FUNCTION (new_block) = block_name; + + BLOCKVECTOR_BLOCK (symtab->blockvector, i) = new_block; + if (begin > BLOCK_START (new_block)) + begin = BLOCK_START (new_block); + if (end < BLOCK_END (new_block)) + end = BLOCK_END (new_block); + + gdb_block_iter->real_block = new_block; + } + + /* Now add the special blocks. */ + block_iter = NULL; + for (i = 0; i < FIRST_LOCAL_BLOCK; i++) + { + struct block *new_block = allocate_block (&objfile->objfile_obstack); + BLOCK_DICT (new_block) = dict_create_linear (&objfile->objfile_obstack, + NULL); + BLOCK_SUPERBLOCK (new_block) = block_iter; + block_iter = new_block; + + BLOCK_START (new_block) = (CORE_ADDR) begin; + BLOCK_END (new_block) = (CORE_ADDR) end; + + BLOCKVECTOR_BLOCK (symtab->blockvector, i) = new_block; + } + + /* Fill up the superblock fields for the real blocks, using the + real_block fields populated earlier. */ + for (gdb_block_iter = stab->blocks; + gdb_block_iter; + gdb_block_iter = gdb_block_iter->next) + { + if (gdb_block_iter->parent != NULL) + BLOCK_SUPERBLOCK (gdb_block_iter->real_block) = + gdb_block_iter->parent->real_block; + } + + /* Free memory. */ + gdb_block_iter = stab->blocks; + + for (gdb_block_iter = stab->blocks, gdb_block_iter_tmp = gdb_block_iter->next; + gdb_block_iter; + gdb_block_iter = gdb_block_iter_tmp) + { + xfree ((void *) gdb_block_iter->name); + xfree (gdb_block_iter); + } + xfree (stab->linetable); + xfree ((char *) stab->file_name); + xfree (stab); +} + +/* Called when closing a gdb_objfile. Converts OBJ to a proper + objfile. */ + +static void +jit_object_close_impl (struct gdb_symbol_callbacks *cb, + struct gdb_object *obj) +{ + struct gdb_symtab *i, *j; + struct objfile *objfile; + jit_dbg_reader_data *priv_data; + + priv_data = cb->priv_data; + + objfile = allocate_objfile (NULL, 0); + objfile->gdbarch = target_gdbarch; + + objfile->msymbols = obstack_alloc (&objfile->objfile_obstack, + sizeof (struct minimal_symbol)); + memset (objfile->msymbols, 0, sizeof (struct minimal_symbol)); + + xfree (objfile->name); + objfile->name = xstrdup ("<< JIT compiled code >>"); + + j = NULL; + for (i = obj->symtabs; i; i = j) + { + j = i->next; + finalize_symtab (i, objfile); + } + add_objfile_entry (objfile, *priv_data); + xfree (obj); +} + +/* Try to read CODE_ENTRY using the loaded jit reader (if any). */ + +static int +jit_reader_try_read_symtab (struct jit_code_entry *code_entry) +{ + void *gdb_mem; + int status = 0; + struct jit_dbg_reader *i; + jit_dbg_reader_data priv_data; + struct gdb_reader_funcs *funcs; + volatile struct gdb_exception e; + struct gdb_symbol_callbacks callbacks = + { + jit_object_open_impl, + jit_symtab_open_impl, + jit_block_open_impl, + jit_symtab_close_impl, + jit_object_close_impl, + + jit_symtab_line_mapping_add_impl, + jit_target_read_impl, + + &priv_data + }; + + priv_data = code_entry->symfile_addr; + + if (!loaded_jit_reader) + return 0; + + gdb_mem = xmalloc (code_entry->symfile_size); + + TRY_CATCH(e, RETURN_MASK_ALL) + { + if (target_read_memory (code_entry->symfile_addr, gdb_mem, + code_entry->symfile_size)) + { + status = 0; + goto cleanup; + } + } + if (e.reason < 0) + { + status = 0; + goto cleanup; + } + + funcs = loaded_jit_reader->functions; + if (funcs->read (funcs, &callbacks, gdb_mem, code_entry->symfile_size) + == GDB_SUCCESS) + { + status = 1; + goto cleanup; + } + + cleanup: + xfree (gdb_mem); + if (jit_debug && status == 0) + fprintf_unfiltered (gdb_stdlog, + "Could not read symtab using the loaded JIT reader.\n"); + return status; +} + +/* Try to read CODE_ENTRY using BFD. */ + +static void +jit_bfd_try_read_symtab (struct jit_code_entry *code_entry, + struct gdbarch *gdbarch) { bfd *nbfd; struct section_addr_info *sai; @@ -346,7 +779,6 @@ jit_register_code (struct gdbarch *gdbarch, struct cleanup *old_cleanups; int i; const struct bfd_arch_info *b; - CORE_ADDR *entry_addr_ptr; if (jit_debug) fprintf_unfiltered (gdb_stdlog, @@ -400,12 +832,34 @@ JITed symbol file is not an object file, ignoring it.\n")); /* This call takes ownership of NBFD. It does not take ownership of SAI. */ objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED, NULL); - /* Remember a mapping from entry_addr to objfile. */ - entry_addr_ptr = xmalloc (sizeof (CORE_ADDR)); - *entry_addr_ptr = entry_addr; - set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr); - do_cleanups (old_cleanups); + add_objfile_entry (objfile, code_entry->symfile_addr); +} + +/* This function registers code associated with a JIT code entry. It uses the + pointer and size pair in the entry to read the symbol file from the remote + and then calls symbol_file_add_from_local_memory to add it as though it were + a symbol file added by the user. */ + +static void +jit_register_code (struct gdbarch *gdbarch, + CORE_ADDR entry_addr, struct jit_code_entry *code_entry) +{ + int i, success; + const struct bfd_arch_info *b; + struct jit_inferior_data *inf_data = get_jit_inferior_data (); + + if (jit_debug) + fprintf_unfiltered (gdb_stdlog, + "jit_register_code, symfile_addr = %s, " + "symfile_size = %s\n", + paddress (gdbarch, code_entry->symfile_addr), + pulongest (code_entry->symfile_size)); + + success = jit_reader_try_read_symtab (code_entry); + + if (!success) + jit_bfd_try_read_symtab (code_entry, gdbarch); } /* This function unregisters JITed code and frees the corresponding @@ -640,6 +1094,14 @@ jit_event_handler (struct gdbarch *gdbarch) } } +/* Called to free the data allocated to the jit_inferior_data slot. */ + +static void +free_objfile_data (struct objfile *objfile, void *data) +{ + xfree (data); +} + /* Provide a prototype to silence -Wmissing-prototypes. */ extern void _initialize_jit (void); @@ -660,7 +1122,8 @@ _initialize_jit (void) observer_attach_inferior_created (jit_inferior_created_observer); observer_attach_inferior_exit (jit_inferior_exit_hook); observer_attach_executable_changed (jit_executable_changed_observer); - jit_objfile_data = register_objfile_data (); + jit_objfile_data = + register_objfile_data_with_cleanup (NULL, free_objfile_data); jit_inferior_data = register_inferior_data_with_cleanup (jit_inferior_data_cleanup); if (is_dl_available()) -- 1.7.7 --------------050308020203010507070800 Content-Type: text/x-diff; name="0006-New-JIT-unwinder.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0006-New-JIT-unwinder.patch" Content-length: 9858 >From bef1cc9ee8532a0518f9b2bd4f9bcc81e31c51ab Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Thu, 3 Nov 2011 12:22:32 +0530 Subject: [PATCH 6/7] New JIT unwinder. Introduce a "proxy unwinder", whcih will pass down all calls to the functions the JIT reader provides. gdb/ChangeLog: * gdb/jit.c: Include regcache.h. (jit_gdbarch_data, jit_frame_unwind): New static variables. (jit_unwind_reg_set_impl, free_reg_value_impl) (jit_unwind_reg_get_impl, jit_frame_sniffer) (jit_frame_unwind_stop_reason, jit_frame_this_id) (jit_frame_prev_register, jit_dealloc_cache) (jit_prepend_unwinder, jit_gdbarch_data_init): New functions. (jit_inferior_init): Prepend (new) pseudo unwinder by calling jit_prepend_unwinder. (_initialize_jit): Register new gdbarch data jit_gdbarch_data. Conflicts: gdb/jit.c --- gdb/jit.c | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 259 insertions(+), 0 deletions(-) diff --git a/gdb/jit.c b/gdb/jit.c index 747477f..edf44aa 100644 --- a/gdb/jit.c +++ b/gdb/jit.c @@ -31,6 +31,7 @@ #include "inferior.h" #include "observer.h" #include "objfiles.h" +#include "regcache.h" #include "symfile.h" #include "symtab.h" #include "target.h" @@ -50,6 +51,12 @@ static const struct inferior_data *jit_inferior_data = NULL; static void jit_inferior_init (struct gdbarch *gdbarch); +/* An unwinder is registered for every gdbarch. This key is used to + remember if the unwinder has been registered for a particular + gdbarch. */ + +static struct gdbarch_data *jit_gdbarch_data; + /* Non-zero if we want to see trace of jit level stuff. */ static int jit_debug = 0; @@ -928,6 +935,242 @@ jit_breakpoint_re_set_internal (struct gdbarch *gdbarch, return 0; } +/* The private data passed around in the frame unwind callback + functions. */ + +struct jit_unwind_private +{ + /* Cached register values. See jit_frame_sniffer to see how this + works. */ + struct gdb_reg_value **registers; + + /* The frame being unwound. */ + struct frame_info *this_frame; +}; + +/* Sets the value of a particular register in this frame. */ + +static void +jit_unwind_reg_set_impl (struct gdb_unwind_callbacks *cb, int dwarf_regnum, + struct gdb_reg_value *value) +{ + struct jit_unwind_private *priv; + int gdb_reg; + + priv = cb->priv_data; + + gdb_reg = gdbarch_dwarf2_reg_to_regnum (get_frame_arch (priv->this_frame), + dwarf_regnum); + if (gdb_reg == -1) + { + if (jit_debug) + fprintf_unfiltered (gdb_stdlog, + _("Could not recognize DWARF regnum %d"), + dwarf_regnum); + return; + } + + gdb_assert (priv->registers); + priv->registers[gdb_reg] = value; +} + +static void +reg_value_free_impl (struct gdb_reg_value *value) +{ + xfree (value); +} + +/* Get the value of register REGNUM in the previous frame. */ + +static struct gdb_reg_value * +jit_unwind_reg_get_impl (struct gdb_unwind_callbacks *cb, int regnum) +{ + struct jit_unwind_private *priv; + struct gdb_reg_value *value; + int gdb_reg, size; + struct gdbarch *frame_arch; + + priv = cb->priv_data; + frame_arch = get_frame_arch (priv->this_frame); + + gdb_reg = gdbarch_dwarf2_reg_to_regnum (frame_arch, regnum); + size = register_size (frame_arch, gdb_reg); + value = xmalloc (sizeof (struct gdb_reg_value) + size - 1); + value->defined = frame_register_read (priv->this_frame, gdb_reg, + value->value); + value->size = size; + value->free = reg_value_free_impl; + return value; +} + +/* gdb_reg_value has a free function, which must be called on each + saved register value. */ + +static void +jit_dealloc_cache (struct frame_info *this_frame, void *cache) +{ + struct jit_unwind_private *priv_data = cache; + struct gdbarch *frame_arch; + int i; + + gdb_assert (priv_data->registers); + frame_arch = get_frame_arch (priv_data->this_frame); + + for (i = 0; i < gdbarch_num_regs (frame_arch); i++) + if (priv_data->registers[i] && priv_data->registers[i]->free) + priv_data->registers[i]->free (priv_data->registers[i]); + + xfree (priv_data->registers); + xfree (priv_data); +} + +/* The frame sniffer for the pseudo unwinder. + + While this is nominally a frame sniffer, in the case where the JIT + reader actually recognizes the frame, it does a lot more work -- it + unwinds the frame and saves the corresponding register values in + the cache. jit_frame_prev_register simply returns the saved + register values. */ + +static int +jit_frame_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, void **cache) +{ + struct jit_inferior_data *inf_data; + struct jit_unwind_private *priv_data; + struct jit_dbg_reader *iter; + struct gdb_unwind_callbacks callbacks; + struct gdb_reader_funcs *funcs; + + inf_data = get_jit_inferior_data (); + + callbacks.reg_get = jit_unwind_reg_get_impl; + callbacks.reg_set = jit_unwind_reg_set_impl; + callbacks.target_read = jit_target_read_impl; + + if (loaded_jit_reader == NULL) + return 0; + + funcs = loaded_jit_reader->functions; + + gdb_assert (!*cache); + + *cache = XZALLOC (struct jit_unwind_private); + priv_data = *cache; + priv_data->registers = + XCALLOC (gdbarch_num_regs (get_frame_arch (this_frame)), + struct gdb_reg_value *); + priv_data->this_frame = this_frame; + + callbacks.priv_data = priv_data; + + /* Try to coax the provided unwinder to unwind the stack */ + if (funcs->unwind (funcs, &callbacks) == GDB_SUCCESS) + { + if (jit_debug) + fprintf_unfiltered (gdb_stdlog, _("Successfully unwound frame using " + "JIT reader.\n")); + return 1; + } + if (jit_debug) + fprintf_unfiltered (gdb_stdlog, _("Could not unwind frame using " + "JIT reader.\n")); + + jit_dealloc_cache (this_frame, *cache); + *cache = NULL; + + return 0; +} + + +/* The frame_id function for the pseudo unwinder. Relays the call to + the loaded plugin. */ + +static void +jit_frame_this_id (struct frame_info *this_frame, void **cache, + struct frame_id *this_id) +{ + struct jit_unwind_private private; + struct gdb_frame_id frame_id; + struct gdb_reader_funcs *funcs; + struct gdb_unwind_callbacks callbacks; + + private.registers = NULL; + private.this_frame = this_frame; + + /* We don't expect the frame_id function to set any registers, so we + set reg_set to NULL. */ + callbacks.reg_get = jit_unwind_reg_get_impl; + callbacks.reg_set = NULL; + callbacks.target_read = jit_target_read_impl; + callbacks.priv_data = &private; + + gdb_assert (loaded_jit_reader); + funcs = loaded_jit_reader->functions; + + frame_id = funcs->get_frame_id (funcs, &callbacks); + *this_id = frame_id_build (frame_id.stack_address, frame_id.code_address); +} + +/* Pseudo unwinder function. Reads the previously fetched value for + the register from the cache. */ + +static struct value * +jit_frame_prev_register (struct frame_info *this_frame, void **cache, int reg) +{ + struct jit_unwind_private *priv = *cache; + struct gdb_reg_value *value; + + if (priv == NULL) + return frame_unwind_got_optimized (this_frame, reg); + + gdb_assert (priv->registers); + value = priv->registers[reg]; + if (value && value->defined) + return frame_unwind_got_bytes (this_frame, reg, value->value); + else + return frame_unwind_got_optimized (this_frame, reg); +} + +/* Relay everything back to the unwinder registered by the JIT debug + info reader.*/ + +static const struct frame_unwind jit_frame_unwind = +{ + NORMAL_FRAME, + default_frame_unwind_stop_reason, + jit_frame_this_id, + jit_frame_prev_register, + NULL, + jit_frame_sniffer, + jit_dealloc_cache +}; + + +/* This is the information that is stored at jit_gdbarch_data for each + architecture. */ + +struct jit_gdbarch_data_type +{ + /* Has the (pseudo) unwinder been prepended? */ + int unwinder_registered; +}; + +/* Check GDBARCH and prepend the pseudo JIT unwinder if needed. */ + +static void +jit_prepend_unwinder (struct gdbarch *gdbarch) +{ + struct jit_gdbarch_data_type *data; + + data = gdbarch_data (gdbarch, jit_gdbarch_data); + if (!data->unwinder_registered) + { + frame_unwind_prepend_unwinder (gdbarch, &jit_frame_unwind); + data->unwinder_registered = 1; + } +} + /* Register any already created translations. */ static void @@ -941,6 +1184,8 @@ jit_inferior_init (struct gdbarch *gdbarch) if (jit_debug) fprintf_unfiltered (gdb_stdlog, "jit_inferior_init\n"); + jit_prepend_unwinder (gdbarch); + inf_data = get_jit_inferior_data (); if (jit_breakpoint_re_set_internal (gdbarch, inf_data) != 0) return; @@ -1102,6 +1347,19 @@ free_objfile_data (struct objfile *objfile, void *data) xfree (data); } +/* Initialize the jit_gdbarch_data slot with an instance of struct + jit_gdbarch_data_type */ + +static void * +jit_gdbarch_data_init (struct obstack *obstack) +{ + struct jit_gdbarch_data_type *data; + + data = obstack_alloc (obstack, sizeof (struct jit_gdbarch_data_type)); + data->unwinder_registered = 0; + return data; +} + /* Provide a prototype to silence -Wmissing-prototypes. */ extern void _initialize_jit (void); @@ -1126,6 +1384,7 @@ _initialize_jit (void) register_objfile_data_with_cleanup (NULL, free_objfile_data); jit_inferior_data = register_inferior_data_with_cleanup (jit_inferior_data_cleanup); + jit_gdbarch_data = gdbarch_data_register_pre_init (jit_gdbarch_data_init); if (is_dl_available()) { add_com ("jit-reader-load", no_class, jit_reader_load_command, _("\ -- 1.7.7 --------------050308020203010507070800 Content-Type: text/x-diff; name="0007-Documentation.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0007-Documentation.patch" Content-length: 6146 >From 1840d0277252988313d85f7e012edf71aed5e0c9 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Tue, 25 Oct 2011 18:50:53 +0530 Subject: [PATCH 7/7] Documentation. Add some basic documentation to gdb.texinfo about the new JIT reader functionality. Most of the actual documentation is still in jit-reader.h. gdb/doc/ChangeLog: * gdb.texinfo (JIT Interface): Add documentation on writing and usind JIT debug info readers. (Custom Debug Info, Using JIT Debug Info Readers, Writing JIT Debug Info Readers): New nodes. --- gdb/doc/gdb.texinfo | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 117 insertions(+), 0 deletions(-) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 25942b3..9deeaf6 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -31709,6 +31709,7 @@ out about additional code. * Declarations:: Relevant C struct declarations * Registering Code:: Steps to register code * Unregistering Code:: Steps to unregister code +* Custom Debug Info:: Emit debug information in a custom format @end menu @node Declarations @@ -31805,6 +31806,122 @@ Set @code{action_flag} to @code{JIT_UNREGISTER} and call 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 Custom Debug Info +@section Custom Debug Info +@cindex custom JIT debug info +@cindex JIT debug info reader + +Generating debug information in platform-native file formats (like ELF +or COFF) may be an overkill for JIT compilers; especially if all the +debug info is used for is displaying a meaningful backtrace. The +issue can be resolved by having the JIT writers decide on a debug info +format and also provide a reader that parses the debug info generated +by the JIT compiler. This section gives a brief overview on writing +such a parser. More specific details can be found in the source file +@file{gdb/jit-reader.in}, which is also installed as a header at +@file{@var{includedir}/gdb/jit-reader.h} for easy inclusion. + +The reader is implemented as a shared object (so this functionality is +not available on platforms which don't allow loading shared objects at +rumtime). Two new @value{GDBN} commands, @code{jit-reader-load} and +@code{jit-reader-unload} are provided, to be used to load and unload +the readers from a preconfigured directory. Once loaded, the shared +object is used the parse the debug information emitted by the JIT +compiler. + +@menu +* Using JIT Debug Info Readers:: How to use supplied readers correctly +* Writing JIT Debug Info Readers:: Creating a debug-info reader +@end menu + +@node Using JIT Debug Info Readers +@subsection Using JIT Debug Info Readers +@kindex jit-reader-load +@kindex jit-reader-unload + +Readers can be loaded and unloaded using the @code{jit-reader-load} +and @code{jit-reader-unload} commands. + +@table @code +@item jit-reader-load @var{reader-name} + +Load the JIT reader named @var{reader-name}. On a UNIX system, this +will usually result load @file{@var{libdir}/gdb/@var{reader-name}}, +where @var{libdir} is the system library directory, usually +@file{/usr/local/lib}. Only one reader can be active at a time; +trying to load a second reader when one is already loaded will result +in @value{GDBN} reporting an error. A new JIT reader can be loaded by +first unloading the current one using @code{jit-reader-load} and +then invoking @code{jit-reader-load}. + +@item jit-reader-unload + +Unload the currently loaded JIT reader. + +@end table + +@node Writing JIT Debug Info Readers +@subsection Writing JIT Debug Info Readers + +As mentioned, a reader is essentially a shared object conforming to a +certain ABI. This ABI is described in @file{jit-reader.h}. + +@file{jit-reader.h} defines the structures, macros and functions +required to write a reader. It is installed (along with +@value{GDBN}), in @file{@var{includedir}/gdb} where @var{includedir} is +the system include directory. + +Readers need to be released under a GPL compatible license. A reader +can be declared as released under such a license by placing the macro +@code{GDB_DECLARE_GPL_COMPATIBLE_READER} in a source file. + +The entry point for readers is the symbol @code{gdb_init_reader}, +which is expected to be a function with the prototype + +@findex gdb_init_reader +@smallexample +extern struct gdb_reader_funcs *gdb_init_reader (void); +@end smallexample + +@cindex @code{struct gdb_reader_funcs} + +@code{struct gdb_reader_funcs} contains a set of pointers to callback +functions. These functions are executed to read the debug info +generated by the JIT compiler (@code{read}), to unwind stack frames +(@code{unwind}) and to create canonical frame IDs +(@code{get_Frame_id}). It also has a callback that is called when the +reader is being unloaded (@code{destroy}). The struct looks like this + +@smallexample +struct gdb_reader_funcs +@{ + /* Must be set to GDB_READER_INTERFACE_VERSION. */ + int reader_version; + + /* For use by the reader. */ + void *priv_data; + + gdb_read_debug_info *read; + gdb_unwind_frame *unwind; + gdb_get_frame_id *get_frame_id; + gdb_destroy_reader *destroy; +@}; +@end smallexample + +@cindex @code{struct gdb_symbol_callbacks} +@cindex @code{struct gdb_unwind_callbacks} + +The callbacks are provided with another set of callbacks by +@value{GDBN} to do their job. For @code{read}, these callbacks are +passed in a @code{struct gdb_symbol_callbacks} and for @code{unwind} +and @code{get_frame_id}, in a @code{struct gdb_unwind_callbacks}. +@code{struct gdb_symbol_callbacks} has callbacks to create new object +files and new symbol tables inside those object files. @code{struct +gdb_unwind_callbacks} has callbacks to read registers off the current +frame and to write out the values of the registers in the previous +frame. Both have a callback (@code{target_read}) to read bytes off the +target's address space. + @node GDB Bugs @chapter Reporting Bugs in @value{GDBN} @cindex bugs in @value{GDBN} -- 1.7.7 --------------050308020203010507070800--