【问题标题】:How did Wine64 manage to handle macOS?Wine64 是如何处理 macOS 的?
【发布时间】:2019-04-14 03:05:23
【问题描述】:

这十年来一直是一个主要障碍。是reported as impossibleForum talks referred to issues related to setting and restoring GSWine HQ FAQ 仍然指的是ABI incompatibility page,它不是实时的 wiki 页面,而是新闻存档链接。

Wine 2.0 announced macOS 64-bit support。但是……怎么办?这不是所有 macOS 黑客都应该知道的吗?对于任何 x86-64 黑客来说,可能有一些优雅(或肮脏)的技巧本身就很有趣。

【问题讨论】:

    标签: macos assembly x86-64 wine


    【解决方案1】:

    主要障碍是在操作系统控制下由CPU维护的GS段基地址(GS.base)的冲突。

    在 64 位 Windows 上,GS.base 用于保存每个线程的线程环境块 (TEB) 结构的地址。 Windows 应用程序希望使用%gs-相对地址访问 TEB。这被硬编码到应用程序代码中,而不是在 API 函数后面。

    在 macOS 上,GS.base 用于保存线程struct _pthread 的线程本地存储区域的基址,这是 Pthreads 实现的内部实现细节。 Mac 应用程序中包含硬编码的%gs-relative 访问权限的情况不太常见,但有些应用程序这样做,系统库也是如此。

    在 Linux 上,GS.base 可供 64 位应用程序用于自己的目的。因此,Wine 只是使用操作系统提供的机制来设置它。 Wine 在 macOS 上无法做到这一点。操作系统不仅没有提供任何机制来做到这一点,而且如果 Wine 可以,它会破坏系统库。 (它还会在上下文切换上给内核带来潜在问题和/或内核可能无法恢复 Wine 可能设置的任何值。)

    我们想出的解决方案只是部分解决方案。 TEB 结构中最常访问的字段是“self”字段 (%gs:0x30) 和线程本地存储实现的字段 (%gs:0x58)。通常,如果应用程序需要访问其他字段,它们会首先读取 self 字段,然后引用该字段。

    在 macOS 上,%gs:0x30%gs:0x58 对应于线程本地存储区域的特定插槽。它们属于 Apple 保留的部分(而不是应用程序使用)。我们发现其中一个插槽未被使用。另一个用于 C 库中的 ttyname() 函数。碰巧的是,Wine 从不调用该函数,也没有理由期望它使用的任何系统库都会这样做。

    所以,Wine 只是在那些%gs-relative 位置戳适当的值。因此,当 64 位 Windows 应用程序代码读取它们时,它会得到它需要的东西。 Wine 分配的实际 TEB 位于其他位置(在堆分配的内存中),但应用程序会在它们期望成为 TEB self 字段的位置找到 TEB 的地址,因此它们会以这种方式找到它。

    此后,Apple 慷慨地永久保留了这两个插槽,以供 Wine 之类的用途使用。 ttyname() 现在使用不同的插槽。

    也就是说,如上所述,这个解决方案只是部分的。一些应用程序直接使用%gs-相对地址在0x300x58 以外的偏移量处访问TEB 的其他字段。当他们这样做时,他们会得到垃圾值和/或覆盖系统其他部分使用的值。因此,Wine 对 64 位 Windows 应用程序的支持在 macOS 上并不完整。一些这样的应用程序会崩溃或行为不端。幸运的是,这种情况很少发生,因此在实践中并不是什么大问题。

    作为参考,以下是实现此解决方案的提交:

    http://source.winehq.org/git/wine.git/?a=commit;h=7501942008f91a9a137fe598ce5ce7cb47de5522 http://source.winehq.org/git/wine.git/?a=commit;h=3d8efb238808a519902e047d8673237debb0f0a2

    【讨论】:

    • 值得一提的是,x86-64 Linux 使用fs 作为 TLS 基础。 i386 Linux为此使用gs基地址,但x86-64 Linux选择FS,因为gs在x86-64中对于syscall/swapgs已经是特殊的,在内核内部供内核查找当前任务的来自系统调用入口点的内核堆栈。
    • “如果 Wine 可以,它会破坏系统库”——你能详细说明一下吗?在 i386 Linux 上,glibc(又称“系统库”)对 fs 有类似的期望,但这种转变由 Wine 处理多年。 macOS x86-64 有什么不同?任何程序都可以通过重写 GS 来破坏内核吗?我有一些想法。如果你不能创建任意 GS.base,你可以创建新的 macOS 线程,让它与它的 GS 通信,了解它的位置,永远挂起线程,用类似 Windows 的 TEB 覆盖这个内存,并在 Wine 线程中使用这个被盗的 GS。它会起作用吗?
    • @OCTAGRAM Wine 线程既运行 Windows 代码又调用系统库。 Windows 代码和系统库对 GS.base 中的信息有不同的期望。他们不能同时满足。 Wine 可以尝试在适当的边界上调整 GS.base。我们考虑过这一点,但是 a) 很难把杂耍的代码放在所有必要的地方; b) 如果 GS.base 与预期不同时发生陷阱,我们不确定内核是否能够应对,或者它是否会正确恢复 GS.base; c) 在任何情况下,内核都不允许这样做。
    • 我不认为你对 i386 Linux 上的 FS 是正确的。我认为应用程序可以免费使用。在 macOS 上,程序无法通过重写 GS 来破坏内核,因为它们无法重写它。它受到保护。他们需要操作系统的帮助才能这样做。在 Linux 上也是如此,但 Linux 以 arch_prctl(ARCH_SET_GS, ...) 的形式提供此类帮助。从另一个线程“窃取” GS 不起作用。重要的是正在运行的线程的上下文中的 GS.base。一个线程的 GS.base 对其他线程是不可见的,对它们没有特殊意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-11
    • 1970-01-01
    • 1970-01-01
    • 2019-01-08
    • 2020-06-03
    • 2021-06-07
    • 1970-01-01
    相关资源
    最近更新 更多