Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I trace through an XS .so file?

I have a small Perl program. The program loads a module. The module loads an .so file with XSLoader. This Perl runs on Linux and is built with gcc and -DDEBUGGING, and subsequently so is the .so file. I can recompile.

When the Perl program is executed, how do I trace through the C functions in the .so file? I need to know the names of the functions in the order they run. It would be nice to have the function arguments, too.

like image 612
daxim Avatar asked Jun 01 '18 12:06

daxim


1 Answers

Here is an example of using gdb to step into a shared library. I am using Linux (Ubuntu 18.04).

I first installed a debugging version of Perl using perlbrew:

perlbrew install perl-5.26.2 --as=5.26.2d -DDEBUGGING
perlbrew use 5.26.2d

Then, I created a shared library (stripped down to a very minimal content for testing purposes) in folder /home/hakon/mylib:

mylib.c

This function is adapted from example 3 in perlxstut:

#include <math.h>
#include "myclib.h"
double my_clib_function( double arg ) {
    if (arg > 0.0) {
        arg = floor(arg + 0.5);
    } else if (arg < 0.0) {
        arg = ceil(arg - 0.5);
    } else {
        arg = 0.0;
    }
    return arg;
}

myclib.h:

double my_clib_function( double arg );

Then I created the shared library libmylib.so:

gcc -g -c -fpic mylib.c
gcc -g -shared -o libmylib.so mylib.o

Note that we have included debugging symbols in libmylib.so by giving the -g switch to gcc.

Now, we can create an .xs file that will call the shared library function (in folder /home/hakon/myxstest):

Mytest.xs

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "myclib.h"

MODULE = Mytest     PACKAGE = Mytest        

void
wrapper(arg)
        double  arg
    CODE:
        arg = my_clib_function( arg);
    OUTPUT:
        arg

Then we need to link the XS file to a Perl package name:

lib/Mytest.pm:

package Mytest;
use 5.022001;
use strict;
use warnings;

require Exporter;
our @ISA = qw(Exporter);
our %EXPORT_TAGS = ( 'all' => [ qw() ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();
our $VERSION = '0.01';

require XSLoader;
XSLoader::load('Mytest', $VERSION);

Next, we need a build system like ExtUtils::MakeMaker to compile the XS file (into another shared library):

Makefile.PL:

use 5.022001;
use ExtUtils::MakeMaker;
my $lib_dir = '/home/hakon/mylib';
WriteMakefile(
    NAME              => 'Mytest',
    VERSION_FROM      => 'lib/Mytest.pm', 
    PREREQ_PM         => {},
    ABSTRACT_FROM     => 'lib/Mytest.pm',
    AUTHOR            => 'Håkon Hægland <[email protected]>',
    LIBS              => ["-L$lib_dir -lmylib"],
    INC               => "-I. -I$lib_dir",
    OPTIMIZE          => '-g',
);

Note that we request debugging symbols (for the Mytest.so shared object) with OPTIMIZE => '-g' and we inform about the location of the other shared library libmylib.so by using the LIBS argument to WriteMakefile().

We then compile the XS code:

perl Makefile.PL
make

Finally, we write a small test Perl script:

p.pl

#! /usr/bin/env perl
use feature qw(say);
use strict;
use warnings;
use ExtUtils::testlib;
use Mytest;

my $res = 3.5;
Mytest::wrapper( $res ); # <-- Warning: modifies $res in place !
say $res;

We can know run gdb on our test script p.pl:

$ gdb -q --args perl p.pl
Reading symbols from perl...done.

We set a breakpoint at line 14 in the XS file:

(gdb) break Mytest.xs:14
No source file named Mytest.xs.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (Mytest.xs:14) pending.

Then run the script:

(gdb) run
Starting program: /home/hakon/perlbrew/perls/5.26.2d/bin/perl p.pl
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, XS_Mytest_wrapper (cv=0x555555bdc860) at Mytest.xs:14
14          arg = my_clib_function( arg);

Now we have stopped at the point in the XS file where we will call the shared library function. If we want, we can inspect the arguments that will be passed:

(gdb) p arg
$1 = 3.5

Then step into the shared library:

(gdb) s
my_clib_function (arg=3.5) at mylib.c:5
5       if (arg > 0.0) {

and so on..

like image 163
Håkon Hægland Avatar answered Oct 12 '22 23:10

Håkon Hægland