xieGieng
xieGieng

Reputation: 61

Calling function from dynamic library?

I'm experimenting with using dynamic libraries and C on Linux. The following code will print wrong ouput:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char **arg)
{
        void *dl = dlopen("./lib.so", RTLD_NOW);
        if (!dl) {
                fprintf(stderr, "ERROR: %s\n", dlerror());
                exit(1);
        }

        char *ver = dlsym(dl, "show_version");
        printf("%s\n", ver);
}

If I make the following change the output will be correct:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char **arg)
{
        void *dl = dlopen("./lib.so", RTLD_NOW);
        if (!dl) {
                fprintf(stderr, "ERROR: %s\n", dlerror());
                exit(1);
        }

        char *(*ver)() = dlsym(dl, "show_version");
        printf("%s\n", ver());
}

I'm not sure what char *(*ver)() is doing and why it's needed? Can anyone explain?

Upvotes: 0

Views: 3062

Answers (2)

Ted Lyngmo
Ted Lyngmo

Reputation: 117298

dlsym - obtain address of a symbol in a shared object or executable

This means that when you do dlsym(dl, "show_version"); you are not actually calling the function show_version in your shared library. You obtain the address of that function - which can be used to call the function over and over again.

To "decode" what char *(*ver)() means, you can use what is often called the Clockwise/Spiral Rule

        +-----+
        |     V
char*  (*ver) ()   ver is a 
 ^      ^ |   |    pointer to
 |      | |   |    a function (taking no arguments)
 |      +-+   |    returning char*
 |            |
 +------------+

I assume the above matches the signature of the show_version function that you put in the shared library. Example:

// a function (taking no arguments), returning a char*
char *show_version(void) {
    static char version[] = "1.0";
    return version;
}

Using the same rule on your first attempt, char* ver:

char* ver
  ^    |     ver is a
  |    |     char*
  +----+

You need a pointer to a function (with the correct signature) to be able to call the function and get the result you want. You can't call a char* and when you do printf("%s\n", ver); it'll just start reading the memory at the address (where your function is stored) until it finds a null terminator. You probably see just gibberish.

If you on the other hand have a proper function pointer, you can as you've noticed, call the function it points at with ver() and you get a char* in return which points at the string your dynamically loaded function returned.

You can also use function pointers in your programs without involving shared libraries.

#include <stdio.h>

long foo(short x, int y) {
    return x + y;
}

int main() {
    long(*foo_ptr)(short, int) = foo;

    // foo_ptr is a pointer to a function taking (short, int) as
    // arguments and returning a long

    printf("%ld\n", foo(1, 2) );       // prints 3
    printf("%ld\n", foo_ptr(1, 2) );   // also prints 3
}

Upvotes: 1

Eric Postpischil
Eric Postpischil

Reputation: 222679

dlsym(dl, "show_version") returns the address for the symbol show_version. Since show_version is a function, that is the address of the function.

char *ver = dlsym(…); puts that pointer in a char *, which is basically useless. The pointer to a function does not point to bytes that are useful to print. Then printf("%s\n", ver); says to print the bytes that ver points to as if they were a string. But the bytes there are (in a typical C implementation) machine code for the function. They are not the bytes of a character string you want to print.

char *(*ver)() = dlsym(…); defines ver to be a pointer to a function whose arguments are not specified and that returns a char *. To see this:

  • char something declares something to be a char.
  • char *something declares something to be a pointer to a char.
  • char *something() declares something to be a function whose arguments are not specified that returns a pointer to char.
  • char *(*something)() declares something to a pointer to such a function.

Then, in printf("%s\n", ver());, ver() calls this function. The char * it returns is passed to printf to be printed.

Upvotes: 2

Related Questions