2

I wrote a program to dlopen itself

void hello()
{
printf("hello world\n");
}
int main(int argc, char **argv)
{
char *buf="hello";
void *hndl = dlopen(argv[0], RTLD_LAZY);
void (*fptr)(void) = dlsym(hndl, buf);
if (fptr != NULL)
fptr();
dlclose(hndl);
}

but I get "segemention fault" error I tested this program with a .so library and it works but can not get it working with itself

alireza_fn
  • 804
  • 1
  • 7
  • 19
  • 1
    possible duplicate of [Using dlopen() on an executable](http://stackoverflow.com/questions/6617679/using-dlopen-on-an-executable) – paulsm4 Oct 31 '13 at 18:02
  • *but can not get it working with itself...*; just curious, what do you expect to happen? – artless noise Oct 31 '13 at 21:02
  • @artless noise : hello world! – alireza_fn Nov 01 '13 at 07:52
  • Ok, I see. However, the process of loading with `dlopen` is going to try to populate the address space with your *executable*, but this *executable* is already loaded. The *concept* works with `dlsym()`, but you can not use the `dlopen()`. The *executable* is the root of a process space. This is why we have `execv`, `fork`, etc. – artless noise Nov 01 '13 at 14:13

2 Answers2

11

You need to code:

  // file ds.c
  #include <stdio.h>
  #include <stdlib.h>
  #include <dlfcn.h>

  void hello ()
  {
    printf ("hello world\n");
  }

  int main (int argc, char **argv)
  {
    char *buf = "hello";
    void *hndl = dlopen (NULL, RTLD_LAZY);
    if (!hndl) { fprintf(stderr, "dlopen failed: %s\n", dlerror()); 
      exit (EXIT_FAILURE); };
    void (*fptr) (void) = dlsym (hndl, buf);
    if (fptr != NULL)
      fptr ();
    else
      fprintf(stderr, "dlsym %s failed: %s\n", buf, dlerror());
    dlclose (hndl);
  }    

Read carefully dlopen(3), always check the success of the dlopen & dlsym functions there, and use dlerror on failure.

and compile the above ds.c file with

  gcc -std=c99 -Wall -rdynamic ds.c -o ds -ldl

Don't forget the -Wall to get all warnings and the -rdynamic flag (to be able to dlsym your own symbols which should go into the dynamic table).

On my Debian/Sid/x86-64 system (with gcc version 4.8.2, and libc6 version 2.17-93 providing the -ldl, kernel 3.11.6 compiled by me, binutils package 2.23.90 providing ld), the execution of ./ds gives the expected output:

  % ./ds 
  hello world

and even:

  % ltrace ./ds
  __libc_start_main(0x4009b3, 1, 0x7fff1d0088b8, 0x400a50, 0x400ae0 <unfinished ...>
  dlopen(NULL, 1)                                = 0x7f1e06c9e1e8
  dlsym(0x7f1e06c9e1e8, "hello")                 = 0x004009a0
  puts("hello world"hello world
  )                            = 12
  dlclose(0x7f1e06c9e1e8)                        = 0
  +++ exited (status 0) +++
Basile Starynkevitch
  • 1
  • 16
  • 251
  • 479
3

In addition to Basile Starynkevitchs excellent answer, I want to point out that this doesn't work on C++ compilers as is.

First of all, you get a warning, because the definition of buf as char* is deprecated. You can make it const char* instead.

Second, C++ doesn't allow you to assign the result of dlsym to fptr like that, because it refuses to convert the type implicitly. You must explicitly cast the type like

void (*fptr) (void) = (void (*)())dlsym (hndl, buf);

or

void (*fptr) (void) = reinterpret_cast<void (*)()>(dlsym (hndl, buf));

And third, the different naming scheme of C++ gives a different name to the function "hello", so dlsym will not find it. You need to declare it as

extern "C" void hello () {...}

Compile as Basile Starynkevitch said, but with C++ instead of C:

% g++ -Wall -rdynamic ds.cpp -o ds -ldl
% ./ds
hello world
Algoman
  • 1,390
  • 11
  • 15