Assembly 我应该在哪里使用;swapgs“;指示

Assembly 我应该在哪里使用;swapgs“;指示,assembly,operating-system,kernel,x86-64,thread-local-storage,Assembly,Operating System,Kernel,X86 64,Thread Local Storage,您好,我是一名核心学习者,对SWAPG有一些问题 根据AMD的文档,它交换了gs.base隐藏寄存器和内核gsbase MSR 此外,“gs:XXXX”的寻址计算为“gs.base+base+(scale*index)+位移” 现在我的第一个问题是: gs.base是段寄存器的隐藏部分 位移是“gs:XXXX”的“XXXX”部分 索引可以是gs中的选择器索引 那么我应该在哪里存储“基准”和“刻度” 此外,在我应该使用它的地方,我当前的项目将虚拟内存空间的上半部分作为内核,编译器通常不会添加“

您好,我是一名核心学习者,对SWAPG有一些问题

根据AMD的文档,它交换了
gs.base
隐藏寄存器和内核gsbase MSR

此外,“gs:XXXX”的寻址计算为“gs.base+base+(scale*index)+位移”


现在我的第一个问题是:

  • gs.base是段寄存器的隐藏部分
  • 位移是“gs:XXXX”的“XXXX”部分
  • 索引可以是gs中的选择器索引
  • 那么我应该在哪里存储“基准”和“刻度”


    此外,在我应该使用它的地方,我当前的项目将虚拟内存空间的上半部分作为内核,编译器通常不会添加“gs:XXXX”作为寻址引用


    因此,特别是在哪里,我应该使用
    swapgs
    指令。

    如果您的内核不使用gs访问内核私有数据,那么您就不需要使用此指令。它用于内核在从用户模式进入内核时使用,以允许内核保存应用程序的gsbase并加载自己的gsbase,而无需访问内存。然后,内核可以将应用程序状态保存在通过gs引用的数据结构中

    因为您显然已经构建了内核,能够在不使用gs的情况下保存来宾状态,所以不需要使用swapgs。您可能仍然希望在每个上下文开关上保存和恢复fsbase和gsbase以及应用程序的其余状态,以便每个用户进程都有自己的值。

    没有隐藏的“基数和比例”,只有一个隐藏的
    gs.base
    ,您可以在正常寻址模式下使用。(和GS寄存器本身的一个隐藏值。这是一个选择器值,如果您实际执行
    mov GS,eax
    ,而不是仅通过MSR或via修改GS基址,它将作为GDT的索引。但这与完整
    GS:[基址+索引*标度]
    寻址模式中偏移量的索引部分无关)

    在内核的
    syscall
    入口点处理程序中使用
    swapgs
    ,然后在一些加载和存储上使用GS段重写,就像在线程本地存储中一样,因此先前隐藏的
    GS.base
    与在每个加载或存储指令中使用的
    [base+idx*scale]
    寻址模式一起使用。e、 例如
    mov[gs:0x10]、rsp
    保存用户空间堆栈指针,以及
    mov-rsp[gs:0x18]
    加载内核堆栈指针

    swapgs
    的存在是因为
    syscall
    不会将RSP更改为指向内核堆栈(并且不会将用户空间RSP保存在任何位置)。因此,您需要某种线程本地(或实际上是内核本地)存储,以便每个内核都可以为在该内核上运行的任务获取正确的内核堆栈指针。隐藏的GS基是该隐藏指针的存储,并且是一种在不破坏任何体系结构寄存器的值的情况下使用它的方法

    不能只使用常规全局变量(绝对地址),因为它只能有一个所有内核都可以读取的值。您也没有任何备用寄存器(它们都包含宝贵的用户空间状态,稍后需要恢复),并且您也没有内核堆栈来推动它们。您不能使用用户空间RSP;在内核模式下使用用户空间RSP运行
    push
    ,会让用户空间在运行
    syscall
    之前让RSP点位于无效位置,从而导致内核崩溃


    当x86-64最初被设计时(早在2000年,即第一块硅芯片诞生前的几年),解释了
    swapgs
    的预期用途。这是在操作系统开发者注意到AMD如何对其进行规范的问题之后,但最初的电子邮件中包含了一个仍然适用的简单示例:

    示例用法
    在内核入口点,操作系统可以使用SwapGS获取指向内核数据结构的指针,同时保存 用户的GS库。退出后,它可以使用SwapGS恢复用户的GS 基数:

    SystemCallEntryPoint:
    SwapGS;设置内核指针,保存用户的GS库
    mov gs:[SavedUserRSP],rsp;保存用户的堆栈指针
    mov rsp,gs:[KernelStackPtr];设置内核堆栈
    推拉;现在我们有了一个堆栈,保存用户的GPRs
    mov rax,gs:[联合国会员];获取CPU编号<或任何>
    .                             ; 执行系统服务
    .
    SwapGS;还原用户的GS,保存内核指针
    
    您可能还想看看Linux内核在其
    syscall
    入口点中是如何使用它的,最好是在Spectre/Meldown缓解之前的旧内核中。e、 g.其
    条目(ENTRY\u SYSCALL\u 64)
    swapgs
    开头,与AMD的示例非常相似

    (另请参见
    int0x80
    中关于其他Linux内核入口点中发生的情况的一些解释)


    Linux内核源代码中的一些评论指出,确保
    swapgs
    在内核外的每个执行路径上只运行一次可能会带来不便。如果有两个操作码,一个用于“交换到用户gs”,另一个用于“交换到内核gs”,则更容易确保您不会意外地交换额外的时间。该错误将使下一个内核条目看起来不正确。(并将错误的gs分配给用户空间,但在GNU/Linux中,fs用于线程本地存储。)

    基本和缩放是内存访问指令中内存寻址模式的一部分。MSR(和gsbase)仅包含gs段的基址。
      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