分享

Lenky个人站点 ? 函数导出可视问题

 astrotycoon 2013-08-17

最近从深圳裸辞回到了长沙,要忙的事情太多,当然,最主要的还是找工作的事情。长沙这边可投的公司实在是太少,但幸运的是,有不少同学和朋友的帮忙,而几个岗位的面试也都是十分的顺利,嘛,厚积薄发,继续努力,所以,来看本文主题:C语言里的函数导出可视问题。

这源于前几天去一家公司面试时做的一个试题,我们知道C语言里有一个很给力的关键字static,既可以修饰变量,也可以修饰函数。关于static的作用,在网上有很多描述,见参考1,2。这里重点关注的是:如果函数没有被static修饰,那么它就是全局可见的,而这对于创建动态共享库会带来一些麻烦,因为那些没有被static修饰的函数在默认情况下都成了导出函数。如果一个工程链接了多个动态共享库,而部分库又存在相同的函数名称时,也许我们链接使用的函数就不是原本期待的那个。之前在项目中就遇到过好几次这样的情况,如果参数不一致还好,因为能够在编译时就得到告警提示,从而及时解决,否则就需在实际执行时才发现程序行为异常,再做问题定位就稍显麻烦些了。

看实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
lenky@lenky-virtual-machine:~/work$ cat foo.c
int foo() {
    return 11;
}
int bar() {
    return 12;
}
lenky@lenky-virtual-machine:~/work$ cat bar.c
int foo() {
    return 21;
}
int bar() {
    return 22;
}
lenky@lenky-virtual-machine:~/work$ gcc -shared -fPIC -o libfoo.so foo.c
lenky@lenky-virtual-machine:~/work$ gcc -shared -fPIC -o libbar.so bar.c

上面准备了两个动态库,分别为libfoo.so和libbar.so,它们都包含有函数foo()和bar(),没有被static修饰,默认也就是导出函数。

一个名为test.c的程序需要使用共享库libfoo.so内的函数foo()以及libbar.so内的bar()函数:

1
2
3
4
5
6
7
8
9
lenky@lenky-virtual-machine:~/work$ cat test.c
#include <stdio.h>
int main()
{
    printf("foo-11:%d\n", foo());
    printf("bar-22:%d\n", bar());
    return 0;
}

编译并执行:

1
2
3
4
5
6
7
8
9
10
11
12
lenky@lenky-virtual-machine:~/work$ gcc test.c -o test -lfoo -lbar
/usr/bin/ld: cannot find -lfoo
/usr/bin/ld: cannot find -lbar
collect2: error: ld returned 1 exit status
lenky@lenky-virtual-machine:~/work$ export LIBRARY_PATH=.
lenky@lenky-virtual-machine:~/work$ gcc test.c -o test -lfoo -lbar
lenky@lenky-virtual-machine:~/work$ ./test
./test: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
lenky@lenky-virtual-machine:~/work$ export LD_LIBRARY_PATH=.
lenky@lenky-virtual-machine:~/work$ ./test
foo-11:11
bar-22:12

打印结果表明调用的bar()函数并不是来自libbar.so共享库。调换一下链接库的先后顺序,发现调用的foo()函数不是来自libfoo.so共享库:

1
2
3
4
lenky@lenky-virtual-machine:~/work$ gcc test.c -o test -lbar -lfoo
lenky@lenky-virtual-machine:~/work$ ./test
foo-11:21
bar-22:22

因此,我们需要做隐藏,比如这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
lenky@lenky-virtual-machine:~/work$ cat foo.c
int foo() {
    return 11;
}
static int bar() {
    return 12;
}
lenky@lenky-virtual-machine:~/work$ cat bar.c
static int foo() {
    return 21;
}
int bar() {
    return 22;
}
lenky@lenky-virtual-machine:~/work$ gcc -shared -fPIC -o libfoo.so foo.c
lenky@lenky-virtual-machine:~/work$ gcc -shared -fPIC -o libbar.so bar.c
lenky@lenky-virtual-machine:~/work$ gcc test.c -o test -lbar -lfoo
lenky@lenky-virtual-machine:~/work$ ./test
foo-11:11
bar-22:22

利用static关键字帮助我们达到了既定目的,但是static限制了其修饰的函数只能在对应的源文件内使用。因此,如果还想在共享库的多个源文件内共享一个函数,static就不可用了。所幸,强大的gcc扩展为此提供了一个选项__attribute__((visibility(“hidden”)))。如此以来,上面的static修饰可以改为这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
lenky@lenky-virtual-machine:~/work$ cat foo.c
int foo() {
    return 11;
}
__attribute__((visibility("<span class="wp_keywordlink_affiliate"><a href="http:///tag/hidden/" title="查看 hidden 中的全部文章">hidden</a></span>")) int bar() {
    return 12;
}
lenky@lenky-virtual-machine:~/work$ cat bar.c
__attribute__((visibility("hidden"))) int foo() {
    return 21;
}
int bar() {
    return 22;
}
lenky@lenky-virtual-machine:~/work$ gcc -shared -fPIC -o libbar.so bar.c
lenky@lenky-virtual-machine:~/work$ gcc -shared -fPIC -o libbar.so bar.c
lenky@lenky-virtual-machine:~/work$ gcc test.c -o test -lbar -lfoo
lenky@lenky-virtual-machine:~/work$ ./test
foo-11:11
bar-21:22

与此相对的一个选项是__attribute__((visibility(“default”))),可以认为它是用于明确表示该符号不被隐藏。给想被导出的函数加上default可视,给不想被导出的函数加上hidden隐藏,就可以避免直接使用static关键字而带来的不便。

参考:
1,http://www.cnblogs.com/dc10101/archive/2007/08/22/865556.html
2,http://blog.163.com/sunshine_linting/blog/static/4489332320119785228616/

转载请保留地址:http:///2013/05/25/%e5%87%bd%e6%95%b0%e5%af%bc%e5%87%ba%e5%8f%af%e8%a7%86%e9%97%ae%e9%a2%98/http:///?p=2289


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多