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

时间:2012-03-10 17:38:53

标签: 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

4 个答案:

答案 0 :(得分:11)

以前有关属性((可见性(“隐藏”))的答案很好,如果您想长期维护代码,但是如果您只想要一些符号可见,那么想要快速修复...在您想要导出使用的符号上,添加

__attribute__ ((visibility ("default"))) 

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

这里有一个详尽的解释:

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

编辑:另一种方法是构建静态库/存档(使用ar -cru mylib.a *.o生成.a存档)或根据此combine two GCC compiled .o object files into a third .o file

将对象组合到单个目标文件中

如果您问“为什么要组合目标文件而不是仅创建静态库?” ...因为链接器将.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中找到不需要的“额外代码”。如果您将[{1}}添加到对象构建中,当您与-fdata-sections -ffunction-sections链接时,它将删除未使用的部分并打印已删除内容的输出。

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

别名(“目标”)

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

-Wl,--gc-sections,--print-gc-sections

定义 void __f () { /* Do something. */; } void f () __attribute__ ((weak, alias ("__f"))); __ f'。在C ++中,必须使用目标的受损名称。如果未在同一翻译单元中定义“__f”,则会出错。

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

答案 1 :(得分:7)

您可以考虑使用GCC function attribute来隐藏,并将其隐藏起来,即在您的标头文件中的许多适当位置添加__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];

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

 signature_t *funtab[MAX_NBFUN];

然后在您的共享对象(.e.g。在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");

但我永远不会把这些东西称为“导出”功能,只能可以访问功能。

执行此类类似操作的程序示例是my MELT(不使用数组,但更复杂的堆分配值)。另一个执行数组函数指针技巧的程序示例是J.Pitrat CAIA/Malice程序。顺便说一下,他关于Artificial Beings (the conscience of a conscious machine)的书非常有趣(并在附录中提到了我曾建议他的那种技巧)。

答案 2 :(得分:1)

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

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

testfile.exp:

{
global:
  myExportedFunction1;
  myExportedFunction2;

local: *;
}

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

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

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

答案 3 :(得分:0)

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

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

<强>生成文件:

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 <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#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;
}
相关问题