运行时加载动态库的一个小问题

简要说明

大致情况是这样的:
程序program引用动态库libAlibB。其中libAlibB都引用动态库libShared和静态库libStatic。在libSharedlibStatic中都含有静态变量。现在就是看这个静态变量是否存在两份。

             program    (可以隐式链接,也可以显示动态链接A和B)
             |    |
libStatic-> libA  libB <- libStatic
             |    |		(隐式链接Shared)
            libShared

因为libAlibB都链接了静态库libStatic,所以libStatic的内容会存在两份。在映射到program的进程空间的时候,这部分就会存在映射到不同地址的两份。在windows上的测试结果确实是如此,在linux上的测试结果中,如果是显示动态链接libAlibB也是,但是如果是隐式链接,那么结果却是只有一份。

因为libShared是动态库,在libA和libB中都不存在,所以在第一个映射到program的进程空间之后,第二次引用的时候就不会再去映射一次了。

这里的一个结论就是,如果一个静态库被多处引用,那么这个静态库在最终程序的进程空间可能会存在多份。如果这个静态库中含有静态变量或全局变量,那么这个变量也会存在多份。如果这个变量是一个类对象,那么变量初始化的时候,每一份都会调用构造函数。因为VS编译的时候默认是不导出符号的,而gcc默认是导出的,也可能是这个原因。但libA和libB都是动态库,这两者中应该都是含有引用的libStatic部分的全部内容的。

所以,在静态库中最好不要去存放全局变量,也不要在这里创建单例对象等。如果对程序文件大小有要求,最好使用动态库。

实验代码

实验代码包括六个部分,libA/libB/libShared/libStaic/test/test2。其中test测试的是显示动态链接,test2测试的是隐式链接。

源代码下载LoadLibraryTest.7z
可以使用QtCreator工具打开。

libStatic

libStatic.hpp

#ifndef LIBSTATIC_HPP
#define LIBSTATIC_HPP

void func_Static();

#endif // LIBSTATIC_HPP

libStatic.cpp

#include "LibStatic.hpp"

#include <stdio.h>
#include <stdlib.h>

void func_Static()
{
    static char buf[1024] =
            "这个是libStatic里的buffer。<---->";
    printf("func_Static 地址 %p|",&func_Static);
    printf("buf 地址 %p
	%s	",&buf,buf);

    static int x = 0;
    if(x == 0) x = rand();
    sprintf(buf,"libStatic的buffer被修改 %d",x);
    printf("%s
--------------------------
",buf);
}

libShared

libshared_global.hpp

#ifndef LIBSHARED_GLOBAL_HPP
#define LIBSHARED_GLOBAL_HPP

#ifdef _WIN32
#if defined(LIBSHARED_LIBRARY)
#  define LIBSHAREDSHARED_EXPORT extern "C" __declspec(dllexport)
#else
#  define LIBSHAREDSHARED_EXPORT extern "C" __declspec(dllimport)
#endif
#else
#  define LIBSHAREDSHARED_EXPORT extern "C"
#endif

#endif // LIBSHARED_GLOBAL_HPP

libShared.hpp

#ifndef LIBSHARED_HPP
#define LIBSHARED_HPP

#include "libshared_global.hpp"

LIBSHAREDSHARED_EXPORT void func_Shared();

#endif // LIBSHARED_HPP

libShared.cpp

#include "LibShared.hpp"

#include <stdio.h>
#include <stdlib.h>

void func_Shared()
{
    static char buf[1024] =
            "这个是libShared里的buffer。<---->";
    printf("func_Shared 地址 %p|",&func_Shared);
    printf("buf 地址 %p
	%s	",&buf,buf);

    static int x = 0;
    if(x == 0) x = rand();
    sprintf(buf,"libShared的buffer被修改 %d",x);
    printf("%s
--------------------------
",buf);
}

libA

libA_global.hpp

#ifndef LIBA_GLOBAL_HPP
#define LIBA_GLOBAL_HPP

#ifdef _WIN32
#if defined(LIBA_LIBRARY)
#  define LIBASHARED_EXPORT extern "C" __declspec(dllexport)
#else
#  define LIBASHARED_EXPORT extern "C" __declspec(dllimport)
#endif
#else
#  define LIBASHARED_EXPORT extern "C"
#endif

#endif // LIBA_GLOBAL_HPP

libA.hpp

#ifndef LIBA_HPP
#define LIBA_HPP

#include "liba_global.hpp"

LIBASHARED_EXPORT int func_A();

#endif // LIBA_HPP

libA.cpp

#include "LibA.hpp"

#include "LibShared.hpp"
#include "LibStatic.hpp"

#include <stdio.h>

int func_A()
{
    static char buf[] =
            "这个是libA里的buffer。---------";
    printf("func_A      地址 %p|",&func_A);
    printf("buf 地址 %p
	%s

",&buf,buf);

    func_Shared();
    func_Static();

    return 0;
}

libB

libB_global.hpp

#ifndef LIBB_GLOBAL_HPP
#define LIBB_GLOBAL_HPP

#ifdef _WIN32
#if defined(LIBB_LIBRARY)
#  define LIBBSHARED_EXPORT extern "C" __declspec(dllexport)
#else
#  define LIBBSHARED_EXPORT extern "C" __declspec(dllimport)
#endif
#else
#  define LIBBSHARED_EXPORT extern "C"
#endif

#endif // LIBB_GLOBAL_HPP

libB.hpp

#ifndef LIBB_HPP
#define LIBB_HPP

#include "libb_global.hpp"

LIBBSHARED_EXPORT int func_B();

#endif // LIBB_HPP

libB.cpp

#include "LibB.hpp"

#include "LibShared.hpp"
#include "LibStatic.hpp"

#include <stdio.h>

int func_B()
{
    static char buf[] =
            "这个是libB里的buffer。---------";
    printf("func_B      地址 %p|",&func_B);
    printf("buf 地址 %p
	%s

",&buf,buf);

    func_Shared();
    func_Static();
    return 0;
}

test

main.cpp

#include <iostream>
#ifdef _WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
using namespace std;

typedef int(func)();

#ifndef _WIN32
#define LoadLibraryA(dllpath) 
    dlopen(dllpath,RTLD_LAZY)
    // RTLD_LAZY 暂缓解出,需要时解
#define FreeLibrary(hdll)   
    dlclose(hdll)
#define GetProcAddress dlsym

#define HMODULE void*
#endif

class dylib{
public:
    dylib(const char* dllpath,const char* funcname)
    {
        hdll = LoadLibraryA(dllpath);
        if(hdll){
            f = (func*)GetProcAddress(hdll,funcname);

            if(f == NULL){
                cout<<funcname<<" 函数查找失败"<<endl;
#ifdef _WIN32
                cout<<"错误码:"<<GetLastError()<<endl;
#else
                cout<<"错误信息:"<<dlerror()<<endl;
#endif
            }
        }else{
            cout<<dllpath<<" 加载失败"<<endl;
            f = NULL;
        }
    }
    ~dylib()
    {
        if(hdll){
            FreeLibrary(hdll);
        }
    }
    int operator()()
    {
        return (f == NULL)?-1:f();
    }

private:
    HMODULE hdll;
    func*   f;
};


int main(int argc, char *argv[])
{
#ifdef _WIN32
    dylib dA("libA.dll","func_A");
    dylib dB("libB.dll","func_B");
#else
    dylib dA("./liblibA.so","func_A");
    dylib dB("./liblibB.so","func_B");
#endif

    dA();
    dB();
    dA();
    dB();

    return 0;
}

test2

main.cpp

#include <iostream>

#include "LibA.hpp"
#include "LibB.hpp"

using namespace std;

int main(int argc, char *argv[])
{
    func_A();
    func_B();
    func_A();
    func_B();
    return 0;
}

实验结果

Windows上VS2015编译x64版本运行结果

test 运行结果

为了便于观察,这里多加了换行

func_A      地址 00007FFF756910AA|buf 地址 00007FFF75699000
        这个是libA里的buffer。---------

func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
        这个是libShared里的buffer。<---->       libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7569109B|buf 地址 00007FFF75699030
        这个是libStatic里的buffer。<---->       libStatic的buffer被修改 18467
--------------------------


func_B      地址 00007FFF71B810AA|buf 地址 00007FFF71B89000
        这个是libB里的buffer。---------

func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
        libShared的buffer被修改 41      libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF71B8109B|buf 地址 00007FFF71B89030
        这个是libStatic里的buffer。<---->       libStatic的buffer被修改 6334
--------------------------


func_A      地址 00007FFF756910AA|buf 地址 00007FFF75699000
        这个是libA里的buffer。---------

func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
        libShared的buffer被修改 41      libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7569109B|buf 地址 00007FFF75699030
        libStatic的buffer被修改 18467   libStatic的buffer被修改 18467
--------------------------


func_B      地址 00007FFF71B810AA|buf 地址 00007FFF71B89000
        这个是libB里的buffer。---------

func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
        libShared的buffer被修改 41      libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF71B8109B|buf 地址 00007FFF71B89030
        libStatic的buffer被修改 6334    libStatic的buffer被修改 6334
--------------------------

这里可以看到func_Afunc_B调用的func_Shared是同一个,但是func_Static却不是同一个。

test2运行结果

func_A      地址 00007FFF756910AA|buf 地址 00007FFF75699000
        这个是libA里的buffer。---------

func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
        这个是libShared里的buffer。<---->       libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7569109B|buf 地址 00007FFF75699030
        这个是libStatic里的buffer。<---->       libStatic的buffer被修改 18467
--------------------------
func_B      地址 00007FFF758810AA|buf 地址 00007FFF75889000
        这个是libB里的buffer。---------

func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
        libShared的buffer被修改 41      libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7588109B|buf 地址 00007FFF75889030
        这个是libStatic里的buffer。<---->       libStatic的buffer被修改 6334
--------------------------
func_A      地址 00007FFF756910AA|buf 地址 00007FFF75699000
        这个是libA里的buffer。---------

func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
        libShared的buffer被修改 41      libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7569109B|buf 地址 00007FFF75699030
        libStatic的buffer被修改 18467   libStatic的buffer被修改 18467
--------------------------
func_B      地址 00007FFF758810AA|buf 地址 00007FFF75889000
        这个是libB里的buffer。---------

func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
        libShared的buffer被修改 41      libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7588109B|buf 地址 00007FFF75889030
        libStatic的buffer被修改 6334    libStatic的buffer被修改 6334

test测试结果一致。

linux上运行测试结果

test运行测试结果

func_A      地址 0x7ffa3cdc47e0|buf 地址 0x7ffa3cfc5060
	这个是libA里的buffer。---------

func_Shared 地址 0x7ffa3cbc2750|buf 地址 0x7ffa3cdc3060
	这个是libShared里的buffer。<---->	libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7ffa3cdc4830|buf 地址 0x7ffa3cfc50a0
	这个是libStatic里的buffer。<---->	libStatic的buffer被修改 846930886
--------------------------


func_B      地址 0x7ffa3c9c07e0|buf 地址 0x7ffa3cbc1060
	这个是libB里的buffer。---------

func_Shared 地址 0x7ffa3cbc2750|buf 地址 0x7ffa3cdc3060
	libShared的buffer被修改 1804289383	libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7ffa3c9c0830|buf 地址 0x7ffa3cbc10a0
	这个是libStatic里的buffer。<---->	libStatic的buffer被修改 1681692777
--------------------------


func_A      地址 0x7ffa3cdc47e0|buf 地址 0x7ffa3cfc5060
	这个是libA里的buffer。---------

func_Shared 地址 0x7ffa3cbc2750|buf 地址 0x7ffa3cdc3060
	libShared的buffer被修改 1804289383	libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7ffa3cdc4830|buf 地址 0x7ffa3cfc50a0
	libStatic的buffer被修改 846930886	libStatic的buffer被修改 846930886
--------------------------


func_B      地址 0x7ffa3c9c07e0|buf 地址 0x7ffa3cbc1060
	这个是libB里的buffer。---------

func_Shared 地址 0x7ffa3cbc2750|buf 地址 0x7ffa3cdc3060
	libShared的buffer被修改 1804289383	libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7ffa3c9c0830|buf 地址 0x7ffa3cbc10a0
	libStatic的buffer被修改 1681692777	libStatic的buffer被修改 1681692777
--------------------------

这里结果也是一样的,func_Static存在两份。

test2运行结果

func_A      地址 0x7fbeb43087e0|buf 地址 0x7fbeb4509060
	这个是libA里的buffer。---------

func_Shared 地址 0x7fbeb39e0750|buf 地址 0x7fbeb3be1060
	这个是libShared里的buffer。<---->	libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7fbeb450a830|buf 地址 0x7fbeb470b0a0
	这个是libStatic里的buffer。<---->	libStatic的buffer被修改 846930886
--------------------------
func_B      地址 0x7fbeb450a7e0|buf 地址 0x7fbeb470b060
	这个是libB里的buffer。---------

func_Shared 地址 0x7fbeb39e0750|buf 地址 0x7fbeb3be1060
	libShared的buffer被修改 1804289383	libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7fbeb450a830|buf 地址 0x7fbeb470b0a0
	libStatic的buffer被修改 846930886	libStatic的buffer被修改 846930886
--------------------------
func_A      地址 0x7fbeb43087e0|buf 地址 0x7fbeb4509060
	这个是libA里的buffer。---------

func_Shared 地址 0x7fbeb39e0750|buf 地址 0x7fbeb3be1060
	libShared的buffer被修改 1804289383	libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7fbeb450a830|buf 地址 0x7fbeb470b0a0
	libStatic的buffer被修改 846930886	libStatic的buffer被修改 846930886
--------------------------
func_B      地址 0x7fbeb450a7e0|buf 地址 0x7fbeb470b060
	这个是libB里的buffer。---------

func_Shared 地址 0x7fbeb39e0750|buf 地址 0x7fbeb3be1060
	libShared的buffer被修改 1804289383	libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7fbeb450a830|buf 地址 0x7fbeb470b0a0
	libStatic的buffer被修改 846930886	libStatic的buffer被修改 846930886
--------------------------

这里运行结果与windows上不一样,func_Static只存在一份。这里说明libStatic在进程空间仅存在一份,虽然它同时存在于libAlibB中。通过strings程序查看libAlibB,发现前一部分是一样的,也许这就是在链接的时候只存留一份的依据吧。

原文地址:https://www.cnblogs.com/oloroso/p/6273295.html