Application of dynamic library loading function dlsym in C/C + + Programming

The function of dlsym function is to obtain the address of symbols (global variables and function symbols) from the shared library (dynamic library). It is usually used to obtain the address of function symbols, which can be used to wrap the functions in the shared library; The following is the function prototype and the header file to be included.

#include <dlfcn.h>
void *dlsym(void *handle, const char *symbol);

Where handle can be the handle value returned by dlopen function or RTLD_DEFAULT or RTLD_NEXT

RTLD_DEFAULT Indicates that symbols in the shared library are searched in the default order symbol First address
RTLD_NEXT Indicates that the symbols in the shared library are searched in the default order after the current library symbol First address

The specific difference can be achieved through the following code dlsym1 C to distinguish:

#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU / / use RTLD_DEFAULT and RTLD_NEXT macro needs to be defined
#include <dlfcn.h>

typedef size_t (*strlen_t)(const char *); 
strlen_t strlen_f = NULL, strlen_f1 = NULL;

size_t strlen(const char *str)
{
    printf("%s strlen\n", __FILE__);
    return strlen_f1(str);
}

int main(int argc, char **argv)
{
    strlen_f = dlsym(RTLD_DEFAULT, "strlen");  
    //Get the address of the function symbol strlen in the current file
    if(!strlen_f) {
        printf("default load error %s\n", dlerror());
        return 1;
    }   
    strlen_f1 = dlsym(RTLD_NEXT, "strlen");
    //Get the address of the function symbol strlen in the system library after the current library
    if(!strlen_f1) {
        printf("next load error %s\n", dlerror());
        return 1;
    }   
    printf("strlen is %p\n", strlen);
    printf("strlen_f is %p\n", strlen_f);
    printf("strlen_f1 is %p\n", strlen_f1);
    printf("strlen_f is %ld\n", strlen_f("xuedaon")); //Call the function strlen in the current file
    printf("=>>>>>>>>>> <<<<<<<<<<<=\n");
    printf("strlen_f1 is %ld\n", strlen_f1("xuedaon"));  //It is equivalent to calling the system library function strlen
    return 0;
}

Compile the current file dlsym1 c
gcc  dlsym1.c  -ldl 
The operation results are as follows:

dlsym function can also obtain the symbol of function or global variable in the specified library. You need to call dlopen first and open the dynamic library file specified by us before you can obtain the symbol address. Specific operation code examples:

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

int main(int argc, char **argv)
{
    // libhello.so is a test shared library file encapsulated by ourselves
    // RTLD_LAZY means that symbols are resolved only when they are referenced, but they are only valid for function symbol references, while references to variable symbols are always bound and resolved immediately when the dynamic library is loaded
    void *handle = dlopen("./libhello.so", RTLD_LAZY);  
    if(!handle) {
        printf("open failed: %s\n", dlerror());
        return 1;
    }   
    void *p = dlsym(handle, argv[2]);  //argv[2] corresponds to the symbolic name of the address to be obtained
    if(!p) {
        printf("load failed: %s\n", dlerror());
        return 1;
    }   
    // argv[1] corresponds to the input of 0, which means that the symbol of the global variable is obtained
    // argv[1] corresponds to input 1, which means that the symbol of the global function is obtained
    if(0 == atoi(argv[1])) {
        printf("global is %d\n", *(int*)p);
    }else if(1 == atoi(argv[1])) {
        void (*fp)() = (void (*)())p;
        fp();
    }   
    dlclose(handle);
    return 0;
}

Here's libhello The so library passes the following test code source file hello C. the specific contents of the package are as follows:

#include "hello.h"

int global = 666;

void hello()
{
    printf("hello xuedaon\n");
}

Compile the above dlsym C. the operation effect of the document is as follows:

At runtime, passing 0 means to obtain the address of the global variable symbol in the shared library, and passing 1 means to obtain the address of the function symbol in the shared library.
Note that the above uses are implemented with the C compiler. If dlsym is used in the C + + compilation environment, it should be noted that the function symbol will rearrange the function name in the compilation stage due to the overload mechanism of C + +. Therefore, when using it, you need to use extern "C" to tell the compiler to process the corresponding variables or objects according to the compilation method of C. The following are the differences between the two symbols generated at compile time:

And if we put hello After adding extern "C" modification to h, we will find that the function modified by extern "C" when compiling with gcc and g + + will not be re adapted.
hello.h 

#pragma once 
#include <stdio.h>
extern "C" {
    extern int global;
    extern void hello();
}

By the way, if you want to use dlsym function to obtain class objects, you can use extern "C" with the function interface to return pointers or references to class objects.
In addition, the dlsym function can also be used to wrap the library functions malloc and free to detect whether there is memory leakage in our code (if malloc and free are not paired, there is memory leakage). Here is a simple implementation idea code for reference:

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

#define TEST_MEM_LEAK 1 / / a value of 1 means to join the memory leak detection, and a value of 0 means not to join

#if TEST_MEM_LEAK

typedef void *(*malloc_t)(size_t size);
malloc_t malloc_f = NULL;

typedef void (*free_t)(void *p);
free_t free_f = NULL;

int malloc_flag = 1;    // It is used to prevent repeated recursion and cannot exit because the printf function will call malloc for memory allocation
int free_flag = 1;

void *malloc(size_t size)Learn Niuniu www.xuedaon.com
{
    if(malloc_flag) {          
        malloc_flag = 0;  // Used to prevent printf from causing an error when calling malloc recursively
        printf("malloc\n");
	   void *p = malloc_f(size);
        malloc_flag = 1;  // It is used to ensure that the initial value of flag flag is consistent when malloc in this file is called again
	   return p;
    } else {
        return malloc_f(size);  // Here, the malloc function in the system library obtained by dlsym is called
    }   
}

void free(void *p) 
{
    if(free_flag) {
        free_flag = 0;
        printf("free\n");
        free_f(p);
	   free_flag = 1;
    } else {
        free_f(p);
    }
}
#endif
int main(int argc, char **argv)
{
#if TEST_MEM_LEAK / / the part from if to endif can be divided into function calls
    malloc_f = dlsym(RTLD_NEXT, "malloc");
    if(!malloc_f) {
        printf("load malloc failed: %s\n", dlerror());
        return 1;
    }
    free_f = dlsym(RTLD_NEXT, "free");
    if(!free_f) {
        printf("load free failed: %s\n", dlerror());
        return 1;
    }
#endif
    void *p1 = malloc(10);  //The malloc function in this article will be called first
    void *p2 = malloc(20);
    //Here, p2 is not released and there is a memory leak. Judge by checking whether the number of malloc and free times printed is the same
    free(p1);
    return 0;
}

The compilation and operation effect is as follows:

It can be seen here that malloc was called twice, while free was called only once. There is a memory leak.
The above is a summary of the use of dlsym for reference and exchange.

Keywords: C C++

Added by guru2k9 on Thu, 10 Mar 2022 09:04:22 +0200