5

我正在尝试创建一个用 Objective-C 编写的静态库。我想对这个库的消费者隐藏所有实现细节。在这个例子中,“OneThing”对象使用了库内部的其他功能,包括“SecretThing”,它可以被库中的许多东西使用(并且不能隐藏在 OneThing 中)。但是,我不希望库的用户看到 OneThing 使用 SecretThing,或者 SecretThing 甚至存在,即使他们在 .a 文件中四处寻找。

@interface OneThing
+ (void) do;
@end
@interface SecretThing
+ (void) undo;
@end
@implementation OneThing
+ (void) do
{
    [SecretThing undo];
}
@end
@implementation SecretThing
+ (void) undo { }
@end

如果我们编译它,并检查符号表:

% cc -c onething.m
% nm onething.o | grep Thing
0000000000000000 t +[OneThing do]
00000000000002f8 s +[OneThing do].eh
0000000000000040 t +[SecretThing undo]
0000000000000320 s +[SecretThing undo].eh
0000000000000078 S _OBJC_CLASS_$_OneThing
0000000000000050 S _OBJC_CLASS_$_SecretThing
00000000000000a0 S _OBJC_METACLASS_$_OneThing
00000000000000c8 S _OBJC_METACLASS_$_SecretThing
0000000000000128 s l_OBJC_$_CLASS_METHODS_OneThing
00000000000001d8 s l_OBJC_$_CLASS_METHODS_SecretThing
0000000000000190 s l_OBJC_CLASS_RO_$_OneThing
0000000000000240 s l_OBJC_CLASS_RO_$_SecretThing
0000000000000148 s l_OBJC_METACLASS_RO_$_OneThing
00000000000001f8 s l_OBJC_METACLASS_RO_$_SecretThing
% 

正如我们所料,我们看到 OneThing 和 SecretThing 同样暴露在外。现在,如果能以某种方式解决 SecretThing 的内部对库的使用,并且只将 OneThing 暴露给外部世界,那就太好了。我想要做的是这个(只选择一种方法来尝试和管理):

% ld -r onething.o -exported_symbol "+[OneThing do]" -o onlyonething.o

我希望这会将“+ [OneThing do]”标记为“T”类型(全局文本),然后它将在“条带”中存活下来。它不是。这让我很烦恼,因为我认为这在某个时间点有效,也许当我更新工具链时,我得到了一个新版本的 ld(“ld -v”==ld64-133.3),它的工作方式不同。

除了获取链接器的源代码并编写自己的新标志来做我想做的事情之外,我没有其他想法。我希望我只是愚蠢,有一些我不明白的东西使这更容易。


在答案中建议这从根本上是不可能的,因为您需要在运行时可用的所有链接信息来进行方法分派。我相信这个实验证明这不是真的。

# Move OneThing and SecertThing into their own files, with their own .h
% cat c_api.m
#include "OneThing.h"

void one_thing_do()
{
    [OneThing do];
}
% cc -c c_api.c
% cat main.m
int main(int argc, char**argv)
{
    extern void one_thing_do();

    one_thing_do();
}
% cc main.m c_api.o onething.o secretthing.o -framework Foundation
% ./a.out
% ( runs to completion, no errors )

现在我们尝试隐藏我们不想让世界看到的部分:

% ld -r c_api.o onething.o secretthing.o -o strip_c_api.o -exported_symbol "_one_thing_do"
% strip -x c_strip_c_api.o
% nm strip_c_api.o
0000000000000118 s EH_Frame1
0000000000000098 s EH_Frame1
00000000000000d8 s EH_Frame1
                 U __objc_empty_cache
                 U __objc_empty_vtable
                 U _objc_msgSend
0000000000000000 T _one_thing_do
0000000000000130 s func.eh
00000000000000f0 s func.eh
00000000000000b0 s func.eh
0000000000000020 t l001
0000000000000060 t l002
0000000000000170 s l003
0000000000000190 s l004
00000000000001d8 s l005
0000000000000220 s l006
0000000000000240 s l007
0000000000000288 s l008
00000000000002f0 s l009
0000000000000318 s l010
0000000000000340 s l011
0000000000000368 s l012
% clang main.m strip_c_api.o -framework Foundation
% ./a.out
% nm a.out
0000000100001280 S _NXArgc
0000000100001288 S _NXArgv
0000000100001298 S ___progname
0000000100000000 T __mh_execute_header
                 U __objc_empty_cache
                 U __objc_empty_vtable
0000000100001290 S _environ
                 U _exit
0000000100000db0 T _main
                 U _objc_msgSend
0000000100000de0 T _one_thing_do
0000000100001000 s _pvars
                 U dyld_stub_binder
0000000100000d70 T start
4

3 回答 3

6

由于 Objective-C 运行时的动态工作方式,这是不可能的。运行时需要这些信息来实例化类并向其发送消息,因此实际上没有办法在不破坏库的情况下隐藏这些信息。

于 2012-07-26T19:06:45.627 回答
3

来自 Apple 的 Documentaion,更多详细信息:C++ Runtime Environment Programming Guide

符号可见性和 Objective-C

Objective-C 是 C 的严格超集,Objective-C++ 是 C++ 的严格超集。这意味着所有关于 C 和 C++ 中符号可见性的讨论也适用于 Objective-C 和 Objective-C++。您可以使用编译器标志、可见性属性和可见性杂注来隐藏 Objective-C 代码文件中的 C 和 C++ 代码。

在 32 位 OS X 项目中,这些可见性控件仅适用于代码的 C 或 C++ 子集。它们不适用于 Objective-C 的类和方法。Objective-C 类和消息名称受 Objective-C 运行时的约束,而不是链接器,因此可见性的概念不适用于它们。没有任何机制可以向该库的客户端隐藏在动态库中定义的 Objective-C 类或方法。

在为 x86_64 OS X 或 iOS 构建时,符号可见性确实会影响 Objective-C 类。隐藏类不是安全灵丹妙药——有进取心的开发人员可以通过 Objective-C 运行时调用访问任何类——但如果你直接引用一个其可见性隐藏在你链接到的库中的类,你会得到一个链接器错误。这意味着,如果给定的类打算在库之外使用或在其定义的可执行文件中使用,则需要确保正确的符号可见性。

于 2012-07-30T20:04:38.293 回答
0

有一种方法可以混淆这些!您可以使用 NSInvocation 或 performSelector 来调用您的方法调用。这样,您可以加密方法/类名称,然后在运行时解密,以便可以调用它们。

于 2020-06-13T05:39:23.197 回答