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..
gdb
. – Topdrawergdb
, see How to print every executed line in GDB automatically until a given breakpoint is reached? and the accepted answer therein. – Domeniga