Get functions names in a shared library programmatically
There is no libc function to do that. However, you can write one yourself (or copy/paste the code from a tool like readelf).
On Linux, dlopen()
returns the address of a link_map
structure, which has a member named l_addr
that points to the base address of the loaded shared object (assuming your system doesn't randomize shared library placement, and that your library has not been prelinked).
On Linux, a way to find the base address (the address of Elf*_Ehdr
) is to use dl_iterate_phdr()
after dlopen()
ing the library.
Having the ELF header, you should be able to iterate over a list of exported symbols (the dynamic symbol table), by first locating the Elf*_Phdr
of type PT_DYNAMIC
, and then locating DT_SYMTAB
, DT_STRTAB
entries, and iterating over all symbols in the dynamic symbol table. Use /usr/include/elf.h
to guide you.
Additionally, you could use libelf, that I don't know very well personally.
However, note that you'll get a list of defined functions, but you'll have no idea how to call them.
How can a shared library (.so) call a function that is implemented in its loader code?
You'll need make a register function in your .so so that the executable can give a function pointer to your .so for it's later used.
Like this:
void in_main_func () {
// this is the function that need to be called from a .so
}
void (*register_function)(void(*)());
void *handle = dlopen("libmylib.so");
register_function = dlsym(handle, "register_function");
register_function(in_main_func);
the register_function needs to store the function pointer in a variable in the .so where the other function in the .so can find it.
Your mylib.c would the need to look something like this:
void (*callback)() = NULL;
void register_function( void (*in_main_func)())
{
callback = in_main_func;
}
void function_needing_callback()
{
callback();
}
function in one shared library calling a function in another shared library
If you have called only print1
in your main.c
. Then set the path of the libfile2.so
in the LD_LIBRARY_PATH
. Because it will try to find the dependencies of libfile1.so
while linking with main.c
.
gcc -o file1.o -c file.c
gcc -o file2.o -c file.c
gcc -o libfile2.so file2.o -shared
gcc -o libfile1.so file1.o -L. -lfile2 -shared
gcc -o main.o -c main.c
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
gcc -o main.exe main.o -L. -lfile1
If you have called both print1
and print2
in main.c
then link both libfile1.so
and libfile2.so
like below.
gcc -o main.o -c main.c
gcc -o main.exe main.o -L$YOUR_LIB_PATH -lfile1 -lfile2
Because all the symbol used in main.c needs to be resolved while generating executable.
Shared Library Discovery
AFAIK, there is no glibc function to enumerate all the public interface functions for a .so file. You can refer to libelf to read all symbols from a dynamic file. Libelf is here http://www.mr511.de/software/. After you find a symbol, you can use dlopen and dlsym to load it.
Programatically determine shared libraries in use by running application
Since it looks like you're using something UNIX-y, just use dlopen
instead of dynamically linking your driver app against the missing symbol.
Full sequence is:
- iterate over all submitted .so library filenames somehow (maybe you have one directory with studentname.so or something)
- load each library
- get the entry point function
- call it
- unload library (optional, I guess)
like so:
void *lib = dlopen(filename, RTLD_LOCAL);
void *libfun = dlsym(lib, "doAction");
if (libfun == NULL)
cout << "student failed by not providing doAction() in " << filename << endl;
else {
void (*doAction)(void) = (void (*)(void)) libfun;
// no, I can't remember the correct syntax for casting to function pointer
cout << "calling " << filename << ":doAction()" << endl;
doAction();
// is there some way to tell if it succeeded?
cout << "unloading " << filename << endl;
dlclose(lib);
}
Notes:
- if the interface is the same in each case (ie,
void (*)()
), you could make this configurable by directory name and symbol name, and it'd work for more than one test - in fact, if the interface is NOT what you expect, the function pointer cast will do horrible things, so careful with this
- finally, if the student used C++, their function name symbol will be mangled. Tell them to declare the entry-point as
extern "C" void doAction()
to avoid that. - the
RTLD_LOCAL
flag should stop anything in one student's library interfering with another (if you don't unload), but there are other flags it may be sensible to add- specifically,
RTLD_NOW
will causedlopen
to fail if the student lib has an unresolved external reference it can't figure out (so you can handle it gracefully, by failing them): otherwise your program may just crash when you calldoAction
.
- specifically,
Although I think the above is better than the solution you're directly asking for help with, I did also find a reference to dl_iterate_phdr
while double-checking the docs. If you're on Linux specifically, and if the dl_phdr_info.dlpi_name
is actually the filename ... you might be able to get it that way.
I still think it's much uglier, though.
Related Topics
"If" Block Without Curly Braces Makes Subsequent "Else If" Nested
Communication Between Native-App and Chrome-Extension
Is Static Object Guaranteed to Be Initialized
How to Output the Actual Array in C++
Access Child Members Within Parent Class, C++
Mbcs Error Building Mfc C++ Project with Visual Studio
How to Implement an Easy_Bind() That Automagically Inserts Implied Placeholders
Why Are Std::Shuffle Methods Being Deprecated in C++14
Compiling Multithread Code with G++ (-Wl,-No-As-Needed Not Working)
Does Class/Function Order Matter in C++
Cmake: How to Change Properties on Subdirectory Project Targets
How to Subtract Two Gettimeofday Instances
Access Violation Writing Location 0Xcccccccc