Skip to content

Instantly share code, notes, and snippets.

@Charo-IT
Created May 1, 2018 05:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Charo-IT/7de4606ad238919713435e2a4d6a2717 to your computer and use it in GitHub Desktop.
Save Charo-IT/7de4606ad238919713435e2a4d6a2717 to your computer and use it in GitHub Desktop.
gdb 8.1 with Ruby

This work is related to libgdbruby.

How to build and install

$ sudo apt-get install build-essential python3 libpython3-dev texinfo
$ ruby -e 'puts "Hello world"'  # Ruby must be installed before compilation
$ # download gdb-8.1.tar.gz from GNU
$ # download gdbruby.patch attached to this Gist
$ tar zxf gdb-8.1.tar.gz
$ cd gdb-8.1
$ patch -p1 < ../gdbruby.patch
$ mkdir build
$ cd build
$ ../configure --with-python=python3 --with-separate-debug-dir=/usr/lib/debug:/usr/local/lib/debug
$ make
$ sudo make install

Known limitations

  • Using Readline inside Ruby will break gdb's Readline
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 17b71c6..03dd88f 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -412,6 +412,15 @@ SUBDIR_PYTHON_DEPS =
SUBDIR_PYTHON_LDFLAGS =
SUBDIR_PYTHON_CFLAGS =
+SUBDIR_RUBY_SRCS = \
+ ruby/ruby.c
+
+SUBDIR_RUBY_OBS = $(patsubst %.c,%.o,$(SUBDIR_RUBY_SRCS))
+
+SUBDIR_RUBY_DEPS =
+SUBDIR_RUBY_LDFLAGS =
+SUBDIR_RUBY_CFLAGS =
+
SUBDIR_UNITTESTS_SRCS = \
unittests/array-view-selftests.c \
unittests/common-utils-selftests.c \
@@ -522,7 +531,7 @@ CONFIG_INSTALL = @CONFIG_INSTALL@
CONFIG_UNINSTALL = @CONFIG_UNINSTALL@
HAVE_NATIVE_GCORE_TARGET = @HAVE_NATIVE_GCORE_TARGET@
-CONFIG_SRC_SUBDIR = arch cli mi compile tui unittests guile python target
+CONFIG_SRC_SUBDIR = arch cli mi compile tui unittests guile python target ruby
CONFIG_DEP_SUBDIR = $(addsuffix /$(DEPDIR),$(CONFIG_SRC_SUBDIR))
# -I. for config files.
@@ -552,7 +561,7 @@ CXXFLAGS = @CXXFLAGS@
# are sometimes a little generic, we think that the risk of collision
# with other header files is high. If that happens, we try to mitigate
# a bit the consequences by putting the Python includes last in the list.
-INTERNAL_CPPFLAGS = @CPPFLAGS@ @GUILE_CPPFLAGS@ @PYTHON_CPPFLAGS@
+INTERNAL_CPPFLAGS = @CPPFLAGS@ @GUILE_CPPFLAGS@ @PYTHON_CPPFLAGS@ @RUBY_CPPFLAGS@
# INTERNAL_CFLAGS is the aggregate of all other *CFLAGS macros.
INTERNAL_CFLAGS_BASE = \
@@ -583,7 +592,7 @@ INTERNAL_LDFLAGS = \
# LIBIBERTY appears twice on purpose.
CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(ZLIB) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \
$(XM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) \
- @LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \
+ @LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ @RUBY_LIBS@ \
$(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \
$(LIBIBERTY) $(WIN32LIBS) $(LIBGNU) $(LIBICONV) $(LIBMPFR)
CDEPS = $(XM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) \
@@ -1609,6 +1618,7 @@ generated_files = \
# Flags needed to compile Python code
PYTHON_CFLAGS = @PYTHON_CFLAGS@
+RUBY_CFLAGS =
all: gdb$(EXEEXT) $(CONFIG_ALL)
@$(MAKE) $(FLAGS_TO_PASS) DO=all "DODIRS=`echo $(SUBDIRS) | sed 's/testsuite//'`" subdir_do
@@ -1625,6 +1635,8 @@ $(CONFIG_DEP_SUBDIR):
# Python files need special flags.
python/%.o: INTERNAL_CFLAGS += $(PYTHON_CFLAGS)
+ruby/%.o: INTERNAL_CFLAGS += $(RUBY_CFLAGS)
+
# Rules for compiling .c files in the various source subdirectories.
%.o: ${srcdir}/common/%.c
$(COMPILE) $<
diff --git a/gdb/configure b/gdb/configure
index 092893d..183e5a9 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -681,6 +681,8 @@ HAVE_PYTHON_TRUE
PYTHON_LIBS
PYTHON_CPPFLAGS
PYTHON_CFLAGS
+RUBY_LIBS
+RUBY_CPPFLAGS
python_prog_path
LTLIBMPFR
LIBMPFR
@@ -17591,8 +17593,13 @@ done
ac_config_links="$ac_config_links $ac_config_links_1"
-
-
+# add ruby stuffs
+CONFIG_OBS="$CONFIG_OBS \$(SUBDIR_RUBY_OBS)"
+CONFIG_SRCS="$CONFIG_SRCS \$(SUBDIR_RUBY_SRCS)"
+CONFIG_DEPS="$CONFIG_DEPS \$(SUBDIR_RUBY_DEPS)"
+ENABLE_CFLAGS="$ENABLE_CFLAGS \$(SUBDIR_RUBY_CFLAGS)"
+RUBY_CPPFLAGS=`ruby ${srcdir}/ruby/config.rb --include`
+RUBY_LIBS=`ruby ${srcdir}/ruby/config.rb --libs`
$as_echo "#define GDB_DEFAULT_HOST_CHARSET \"UTF-8\"" >>confdefs.h
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 4844c86..92cfa7d 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -37,6 +37,8 @@
#include "location.h"
#include "ser-event.h"
+extern void initialize_ruby();
+
/* Declared constants and enum for python stack printing. */
static const char python_excp_none[] = "none";
static const char python_excp_full[] = "full";
@@ -1893,6 +1895,8 @@ message == an error message without a stack will be printed."),
if (!do_start_initialization () && PyErr_Occurred ())
gdbpy_print_stack ();
#endif /* HAVE_PYTHON */
+
+ initialize_ruby();
}
#ifdef HAVE_PYTHON
diff --git a/gdb/ruby/config.rb b/gdb/ruby/config.rb
new file mode 100644
index 0000000..a2bfa1a
--- /dev/null
+++ b/gdb/ruby/config.rb
@@ -0,0 +1,13 @@
+#coding:utf-8
+require "mkmf"
+
+if ARGV.length != 1
+ exit 1
+end
+
+case ARGV[0]
+when "--include"
+ print RbConfig.expand("-I $(rubyarchhdrdir) -I $(rubyhdrdir)")
+when "--libs"
+ print RbConfig.expand(libpathflag + " -l$(RUBY_SO_NAME)")
+end
diff --git a/gdb/ruby/ruby.c b/gdb/ruby/ruby.c
new file mode 100644
index 0000000..edf49f1
--- /dev/null
+++ b/gdb/ruby/ruby.c
@@ -0,0 +1,135 @@
+#include <stdio.h>
+#include <build-gnulib/config.h> // to make gcc happy :P
+#include <string.h>
+#include <string>
+#include "defs.h"
+#include "gdbcmd.h"
+#include <ruby.h>
+
+static VALUE gdbruby_binding;
+static VALUE rb_eGdbError;
+
+static VALUE eval_ruby_internal(VALUE command){
+ RUBY_INIT_STACK;
+
+ return rb_funcall(gdbruby_binding, rb_intern("eval"), 1, command);
+}
+
+static VALUE error_handler(VALUE args, VALUE exception_object){
+ unsigned long i, length;
+ VALUE message;
+ VALUE backtrace;
+ VALUE item;
+ RUBY_INIT_STACK;
+
+ if(NIL_P(exception_object)){
+ puts("error");
+ return Qnil;
+ }
+
+ message = rb_funcall(exception_object, rb_intern("to_s"), 0);
+ backtrace = rb_funcall(exception_object, rb_intern("backtrace"), 0);
+
+ if(NIL_P(message)){
+ printf("%1$s: %1$s\n", rb_obj_classname(exception_object));
+ }else{
+ SafeStringValue(message);
+ printf("%s: %s\n", rb_obj_classname(exception_object), RSTRING_PTR(message));
+ }
+
+ if(NIL_P(backtrace)){
+ return Qnil;
+ }
+
+ length = NUM2LONG(rb_funcall(backtrace, rb_intern("length"), 0));
+ for(i = 0; length > 1 && i < length - 1; i++){
+ item = rb_ary_entry(backtrace, i);
+ if(NIL_P(item)){
+ continue;
+ }
+ SafeStringValue(item);
+ printf(" from %s\n", RSTRING_PTR(item));
+ }
+
+ return Qnil;
+}
+
+static void eval_ruby(const char *script){
+ VALUE command;
+ RUBY_INIT_STACK;
+
+ if(script == NULL){
+ puts("no script given");
+ return;
+ }
+
+ command = rb_str_new2(script);
+ rb_rescue((VALUE (*)(ANYARGS))eval_ruby_internal, command, (VALUE (*)(ANYARGS))error_handler, Qnil);
+}
+
+static VALUE gdb_execute(VALUE self, VALUE arg1){
+ char *code;
+ std::string result;
+ char *result_cstr;
+ VALUE rb_result;
+ RUBY_INIT_STACK;
+
+ SafeStringValue(arg1);
+
+ code = (char *)calloc(1, RSTRING_LEN(arg1) + 1);
+ if(code == NULL){
+ rb_raise(rb_eRuntimeError, "error");
+ }
+ memcpy(code, RSTRING_PTR(arg1), RSTRING_LEN(arg1));
+
+ TRY{
+ result = execute_command_to_string(code, 0);
+ }CATCH(except, RETURN_MASK_ALL){
+ free(code);
+
+ if(except.reason == RETURN_QUIT){
+ rb_raise(rb_eInterrupt, "Interrupt");
+ }else{
+ rb_raise(rb_eGdbError, "%s", except.message);
+ }
+ }END_CATCH
+
+ free(code);
+
+ result_cstr = strdup(result.c_str());
+ if(result_cstr == NULL){
+ rb_raise(rb_eRuntimeError, "error");
+ }
+
+ rb_result = rb_str_new2(result_cstr);
+ free(result_cstr);
+
+ return rb_result;
+}
+
+void initialize_ruby(){
+ int a = 3;
+ const char *args[] = {"ruby", "-e", "", NULL};
+ char **arg = (char **)args;
+ int state;
+
+ /* Initialize Ruby VM */
+ ruby_sysinit(&a, &arg);
+ {
+ RUBY_INIT_STACK;
+
+ ruby_init();
+ ruby_options(a, arg);
+ ruby_script("gdbruby");
+
+ rb_define_global_function("gdb_execute", (VALUE (*)(ANYARGS))gdb_execute, 1);
+
+ gdbruby_binding = rb_funcall(Qnil, rb_intern("binding"), 0);
+ rb_define_readonly_variable("gdbruby_binding", &gdbruby_binding);
+
+ rb_eGdbError = rb_define_class("GdbError", rb_eStandardError);
+ }
+
+ /* Add ruby command to gdb */
+ add_com("ruby", no_class, (void (*)(char const *, int))eval_ruby, "Evaluate a Ruby command.");
+}
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment