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',则会出错.
并非所有目标计算机都支持此属性.
您可以考虑使用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程序.顺便说一句,他关于" 人造生物"(有意识机器的良心)的书非常有趣(并提到了这个技巧 - 我在附录中提出了他的建议).
小智 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)
等等.
在少数功能的情况下,可以用手完成.如果您需要数千个,则应使用代码生成.
唯一的问题是如何获得字典.好吧,我在这里看到几个选项:
__lxstat -> lxstat__这仅限于你的想象力.