首页 > 解决方案 > 在 macOS 桌面应用程序中嵌入 Racket 静态库(Xcode 项目)

问题描述

互联网上有一篇关于将手动构建的 Racket 库嵌入 iOS 应用程序的文章。忽略这种可能性比有用更有趣的事实,我已经按照说明进行操作并且(ta-dah)能够创建一个工作示例(令我惊讶!)。

无论如何,我更渴望在 macOS Xcode 项目中重复一个技巧。Racket 发行版中包含 Racket.framework,它与 gcc(和-framework选项)一起工作得很好,但从 Xcode 的角度来看,这个框架没有有效的结构,xcodetools 无法链接到它,而且代码设计。

因此,我决定遵循文章中的 iOS 说明,但针对 macOS。我使用没有指定主机librktiolibracketlibmzgc./configure

(注意:两者的最终结果相同)。

我创建了互操作 C 源代码:

#include "scheme.h"
#include "interop.h"
#include "racketmac.c" // <- this is my rkt module made with raco ctool

static int init(Scheme_Env *e, int argc, char *argv[]) {
    declare_modules(e);

    return 0;
}

int init_racket() {
    return scheme_main_setup(1, init, 0, NULL);
}

和标题:

#ifndef Interop_h
#define Interop_h

int init_racket(void);

#endif /* Interop_h */

,然后将“interop.h”添加到 Swift 的桥接头和 C 函数调用中,例如:

import Cocoa

final public class RacketInteractor: NSObject {

    public override init() {
        super.init()

        init_racket()
    }    
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var interactor: RacketInteractor!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        interactor = RacketInteractor()
    }

// ...

}

项目,macOS Cocoa 应用程序,是可构建和可运行的。但是init_racket每次调用都会导致 EXC_BAD_ACCESS。研究堆栈跟踪让我了解到方案解释器试图发出“内存不足”错误(查找第 5 帧):

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x10ad2153d)
    frame #0: 0x000000010ad2153d
  * frame #1: 0x0000000100102814 RacketMapApp`scheme_native_stack_trace at jitstack.c:215:7 [opt]
    frame #2: 0x0000000100058f7b RacketMapApp`continuation_marks(p=0x000000010a3b1250, _cont=0x0000000000000000, econt=0x0000000000000000, mc=<unavailable>, prompt_tag=0x000000010a4001d0, who="continuation-marks", just_chain=0) at fun.c:7906:10 [opt]
    frame #3: 0x000000010002d565 RacketMapApp`do_raise(arg=0x000000010ac49028, need_debug=1, eb=180654120) at error.c:4606:13 [opt]
    frame #4: 0x0000000100028f8a RacketMapApp`scheme_raise_exn(id=17) at error.c:4402:3 [opt]
    frame #5: 0x000000010002c4f7 RacketMapApp`scheme_raise_out_of_memory(where=<unavailable>, msg=<unavailable>) at error.c:2541:3 [opt]
    frame #6: 0x00000001001a02af RacketMapApp`scheme_malloc_code [inlined] malloc_page(size=<unavailable>) at salloc.c:1047:5 [opt]
    frame #7: 0x00000001001a0271 RacketMapApp`scheme_malloc_code(size=35320) at salloc.c:1156 [opt]
    frame #8: 0x00000001001035ef RacketMapApp`scheme_generate_one(old_jitter=0x0000000000000000, generate=(RacketMapApp`scheme_do_generate_common at jitcommon.c:3576), data=0x0000000000000000, gcable=0, save_ptr=0x0000000000000000, ndata=0x0000000000000000) at jitstate.c:256:18 [opt]
    frame #9: 0x000000010008f593 RacketMapApp`create_native_lambda(lam=0x000000010ac5b348, clear_code_after_jit=1, case_lam=0x0000000000000000) at jit.c:4127:5 [opt]
    frame #10: 0x0000000100101f4b RacketMapApp`scheme_jit_closure(code=0x000000010ac5b308, context=0x0000000000000000) at jitprep.c:558:13 [opt]
    frame #11: 0x0000000100101abe RacketMapApp`jit_expr(expr=0x000000010a2e3c68) at jitprep.c:0 [opt]
    frame #12: 0x0000000100101cd9 RacketMapApp`jit_expr [inlined] define_values_jit(data=<unavailable>) at jitprep.c:301:12 [opt]
    frame #13: 0x0000000100101c82 RacketMapApp`jit_expr(expr=0x000000010ac48fe8) at jitprep.c:651 [opt]
    frame #14: 0x00000001001020ce RacketMapApp`scheme_jit_linklet(linklet=0x000000010a4bfb88, step=<unavailable>) at jitprep.c:704:9 [opt]
    frame #15: 0x00000001001088f4 RacketMapApp`instantiate_linklet_k at linklet.c:0 [opt]
    frame #16: 0x000000010004ffd9 RacketMapApp`scheme_top_level_do_worker(k=(RacketMapApp`instantiate_linklet_k at linklet.c:1325), eb=<unavailable>, new_thread=0) at fun.c:1314:11 [opt]
    frame #17: 0x000000010002450f RacketMapApp`scheme_basic_env [inlined] place_instance_init(stack_base=<unavailable>, initial_main_os_thread=1) at env.c:501:3 [opt]
    frame #18: 0x000000010002436b RacketMapApp`scheme_basic_env at env.c:214 [opt]
    frame #19: 0x000000010019f7f8 RacketMapApp`scheme_main_setup [inlined] call_with_basic(data=<unavailable>) at salloc.c:178:16 [opt]
    frame #20: 0x000000010019f7f3 RacketMapApp`scheme_main_setup [inlined] do_main_stack_setup(no_auto_statics=<unavailable>, data=<unavailable>) at salloc.c:203 [opt]
    frame #21: 0x000000010019f7c3 RacketMapApp`scheme_main_setup [inlined] scheme_main_stack_setup(no_auto_statics=<unavailable>, data=<unavailable>) at salloc.c:337 [opt]
    frame #22: 0x000000010019f758 RacketMapApp`scheme_main_setup(no_auto_statics=<unavailable>, _main=(RacketMapApp`init at interop.c:14), argc=0, argv=0x0000000000000000) at salloc.c:187 [opt]
    frame #23: 0x00000001000017fb RacketMapApp`@objc RacketInteractor.init() [inlined] RacketMapApp.RacketInteractor.init() -> RacketMapApp.RacketInteractor at RacketInteractor.swift:19:9 [opt]
    frame #24: 0x00000001000017bc RacketMapApp`@objc RacketInteractor.init() at <compiler-generated>:15 [opt]
    frame #25: 0x00000001000024ed RacketMapApp`@objc AppDelegate.applicationDidFinishLaunching(_:) [inlined] RacketMapApp.RacketInteractor.__allocating_init() -> RacketMapApp.RacketInteractor at <compiler-generated>:0 [opt]
    frame #26: 0x00000001000024e2 RacketMapApp`@objc AppDelegate.applicationDidFinishLaunching(_:) [inlined] RacketMapApp.AppDelegate.applicationDidFinishLaunching(self=0x0000600000004320) -> () at AppDelegate.swift:17 [opt]
    frame #27: 0x00000001000024e2 RacketMapApp`@objc AppDelegate.applicationDidFinishLaunching(_:) at <compiler-generated>:16 [opt]
    frame #28: 0x00007fff3443135f CoreFoundation`__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
    frame #29: 0x00007fff344312f3 CoreFoundation`___CFXRegistrationPost1_block_invoke + 63
    frame #30: 0x00007fff34431268 CoreFoundation`_CFXRegistrationPost1 + 372
    frame #31: 0x00007fff34430ebe CoreFoundation`___CFXNotificationPost_block_invoke + 97
    frame #32: 0x00007fff344007e2 CoreFoundation`-[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1575
    frame #33: 0x00007fff343ffc82 CoreFoundation`_CFXNotificationPost + 1351
    frame #34: 0x00007fff36a85a02 Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 59
    frame #35: 0x00007fff3160b2ff AppKit`-[NSApplication _postDidFinishNotification] + 312
    frame #36: 0x00007fff3160b042 AppKit`-[NSApplication _sendFinishLaunchingNotification] + 208
    frame #37: 0x00007fff31608103 AppKit`-[NSApplication(NSAppleEventHandling) _handleAEOpenEvent:] + 549
    frame #38: 0x00007fff31607d49 AppKit`-[NSApplication(NSAppleEventHandling) _handleCoreEvent:withReplyEvent:] + 688
    frame #39: 0x00007fff36ab1226 Foundation`-[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:] + 308
    frame #40: 0x00007fff36ab1090 Foundation`_NSAppleEventManagerGenericHandler + 98
    frame #41: 0x00007fff357b5092 AE`___lldb_unnamed_symbol77$$AE + 2172
    frame #42: 0x00007fff357b47b9 AE`___lldb_unnamed_symbol76$$AE + 41
    frame #43: 0x00007fff357aca27 AE`aeProcessAppleEvent + 449
    frame #44: 0x00007fff32fa22b8 HIToolbox`AEProcessAppleEvent + 54
    frame #45: 0x00007fff3160215c AppKit`_DPSNextEvent + 1670
    frame #46: 0x00007fff31600690 AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1352
    frame #47: 0x00007fff315f23ae AppKit`-[NSApplication run] + 658
    frame #48: 0x00007fff315c4775 AppKit`NSApplicationMain + 777
    frame #49: 0x0000000100001a29 RacketMapApp`main at AppDelegate.swift:12:7 [opt]
    frame #50: 0x00007fff6ba967fd libdyld.dylib`start + 1
    frame #51: 0x00007fff6ba967fd libdyld.dylib`start + 1

我尝试使用互操作源创建静态库并将其添加到 Xcode 项目中,结果相同。同时,createdlibrktiolibracketgcc libmzgccli 完全可以使用。所以我很困惑,因为我什至不知道如何正确调试此类错误。也许我需要调整一些 Xcode 构建选项?为什么支持 iOS 的库运行良好,而支持 macOS 的库却不行?有没有更简单的方法将 Racket 嵌入到 Xcode macOS 项目中(例如,通过 CMake 生成它)?

编辑:构建 racket3m 版本时,崩溃消息更清晰,例如:

“警告:无法保护 16384 字节的页面 0x10a350000(os/kern) 保护失败”

这就像内存分配的系统调用没有提供足够的资源,这很奇怪。

标签: swiftmacosschemeracket

解决方案


“警告:无法保护 16384 字节的页面 0x10a350000(os/kern) 保护失败”

当您的应用缺少“允许未签名的可执行内存”权利时发生。确保您的应用程序具有该权限和“允许 JIT”权限。

但是一旦你克服了这一点,你就会遇到 Racket 3m 的 GC 写入障碍,我在文章中简要地谈到了这一点。为了使其在调试模式下工作,您必须在 lldb 中注册一个信号处理程序,以便SIGSEGV在它发生时传递预期的值。不幸的是,有一个LLDB 错误,这在 arm 上不起作用,但它可能适用于 x86-64。


推荐阅读