使用 LLVM ExecutionEngine 调用 Objective-C 方法时,所有选择器都无法识别

Posted

技术标签:

【中文标题】使用 LLVM ExecutionEngine 调用 Objective-C 方法时,所有选择器都无法识别【英文标题】:All selectors unrecognised when invoking Objective-C methods using the LLVM ExecutionEngine 【发布时间】:2012-04-29 19:39:01 【问题描述】:

我在 OSX 上运行,使用 Clang 编译一些使用 OSX Cocoa 类的 Obj-C 代码,我正在尝试使用 LLVM JIT 编译器运行结果。我使用的是最新的 LLVM/Clang 版本。

编译或链接我的代码没有问题,我可以愉快地进行 C 和 C++ 系统调用而没有任何麻烦。但是我所有的 Obj-C 调用都惨遭失败,而且我已经超出了我的深度试图找出原因! objc_msgSend() 函数似乎被正确调用,但运行时拒绝识别即使是最简单的选择器。

我已经设法仅使用 Clang 和 LLI 重现了该问题,方法如下:

创建一个简单的文件test.mm:

#include <Cocoa/Cocoa.h>
#include <cstdio>
#include <iostream>

extern "C" int main (int, char**)

    std::cout << "==== step 1" << std::endl;

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [pool release];

    std::cout << "==== step 2" << std::endl;

    return 0;

..用clang将其编译为位码:

clang -emit-llvm test.mm -c -o test.bc

然后用 lli 运​​行它:

lli -load=/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation test.bc

lli 的输出如下所示:

==== step 1
objc[45353]: Object 0x101a362a0 of class __NSCFString autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
2012-04-29 20:07:35.384 lli[45353:707] -[NSAutoreleasePool init]: unrecognized selector sent to instance 0x101a35170
2012-04-29 20:07:35.386 lli[45353:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSAutoreleasePool init]: unrecognized selector sent to instance 0x101a35170'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff89c76fc6 __exceptionPreprocess + 198
    1   libobjc.A.dylib                     0x00007fff8c9e6d5e objc_exception_throw + 43
    2   CoreFoundation                      0x00007fff89d032ae -[NSObject doesNotRecognizeSelector:] + 190
    3   CoreFoundation                      0x00007fff89c63e73 ___forwarding___ + 371
    4   CoreFoundation                      0x00007fff89c63c88 _CF_forwarding_prep_0 + 232
    5   ???                                 0x0000000101929111 0x0 + 4321349905
    6   lli                                 0x000000010148f36b _ZN4llvm15ExecutionEngine17runFunctionAsMainEPNS_8FunctionERKSt6vectorISsSaISsEEPKPKc + 1259
    7   lli                                 0x0000000101016657 main + 3095
    8   lli                                 0x0000000101015a34 start + 52
    9   ???                                 0x0000000000000003 0x0 + 3
)
terminate called throwing an exception0  lli               0x00000001015c5b02 _ZL15PrintStackTracePv + 34
1  lli               0x00000001015c5fd9 _ZL13SignalHandleri + 633
2  libsystem_c.dylib 0x00007fff8f8bccfa _sigtramp + 26
3  libsystem_c.dylib 0x0000000000000001 _sigtramp + 18446603338107859745
4  libsystem_c.dylib 0x00007fff8f85ba7a abort + 143
5  libc++abi.dylib   0x00007fff8518a7bc abort_message + 214
6  libc++abi.dylib   0x00007fff85187fcf default_terminate() + 28
7  libobjc.A.dylib   0x00007fff8c9e71b9 _objc_terminate + 94
8  libc++abi.dylib   0x00007fff85188001 safe_handler_caller(void (*)()) + 11
9  libc++abi.dylib   0x00007fff8518805c __cxa_bad_typeid + 0
10 libc++abi.dylib   0x00007fff85189152 __gxx_exception_cleanup(_Unwind_Reason_Code, _Unwind_Exception*) + 0
11 libobjc.A.dylib   0x00007fff8c9e6e7a _objc_exception_destructor + 0
12 CoreFoundation    0x00007fff89d032ae -[NSObject doesNotRecognizeSelector:] + 190
13 CoreFoundation    0x00007fff89c63e73 ___forwarding___ + 371
14 CoreFoundation    0x00007fff89c63c88 _CF_forwarding_prep_0 + 232
15 CoreFoundation    0x0000000101929111 _CF_forwarding_prep_0 + 18446603342526043505
16 lli               0x000000010148f36b llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*, std::vector<std::string, std::allocator<std::string> > const&, char const* const*) + 1259
17 lli               0x0000000101016657 main + 3095
18 lli               0x0000000101015a34 start + 52
19 lli               0x0000000000000003 start + 18446744069397718531
Stack dump:
0.  Program arguments: Release/bin/lli -load=/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation /Users/jules/Desktop/test.bc 
Abort trap: 6

正如您在日志中看到的,它说-[NSAutoreleasePool init] 是一个无法识别的选择器。任何其他选择器也会发生同样的情况,例如-[NSString init] 或其他明显应该起作用的东西。

任何帮助或线索将不胜感激!对于这是否是一个错误,或者我做错了什么,或者可能只是一个尚未完成的功能,我有点迷茫。我在 LLVM 文档或互联网上的任何地方都找不到对这个问题的任何引用。

我尝试了不同的 clang 选项,例如遗留的 Obj-C 脆弱 ABI,但没有运气。我不是 LLVM 或 Obj-C 运行时方面的专家,这让我很困惑。

--编辑--

只是更多的信息,希望它可能与某人敲响警钟..

当我尝试用显式调用 objc_msgSend 替换正常的 obj-C 消息调用时,我发现:

SEL s = sel_getUid ("init");
objc_msgSend (myObject, s);   // Succeeds!

SEL s = @selector (init);
objc_msgSend (myObject, s);   // Fails!

..所以当 clang 自动生成 SEL 值时,它似乎生成了一个运行时无法使用的值。谁能建议我应该在 LLVM/Clang 代码库中的哪个位置尝试了解可能发生的情况?

【问题讨论】:

从那以后你在这方面有什么进展吗? 任何有兴趣的人都可以在 LLVM 开发人员上讨论这个问题:Is it possible to execute Objective-C code via LLVM JIT?。 另一个关于运行混合 Objective-C/Swift 代码的线程:How to run mixed Objective-C/Swift code with LLVM JIT?. 【参考方案1】:

Objective-C 使用特殊命名的全局变量来引用选择器,链接器和 ObjC 运行时对这些全局变量有特殊的了解,这使得一切正常工作。 lli 不了解 Objective-C;因此,ObjC 运行时永远不会对有问题的全局变量进行特殊处理。

不过,在我的脑海中,我不知道你到底需要做什么才能让它工作。

【讨论】:

是的,我怀疑是这样的。调试日志确实正确显示了选择器的名称,因此必须在一定程度上初始化结构,但可能存在某种未正确设置的类结构..【参考方案2】:

在对这个主题进行了几个月的研究之后,我想分享一下我的发现。引用我自己的话:

可以在 macOS 系统上使用 LLVM JIT 运行 Objective-C 和 Swift 代码。使其工作的一种方法是对 LLVM JIT 引擎使用的 SectionMemoryManager 进行子类化,在内存中分配与 Objective-C 相关的内存部分时拦截它们,在这些部分中查找 Objective-C 元数据,解析来自的 Objective-C 类信息这个元数据,使用一些 Objective-C Runtime API 方法在 Objective-C 运行时注册找到的 Objective-C 类。

虽然这种方法只针对 Objective-C 代码和 Objective-C 运行时,但它似乎也支持组合 Swift 和 Objective-C 代码:鉴于已注册 Objective-C 类,启用 Objective-C 的 Swift 代码C 互操作性似乎在 LLVM JIT 中没有任何重大问题。

详情见全文:

LLVM JIT, Objective-C and Swift on macOS: knowledge dump.

【讨论】:

以上是关于使用 LLVM ExecutionEngine 调用 Objective-C 方法时,所有选择器都无法识别的主要内容,如果未能解决你的问题,请参考以下文章

llvm 创建函数

如何告诉 LLVM 它可以优化离店?

爬虫日记(85):Scrapy的ExecutionEngine类

C# 中图表的 System.ExecutionEngine 异常

ExecutionEngine.execute(String,Map<String,Object>) 会阻止“NoSQL 注入攻击”吗?

Apache pig 错误 org.apache.pig.backend.hadoop.executionengine.Launcher - 错误:org.apache.avro.file.DataF