Reputation: 61
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
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
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