鱼C论坛

 找回密码
 立即注册
查看: 3630|回复: 2

[学习笔记] 【原创】保护模式CALL指令-远跳转相关翻译资料(2)-CALL跳转过程逻辑说明

[复制链接]
发表于 2017-12-24 14:45:25 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 兰陵月 于 2017-12-24 16:03 编辑

逻辑代码段1
        IF far call and (PE = 1 and VM = 0) (* Protected mode or IA-32e Mode, not virtual-8086 mode*)
        THEN
                IF segment selector in target operand NULL
                THEN #GP(0); FI;
                
                IF segment selector index not within descriptor table limits
                THEN #GP(new code segment selector); FI;
                
                Read type and access rights of selected segment descriptor;

                IF IA32_EFER.LMA = 0
                THEN
                        IF segment type is not a conforming or nonconforming code segment, call gate, task gate, or TSS
                        THEN #GP(segment selector); FI;
                ELSE
                        IF segment type is not a conforming or nonconforming code segment or 64-bit call gate,
                        THEN #GP(segment selector); FI;
                FI;

                Depending on type and access rights:
                GO TO CONFORMING-CODE-SEGMENT;
                GO TO NONCONFORMING-CODE-SEGMENT;
                GO TO CALL-GATE;
                GO TO TASK-GATE;
                GO TO TASK-STATE-SEGMENT;
        FI;

                               
登录/注册后可看大图

                               
登录/注册后可看大图

逻辑代码段2【由逻辑代码段1第21行跳转而来】-跳转到一致代码段
  CONFORMING-CODE-SEGMENT:
    IF L bit = 1 and D bit = 1 and IA32_EFER.LMA = 1
      THEN GP(new code segment selector); FI;
    
        IF DPL > CPL
      THEN #GP(new code segment selector); FI;

        IF segment not present
      THEN #NP(new code segment selector); FI;
    
        IF stack not large enough for return address
      THEN #SS(0); FI;
    
        tempEIP ← DEST(Offset);
    
        IF OperandSize = 16
      THEN
        tempEIP ← tempEIP AND 0000FFFFH; FI; (* Clear upper 16 bits *)
    
        IF (EFER.LMA = 0 or target mode = Compatibility mode) and (tempEIP outside new code segment limit)
      THEN #GP(0); FI;

        IF tempEIP is non-canonical
      THEN #GP(0); FI;
    
        IF OperandSize = 32
      THEN
        Push(CS); (* Padded with 16 high-order bits *)
        Push(EIP);
        CS ← DEST(CodeSegmentSelector);
        (* Segment descriptor information also loaded *)
        CS(RPL) ← CPL;
        EIP ← tempEIP;
      ELSE
        IF OperandSize = 16
          THEN
            Push(CS);
            Push(IP);
            CS ← DEST(CodeSegmentSelector);
            (* Segment descriptor information also loaded *)
            CS(RPL) ← CPL;
            EIP ← tempEIP;
          ELSE (* OperandSize = 64 *)
            Push(CS); (* Padded with 48 high-order bits *)
            Push(RIP);
            CS ← DEST(CodeSegmentSelector);
            (* Segment descriptor information also loaded *)
            CS(RPL) ← CPL;
            RIP ← tempEIP;
        FI;
    FI;
  END;
可以看到,上述各种情况下,CS段寄存器的RPL均被当前特权级CPL刷新,导致调用后的当前特权级CPL仍然为调用前的CPL的数值,程序跳转后运行在调用前的CPL级别下。

                               
登录/注册后可看大图

                               
登录/注册后可看大图

逻辑代码段3【由逻辑代码段1第22行跳转而来】-跳转到非一致代码段
NONCONFORMING-CODE-SEGMENT:
        IF L-Bit = 1 and D-BIT = 1 and IA32_EFER.LMA = 1
          THEN GP(new code segment selector); FI;
        
        IF (RPL > CPL) or (DPL ≠ CPL)
          THEN #GP(new code segment selector); FI;
        
        IF segment not present
          THEN #NP(new code segment selector); FI;

        IF stack not large enough for return address
          THEN #SS(0); FI;

        tempEIP ← DEST(Offset);

        IF OperandSize = 16
          THEN tempEIP ← tempEIP AND 0000FFFFH; FI; (* Clear upper 16 bits *)

        IF (EFER.LMA = 0 or target mode = Compatibility mode) and (tempEIP outside new code segment limit)
          THEN #GP(0); FI;

        IF tempEIP is non-canonical
          THEN #GP(0); FI;
        
        IF OperandSize = 32
          THEN
                Push(CS); (* Padded with 16 high-order bits *)
                Push(EIP);
                CS ← DEST(CodeSegmentSelector);
                (* Segment descriptor information also loaded *)
                CS(RPL) ← CPL;
                EIP ← tempEIP;
          ELSE        
                IF OperandSize = 16
                  THEN
                        Push(CS);
                        Push(IP);
                        CS ← DEST(CodeSegmentSelector);
                        (* Segment descriptor information also loaded *)
                        CS(RPL) ← CPL;
                        EIP ← tempEIP;
                  ELSE (* OperandSize = 64 *)
                        Push(CS); (* Padded with 48 high-order bits *)
                        Push(RIP);
                        CS ← DEST(CodeSegmentSelector);
                        (* Segment descriptor information also loaded *)
                        CS(RPL) ← CPL;
                        RIP ← tempEIP;
                FI;
        FI;
  END;
可以看到,上述各种情况下,CS段寄存器的RPL均被当前特权级CPL刷新,导致调用后的当前特权级CPL仍然为调用前的CPL的数值,程序跳转后运行在调用前的CPL级别下。

                               
登录/注册后可看大图

                               
登录/注册后可看大图

逻辑代码段4【由逻辑代码段1第23行跳转而来】-通过调用门调用
  CALL-GATE:
    IF call gate (DPL < CPL) or (RPL > DPL)
      THEN #GP(call-gate selector); FI;
    
        IF call gate not present
      THEN #NP(call-gate selector); FI;
    
        IF call-gate code-segment selector is NULL
      THEN #GP(0); FI;
    
        IF call-gate code-segment selector index is outside descriptor table limits
      THEN #GP(call-gate code-segment selector); FI;
    
        Read call-gate code-segment descriptor;
    
        IF call-gate code-segment descriptor does not indicate a code segment
       or call-gate code-segment descriptor DPL > CPL
      THEN #GP(call-gate code-segment selector); FI;
    
        IF IA32_EFER.LMA = 1 AND (call-gate code-segment descriptor is
       not a 64-bit code segment or call-gate code-segment descriptor has both L-bit and D-bit set)
      THEN #GP(call-gate code-segment selector); FI;
    
        IF call-gate code segment not present
      THEN #NP(call-gate code-segment selector); FI;
    
        IF call-gate code segment is non-conforming and DPL < CPL
      THEN go to MORE-PRIVILEGE;
      ELSE go to SAME-PRIVILEGE;
    FI;
  END;

                               
登录/注册后可看大图

                               
登录/注册后可看大图

逻辑代码段5【由逻辑代码段4第28行跳转而来】-通过调用门(低特权级调用高特权级代码)
  MORE-PRIVILEGE:
    IF current TSS is 32-bit
      THEN
        TSSstackAddress ← (new code-segment DPL*8) + 4;
        IF (TSSstackAddress + 5) > current TSS limit
          THEN #TS(current TSS selector); FI;
        NewSS ← 2 bytes loaded from (TSS base + TSSstackAddress + 4);
        NewESP ← 4 bytes loaded from (TSS base + TSSstackAddress);
      ELSE
        IF current TSS is 16-bit
          THEN
            TSSstackAddress ← (new code-segment DPL*4) + 2
            IF (TSSstackAddress + 3) > current TSS limit
              THEN #TS(current TSS selector); FI;
            NewSS ← 2 bytes loaded from (TSS base + TSSstackAddress + 2);
            NewESP ← 2 bytes loaded from (TSS base + TSSstackAddress);
          ELSE (* current TSS is 64-bit *)
            TSSstackAddress ← (new code-segment DPL*8) + 4;
            IF (TSSstackAddress + 7) > current TSS limit
              THEN #TS(current TSS selector); FI;
            NewSS ← new code-segment DPL; (* NULL selector with RPL = new CPL *)
            NewRSP ← 8 bytes loaded from (current TSS base + TSSstackAddress);
        FI;
    FI;
    IF IA32_EFER.LMA = 0 and NewSS is NULL
      THEN #TS(NewSS); FI;
    Read new code-segment descriptor and new stack-segment descriptor;
    IF IA32_EFER.LMA = 0 and 
           (NewSS RPL ≠ new code-segment DPL or 
           new stack-segment DPL ≠ new code-segment DPL or 
           new stack segment is not a writable data segment)
      THEN #TS(NewSS); FI
    IF IA32_EFER.LMA = 0 and new stack segment not present
      THEN #SS(NewSS); FI;
    IF CallGateSize = 32
      THEN
        IF new stack does not have room for parameters plus 16 bytes
          THEN #SS(NewSS); FI;
        IF CallGate(InstructionPointer) not within new code-segment limit
          THEN #GP(0); FI;
        SS ← newSS; (* Segment descriptor information also loaded *)
        ESP ← newESP;
        CS:EIP ← CallGate(CS:InstructionPointer);
        (* Segment descriptor information also loaded *)
        Push(oldSS:oldESP); (* From calling procedure *)
        temp ← parameter count from call gate, masked to 5 bits;
        Push(parameters from calling procedure’s stack, temp)
        Push(oldCS:oldEIP); (* Return address to calling procedure *)
      ELSE
        IF CallGateSize = 16
          THEN
            IF new stack does not have room for parameters plus 8 bytes
              THEN #SS(NewSS); FI;
            IF (CallGate(InstructionPointer) AND FFFFH) not in new code-segment limit
              THEN #GP(0); FI;
            SS ← newSS; (* Segment descriptor information also loaded *)
            ESP ← newESP;
            CS:IP ← CallGate(CS:InstructionPointer);
            (* Segment descriptor information also loaded *)
            Push(oldSS:oldESP); (* From calling procedure *)
            temp ← parameter count from call gate, masked to 5 bits;
            Push(parameters from calling procedure’s stack, temp)
            Push(oldCS:oldEIP); (* Return address to calling procedure *)
          ELSE (* CallGateSize = 64 *)
            IF pushing 32 bytes on the stack would use a non-canonical address
              THEN #SS(NewSS); FI;
            IF (CallGate(InstructionPointer) is non-canonical)
              THEN #GP(0); FI;
            SS ← NewSS; (* NewSS is NULL)
            RSP ← NewESP;
            CS:IP ← CallGate(CS:InstructionPointer);
            (* Segment descriptor information also loaded *)
            Push(oldSS:oldESP); (* From calling procedure *)
            Push(oldCS:oldEIP); (* Return address to calling procedure *)
        FI;
    FI;
    CPL ← CodeSegment(DPL)
    CS(RPL) ← CPL
END;
可以看到,各种检查通过后,目标代码段的DPL数值被传送给了当前特权级CPL(第77行),然后再用CPL的数值刷新了CS段寄存器的RPL数值(第78行)。所以程序调用后的当前特权级CPL数值是目标代码段的特权级,而不是程序调用前的特权级,这样程序由当前代码的低特权级提升到了目标代码的高特权级,这是唯一的可以提升当前特权级CPL的情况

                               
登录/注册后可看大图

                               
登录/注册后可看大图

逻辑代码段6【由逻辑代码段4第2行跳转而来】-通过调用门(特权级相同)
  SAME-PRIVILEGE:
    IF CallGateSize = 32
      THEN
        IF stack does not have room for 8 bytes
          THEN #SS(0); FI;
        IF CallGate(InstructionPointer) not within code segment limit
          THEN #GP(0); FI;
        CS:EIP ← CallGate(CS:EIP) (* Segment descriptor information also loaded *)
        Push(oldCS:oldEIP); (* Return address to calling procedure *)
      ELSE
        If CallGateSize = 16
          THEN
            IF stack does not have room for 4 bytes
              THEN #SS(0); FI;
            IF CallGate(InstructionPointer) not within code segment limit
              THEN #GP(0); FI;
            CS:IP ← CallGate(CS:instruction pointer);
            (* Segment descriptor information also loaded *)
            Push(oldCS:oldIP); (* Return address to calling procedure *)
          ELSE (* CallGateSize = 64)
            IF pushing 16 bytes on the stack touches non-canonical addresses
              THEN #SS(0); FI;
            IF RIP non-canonical
              THEN #GP(0); FI;
            CS:IP ← CallGate(CS:instruction pointer);
            (* Segment descriptor information also loaded *)
            Push(oldCS:oldIP); (* Return address to calling procedure *)
        FI;
    FI;
    CS(RPL) ← CPL
  END;
可以看到,上述各种情况下,CS段寄存器的RPL被当前特权级CPL刷新,导致调用后的当前特权级CPL仍然为调用前的CPL的数值,程序跳转后运行在调用前的CPL级别下。

                               
登录/注册后可看大图

                               
登录/注册后可看大图

逻辑代码段7【由逻辑代码段1第24行跳转而来】-跳转到任务门
  TASK-GATE:
    IF task gate DPL < CPL or RPL
      THEN #GP(task gate selector); FI;
    IF task gate not present
      THEN #NP(task gate selector); FI;
    Read the TSS segment selector in the task-gate descriptor;
    IF TSS segment selector local/global bit is set to local
       or index not within GDT limits
      THEN #GP(TSS selector); FI;
    Access TSS descriptor in GDT;
    IF TSS descriptor specifies that the TSS is busy (low-order 5 bits set to 00001)
      THEN #GP(TSS selector); FI;
    IF TSS not present
      THEN #NP(TSS selector); FI;
    SWITCH-TASKS (with nesting) to TSS;
    IF EIP not within code segment limit
      THEN #GP(0); FI;
  END;

                               
登录/注册后可看大图

                               
登录/注册后可看大图

逻辑代码段8【由逻辑代码段1第25行跳转而来】-直接跳转到任务状态段TSS
  TASK-STATE-SEGMENT:
    IF TSS DPL < CPL or RPL
       or TSS descriptor indicates TSS not available
      THEN #GP(TSS selector); FI;
    IF TSS is not present
      THEN #NP(TSS selector); FI;
    SWITCH-TASKS (with nesting) to TSS;
    IF EIP not within code segment limit
      THEN #GP(0); FI;
  END;

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2017-12-24 16:08:33 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-12-24 16:34:32 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-24 22:20

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表