在 x86 上,Zircon 需要为在 Ring 0(内核模式)中运行的代码保持以下不变性。
这里记录了这些不变特性,因为它们不一定易于测试;Zircon 的测试套件不会捕获到破坏不变变异。
标记会注册:
- 方向标记 (DF) 应为 0。这是 x86 调用规范的要求。
如果将此标志设置为 1,使用 x86 字符串指令(例如,在
memcpy()
中使用rep movs
或由编译器内联)可能会出错,并且复制方向错误。函数可以将此标志暂时设置为 1,前提是在返回或调用其他函数之前将其改回 0。- 对齐检查标志 (AC) 通常应为 0。在支持 SMAP 的 CPU 上,这样可以防止内核意外读取或写入用户空间数据。
每当在内核模式下运行并启用中断时,
gs_base
寄存器都必须指向当前 CPU 的x86_percpu
结构体。只有在停用中断时,才能将gs_base
更改为指向其他内容。例如,swapgs
指令应仅在停用中断时使用。以下各项由编译器部分强制执行:
- 不允许使用扩展寄存器(SSE、AVX、x87 等),因为这会破坏用户区的注册状态。
这部分是通过将
-mno-sse
传递给编译器来实现的。要防止编译器在优化(例如内存副本)中使用 SSE 寄存器,必须使用此选项。我们希望防止在内核代码中意外使用
float
或double
类型,但 GCC 和 Clang 无法在所有情况下都能做到这一点。-mno-sse
不会阻止将float
/double
与任一编译器一起使用;编译器将改用 x87 指令。我们使用
-msoft-float
进行编译,这似乎可以阻止 GCC 生成 x87 指令(并因此使用 x87 寄存器):GCC 6.3.0 会针对float
/double
算术和返回值发出错误,但不会阻止这些类型作为参数传递。但是,将-msoft-float
传递给 Clang 似乎没有任何作用:Clang 7.0.0 仍会为使用float
或double
的代码生成 x87 指令(并使用 x87 寄存器)。- 没有将数据存储在堆栈中的
%rsp
以下。请注意,userland 代码可以做到这一点:SysV x86-64 ABI 允许函数将数据存储在“红色区域”(即 %rsp 以下 128 个字节)中。不过,内核代码无法使用红色区域,因为中断可能会破坏此区域 - CPU 在调用中断处理程序时,会将数据推送到 %rsp 正下方的堆栈。
这通常是通过将
-mno-red-zone
传递给编译器来实现的。