Why is the value of the character returned by dlsym () null?

On Linux On the dlsym (3) Linux man page,

*Since the value of the symbol could actually be NULL (so that a NULL return from dlsym() need not indicate an error),* 

Why is it when a character (for a function, specifically) is actually NULL? I look through the code and found a snippet, using dlerror to clear first, dlsym next and dlerror to check for errors. But it does not check the received function from zero to its call:

  • dlerror ();
  • a_func_name = ... dlsym (...);
  • if (dlerror ()) goto end;
  • a_func_name (...); // Never checked if a_func_name == NULL;

I'm just a reviewer, so I donโ€™t have the option to just add a check. And perhaps the author knows that NULL can never be returned. My task is to dispute this, but I donโ€™t know what can cause it to return a valid NULL, so I can check if such a condition can be fulfilled in this code context. If you didnโ€™t find the right thing to read on Google, a pointer to good documentation would be enough if you donโ€™t want to explicitly explain what would be great.

+5
source share
4 answers

Well, if it returned without errors, then the pointer is valid and NULL about as illegal as any random pointer from a shared object. Like a wrong function, data or something else.

+1
source

I know of one specific case where the character value returned by dlsym () can be NULL, which is used when using the GNU indirect functions (IFUNC). However, there are apparently other cases, since the text in the dlsym (3) manual page precedes the invention of IFUNC.

Here is an example of using IFUNC. First, the file that will be used to create the shared library:

 $ cat foo.c /* foo.c */ #include <stdio.h> /* This is a 'GNU indirect function' (IFUNC) that will be called by dlsym() to resolve the symbol "foo" to an address. Typically, such a function would return the address of an actual function, but it can also just return NULL. For some background on IFUNCs, see https://willnewton.name/uncategorized/using-gnu-indirect-functions/ */ asm (".type foo, @gnu_indirect_function"); void * foo(void) { fprintf(stderr, "foo called\n"); return NULL; } 

Now the main program that will look for the foo character in the shared library:

 $ cat main.c /* main.c */ #include <dlfcn.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { void *handle; void (*funcp)(void); handle = dlopen("./foo.so", RTLD_LAZY); if (handle == NULL) { fprintf(stderr, "dlopen: %s\n", dlerror()); exit(EXIT_FAILURE); } dlerror(); /* Clear any outstanding error */ funcp = dlsym(handle, "foo"); printf("Results after dlsym(): funcp = %p; dlerror = %s\n", (void *) funcp, dlerror()); exit(EXIT_SUCCESS); } 

Now create and run to see the case where dlsym() returns NULL and dlerror() also returns NULL :

 $ cc -Wall -fPIC -shared -o libfoo.so foo.c $ cc -Wall -o main main.c libfoo.so -ldl $ LD_LIBRARY_PATH=. ./main foo called Results after dlsym(): funcp = (nil); dlerror = (null) 
0
source

It may not be if the / PIE library is a product of normal C compilation, but you can get a character to enable NULL with special tricks:

null.c:

 #include <stdio.h> extern char null_addressed_char; int main(void) { printf("&null_addressed_char=%p\n", &null_addressed_char); } 

Compile, link and run:

 $ clang null.c -Xlinker --defsym -Xlinker null_addressed_char=0 && ./a.out &null_addressed_char=(nil) 

If you don't allow this kind of weirdness, you may consider NULL returns from dlsym as errors.

0
source

dlerror() returns the last error, not the status of the last call. Therefore, if there is nothing else that you show, you can potentially get a valid result from dlsym() and fool yourself if you think that an error has occurred (because there is still one in the queue). The purpose of dlerror is to provide human readable messages. If you do not print the result, you are using it incorrectly.

-1
source

All Articles