Wine64如何处理macOS系统?

6
1个回答

15
主要问题是CPU在操作系统控制下维护的GS段基地址(GS.base)存在冲突。
在64位Windows上,GS.base用于保存每个线程的线程环境块(TEB)结构体的地址。Windows应用程序希望使用%gs相对地址访问TEB。这是硬编码到应用程序代码中而不是在API函数后面。
在macOS上,GS.base用于保存线程的struct _pthread的线程本地存储区域的基地址,这是Pthreads实现的内部实现细节。Mac应用程序很少硬编码%gs相对访问,但有些应用程序和系统库会这样做。
在Linux上,GS.base可供64位应用程序用于自己的目的。因此,在那里,Wine只需使用操作系统提供的机制设置它。Wine无法在macOS上执行此操作。操作系统不仅没有提供任何机制来执行此操作,而且如果Wine可以这样做,它将破坏系统库。(它还可能在上下文切换时对内核造成潜在问题,或者内核可能无法恢复Wine可能设置的任何值。)

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

在macOS上,%gs:0x30%gs:0x58对应于线程本地存储区的特定槽位。它们位于一个保留给苹果公司 (而不是应用程序使用) 的部分中。我们发现其中一个槽位未使用。另一个槽位用于C库中的ttyname()函数。恰好,Wine从未调用该函数,并且没有理由期望它所使用的任何系统库也会这样做。

因此,Wine只需将适当的值插入到这些%gs相关位置。因此,当64位Windows应用程序代码读取它们时,它会得到所需的内容。Wine分配的实际TEB位于其他位置(在堆分配的内存中),但应用程序在它们期望的TEB self字段位置找到了TEB的地址,因此它们以这种方式找到了它。

苹果公司已经慷慨地永久保留了这两个槽位,供像Wine这样的使用。ttyname()现在使用不同的槽位。

如上所述,这个解决方案只是部分地解决了问题。一些应用程序直接使用%gs相对地址访问TEB的其他字段,偏移量不是0x300x58。当它们这样做时,它们会获得垃圾值和/或覆盖系统其他部分使用的值。因此,Wine对于macOS上的64位Windows应用程序的支持并不完整。一些这样的应用程序会崩溃或表现不当。幸运的是,它发生得不太频繁,在实践中不是很大的问题。

以下是实施此解决方案的提交记录:

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


1
值得一提的是,x86-64 Linux使用fs作为TLS基址。i386 Linux使用gs基址,但是x86-64 Linux选择了FS,因为在x86-64中,gs已经对于内核中的syscall / [swapgs](http://felixcloutier.com/x86/SWAPGS.html)是特殊的,内核需要从系统调用入口点找到当前任务的内核栈。 - Peter Cordes
如果Wine可以的话,它会破坏系统库。你能详细解释一下吗?在i386 Linux上,glibc(又称“系统库”)对fs有类似的期望,但这个转换由Wine处理了多年。那么在macOS x86-64上有什么不同呢?任何程序都可以通过重写GS来破坏内核吗?我有一些想法。如果您无法创建任意的GS.base,您可以创建一个新的macOS线程,使其通信其GS,学习其位置,永远暂停该线程,用类似Windows的TEB覆盖此内存,并在Wine线程中使用此窃取的GS。这样行得通吗? - OCTAGRAM
@OCTAGRAM Wine线程既可以运行Windows代码,也可以调用系统库。Windows代码和系统库对于GS.base中的信息有不同的期望。它们不能同时满足。Wine可以尝试在适当的边界处操纵GS.base。我们考虑过这一点,但是a)很难将那些操纵代码放在所有必要的位置;b)我们不确定内核是否能够处理如果陷阱发生时GS.base与其预期不同或者它是否能恢复GS.base;c)无论如何,内核都不允许这样做。 - Ken Thomases
1
我认为你对i386 Linux上的FS不正确。我认为应用程序可以免费使用它。在macOS上,程序无法通过重写GS来破坏内核,因为它们无法重写它。它是受保护的。他们需要操作系统的帮助才能这样做。在Linux上也是如此,但Linux通过arch_prctl(ARCH_SET_GS, ...)提供了这样的帮助。从另一个线程“窃取”GS是行不通的。重要的是正在运行的线程上下文中的GS.base。一个线程的GS.base对其他线程不可见,对它们没有特殊意义。 - Ken Thomases

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接