How to solve the conflict of dependent dynamic library?

Project scenario:

In the product iteration, it is necessary to upgrade a dependent SDK to the second generation. After obtaining the third-party second-generation SDK, compiling and testing, it is found that after linking to the so library of the third-party SDK, the original others are completely irrelevant. A certain function of the computer cannot be used normally.


Cause Analysis:

LD_DEBUG ./XXX (XXX is the executable program of the app)

Use the LD_DEBUG command to analyze the feasibility program, check its so binding log file, and find that the function called by the first so is bound to the second so, resulting in an error in the function call.

insert image description here
insert image description here
The comparison of the two versions of so (before and after the upgrade) shows that, the character set conflicts, there are two versions of mbedtls implementation, there should be another version of mbedtls implementation under the system PATH

solution:

plan 1

In the case of not changing so, there is another solution, remove -lagora-rtc-sdk, and use lazy mode to pull the character set

void* dlopen(const char* libfile,int flag);
void* dlsym(void* handler, const char* symbol);

Step 1: Remove -lagora-rtc-sdk in the gcc link
Step 2: Compile the function that reports undefined symbol in the link and load it with dlsym

Scenario 2

Regenerate a new sdk to resolve character set conflicts.

Knowledge expansion

1、 LD_DEBUG

LD_DEBUG is an environment variable set by the loader in glibc, you can easily see the loading process of the loader. glibc is the libc library released by gnu, that is, the c runtime library. glibc is the lowest api in the linux system, almost any other running library will depend on glibc

Valid options for the LD_DEBUG environment variable are:

  libs        display library search paths
  reloc       display relocation processing
  files       display progress for input file
  symbols     display symbol table processing
  bindings    display information about symbol binding
  versions    display version dependencies
  all         all previous options combined
  statistics  display relocation statistics
  unused      determined unused DSOs
  help        display this help message and exit

To direct the debugging output into a file instead of standard output
a filename can be specified using the LD_DEBUG_OUTPUT environment variable.

Take the problems encountered during the installation of memcached as an example:

命令: /usr/local/bin/memcached -p 11211 -m 64m -vv
错误:/usr/local/bin/memcached: error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: No such file or directory

Use LD_DEBUG to display library lookup information

LD_DEBUG=libs /usr/local/bin/memcached -vv
     15759:    find library=libevent-2.0.so.5 [0]; searching
     15759:     search cache=/etc/ld.so.cache
     15759:     search path=/lib64/tls/x86_64:/lib64/tls:/lib64/x86_64:/lib64:/usr/lib64/tls/x86_64:/usr/lib64/tls:/usr/lib64/x86_64:/usr/lib64           (system search path)
     15759:      trying file=/lib64/tls/x86_64/libevent-2.0.so.5
     15759:      trying file=/lib64/tls/libevent-2.0.so.5
     15759:      trying file=/lib64/x86_64/libevent-2.0.so.5
     15759:      trying file=/lib64/libevent-2.0.so.5
     15759:      trying file=/usr/lib64/tls/x86_64/libevent-2.0.so.5
     15759:      trying file=/usr/lib64/tls/libevent-2.0.so.5
     15759:      trying file=/usr/lib64/x86_64/libevent-2.0.so.5
     15759:      trying file=/usr/lib64/libevent-2.0.so.5

As can be seen from the last line, memcached goes to the /usr/lib64 directory to find libevent-2.0.so.5, first locate the actual directory of libevent-2.0.so.5

命令:whereis libevent-2.0.so.5
结果:libevent-2.0.so: /usr/lib/libevent-2.0.so.5

At this time, you need to make a soft connection

命令:ln -s /usr/lib/libevent-2.0.so.5 /usr/lib64/libevent-2.0.so.5

If you want to delete the soft link

命令: rm -rf filename

2、 dlopen

dlopen This function loads a dynamic shared object (shared library) file named by a null-terminated string filename and returns an opaque "handle" to the loaded object. This handle is used with other functions in the dlopen API, such as dlsym(), dladdr(), dlinfo(), and dlclose() [1].

If filename is NULL, the returned handle is for the main program. If filename contains slashes ("/"), it is interpreted as a (relative or absolute) pathname. Otherwise, the dynamic linker searches for the object as follows (see ld.so(8) for details):

  • (ELF only) If the calling program's executable contains a DT_RPATH flag, and does not contain a DT_RUNPATH flag, the directories listed in the DT_RPATH flag are searched.
  • If the environment variable LD_LIBRARY_PATH is defined to contain a colon-separated list of directories when the program starts, those directories are searched. (As a security measure, the set-user-ID and set-group-ID programs ignore this variable.)
  • (ELF only) If the calling program's executable contains a DT_RUNPATH flag, the directories listed in that flag are searched.
  • Check the cache file /etc/ld.so.cache (maintained by ldconfig(8)) to see if it contains an entry for filename. The directories /lib and /usr/lib (in that order) are searched.

If the object specified by filename depends on other shared objects, the same rules are used by the dynamic linker to automatically load those objects as well. (This process can happen recursively if the objects in turn have dependencies)
The flags parameter must include one of the following two values:

  • RTLD_LAZY performs lazy binding. Symbols are only resolved when the code that references them is executed. If the symbol is never referenced, it is never resolved (only function references perform lazy binding; references to variables are always bound immediately when a shared object is loaded). Since glibc 2.1.1, this flag is overridden by the effect of the LD_BIND_NOW environment variable.
  • RTLD_NOW If this value is specified, or the environment variable LD_BIND_NOW is set to a non-empty string, all undefined symbols in the shared object will be resolved before dlopen() returns. If this operation cannot be performed, an error is returned. flags can also be ORed with zero or more of the following values
  • RTLD_GLOBAL Symbols defined by this shared object will be available for symbol resolution in subsequently loaded shared objects.
  • RTLD_LOCAL This is the opposite of RTLD_GLOBAL and is the default if no flag is specified. Symbols defined in this shared object cannot be used to resolve references in subsequently loaded shared objects
  • RTLD_NODELETE (since glibc2.2) Do not unload shared objects during dlclose(). Therefore, if the object is later reloaded with dlopen(), the object's static variables will not be reinitialized
  • RTLD_NOLOAD (since glibc 2.2) Do not load shared objects. This can be used to test whether the object is already resident (dlopen() returns NULL if not, and returns the handle to the object if it is resident). This flag can also be used to raise flags on already loaded shared objects.

For example, a shared object previously loaded with RTLD_LOCAL can be reopened with RTLD_NOLOAD|RTLD_GLOBAL. RTLD_DEEPBIND (since glibc 2.3.4) places the lookup scope for symbols before the global scope for this shared object. This means that self-contained objects will use their own symbols in preference to global symbols, which are included in the loaded object.

basic definition

Function

Open a dynamic link library and return the handle of the dynamic link library
Include the header file
#include <dlfcn.h>

function definition

void * dlopen( const char * pathname, int mode);

function description

mode is the opening method, and there are multiple values. The functions implemented on different operating systems are different. Under Linux, they can be divided into three categories according to functions:

  1. Parsing mode
    RTLD_LAZY: Before dlopen returns, undefined symbols in the dynamic library are not parsed (only valid for function references, always immediately parsed for variable references).
    RTLD_NOW: Before dlopen returns, all undefined symbols need to be parsed out, if not, dlopen will return NULL, the error is:
    undefined symbol: xxxx...
  2. The scope of action can be used in combination with the parsing method via "|".
    RTLD_GLOBAL: The symbols defined in the dynamic library can be resolved by other libraries opened later. RTLD_LOCAL:
    Contrary to RTLD_GLOBAL, the symbols defined in the dynamic library cannot be relocated by other libraries opened later. If RTLD_GLOBAL or RTLD_LOCAL is not specified, the default is RTLD_LOCAL.
  3. Mode of action RTLD_NODELETE:
    The library is not unloaded during dlclose(), and static variables in the library are not initialized when the library is reloaded later using dlopen(). This flag is not POSIX-2001 compliant.
    RTLD_NOLOAD:
    Do not load the library. It can be used to test whether the library is loaded (dlopen() returns NULL to indicate that it is not loaded, otherwise it indicates that it has been loaded), and it can also be used to change the flag of the loaded library, such as: the flag of the previously loaded library is RTLD_LOCAL, use dlopen(RTLD_NOLOAD|RTLD_GLOBAL) After flag will become RTLD_GLOBAL. This flag is not POSIX-2001 compliant.
    RTLD_DEEPBIND: Search for symbols in the library before searching for global symbols to avoid conflicts with symbols of the same name. This flag is not POSIX-2001 compliant.

return value:

  • Open error returns NULL
  • On success, return the library reference

When compiling, add -ldl (specify the dl library) -rdynamic (notify the linker to add all symbols to the dynamic symbol table (the purpose is to be able to use dlopen to achieve backward tracking)

For example

gcc test.c -o test -ldl-rdynamic

process

Open library: void* dlopen(const char* libfile,int flag);
Get function: void* dlsym(void* handler, const char* symbol);
Run function: func
Close library: int dlclose(void* handler);

use dlopen

dlopen() is a powerful library function. This function will open a new library and load it into memory. This function is mainly used to load symbols in the library, which are not known at compile time. For example, the Apache web server uses this function to load modules at runtime, which provides it with additional capabilities. A configuration file controls the process of loading modules. This mechanism makes it unnecessary to recompile when adding or deleting a module in the system.
You can use dlopen() in your own program. dlopen() is defined in dlfcn.h and implemented in the dl library. It takes two parameters: a filename and a flag. The filename can be a soname in the library we have studied. Flag indicating whether to compute library dependencies immediately. If it is set to RTLD_NOW, it will be calculated immediately; if it is set to RTLD_LAZY, it will be calculated when needed. Additionally, RTLD_GLOBAL can be specified, which makes symbols available to libraries that are loaded later.
When the library is loaded, you can use the handle returned by dlopen() as the first argument to dlsym() to get the address of the symbol in the library. Using this address, you can get a pointer to a specific function in the library and call the corresponding function in the loaded library.

example

runlib.c

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#define DLL_PATH "./libsd.so"
typedef int (*func)(int, int);
int main(){
    
    
    void *dlhandler;
    char *error;
    func func = NULL;
    dlhandler = dlopen(DLL_PATH,RTLD_LAZY);
    if(dlhandler == NULL){
    
    
        fprintf(stderr,"%s\n",dlerror());
        exit(-1);
    }
    dlerror();
    func = dlsym(dlhandler,"sumab");
    printf("%d\n",func(1,2));
    dlclose(dlhandler);
    return 0;
}

Compile:
gcc -rdynamic -o runlib runlib.c -ldl
Run:
[teanee@localhost runlib]$ ./runlib
successfully calls the sumab function.

Guess you like

Origin blog.csdn.net/u011942101/article/details/127358446