Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly ljmp指令在linux内核fork系统调用中做什么?_Assembly_Linux Kernel_X86_Gnu Assembler_Att - Fatal编程技术网

Assembly ljmp指令在linux内核fork系统调用中做什么?

Assembly ljmp指令在linux内核fork系统调用中做什么?,assembly,linux-kernel,x86,gnu-assembler,att,Assembly,Linux Kernel,X86,Gnu Assembler,Att,我正在学习linux内核源代码(旧版本0.11v)。 当我检查fork系统调用时,有一些asm代码用于上下文切换,如下所示: /* * switch_to(n) should switch tasks to task nr n, first * checking that n isn't the current task, in which case it does nothing. * This also clears the TS-flag if the task we switche

我正在学习linux内核源代码(旧版本0.11v)。 当我检查fork系统调用时,有一些asm代码用于上下文切换,如下所示:

/*
 * switch_to(n) should switch tasks to task nr n, first
 * checking that n isn't the current task, in which case it does nothing.
 * This also clears the TS-flag if the task we switched to has used
 * tha math co-processor latest.
 */
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,current\n\t" \
    "je 1f\n\t" \
    "movw %%dx,%1\n\t" \
    "xchgl %%ecx,current\n\t" \
    "ljmp *%0\n\t" \
    "cmpl %%ecx,last_task_used_math\n\t" \
    "jne 1f\n\t" \
    "clts\n" \
    "1:" \
    ::"m" (*&__tmp.a),"m" (*&__tmp.b), \
    "d" (_TSS(n)),"c" ((long) task[n])); \
}
我想
“ljmp%0\n\t”
可以用于更改TSS和LDT。 我知道
ljmp
指令需要两个参数,比如
ljmp$section,$offset
。 我认为
ljmp
指令必须使用
\u TSS(n),xx
。 我们不需要提供有意义的偏移量值,因为cpu将更改cpu的寄存器,包括新任务的eip

  • 我不知道
    ljmp%0
    如何像
    ljmp$section、$offset
    那样工作,也不知道为什么这条指令使用
    %0
    %0
    只是
    \uu tmp.a
    的地址吗

  • 在执行
    ljmp
    指令时,CPU可能会将EIP寄存器保存到TSS以用于旧任务。旧任务的EIP值是
    “cmpl%%ecx,\u last\u task\u used\u math\n\t”
    的地址,对吗

  • 这个语法到底是什么意思? 这种不可读的混乱是GCC的,它的一般格式是

     asm [volatile] ( AssemblerTemplate
                    : OutputOperands
                  [ : InputOperands
                  [ : Clobbers ] ] )
    
    在这种情况下,
    \uu asm\uu
    语句只包含一个
    AssemblerTemplate
    InputOperands
    。输入操作数部分解释了
    %0
    %1
    的含义,以及
    ecx
    edx
    如何获取其值:

    • 第一个输入操作数是
      “m”(*&&uu tmp.a)
      ,因此
      %0
      成为
      \uu tmp.a
      m内存地址(老实说,我不知道这里为什么需要
      *&
    • 第二个输入操作数是
      “m”(*&uu tmp.b)
      ,因此
      %1
      成为
      \uu tmp.b
      m内存地址
    • 第三个输入操作数是
      “d”(\u TSS(n))
      ,因此当此代码启动时,dX寄存器将包含
      \u TSS(n)
    • 第四个输入操作数是
      “c”((长)任务[n])
      ,因此当此代码启动时,EcX寄存器将包含
      task[n]
    清理后,代码可以解释如下

        cmpl %ecx, _current
        je 1f
    
        movw %dx, __tmp.b          ;; the address of __tmp.b
        xchgl %ecx, _current
        ljmp __tmp.a               ;; the address of __tmp.a
    
        cmpl %ecx, _last_task_used_math
        jne 1f
        clts
    1:
    
    ljmp%0
    如何工作? 请注意,
    ljmp
    (也称为
    jmpf
    )指令有两种形式。您知道的一个(操作码
    EA
    )接受两个直接参数:一个用于段,一个用于偏移。此处使用的参数(操作码
    FF/5
    )不同:段和地址参数不在代码流中,而是在内存中的某个位置,指令点位于地址

    在本例中,
    ljmp
    的参数指向
    \uu tmp
    结构的开头。前四个字节(
    \u tmp.a
    )包含偏移量,后面的两个字节(
    \u tmp.b
    的下半部分)包含段

    这个间接的
    ljmp\uu tmp.a
    相当于
    ljmp[\uu tmp.b]:[\uu tmp.a]
    ,除了
    ljmp段:offset
    只能接受立即参数。如果您想切换到任意TSS而不使用自修改代码(这是一个糟糕的想法),那么可以使用间接指令

    还要注意,
    \uu tmp.a
    从未初始化。我们可以假设
    \u TSS(n)
    引用任务门(因为这是使用TSS进行上下文切换的方式),并且忽略“通过”任务门的跳转偏移量

    旧的指令指针到哪里去了? 这段代码不在TSS中存储旧的EIP

    (我在这一点上进行猜测,但我认为这一猜测是合理的。)

    旧EIP存储在与旧任务对应的内核空间堆栈上


    Linux 0.11为每个任务分配一个环0堆栈(即内核的堆栈)(参见
    fork.c
    中的
    copy\u进程
    函数,它初始化TSS)。在任务A期间发生中断时,旧的EIP保存在内核空间堆栈而不是用户空间堆栈上。如果内核决定切换到任务B,那么内核空间堆栈也会切换。当内核最终切换回任务A时,这个堆栈被切换回,通过
    iret
    我们可以返回到任务A中的位置。

    读起来很糟糕,Linus的一些评论可能会很好
    ljmp%0
    将跳转到内存地址%0中包含的48位地址。因此,它将
    ljmp
    有效地发送到内存地址
    \uu tmp
    中包含的地址。您将看到
    movw%%dx,%1
    使用值
    \u TSS(n)
    有效初始化
    \u tmp.b
    _TSS(n)将是任务门的段描述符。您会注意到
    %0
    (\uu tmp.a)未初始化。不需要这样做,因为当通过任务门ljmp时,会忽略偏移量(即_tmp.a表示的偏移量)。实际上,您可以执行ljmp来分段:offset _TSS(n):garbage.ljmp _TSS(n):当_TSS(n)表示任务门选择器时,垃圾将基于任务选择器切换任务,忽略偏移量(因此不需要设置为任何值),并在新任务上下文中ljmp之后继续执行指令。
    cmpl%%ecx,_上次使用的任务将在ljmp之后的新任务上下文中执行。我没有看过旧的内核源代码,但似乎_last_task_used_math是使用数学指令的上一个任务的taskid。如果它与当前的taskid不同,那么就避免使用
    clts
    指令。还要注意的是,TSS任务切换在linux中已被放弃,因此它只是具有历史意义。是的@Jester,我只是对旧内核进行了评论,因为这似乎是OP感兴趣的。Linux自0.11以来已经走过了漫长的道路;)一个受欢迎的补充是解释数学协处理器与TS标志的关系