GCC制作动态库导出符号表

栏目: 服务器 · 编程工具 · 发布时间: 5年前

内容简介:GCC制作动态链接库时默认会将所有的函数及变量都导出到符号表,这里的函数及变量指的是没有使用static修饰的,使用static修饰的函数及变量不会导出。正常情况下所有符号均导出是不会有问题的,但是有时会有问题,在下边的例子中会说明。为了避免这种情况,就需要定制符号表,即仅仅将需要提供给其他模块使用的接口或变量导出到符号表,本文就是介绍制作这样的动态库的方法。介绍制作方法前先举例说明其必要性,有两个动态库libhello.so及libhello1.so,对应的源文件是hello.c及hello1.c,其源码

GCC制作动态链接库时默认会将所有的函数及变量都导出到符号表,这里的函数及变量指的是没有使用static修饰的,使用static修饰的函数及变量不会导出。正常情况下所有符号均导出是不会有问题的,但是有时会有问题,在下边的例子中会说明。为了避免这种情况,就需要定制符号表,即仅仅将需要提供给其他模块使用的接口或变量导出到符号表,本文就是介绍制作这样的动态库的方法。

一 问题示例

介绍制作方法前先举例说明其必要性,有两个动态库libhello.so及libhello1.so,对应的源文件是hello.c及hello1.c,其源码如下:

/*
 *hello.c源码
*/

int hello()
{
    return 1;
}
/*
 *hello1.c源码
*/

int hello()
{
    return 10;
}
/*
 *hello.h源码
*/

int hello();

将hello.c和hello1.c分别编译成动态库:

gcc -shared -o libhello.so hello.c
gcc -shared -o libhello1.so hello1.c

测试文件源码:

#include <stdio.h>
#include "hello.h"

int main()
{
printf(">>>%d\n", hello());

return 0;
}

现在编译测试文件,并且同时连接libhello.so和libhello1.so两个文件:

gcc main.c -L. -lhello -lhello1
运行结果:
>>>1

结果如下图所示:

GCC制作动态库导出符号表

由上图ldd查看结果可知,实际上只连接了libhello.so库,现在交换编译测试文件时连接库的顺序并运行:

gcc main.c -L. –lhello1 -lhello
运行结果:
>>>10

结果如下图所示:

GCC制作动态库导出符号表

由上图ldd查看结果可知,实际上只连接了libhello1.so库。 

由此可知有相同函数时,调用先找到的库中的函数实体。假设项目中有两个模块以库的方式提供分别为A、B库,而两个库中均有函数D并且函数格式一样但功能不同,其中A库中的D是模块内部使用的,但是没有用static修饰,这样两个库的符号表中均有D函数符号,上层模块调用了D函数接口,编译时同时连接了A、B库,则实际调用的D函数就有编译链接时A在前还是B在前,如果B在前则不会有问题,但是如果A在前就会调用A中的D函数,功能就会出错。 

这种问题遇到了就很不好查找与排除,因为并非代码问题,修改代码解决不了问题,所以才有了本文描述的制作自己需要的库,该库仅仅暴露提供给外部模块使用的接口符号表。

二 定制方法

2.1 前期准备

前期准备只是熟悉查看库的符号表方法及相关介绍。查看符号表用的命令是readelf,选项是-s,如readelf –s libhello.so,查看的结果如图所示:

GCC制作动态库导出符号表

(注:图比较长,并未截完整,剩下的都是.symtab的内容) 

从上图可知,符号表有两部分:.dynsyn和.symtab,这里只做简单介绍,具体信息可自行查找。 

.symtab和.dynsym两个不同的symbol table,.dynsym用来保存与动态链接相关的导入导出符号,而 .symtab 则保存所有符号,包括 .dynsym 中的符号,.dynsym是.symtab的一个子集。 

ELF文件包含一些sections(如code和data)是在运行时需要的, 这些sections被称为allocable;而其他一些sections仅仅是linker、debugger等 工具 需要,在运行时并不需要,这些sections被称为non-allocable。当linker构建ELF文件时,它把allocable的数据放到一个地方,将non-allocable的数据放到其他地方,当OS加载ELF文件时,仅仅allocable的数据被映射到内存,non-allocable的数据仍静静地呆在文件里不被处理。strip就是用来移除某些non-allocable sections的,移除后不影响使用。

2.2 制作动态库

本文制作的动态库的目的是控制暴露在符号表中的符号,使用的是gcc的visibility属性,网上介绍还有其他方法,本文不做介绍。

制作动态库的源码hello.c如下:

/*
 *hello.c源码
*/
int call_count = 0;

int hello_init()
{
    call_count = 0;

    return 0;
}

int hello_call_count_add()
{
    return ++call_count;
}

int hello_handle()
{
    return hello_call_count_add();
}

void hello_exit()
{
    call_count = 0;
}
/*
 *hello.h源码
*/
#ifndef __HELLO_H
#define __HELLO_H

int hello_init();

int hello_handle();

void hello_exit();

#endif

由hello.h头文件可知hello_init()、hello_handle()和hello_exit()是对外接口,而hello_call_count_add()不是。先用常规方法制作libhello.so,查看符号表,此处仅查看.dynsyn部分,.symtab部分用strip命令处理掉。

gcc -shared -o libhello.so hello.c
strip libhello.so
readelf -s libhello.so

查看结果:

GCC制作动态库导出符号表

由图可知,不仅所有的函数在符号表中,未用static修饰的变量call_count也在符号表中,这没有达到目的,需要将hello_call_count_add()函数和变量call_count去掉。 

要达到目的,需要用gcc的visibility属性,将需要导出的符号用__attribute__ ((visibility (“default”)))修饰,并且gcc编译时需要加入-fvisibility=hidden选项。 

使用visibility属性后的源码:

#define DLL_PUBLIC __attribute__ ((visibility ("default")))

int call_count = 0;

DLL_PUBLIC int hello_init()
{
    call_count = 0;

    return 0;
}

int hello_call_count_add()
{
    return ++call_count;
}

DLL_PUBLIC int hello_handle()
{
    return hello_call_count_add();
}

DLL_PUBLIC void hello_exit()
{
    call_count = 0;
}

编译并用strip命令处理掉.symtab部分:

gcc -shared -fvisibility=hidden -o libhello.so hello.c
strip libhello.so
readelf -s libhello.so

结果如下:

GCC制作动态库导出符号表

从结果可知达到了目的,经验证制作的库可以正常使用。

2.3 整理

gcc在链接时设置-fvisibility=hidden,则不加 visibility声明的都默认为hidden,即都是隐藏的, gcc默认设置-fvisibility=default,即全部可见。

三 总结

本文只介绍了 linux 下gcc的编译选项,交叉编译时选项参考GNU文档

本文永久更新链接: http://embeddedlinux.org.cn/emb-linux/system-development/201904/21-8638.html


以上所述就是小编给大家介绍的《GCC制作动态库导出符号表》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

断点:互联网进化启示录

断点:互联网进化启示录

[美]杰夫·斯蒂贝尔 / 师蓉 / 中国人民大学出版社有限公司 / 2014-11-1 / CNY 49.00

一部神经学、生物学与互联网技术大融合的互联网进化史诗巨著。 我们正置身网络革命中。互联网的每一丝变化都与你我息息相关。当科技变得无处不在时,它就会改变你我。在《断点》一书中,大脑科学家和企业家杰夫·斯蒂贝尔将带领读者来到大脑、生物与技术的交汇处,向读者展示生物学和神经学是如何与互联网技术发生联系的;我们是如何通过生物学上的前车之鉴,来预测互联网的发展的;互联网在经历增长、断点和平衡后又会发生......一起来看看 《断点:互联网进化启示录》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具