一、偶遇 error: undefined reference to  xxx 问题

  尝试封装通用的接口到一个private.so,然后供客户端使用,private.so编译出来后由sample.cpp依赖调用其中封装的接口,但是一直报error: undefined reference to  xxx的错误,并且检查so、头文件都依赖正确,c方式编译的函数也用extern "C" 声明。

  1. #ifdef __cplusplus
  2. extern "C" {
  3. #endif
  4.  
  5. xxx
  6.  
  7. #ifdef __cplusplus
  8. }
  9. #endif

  于是用如下方法查看so的符号表根本找不到定义的 xxx 函数:

  1. readelf -s private.so
  2. nm -D private.so

  从Android.mk定位问题发现编译参数 CAMX_CFLAGS += -fvisibility=hidden ,此参数的作用就是将函数名隐藏,需要暴露给用户的函数接口可以单独通过 __attribute__((visibility ("default"))) 声明避免被隐藏。

  1. LOCAL_CFLAGS := $(CAMX_CFLAGS)
  2. LOCAL_CPPFLAGS := $(CAMX_CPPFLAGS)

  因此如下声明后,xxx函数就能被链接找到,编译通过。

  1. #ifdef __cplusplus
  2. extern "C" {
  3. #endif
  4.  
  5. __attribute__((visibility ("default"))) xxx
  6.  
  7. #ifdef __cplusplus
  8. }
  9. #endif

  -fvisibility 参数具体说明如下:

  1. man gcc:
  2.  
  3. -fvisibility=default|internal|hidden|protected
  4. 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
  5. 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
  6. strongly recommended that you use this in any shared objects you distribute.
  7.  
  8. 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
  9. 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
  10. causes the same behavior as previous versions of GCC.
  11.  
  12. 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
  13. 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
  14. default hidden and mark things public. This is the norm with DLLs on Windows and with -fvisibility=hidden and "__attribute__ ((visibility("default")))" instead of
  15. "__declspec(dllexport)" you get almost identical semantics with identical syntax. This is a great boon to those working with cross-platform projects.
  16.  
  17. 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
  18. 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
  19. 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
  20. 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
  21. C++ specification requirements, "operator new" and "operator delete" must always be of default visibility.
  22.  
  23. 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
  24. other than the default. You may need to explicitly say #pragma GCC visibility push(default) before including any such headers.
  25.  
  26. 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
  27. "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
  28. which "extern" declarations should be treated as hidden.
  29.  
  30. 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
  31. with default visibility so that the type_info nodes are unified between the DSOs.
  32.  
  33. 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")))设置成可见。

例如:

  1. __attribute__ ((visibility("default")))
  2. void hello(void)
  3. {
  4. }

  实际项目开发中可以通过宏来控制,更加方便:

  1. #ifdef DCIRDLL_EXPORTS
  2. #ifdef PLATFORM_LINUX
  3. #define MYDCIR_API __attribute__((visibility ("default"))) //Linux动态库(.so)
  4. #else
  5. #define MYDCIR_API __declspec(dllexport)            //Windows动态库(.dll)
  6. #endif
  7. #else
  8. #define MTDCIR_API
  9. #endif

  在头文件中声明即可:

  1.  
  1. MTDCIR_API const voide hello();

  

-end-

动态库(.so)隐藏函数名的更多相关文章

  1. C#将C++动态库的回调函数封装成事件

    关于C#调用C++动态库的文章很多,调用动态库中回调函数的方法也不在少数.但大多数调用回调函数的方法依然保留了C++的语法特点. 比如有一段C++的回调函数代码,为了表达它的意思,我把注释也粘贴了进来 ...

  2. Golang调用windows下的dll动态库中的函数

    Golang调用windows下的dll动态库中的函数 使用syscall调用. package main import ( "fmt" "syscall" & ...

  3. windows下查看静态库和动态库的导出函数

    在window下查看动态库的导出函数可以用vs自带的Depends工具: 查看静态库的信息要用命令行来实现: dumpbin   /LINKERMEMBER   Test.lib   >   1 ...

  4. Golang调用windows下的dll动态库中的函数 Golang 编译成 DLL 文件

    Golang调用windows下的dll动态库中的函数 package main import ( "fmt" "syscall" "time&quo ...

  5. Golang编写动态库实现回调函数

    Golang编写动态库实现回调函数 我们现在要做一个动态库,但是C++实在是比较难,于是就想能不能用更简单的golang来实现,golang也就是最近的版本才支持编译成动态库,在网上也没找到可用的案例 ...

  6. [Python] 动态函数调用(通过函数名)

    2018-04-09 update 利用python中的内置函数 eval() ,函数说明: def eval(*args, **kwargs): # real signature unknown & ...

  7. 如何调用.so动态库中的函数,如何把自己的函数导出为.so的动态库函数供别人调用

    调用.so中的函数和平常的函数没有区别,只是在编译连接时加上-lxxxx就行了.要生成.so库,则编译时用下面的语句:gcc -shared -Wl,-soname,libmyfun.so -o li ...

  8. Linux查看动态库.so导出函数列表

    https://blog.csdn.net/chrisnotfound/article/details/80662923

  9. 024-linux中动态库libXXX.so

    1.动态库的概念.动态链接库与普通的程序相比而言,没有main函数,是一系列函数的实现.通过shared和fPIC编译参数生产so动态链接库文件.程序在调用库函数时,只需要连接上这个库即可. 2.动态 ...

随机推荐

  1. 为什么有了uwsgi还要nginx这个“前端”服务器

    相信每一个使用nginx+uwsgi+django部署过的人,都感到非常复杂.到底为什么一个项目的发布要经过这么多层级,他们每一层有什么理由存在?这就带大家宏观地看待一下 首先nginx 是对外的服务 ...

  2. new Function()语法

    函数的语法: let func = new Function(...args, body); 历史原因,参数也可以以逗号分隔的列表的形式给出,这三个意思相同: new Function('a', 'b ...

  3. CentOS7 内核优化 修改参数

    一:内核简介 内核是操作系统最基本的部分.它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间. 内核的分类可分为单内核和 ...

  4. bind支持mysql

    最近打算将bind的记录信息存入到数据库中去,网上找了下,原来早有老外写好了mysql-bind的补丁,重新编译bind即可实现bind支持mysql存储.(http://mysql-bind.sou ...

  5. elementUI el-date-picker 时间范围设置 固定时间段可选 配置

    https://blog.csdn.net/sinat_37255207/article/details/91793889 <el-date-picker v-model="start ...

  6. 本地资源图片无法通过 WXSS 获取,可以使用网络图片,或者 base64,或者使用<image/>标签

    在微信小程序开发中,当在CSS中使用背景图片格式为png时就会出现: 只要把png格式改掉就可以或者在<image/>标签里面写,我实测用JPG格式和把图片转成base64是没问题的.

  7. 编码问题2 utf-8和Unicode的区别

    utf-8和Unicode到底有什么区别?是存储方式不同?编码方式不同?它们看起来似乎很相似,但是实际上他们并不是同一个层次的概念 要想先讲清楚他们的区别,首先应该讲讲Unicode的来由. 众所周知 ...

  8. Luogu P5018 对称二叉树 瞎搞树&哈希

    我的天..普及组这么$hard$... 然后好像没有人用我的垃圾做法,,,好像是$O(n)$,但十分的慢,并且极其暴力$qwq$ 具体来说,就是直接$dfs$求出树高,然后想像出把原来的树补成满二叉树 ...

  9. gcc/g++以c++11编译

    方法一: //在程序头加上预定义编译器命令 #pragma GCC diagnostic error "-std=c++11" //通过#pragma 指示 GCC编译器处理错误的 ...

  10. HGOI 20191105 题解

    Problem A Joker 老虎和蒜头是好朋友. 夏天过去了,凉爽的秋天来临,老虎和蒜头又有了新的娱乐项目.老虎有一个远房表亲是西伯利亚虎,那里流行着一个纸牌游戏:两位玩家参与游戏,道具是一副54 ...