我应该在哪里使用 "swapgs" 指令
Where I should use "swapgs" instruction
您好,我是内核学习者,对 swapgs 有一些疑问。
根据 AMD 的文档,它交换了 gs.base
隐藏寄存器和 KernelGSBase MSR。
此外,用“gs:XXXX”寻址计算为“gs.base + base + (scale*index) + displacement”
现在我的第一个问题是:
- gs.base是段寄存器的隐藏部分
- 位移是“gs:XXXX”的“XXXX”部分
- index 可能是 gs 中的选择器索引
那我应该把“base”和“scale”放在哪里呢?
此外,在我应该使用它的地方,我当前的项目将虚拟内存space的上半部分作为内核,编译器通常不会添加“gs:XXXX”作为寻址参考。
所以,特别是我应该使用 swapgs
指令的地方。
如果你的内核不使用gs来访问内核私有数据,那么你就没有必要使用这个指令。它旨在供内核在从用户模式进入内核时使用,以允许内核保存应用程序的 gsbase 并加载自己的 gsbase,而无需访问内存。然后内核可以将应用程序状态保存在通过 gs.
引用的数据结构中
由于您显然已将内核构建为能够在不使用 gs 的情况下保存来宾状态,因此您不需要使用 swapgs。您可能仍然希望在每次上下文切换时保存和恢复 fsbase 和 gsbase 以及应用程序的其余状态,以便每个用户进程都可以有自己的值。
没有隐藏的“base and scale”,只有一个隐藏的gs.base
,你可以在正常的寻址模式下使用。 (以及 GS 寄存器本身的隐藏值。这是一个选择器值,如果您确实 mov gs, eax
而不是仅修改 GS,它将充当 GDT 的索引 通过 MSR 或通过 wrgsbase
建立基址。但这与完整 gs:[base + index*scale]
寻址模式中偏移量的索引部分无关。
您在内核的 syscall
入口点处理程序中使用 swapgs
,然后在某些加载和存储上使用 GS 段覆盖,就像您对线程本地存储所做的那样,因此之前隐藏的 gs.base
用于 和 您在每个加载或存储指令中使用的 [base + idx*scale]
寻址模式。例如像 mov [gs:0x10], rsp
保存用户-space 堆栈指针和 mov rsp, [gs:0x18]
加载内核堆栈指针。
swapgs
存在是因为 syscall
不会将 RSP 更改为指向内核堆栈(并且不会保存用户-space RSP 任何地方)。因此,您需要某种线程本地(或实际上是内核本地)存储,以便每个内核都能为该内核上的任务 运行 获取正确的内核堆栈指针。隐藏的 GS 基是隐藏指针的存储,以及一种在不破坏任何体系结构寄存器的值的情况下使用它的方法。
您不能只使用一个常规的全局变量(绝对地址),因为它只能有一个所有内核都可以读取的值。您也没有任何备用寄存器(它们都包含您稍后需要恢复的宝贵用户 space 状态),并且您没有内核堆栈来推动它们。而且你不能使用 user-space RSP; 运行 push
在带有 user-space RSP 的内核模式下,user-space 会通过在 运行 [=16] 之前使 RSP 点无效而使内核崩溃=].
最初设计 x86-64 时(早在 2000 年,比第一个硅还早几年),this mailing list message explained the intended purpose of swapgs
. It was revised a day later 在 OS 之后,开发人员注意到 AMD 对其进行规范的方式存在问题,但最初电子邮件包含一个仍然适用的简单示例:
Example usage
At a kernel entry point the OS can use SwapGS to obtain a pointer to kernel data structures and simultaneously save the
user's GS base. Upon exit it can use SwapGS to restore the user's GS
base:
SystemCallEntryPoint:
SwapGS ; set up kernel pointer, save user's GS base
mov gs:[SavedUserRSP], rsp ; save user's stack pointer
mov rsp, gs:[KernelStackPtr] ; set up kernel stack
push rax ; now that we have a stack, save user's GPRs
mov rax, gs:[CPUnumber] ; get CPU number < or whatever >
. ; perform system service
.
SwapGS ; restore user's GS, save kernel pointer
您可能还想看看 Linux 内核如何在其 syscall
入口点使用它,最好是在 Spectre / Meltdown 缓解措施使一切复杂化之前的旧内核中。例如Linux 4.12's entry_64.S
的 ENTRY(entry_SYSCALL_64)
以 swapgs
开头,非常像 AMD 的示例。
(另请参阅 Why does Windows64 use a different calling convention from all other OSes on x86-64? 了解其他 Linux 内核入口点发生的情况,来自 int 0x80
)。
Linux 内核源代码中的一些评论指出,确保 swapgs
在内核外的每条执行路径上只运行一次可能很不方便。如果有两个操作码,一个用于“swap to user gs”,一个用于“swap to kernel gs”,则更容易确保您不会不小心交换额外的时间。该错误会使下一个内核条目在错误的位置查找。 (并给 user-space 错误的 gs,但在 GNU/Linux fs
中用于线程本地存储。)
您好,我是内核学习者,对 swapgs 有一些疑问。
根据 AMD 的文档,它交换了 gs.base
隐藏寄存器和 KernelGSBase MSR。
此外,用“gs:XXXX”寻址计算为“gs.base + base + (scale*index) + displacement”
现在我的第一个问题是:
- gs.base是段寄存器的隐藏部分
- 位移是“gs:XXXX”的“XXXX”部分
- index 可能是 gs 中的选择器索引
那我应该把“base”和“scale”放在哪里呢?
此外,在我应该使用它的地方,我当前的项目将虚拟内存space的上半部分作为内核,编译器通常不会添加“gs:XXXX”作为寻址参考。
所以,特别是我应该使用 swapgs
指令的地方。
如果你的内核不使用gs来访问内核私有数据,那么你就没有必要使用这个指令。它旨在供内核在从用户模式进入内核时使用,以允许内核保存应用程序的 gsbase 并加载自己的 gsbase,而无需访问内存。然后内核可以将应用程序状态保存在通过 gs.
引用的数据结构中由于您显然已将内核构建为能够在不使用 gs 的情况下保存来宾状态,因此您不需要使用 swapgs。您可能仍然希望在每次上下文切换时保存和恢复 fsbase 和 gsbase 以及应用程序的其余状态,以便每个用户进程都可以有自己的值。
没有隐藏的“base and scale”,只有一个隐藏的gs.base
,你可以在正常的寻址模式下使用。 (以及 GS 寄存器本身的隐藏值。这是一个选择器值,如果您确实 mov gs, eax
而不是仅修改 GS,它将充当 GDT 的索引 通过 MSR 或通过 wrgsbase
建立基址。但这与完整 gs:[base + index*scale]
寻址模式中偏移量的索引部分无关。
您在内核的 syscall
入口点处理程序中使用 swapgs
,然后在某些加载和存储上使用 GS 段覆盖,就像您对线程本地存储所做的那样,因此之前隐藏的 gs.base
用于 和 您在每个加载或存储指令中使用的 [base + idx*scale]
寻址模式。例如像 mov [gs:0x10], rsp
保存用户-space 堆栈指针和 mov rsp, [gs:0x18]
加载内核堆栈指针。
swapgs
存在是因为 syscall
不会将 RSP 更改为指向内核堆栈(并且不会保存用户-space RSP 任何地方)。因此,您需要某种线程本地(或实际上是内核本地)存储,以便每个内核都能为该内核上的任务 运行 获取正确的内核堆栈指针。隐藏的 GS 基是隐藏指针的存储,以及一种在不破坏任何体系结构寄存器的值的情况下使用它的方法。
您不能只使用一个常规的全局变量(绝对地址),因为它只能有一个所有内核都可以读取的值。您也没有任何备用寄存器(它们都包含您稍后需要恢复的宝贵用户 space 状态),并且您没有内核堆栈来推动它们。而且你不能使用 user-space RSP; 运行 push
在带有 user-space RSP 的内核模式下,user-space 会通过在 运行 [=16] 之前使 RSP 点无效而使内核崩溃=].
最初设计 x86-64 时(早在 2000 年,比第一个硅还早几年),this mailing list message explained the intended purpose of swapgs
. It was revised a day later 在 OS 之后,开发人员注意到 AMD 对其进行规范的方式存在问题,但最初电子邮件包含一个仍然适用的简单示例:
Example usage
At a kernel entry point the OS can use SwapGS to obtain a pointer to kernel data structures and simultaneously save the user's GS base. Upon exit it can use SwapGS to restore the user's GS base:SystemCallEntryPoint: SwapGS ; set up kernel pointer, save user's GS base mov gs:[SavedUserRSP], rsp ; save user's stack pointer mov rsp, gs:[KernelStackPtr] ; set up kernel stack push rax ; now that we have a stack, save user's GPRs mov rax, gs:[CPUnumber] ; get CPU number < or whatever > . ; perform system service . SwapGS ; restore user's GS, save kernel pointer
您可能还想看看 Linux 内核如何在其 syscall
入口点使用它,最好是在 Spectre / Meltdown 缓解措施使一切复杂化之前的旧内核中。例如Linux 4.12's entry_64.S
的 ENTRY(entry_SYSCALL_64)
以 swapgs
开头,非常像 AMD 的示例。
(另请参阅 Why does Windows64 use a different calling convention from all other OSes on x86-64? 了解其他 Linux 内核入口点发生的情况,来自 int 0x80
)。
Linux 内核源代码中的一些评论指出,确保 swapgs
在内核外的每条执行路径上只运行一次可能很不方便。如果有两个操作码,一个用于“swap to user gs”,一个用于“swap to kernel gs”,则更容易确保您不会不小心交换额外的时间。该错误会使下一个内核条目在错误的位置查找。 (并给 user-space 错误的 gs,但在 GNU/Linux fs
中用于线程本地存储。)