如何在共享库中隐藏导出的符号名称

ASB*_*Bai 13 c c++ gcc shared-libraries

对于VC,我可以编写一个DEF文件并使用'NONAME'指令在dll的导出表中只保留序号.

我怎么能用gcc和ELF格式共享库做同样的事情?

或者,ELF共享库中是否有类似PE格式DLL中的序号?如果没有,我如何在共享库中隐藏导出的符号名称?

======================================

更新:一些额外的描述:

在Windows中,您可以通过仅放置具有空名称的整数ID(序号)来导出函数.

要显示它,一个DLL的导出表正常布局看起来是这样的:http://home.hiwaay.net/~georgech/WhitePapers/Exporting/HowTo22.gif.

"NONAME"看起来像这样:http://home.hiwaay.net/~georgech/WhitePapers/Exporting/HowTo23.gif .

请注意,第二张图片中的函数名称为"N/A".以下是对它的完整解释:hxxp://home.hiwaay.net/~georgech/WhitePapers/Exporting/Exp.htm.

======================================

更新:非常感谢为我提供建议的每个人.最后,我决定在linux/posix平台上使用静态库.但是将小的"特殊部分"(使用一些不适合静态库的特性,例如:TLS Slot等)提取到普通的共享库中.因为小型普通共享库只做很少的事情,而且这些工作完全不敏感,所以不需要隐藏/隐藏它的API.

我认为这是解决我问题的最简单方法:-D

tec*_*rus 11

关于属性((visibility("hidden")))的先前答案在您想要长期维护代码时是好的,但是如果您只想要一些符号可见并希望快速修复...在符号上你导出的使用,添加

__attribute__ ((visibility ("default"))) 
Run Code Online (Sandbox Code Playgroud)

然后你可以传递-fvisibility=hidden给编译器

这里有一个详尽的解释:

http://gcc.gnu.org/wiki/Visibility

编辑:另一种方法是构建静态库/存档(使用.a存档ar -cru mylib.a *.o)或将对象组合成单个目标文件,根据这两个GCC编译的.o目标文件组合成第三个.o文件

如果你问"为什么要组合目标文件而不是只创建一个静态库?" ...因为链接器将.o文件与.a文件区别对待(我不知道为什么,只是它确实如此),特别是它允许您将.o文件链接到共享库或二进制文件中,即使所有符号都是隐藏的(即使是你正在使用的符号)这还有一个额外的好处,即减少启动时间(少一个DSO和少量符号查找)和二进制大小(符号通常占20%尺寸和剥离只需要大约一半 - 只是外部可见的部分)

对于二进制文件 strip --strip-all -R .note -R .comment mybinary

对于图书馆 strip --strip-unneeded -R .note -R .comment mylib.so

更多关于静态链接的好处:http://sta.li/faq但他们不讨论许可问题,这是使用静态库的主要原因,因为你想要隐藏你的API,可能是一个问题

现在我们知道有一个"符号清洁"的对象,可以使用我们的组合对象来构建一个libpublic.so,方法是将private.o和public.c(只将你想要公开的别名/导出)链接到一个共享库.

这种方法很适合在公共API中找到不需要的"额外代码".如果添加-fdata-sections -ffunction-sections到对象构建中,当您链接时-Wl,--gc-sections,--print-gc-sections,它将消除未使用的部分并打印已删除的内容的输出.

编辑2 - 或者您可以隐藏整个API并仅为您要导出的函数设置别名

别名("目标")

alias属性使声明作为另一个符号的别名发出,必须指定该符号.例如,

          void __f () { /* Do something. */; }
          void f () __attribute__ ((weak, alias ("__f")));
Run Code Online (Sandbox Code Playgroud)

定义f' to be a weak alias for__f'.在C++中,必须使用目标的受损名称.如果在同一翻译单元中未定义`__f',则会出错.

并非所有目标计算机都支持此属性.


Bas*_*tch 7

您可以考虑使用GCC函数属性进行可见性并使其隐藏,即__attribute__((visibility ("hidden")))在头文件中的许多适当位置添加.

然后,你将隐藏你无用的符号,并保留好的符号.

这是GCC扩展(可能由Clang或Icc等其他编译器支持).

附加物

在Linux世界中,共享库应该按名称导出函数(或者可能是全局数据),如头文件中所发布的那样.否则,不要将这些功能称为"导出" - 它们不是!

如果您绝对希望可访问但未导出的共享库中具有函数,则可以以某种方式注册它(例如,将函数指针放在全局数据的某个槽中,例如数组),这意味着你有(或提供)一些功能注册机制.但这不再是导出功能了.

更具体地说,您可以在主程序中使用全局函数指针数组

 // in a global header.h
  // signature of some functions
 typedef void signature_t(int, char*);
 #define MAX_NBFUN 100
 // global array of function pointers
 extern signature_t *funtab[MAX_NBFUN];
Run Code Online (Sandbox Code Playgroud)

然后在你main.c的程序文件中

 signature_t *funtab[MAX_NBFUN];
Run Code Online (Sandbox Code Playgroud)

然后在你的共享对象(myshared.c编译成文件的.eg libmyshared.so)中构造函数:

 static my_constructor(void) __attribute__((constructor));

 static myfun(int, char*); // defined elsewhere is the same file
 static void 
 my_constructor(void) { // called at shared object initialization
    funtab[3] = myfun;
 }
Run Code Online (Sandbox Code Playgroud)

稍后您的主程序(或其他一些共享对象)可能会调用

 funtab[3](124, "foo");
Run Code Online (Sandbox Code Playgroud)

但我永远不会把这些东西称为"导出"功能,只能调用可达到的功能.

执行类似操作的程序的一个示例是我的MELT(不使用数组,但更复杂的堆分配值).另一个执行数组函数指针技巧的程序示例是J.Pitrat CAIA/Malice程序.顺便说一句,他关于" 人造生物"(有意识机器的良心)的书非常有趣(并提到了这个技巧 - 我在附录中提出了他的建议).

  • 不,我相信你应该问一下你的真正问题是什么,并以普通的Linux方式解决它.您认为它过于以Windows为中心,并且在Linux上无法正常工作(反之亦然).标准的Linux方法是在共享对象中包含数千个导出的函数(其名称对于链接或动态加载`dlopen`共享库的应用程序是可见的). (2认同)
  • @ASBai:通过模糊处理的安全性不起作用. (2认同)

小智 6

要隐藏UNIX上导出函数的含义,可以使用#defines通过简单重命名来模糊其名称.像这样:

#define YourGoodFunction_CreateSomething              MeaninglessFunction1
#define YourGoodFunction_AddSomethingElseToSomething  lxstat__
#define YourGoodFunction_SaveSomething                GoAway_Cracker
#define YourGoodFunction_ReleaseSomething             Abracadabra
Run Code Online (Sandbox Code Playgroud)

等等.

在少数功能的情况下,可以用手完成.如果您需要数千个,则应使用代码生成.

  1. 获取您的真实函数名称列表,使用grep,awk,cut等.
  2. 准备无意义名字的字典
  3. 编写一个脚本(或二进制)生成器,它将输出带有#defines的C头文件,如上所示.

唯一的问题是如何获得字典.好吧,我在这里看到几个选项:

  • 你可以让你的同事随机键入他们的键盘;-)
  • 生成一个随机字符串,如:read(/ dev/urandom,10-20 bytes)| BASE64
  • 使用一些真正的字典(通用英语,特定域名)
  • 收集真实的系统API名称并稍微改变它们: __lxstat -> lxstat__

这仅限于你的想象力.