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

对于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

以前关于属性 ((visibility(“hidden”)))的答案很好,如果你想长期维护代码,但是如果你只想要一些你想要的符号并且需要快速修复……就符号而言导出使用,添加

 __attribute__ ((visibility ("default"))) 

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

这里有一个详尽的解释:

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

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

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

for binaries 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"))); 

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

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

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

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

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

附加物

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

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

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

  // 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]; 

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

  signature_t *funtab[MAX_NBFUN]; 

然后在你的共享对象( myshared.c中的myshared.c文件中编译成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; } 

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

  funtab[3](124, "foo"); 

但我永远不会把这些东西称为“导出”function,只能称为可达function。

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

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

 #define YourGoodFunction_CreateSomething MeaninglessFunction1 #define YourGoodFunction_AddSomethingElseToSomething lxstat__ #define YourGoodFunction_SaveSomething GoAway_Cracker #define YourGoodFunction_ReleaseSomething Abracadabra 

等等。

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

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

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

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

这仅限于你的想象力。

您可以编写版本脚本并将其传递给链接器以执行此操作。

一个简单的脚本如下所示:

testfile.exp:

 { global: myExportedFunction1; myExportedFunction2; local: *; } 

然后使用以下选项链接您的可执行文件:

  -Wl,--version-script=testfile.exp 

当应用于共享库时,仍将列出.so文件中的符号以进行调试,但无法从库外部访问它们。

我正在为同样的问题寻找解决方案。 所以,我找不到一个强大的解决方案。 然而,作为概念的certificate,我使用objcopy来达到预期的效果。 基本上,在编译目标文件后,我重新定义了它的一些符号。 然后,翻译的目标文件用于构建最终的共享对象或可执行文件。 因此,可以用作反向工程我的算法的提示的类/方法名称被一些无意义的名称m1,m2,m3完全重命名。

这是我用来确保这个想法有效的测试:

Makefile文件:

 all: libshared_object.so executable.exe clean: rm *.o *.so *.exe libshared_object.so : shared_object.o g++ -fPIC --shared -O2 $< -o $@ strip $@ shared_object.o : shared_object.cpp interface.h g++ -fPIC -O2 $< -c -o $@ objcopy --redefine-sym _ZN17MyVerySecretClass14secret_method1Ev=m1 \ --redefine-sym _ZN17MyVerySecretClass14secret_method2Ev=m2 \ --redefine-sym _ZN17MyVerySecretClass14secret_method3Ev=m3 $@ executable.exe : executable.o libshared_object.so g++ -O2 -lshared_object -L. $< -o $@ strip $@ executable.o : executable.cpp interface.h g++ -O2 -lshared_object -L. $< -c -o $@ objcopy --redefine-sym _ZN17MyVerySecretClass14secret_method1Ev=m1 \ --redefine-sym _ZN17MyVerySecretClass14secret_method2Ev=m2 \ --redefine-sym _ZN17MyVerySecretClass14secret_method3Ev=m3 $@ run: all LD_LIBRARY_PATH=. ./executable.exe 

interface.h

 class MyVerySecretClass { private: int secret_var; public: MyVerySecretClass(); ~MyVerySecretClass(); void secret_method1(); void secret_method2(); void secret_method3(); }; 

shared_object.cpp

 #include  #include  #include  #include "interface.h" MyVerySecretClass::MyVerySecretClass() : secret_var(0) {} MyVerySecretClass::~MyVerySecretClass() { secret_var = -1; } void MyVerySecretClass::secret_method1() { ++secret_var; } void MyVerySecretClass::secret_method2() { printf("The value of secret variable is %d\n", secret_var); } void MyVerySecretClass::secret_method3() { char cmdln[128]; sprintf( cmdln, "pstack %d", getpid() ); system( cmdln ); } 

executable.cpp

 #include "interface.h" int main ( void ) { MyVerySecretClass o; o.secret_method1(); o.secret_method2(); o.secret_method1(); o.secret_method2(); o.secret_method1(); o.secret_method2(); o.secret_method3(); return 0; }