首页 > *nix技术 > 函数导出可视问题

函数导出可视问题

2013年5月25日 发表评论 阅读评论 769 次浏览

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

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

看实例:

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()函数:

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;
}

编译并执行:

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共享库:

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

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

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修饰可以改为这样:

lenky@lenky-virtual-machine:~/work$ cat foo.c
int foo() {
        return 11;
}

__attribute__((visibility("hidden")) 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://lenky.info/archives/2013/05/25/2289http://lenky.info/?p=2289


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

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

  1. 9drops
    2013年11月6日15:29 | #1

    除了代码中用编译选项__attribute__ ((visibility(“hidden”)))外,还可以用形如
    bash$ cat vis.map
    VER_1 {
    global:
    vis_f1;
    vis_f2;
    local:
    *;
    };

    的脚本来配置动态库函数服务作用域,其中vis_f1 vis_f2在动态库外部可见,
    用形如 gcc -g -shared -o vis.so vis_comm.o vis_f1.o vis_f2.o \
    -Wl,–version-script,vis.map
    编译选项编译动态库。

  2. ownone
    2013年10月23日11:01 | #2

    很清楚static的作用。不过gcc的扩展还是非常强大的。

  3. jet.chenzy
    2013年7月16日13:59 | #3

    没看出有什么好处,一样要改代码

  4. xiaoli
    2013年5月27日10:59 | #4

    你好,跟你是老乡 也算湖南人
    你会长沙发展了?据我所知长沙没看到有互联网公司啊

  1. 本文目前尚无任何 trackbacks 和 pingbacks.