动态库(.so)隐藏函数名
一、偶遇 error: undefined reference to xxx 问题
尝试封装通用的接口到一个private.so,然后供客户端使用,private.so编译出来后由sample.cpp依赖调用其中封装的接口,但是一直报error: undefined reference to xxx的错误,并且检查so、头文件都依赖正确,c方式编译的函数也用extern "C" 声明。
- #ifdef __cplusplus
- extern "C" {
- #endif
- xxx
- #ifdef __cplusplus
- }
- #endif
于是用如下方法查看so的符号表根本找不到定义的 xxx 函数:
- readelf -s private.so
- nm -D private.so
从Android.mk定位问题发现编译参数 CAMX_CFLAGS += -fvisibility=hidden ,此参数的作用就是将函数名隐藏,需要暴露给用户的函数接口可以单独通过 __attribute__((visibility ("default"))) 声明避免被隐藏。
- LOCAL_CFLAGS := $(CAMX_CFLAGS)
- LOCAL_CPPFLAGS := $(CAMX_CPPFLAGS)
因此如下声明后,xxx函数就能被链接找到,编译通过。
- #ifdef __cplusplus
- extern "C" {
- #endif
- __attribute__((visibility ("default"))) xxx
- #ifdef __cplusplus
- }
- #endif
-fvisibility 参数具体说明如下:
- man gcc:
- -fvisibility=default|internal|hidden|protected
- Set the default ELF image symbol visibility to the specified option---all symbols are marked with this unless overridden within the code. Using this feature can very
- substantially improve linking and load times of shared object libraries, produce more optimized code, provide near-perfect API export and prevent symbol clashes. It is
- strongly recommended that you use this in any shared objects you distribute.
- Despite the nomenclature, "default" always means public; i.e., available to be linked against from outside the shared object. "protected" and "internal" are pretty useless
- in real-world usage so the only other commonly used option is "hidden". The default if -fvisibility isn't specified is "default", i.e., make every symbol public---this
- causes the same behavior as previous versions of GCC.
- A good explanation of the benefits offered by ensuring ELF symbols have the correct visibility is given by "How To Write Shared Libraries" by Ulrich Drepper (which can be
- found at <http://people.redhat.com/~drepper/>)---however a superior solution made possible by this option to marking things hidden when the default is public is to make the
- default hidden and mark things public. This is the norm with DLLs on Windows and with -fvisibility=hidden and "__attribute__ ((visibility("default")))" instead of
- "__declspec(dllexport)" you get almost identical semantics with identical syntax. This is a great boon to those working with cross-platform projects.
- For those adding visibility support to existing code, you may find #pragma GCC visibility of use. This works by you enclosing the declarations you wish to set visibility
- for with (for example) #pragma GCC visibility push(hidden) and #pragma GCC visibility pop. Bear in mind that symbol visibility should be viewed as part of the API interface
- contract and thus all new code should always specify visibility when it is not the default; i.e., declarations only for use within the local DSO should always be marked
- explicitly as hidden as so to avoid PLT indirection overheads---making this abundantly clear also aids readability and self-documentation of the code. Note that due to ISO
- C++ specification requirements, "operator new" and "operator delete" must always be of default visibility.
- Be aware that headers from outside your project, in particular system headers and headers from any other library you use, may not be expecting to be compiled with visibility
- other than the default. You may need to explicitly say #pragma GCC visibility push(default) before including any such headers.
- extern declarations are not affected by -fvisibility, so a lot of code can be recompiled with -fvisibility=hidden with no modifications. However, this means that calls to
- "extern" functions with no explicit visibility use the PLT, so it is more effective to use "__attribute ((visibility))" and/or "#pragma GCC visibility" to tell the compiler
- which "extern" declarations should be treated as hidden.
- Note that -fvisibility does affect C++ vague linkage entities. This means that, for instance, an exception class that is be thrown between DSOs must be explicitly marked
- with default visibility so that the type_info nodes are unified between the DSOs.
- An overview of these techniques, their benefits and how to use them is at <http://gcc.gnu.org/wiki/Visibility>.
二、动态库函数隐藏技巧
向客户提供动态链接库(.so)时,有些关键的函数名不希望暴露出去,此时便可以通过gcc的-fvisibility=hidden选项对编译生成的so进行函数符号隐藏,如:LOCAL_CPPFLAGS +=-fvisibility=hidden,执行编译后,使用nm -D xxx.so命令或者readelf --symbols xxx.so查看函数名的确被隐藏,但此时是将所有函数名都隐藏了,那么客户加载so时需要调用的接口函数名(xxx)也会找不到定义,导致编译报undefined reference to xxx错误,所以需要暴露(导出)的函数前应该增加属性__attribute__ ((visibility("default")))设置成可见。
例如:
- __attribute__ ((visibility("default")))
- void hello(void)
- {
- }
实际项目开发中可以通过宏来控制,更加方便:
- #ifdef DCIRDLL_EXPORTS
- #ifdef PLATFORM_LINUX
- #define MYDCIR_API __attribute__((visibility ("default"))) //Linux动态库(.so)
- #else
- #define MYDCIR_API __declspec(dllexport) //Windows动态库(.dll)
- #endif
- #else
- #define MTDCIR_API
- #endif
在头文件中声明即可:
- MTDCIR_API const voide hello();
-end-
动态库(.so)隐藏函数名的更多相关文章
- C#将C++动态库的回调函数封装成事件
关于C#调用C++动态库的文章很多,调用动态库中回调函数的方法也不在少数.但大多数调用回调函数的方法依然保留了C++的语法特点. 比如有一段C++的回调函数代码,为了表达它的意思,我把注释也粘贴了进来 ...
- Golang调用windows下的dll动态库中的函数
Golang调用windows下的dll动态库中的函数 使用syscall调用. package main import ( "fmt" "syscall" & ...
- windows下查看静态库和动态库的导出函数
在window下查看动态库的导出函数可以用vs自带的Depends工具: 查看静态库的信息要用命令行来实现: dumpbin /LINKERMEMBER Test.lib > 1 ...
- Golang调用windows下的dll动态库中的函数 Golang 编译成 DLL 文件
Golang调用windows下的dll动态库中的函数 package main import ( "fmt" "syscall" "time&quo ...
- Golang编写动态库实现回调函数
Golang编写动态库实现回调函数 我们现在要做一个动态库,但是C++实在是比较难,于是就想能不能用更简单的golang来实现,golang也就是最近的版本才支持编译成动态库,在网上也没找到可用的案例 ...
- [Python] 动态函数调用(通过函数名)
2018-04-09 update 利用python中的内置函数 eval() ,函数说明: def eval(*args, **kwargs): # real signature unknown & ...
- 如何调用.so动态库中的函数,如何把自己的函数导出为.so的动态库函数供别人调用
调用.so中的函数和平常的函数没有区别,只是在编译连接时加上-lxxxx就行了.要生成.so库,则编译时用下面的语句:gcc -shared -Wl,-soname,libmyfun.so -o li ...
- Linux查看动态库.so导出函数列表
https://blog.csdn.net/chrisnotfound/article/details/80662923
- 024-linux中动态库libXXX.so
1.动态库的概念.动态链接库与普通的程序相比而言,没有main函数,是一系列函数的实现.通过shared和fPIC编译参数生产so动态链接库文件.程序在调用库函数时,只需要连接上这个库即可. 2.动态 ...
随机推荐
- 为什么有了uwsgi还要nginx这个“前端”服务器
相信每一个使用nginx+uwsgi+django部署过的人,都感到非常复杂.到底为什么一个项目的发布要经过这么多层级,他们每一层有什么理由存在?这就带大家宏观地看待一下 首先nginx 是对外的服务 ...
- new Function()语法
函数的语法: let func = new Function(...args, body); 历史原因,参数也可以以逗号分隔的列表的形式给出,这三个意思相同: new Function('a', 'b ...
- CentOS7 内核优化 修改参数
一:内核简介 内核是操作系统最基本的部分.它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间. 内核的分类可分为单内核和 ...
- bind支持mysql
最近打算将bind的记录信息存入到数据库中去,网上找了下,原来早有老外写好了mysql-bind的补丁,重新编译bind即可实现bind支持mysql存储.(http://mysql-bind.sou ...
- elementUI el-date-picker 时间范围设置 固定时间段可选 配置
https://blog.csdn.net/sinat_37255207/article/details/91793889 <el-date-picker v-model="start ...
- 本地资源图片无法通过 WXSS 获取,可以使用网络图片,或者 base64,或者使用<image/>标签
在微信小程序开发中,当在CSS中使用背景图片格式为png时就会出现: 只要把png格式改掉就可以或者在<image/>标签里面写,我实测用JPG格式和把图片转成base64是没问题的.
- 编码问题2 utf-8和Unicode的区别
utf-8和Unicode到底有什么区别?是存储方式不同?编码方式不同?它们看起来似乎很相似,但是实际上他们并不是同一个层次的概念 要想先讲清楚他们的区别,首先应该讲讲Unicode的来由. 众所周知 ...
- Luogu P5018 对称二叉树 瞎搞树&哈希
我的天..普及组这么$hard$... 然后好像没有人用我的垃圾做法,,,好像是$O(n)$,但十分的慢,并且极其暴力$qwq$ 具体来说,就是直接$dfs$求出树高,然后想像出把原来的树补成满二叉树 ...
- gcc/g++以c++11编译
方法一: //在程序头加上预定义编译器命令 #pragma GCC diagnostic error "-std=c++11" //通过#pragma 指示 GCC编译器处理错误的 ...
- HGOI 20191105 题解
Problem A Joker 老虎和蒜头是好朋友. 夏天过去了,凉爽的秋天来临,老虎和蒜头又有了新的娱乐项目.老虎有一个远房表亲是西伯利亚虎,那里流行着一个纸牌游戏:两位玩家参与游戏,道具是一副54 ...