Embedding problem - SEGV

I have a problem with embedding Ruby, which hopefully someone can shed some
light on…

Basically, I don’t want to just load some code and then ruby_run() it. I
want to choose at run-time some modules to load with rb_require, then create
instances of objects, and send messages to those objects. There is no ‘main
ruby program’ as such.

It seems to work, but the problem is that untrapped exceptions cause a SEGV
and a core dump. Another problem is that $: is set to an empty array. It
looks like I’m missing some initialisation function or functions.

I have grepped the source and found “ruby_init_loadpath” to set $:, but is
there something else I need to call to fix the SEGV problem?

Thanks,

Brian.

$ cat embed.c
#include <stdio.h>
#include <ruby.h>

int main(void)
{
printf(“Starting\n”);
ruby_init();
ruby_script(“embed”);
rb_require("/nonexistent/file");

#if RUBY_VERSION_CODE >= 180
rb_cleanup(0);
#endif
printf(“Done\n”);
return 0;
}
$ gcc -Wall -g -o embed embed.c -lruby -lcrypt -lm -I /usr/local/lib/ruby/1.6/i386-freebsd4.7 -L /usr/local/lib/ruby/1.6/i386-freebsd4.7
$ ./embed
Starting
embed: No such file to load – /nonexistent/file (LoadError)
embed: [BUG] Segmentation fault
ruby 1.6.8 (2002-12-24) [i386-freebsd4.7]
Abort trap (core dumped)

$ gcc -Wall -g -o embed embed.c -lruby-static -lcrypt -lm -I /usr/local/lib/ruby/1.8/i386-freebsd4.7 -L /usr/local/lib
$ ./embed
Starting
embed: No such file to load – /nonexistent/file (LoadError)
embed: [BUG] Segmentation fault
ruby 1.8.0 (2003-06-23) [i386-freebsd4.7]

Abort trap (core dumped)

*** NOTE ***
Changing the rb_require to
rb_eval_string(“require ‘/nonexistent/file’”);
still generates the SEGV and core dump

···

I have grepped the source and found "ruby_init_loadpath" to set $:, but is
there something else I need to call to fix the SEGV problem?

Use rb_protect() to run your code.

Something like

    ruby_init();
    ruby_init_loadpath();
    result = rb_protect(some_functions, some_args, &status);

some_functions() will call rb_require(), ...

Changing the rb_require to
   rb_eval_string("require '/nonexistent/file'");

      rb_eval_string_protect("require '/nonexistent/file'", &status);

still generates the SEGV and core dump
************

Guy Decoux

Spoke too soon - requiring a .so also causes it to segfault.

$ cat embed.c
#include <stdio.h>
#include <ruby.h>

int main(void)
{
printf(“Starting\n”);
ruby_init();
ruby_init_loadpath();
ruby_script(“embed”);
rb_require(“socket”); /* <<< segfaults here */

#if RUBY_VERSION_CODE >= 180
rb_cleanup(0);
#endif
printf(“Done\n”);
return 0;
}

The backtrace is not helpful though:

(gdb) bt
#0 0x28150b78 in kill () from /usr/lib/libc.so.4
#1 0x28191742 in abort () from /usr/lib/libc.so.4
#2 0x80afb92 in rb_bug (fmt=0x80c54ce “Segmentation fault”) at error.c:179
#3 0x8097667 in sigsegv (sig=11) at signal.c:393
#4 0xbfbfffac in ?? ()
#5 0x8055ab0 in rb_require (fname=0x80b0f91 “socket”) at eval.c:5538
#6 0x804a809 in main () at embed.c:10
#7 0x804a73d in _start ()

So it’s definitely looks like some missing initialisation.
rb_load_file(“/dev/null”) doesn’t help, and I can’t use ruby_run() because
that exits the C program.

Thanks for any suggestions…

Brian.

···

On Wed, Jul 23, 2003 at 08:49:38PM +0900, Brian Candler wrote:

It seems to work, but the problem is that untrapped exceptions cause a SEGV
and a core dump.

Spoke too soon - requiring a .so also causes it to segfault.

How do you link your .out ?

For example on linux, you need '-ldl -lcrypt -lm'

-ldl for dynamic libraries.

Look how ruby is build on your system

Guy Dcoux

That’s pretty icky. I’m happy for Ruby to generate an exception report and
exit; I just don’t want it to dump core.

Here’s the sort of thing I want to do when processing a config file:

    static VALUE obj;
    static void process_line(char *cmd, char *arg)
    {
            if (!strcmp(cmd, "loadLibrary"))
                    rb_require(arg);
            else if (!strcmp(cmd, "loadObject")) {
                    VALUE klass = rb_const_get(rb_cObject, rb_intern(arg));
                    obj = rb_funcall(klass, rb_intern("new"), 0);
            }
            /* ... etc */
    }

Do I really have to write each section as a separate C function, and call it
under rb_protect?

Regards,

Brian.

···

On Wed, Jul 23, 2003 at 08:57:55PM +0900, ts wrote:

I have grepped the source and found “ruby_init_loadpath” to set $:, but is
there something else I need to call to fix the SEGV problem?

Use rb_protect() to run your code.

Something like

ruby_init();
ruby_init_loadpath();
result = rb_protect(some_functions, some_args, &status);

some_functions() will call rb_require(), …

I gave the compile/link command in the previous post, and adding -ldl makes
it fail:

$ gcc -Wall -g -o embed embed.c -lruby -lcrypt -lm -ldl -I /usr/local/lib/ruby/1.6/i386-freebsd4.7 -L /usr/local/lib/ruby/1.6/i386-freebsd4.7
/usr/libexec/elf/ld: cannot find -ldl
$

In FreeBSD, dlopen() and friends are in libc.

Regards,

Brian.

···

On Wed, Jul 23, 2003 at 09:04:50PM +0900, ts wrote:

Spoke too soon - requiring a .so also causes it to segfault.

How do you link your .out ?

For example on linux, you need ‘-ldl -lcrypt -lm’

Do I really have to write each section as a separate C function, and call it
under rb_protect?

Something like this

struct tt {
   char *cmd, *arg;
};

static VALUE
process_line(struct tt *tts)
{
    VALUE obj = Qnil;

    if (!strcmp(tt->cmd, "loadLibrary"))
        rb_require(tt->arg);
    else if (!strcmp(tt->cmd, "loadObject")) {
         VALUE klass = rb_const_get(rb_cObject, rb_intern(tt->arg));
         obj = rb_funcall(klass, rb_intern("new"), 0);
   }
   /* ... etc */
   return obj;
}

int main(int argc, char **argv)
{
    struct tt tts;
    int status;
    VALUE result;

    ruby_init();
    ruby_init_loadpath();
    tt.cmd = argv[1];
    tt.arg = argv[2];
    result = rb_protect(process_line, &tts, &status);
    if (status) {
        /* display an error message */
    }
    else {
        /* do what you want with result */
    }
    return 0;
}

The idea is to initialize ruby, then make all call to ruby functions in
one (or more) function protected by rb_protect()

Guy Decoux

I gave the compile/link command in the previous post, and adding -ldl makes
it fail:

svg% ruby -rrbconfig -e 'p Config::CONFIG["LIBS"], Config::CONFIG["LDFLAGS"]'
"-ldl -lcrypt -lm "
"-rdynamic"
svg%

See Config::CONFIG["LDFLAGS"]

Guy Decoux

… etc

OK, thanks (it doesn’t have to be a VALUE argument then)

If I still want a regular Ruby exception report printed, after catching it,
is there a function I can call?

Cheers,

Brian.

···

On Wed, Jul 23, 2003 at 10:33:59PM +0900, ts wrote:

Something like this

struct tt {
char *cmd, *arg;
};

Thanks: adding -rdynamic lets me load external .so’s

Cheers,

Brian.

···

On Wed, Jul 23, 2003 at 10:10:12PM +0900, ts wrote:

I gave the compile/link command in the previous post, and adding -ldl makes
it fail:

svg% ruby -rrbconfig -e ‘p Config::CONFIG[“LIBS”], Config::CONFIG[“LDFLAGS”]’
"-ldl -lcrypt -lm "
“-rdynamic”
svg%

See Config::CONFIG[“LDFLAGS”]

[snip]

What if you static link with ruby like this:

ruby 1.8.0

RUBY=$(HOME)/install/ruby-1.8.0
LIBS=$(RUBY)/libruby-static.a -lcrypt

all:
g++ main.cpp -I$(RUBY) $(LIBS)

Does static linking work for you ?

···

On Wed, 23 Jul 2003 23:10:12 +0900, ts wrote:

I gave the compile/link command in the previous post, and adding -ldl makes
it fail:


Simon Strandgaard

If I still want a regular Ruby exception report printed, after catching it,
is there a function I can call?

rb_gv_get("$!") to retrieve $!, then rb_inspect() or what you want
rb_backtrace() to have a backtrace on stdout

Guy Decoux

(Partially answering own question) error_print is private, but ruby_stop()
does generate the error report.

I still think there must be a better solution. I am actually writing
back-ruby for openldap, and it’s full of functions like this:

int
ruby_back_db_open(
BackendDB *be
)
{
RubyBackend *ruby_back = (RubyBackend *) be->be_private;

    ldap_pvt_thread_mutex_lock( &ruby_interpreter_mutex );

    ruby_back->rb_obj_ref = rb_funcall(ruby_back->rb_class,
            rb_intern("new"), 0);

    ldap_pvt_thread_mutex_unlock( &ruby_interpreter_mutex );

    return 0;

}

It looks like I’m going to have to split all these functions into two: one
to do the Ruby work, and one to call it using rb_protect (and pass a struct*
between the two if there’s more than one argument), purely to prevent the
coredumps. This is pretty nasty. Before now, it was looking that back-ruby
was going to be much cleaner than back-perl :frowning:

Cheers,

Brian.

···

On Wed, Jul 23, 2003 at 10:58:55PM +0900, Brian Candler wrote:

If I still want a regular Ruby exception report printed, after catching it,
is there a function I can call?

If you look at my previous post, you’ll see that’s what I did.

Regards,

Brian.

···

On Wed, Jul 23, 2003 at 10:45:01PM +0900, Simon Strandgaard wrote:

On Wed, 23 Jul 2003 23:10:12 +0900, ts wrote:

I gave the compile/link command in the previous post, and adding -ldl makes
it fail:

[snip]

What if you static link with ruby like this:

ruby 1.8.0

RUBY=$(HOME)/install/ruby-1.8.0
LIBS=$(RUBY)/libruby-static.a -lcrypt

all:
g++ main.cpp -I$(RUBY) $(LIBS)

If I still want a regular Ruby exception report printed, after catching it,
is there a function I can call?

I don’t know if this can be useful to you ?
http://metaeditor.sourceforge.net/embed/#id2841406

[snip]

It looks like I’m going to have to split all these functions into two: one
to do the Ruby work, and one to call it using rb_protect (and pass a struct*
between the two if there’s more than one argument), purely to prevent the
coredumps. This is pretty nasty. Before now, it was looking that back-ruby
was going to be much cleaner than back-perl :frowning:

You could do like this C++ code:

/* ------------------------------
wrap rb_funcall
------------------------------ */
struct Arguments {
VALUE recv;
ID id;
int n;
VALUE *argv;
Arguments(VALUE recv, ID id, int n, VALUE *argv) :
recv(recv), id(id), n(n), argv(argv) {
}
};

VALUE FuncallWrap(VALUE arg) {
Arguments &a = reinterpret_cast<Arguments>(arg);
return rb_funcall2(a.recv, a.id, a.n, a.argv);
}

/*
purpose:
call a ruby function in a safe way.
translate ruby errors into c++ exceptions.

VALUE Unsafe() {
return rb_funcall(
self,
rb_intern(“test”),
1,
INT2NUM(42)
);
}

VALUE Safe() {
return RUBY_CPP::Funcall(
self,
rb_intern(“test”),
1,
INT2NUM(42)
);
}
*/
VALUE Funcall(VALUE recv, ID id, int n, …) {
VALUE *argv = 0;

if (n > 0) {
    argv = ALLOCA_N(VALUE, n);
    va_list ar;
    va_start(ar, n);
    int i;
    for(i=0;i<n;i++) {
        argv[i] = va_arg(ar, VALUE);
    }
    va_end(ar);
}

Arguments arg(recv, id, n, argv);
int error = 0;
VALUE result = rb_protect(FuncallWrap, reinterpret_cast<VALUE>(&arg), &error);
if(error)
    throw RubyError::Create("cannot invoke ruby-function");
return result;

}

···

On Thu, 24 Jul 2003 00:49:04 +0900, Brian Candler wrote:

On Wed, Jul 23, 2003 at 10:58:55PM +0900, Brian Candler wrote:


Simon Strandgaard

Thanks for that. I’m writing C not C++, but I guess writing
“rb_funcall_protect” will cover 90% of what I need to do.

Cheers,

Brian.

···

On Thu, Jul 24, 2003 at 12:05:52AM +0900, Simon Strandgaard wrote:

You could do like this C++ code: