I was trying call a function dynamically in an executable file itself.
The target function test_func
was coded with the main
function in the same source file. Here is the sample code:
#include <stdio.h>
#include <dlfcn.h>
// or with some tricks
// void __attribute__((visibility("default")))
void test_func(void)
{
printf("test_func in %s \n",__FILE__);
}
int main()
{
void *handle = dlopen(0, RTLD_LAZY); // or RTLD_GLOBAL
printf("dlopen return %p\n", handle);
void (*func)() = dlsym(handle, "test_func");
printf("dlsym return %p\n", func);
if (func) (*func)();
return 0;
}
Then I compiled it with gcc
and it failed with output like this:
$ ./a.out
dlopen return 0xe29dfbec1c7bc0d
dlsym return 0x0
I can read the symbol in the ELF. It hits my head that it must be some way to get the test_func
function and call it.
$ readelf -s a.out | grep test_func
40: 00000000000017c8 36 FUNC GLOBAL DEFAULT 13 test_func
Is there something I am missing?
Are you using windows? Then you should consider using DLLs for dynamic function loading.
Generally when you call dlopen(0, RTLD_LAZY)
, it attempts to open the executable's own symbol table; it may not work quite as expected for retrieving symbols. This is because the dynamic linker might not expose all of the symbols of the executable in the same way that it does for shared libraries.
Although you are able to see test_func
in the readelf
output, it still may not be accessible via dlsym
if it is not properly exposed or if the dynamic symbol table is not properly populated.
So, since the purpose of dlopen
is mainly about shared libraries. A common solution would be to switch to a shared library, but if you really have to use an executable:
You must make sure to compile with the -rdynamic
flag so symbols are included in the dynamic symbol table.
gcc -rdynamic -o a.out <filename.c>
This is important to make sure that symbols are visible via dlsym
.
Now, if the position -rdynamic
were to be passed, then dlopen(NULL, RTLD_LAZY)
really ought to work. Assuming that it doesn't, then it's probably a bug, or a misfeature in your linker or dynamic loader and how it handles symbols.
So in-sort we can say:
-rdynamic
when compiling to expose symbols.dlopen
and dlsym
as intended.Here is the full code where i have used the -rdynamic
and also checked if the symbol is visible or not:
#include <stdio.h>
#include <dlfcn.h>
// Define the function to be called
void __attribute__((visibility("default"))) test_func(void)
{
printf("test_func in %s \n", __FILE__);
}
int main()
{
// Open the executable's symbol table
void *handle = dlopen(NULL, RTLD_LAZY);
if (!handle) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
return 1;
}
printf("dlopen return %p\n", handle);
// Retrieve the address of test_func
void (*func)() = dlsym(handle, "test_func");
if (!func) {
fprintf(stderr, "dlsym failed: %s\n", dlerror());
dlclose(handle);
return 1;
}
printf("dlsym return %p\n", func);
// Call the function if found
(*func)();
// Close the handle
dlclose(handle);
return 0;
}