linux编译连接选项解析

来源:互联网 发布:程序员爱逛的网站 编辑:程序博客网 时间:2024/06/06 03:41

(1)动态库同名符号和visibility选项

结论:

1) 不加任何导出可见参数时,gcc编译动态库时默认导出所有的函数和全局变量(即可认为在全局变量和函数前都有__attribute__((__visibility__("default")))修饰),加了-fvisibility=hidden之后,则所有的函数和全局变量都不再导出(即可认为在全局变量和函数前都有__attribute__((__visibility__("hidden")))修饰),要导出特定的全局变量和函数需要手动显式地在前面添加__attribute__((__visibility__("default")))进行修饰。

2) 不同动态库中有同名的导出符号(全局变量或函数),并不影响主程序编译运行,但是容易导致混淆。此种情况,无论是通过dlopen方式动态加载,还是通过编译选项-l编译连接来引用动态库,主程序连接的始终是第一个被连接的符号地址,后续的符号全部被忽略,可参考:http://blog.csdn.net/zhongyunde/article/details/5939733

案例如下:

t1.c

#include <stdio.h>
int g_var = 11; 
void f1()
{
    printf("%d\n", g_var);
}

t2.c

#include <stdio.h>
int g_var = 22; 
void f2()
{
    printf("%d\n", g_var);
}

main.c

#include <stdio.h>
int main()
{
    f1();
    f2();
    f1();
    return 0;
}


编译连接

gcc -shared -fPIC -o libt1.so t1.c

gcc -shared -fPIC -o libt2.so t2.c

gcc -L./ -lt1 -lt2 -o main main.c

运行:

export LD_LIBRARY_PATH=./

./main

运行结果如下:

11
11
11

修改编译main时连接动态库的顺序:

gcc -L./ -lt2 -lt1 -o main main.c

再次运行:

./main

运行结果如下:

22
22
22

以上运行结果可以看出,main主程序始终连接首先加载动态库的导出全局符号g_var,另一个则被忽略。

采用dlopen试试看如何。

修改main.c为如下:

#include <stdio.h>
#include <dlfcn.h>
int main()
{
    void (*f1)();
    void (*f2)();
    void* h1;
    void* h2;
    h1 = dlopen("./libt1.so", RTLD_LAZY | RTLD_GLOBAL);
    h2 = dlopen("./libt2.so", RTLD_LAZY | RTLD_GLOBAL);
    f1 = dlsym(h1, "f1");
    f2 = dlsym(h2, "f2");
    f1();
    f2();
    f1();
    dlclose(h1);
    dlclose(h2);
    return 0;
}

重新编译运行看看

gcc -L./ -ldl -lt1 -lt2 -o main main.c

./main 

输入如下:

11
11
11

从结果看,和之前之一样的。

如果采用dlopen调用不同动态库的同名导出函数又如何。

修改t2.c如下:

#include <stdio.h>
int g_var = 22; 
void f1()
{
    printf("f1 in t2 %d\n", g_var);
}

修改main.c

#include <stdio.h>
#include <dlfcn.h>
int main()
{
    void (*f1)();
    void (*f2)();
    void* h1;
    void* h2;
    h1 = dlopen("./libt1.so", RTLD_LAZY | RTLD_GLOBAL);
    h2 = dlopen("./libt2.so", RTLD_LAZY | RTLD_GLOBAL);
    f1 = dlsym(h1, "f1");
    f2 = dlsym(h2, "f1");
    f1();
    f2();
    f1();
    dlclose(h1);
    dlclose(h2);
    return 0;
}

编译运行:

gcc -shared -fPIC -o libt2.so t2.c

gcc -L./ -ldl -o main main.c

运行结果如下:

11
f1 in t2 11
11

从运行结果看,不同so动态库同名导出函数,用dlopen动态加载方式,由于地址不同,可以明显的区别,不会有任何冲突和混淆。

同名导出函数是否可以用-ldl选项进行编译,是否会冲突呢,调用的又是哪个库的函数呢?

修改main.c函数,如下:

#include <stdio.h>
int main()
{
    f1();
    return 0;
}

编译运行:

 gcc -L./ -ldl -lt1 -lt2 -o main main.c

./main

输出

11

t2和t1连接顺序换一下,看一下输出如下:

gcc -L./ -ldl -lt2 -lt1 -o main main.c

./main

输出

f1 in t2 22

从运行结果看也是先连接的先运行,后续相同的函数被忽略。


解决方法,对于g_var可以通过visibility参数来禁止导出,这样每个so里面的全局变量,都在本so里面用,外面无法使用。

修改后的代码如下:

t1.c

#include <stdio.h>
int g_var = 11;
__attribute__((__visibility__("default"))) void f1()
{
    printf("g_var in t1 = %d\n", g_var);
}

t2.c

#include <stdio.h>
int g_var = 22;
__attribute__((__visibility__("default"))) void f2()
{
    printf("g_var in t2 = %d\n", g_var);
}

main.c

#include <stdio.h>
#include <dlfcn.h>
int main()
{
    void (*f1)();
    void (*f2)();
    void* h1;
    void* h2;
    h1 = dlopen("./libt1.so", RTLD_LAZY | RTLD_GLOBAL);
    h2 = dlopen("./libt2.so", RTLD_LAZY | RTLD_GLOBAL);
    f1 = dlsym(h1, "f1");
    f2 = dlsym(h2, "f2");
    f1();
    f2();
    f1();
    dlclose(h1);
    dlclose(h2);
    return 0;
}

增加-fvisibility=hidden选项重新编译运行,如下:

gcc -shared -fPIC -fvisibility=hidden -o libt1.so t1.c

gcc -shared -fPIC -fvisibility=hidden -o libt2.so t2.c

gcc -L./ -ldl -o main main.

运行结果如下:

g_var in t1 = 11
g_var in t2 = 22
g_var in t1 = 11

从结果看,不同so里面的全部变量g_var都在本so中有效,互补影响了,这种方式是推荐的方式,gcc自带的so库都是通过这种方式,默认全部不导出,要导出那些函数和变量,自行的显式地添加__attribute__((__visibility__("default")))来修饰。











0 0
原创粉丝点击