Assembly 对非常量TSS段使用jmp指令

Assembly 对非常量TSS段使用jmp指令,assembly,x86,operating-system,kernel,gnu-assembler,Assembly,X86,Operating System,Kernel,Gnu Assembler,根据文档,我们可以对恒定远段执行jmp: jmp 0x18:00 这里,0x18是GDT全局描述符表中的有效段选择器 jmp可与包含有效GDT条目(即代码/数据段描述符)的段寄存器一起使用: mov es, 0x18 jmp es:0x0 这里,0x18是一个TSS(任务状态段)描述符,当跳转到该描述符时,CPU执行一个任务切换,自动将其状态保存到当前TSS中,然后用保存在新TSS中的状态填充 但是,TSS是一个系统段描述符,因此无法加载到任何段寄存器中(如Intel文档所建议)。那么,如何

根据文档,我们可以对恒定远段执行
jmp

jmp 0x18:00
这里,
0x18
是GDT全局描述符表中的有效段选择器

jmp
可与包含有效GDT条目(即代码/数据段描述符)的段寄存器一起使用:

mov es, 0x18
jmp es:0x0
这里,
0x18
是一个TSS(任务状态段)描述符,当跳转到该描述符时,CPU执行一个任务切换,自动将其状态保存到当前TSS中,然后用保存在新TSS中的状态填充

但是,TSS是一个系统段描述符,因此无法加载到任何段寄存器中(如Intel文档所建议)。那么,如何在运行时使用动态分配的TSS跳转到任务

我能想到的唯一方法是使用
iret
指令,但我觉得这像是一种黑客行为,因为我需要修改链接字段,然后在EFLAGS中设置NT位以执行反向链接任务切换。

推送字
push WORD <TSS_selector>
push DWORD 0
jmp FAR [esp]
推送DWORD 0 远[尤指]
假设32位代码和可用堆栈。
这将使调用线程中的堆栈不平衡且未对齐,您可能需要使用专用内存位置:

mov WORD [tss_pointer + 4], <TSS_selector>
jmp FAR [tss_pointer]

tss_pointer dd 0, dw 0
mov字[tss_指针+4],
jmp FAR[tss_指针]
tss_指针dd 0,dw 0

不仅不能使用TSS选择器加载ES,指令
jmp ES:0x0
也无效。没有指令将一个段寄存器移动到另一个段寄存器(例如ES到CS)。也没有从通用寄存器加载CS的指令。正如Margaret Bloom的回答所示,您需要使用JMP指令加载CS,该指令采用内存操作数,特别是将远指针作为内存操作数的指令,这样您就可以得到设置CS的远跳转指令

就实现这一点而言,将这个远指针放在任务结构中是有意义的,任务结构是放置任务TSS和其他任务特定信息的结构。例如,要切换任务,可以使用如下代码:

struct task {
    struct {
        unsigned offset;
        unsigned short selector;
    } far_jmp_ptr;
    struct tss tss;
    // ...
};

void
switch_tasks(struct task *new_task) {
    asm("jmp FAR PTR %0" : : "m" (new_task->far_jmp_ptr));
}
代码假定“任务结构”具有远指针,其中包含为任务分配的TSS选择器(偏移部分被忽略)

从技术上讲,您还可以使用LTR指令后跟JMP指令跳转到任务。这会在不执行任务切换的情况下更改任务,因此不会影响任何寄存器(TR、CS:EIP和您显式更改的任何其他寄存器除外)。例如:

mov  esi, [new_task]
ltr  [esi + TASK_FAR_JMP_PTR + 4]
jmp  [esi + TASK_TSS + TSS_EIP]
只有当新任务在环0上运行,并且在不需要恢复其寄存器的已知点刚刚开始或停止时,这才是可行的。特别是,这是启动初始内核任务(或单个TSS操作系统中的唯一任务)的方式


请注意,大多数操作系统对所有任务只使用一个TSS,因此不使用CPU提供的任务切换机制。对于64位操作系统,这是必需的,因为在长模式下不支持任务切换。

此处建议的答案是正确的,但缺少一点:建议的语法不会生成长跳转。我也这么做了 建议了,但没用。我的代码一定有问题,因为我知道她给了我正确的答案,其他人也提出了同样的建议。看看GDB,当我应用上述语法时:

asm("pushw 0xa0");
asm("pushd 0x0");
asm("jmp  far [esp]");
(以上语法为内联汇编,GCC样式)

查看GDB,
jmp far
生成为:

0x30a9 <task1_start+1>  mov    ebp,esp
0x30ab <task1_start+3>  pushw  0xa0
0x30af <task1_start+7>  push   0x0
0x30b1 <task1_start+9>  jmp    DWORD PTR [esp+0xff06]
注意
0x30ab
处的操作码,它对应于
jmp
指令。查看英特尔手册,该操作码几乎是跳跃式的:

  • 0xff
    代表
    jmp
    指令
  • 0xa4
    [-][-]+disp32
    esp的有效地址的ModR/M字节。这意味着需要一个SiB字节,即偏移量。(参考:表2-2.带ModR/M字节的32位寻址形式)
  • 0x24
    是表示ESP的
    SiB
    字节,但没有任何缩放(值为
    none
    ),有效地保持不变。(参考:表2-3.带SIB字节的32位寻址形式)
上述生成的
jmp
对应于
FF/4
操作码(参考:),这意味着一个近跳转,因为生成的ModR/M字节是
0xa4
。跳远的正确操作码是
FF/5

显然,我必须为汇编程序做一些事情来生成跳远。因此,事实证明,使用
ljmp
指令而不是
jmp-far
语法很容易修复:

ljmp [esp]
之后,我们得到了正确生成的代码:

00003088 <task1_start>:
    3088:       55                      push   %ebp
    3089:       89 e5                   mov    %esp,%ebp
    308b:       66 68 a0 00             pushw  $0xa0
    308f:       6a 00                   push   $0x0
    3091:       ff 2c 24                ljmp   *(%esp)
    3094:       90                      nop
    3095:       5d                      pop    %ebp
    3096:       c3                      ret  
现在,
FWORD
是一个新东西,但至少它不再增加随机位移。实际上,任务已正确切换到
0xa0


谢谢大家的建议。如果没有它,我就永远无法对此进行调查。

但是
ltr
不会导致任务切换,即当前状态会自动保存。但我想我会尝试切换到一个新任务(非中断任务;中断处理程序,因为任务已经在工作),然后稍后解决这个限制。我误解了这个问题。我不确定为什么不能将JMP也用作内存引用的操作数。内存操作数将指向包含选择器/偏移量的内存地址。可以在运行时修改该内存地址,如下所示:
jmp[mem1]:[mem2]
?但汇编程序拒绝允许非常量段。它允许的唯一内存操作数是
jmp[seg:eip]
,但这意味着从远地址获取内存位置只是为了执行近调用。我已经尝试过了,它检索地址,但随后执行近jmp/调用。我错过什么了吗?也许我会在
iret
hack之前再试一次。根据您的上下文,您可以使用red-stack:cli;移动字
00003088 <task1_start>:
    3088:       55                      push   %ebp
    3089:       89 e5                   mov    %esp,%ebp
    308b:       66 68 a0 00             pushw  $0xa0
    308f:       6a 00                   push   $0x0
    3091:       ff 2c 24                ljmp   *(%esp)
    3094:       90                      nop
    3095:       5d                      pop    %ebp
    3096:       c3                      ret  
0x308b <task1_start+3>  pushw  0xa0           
0x308f <task1_start+7>  push   0x0            
0x3091 <task1_start+9>  jmp    FWORD PTR [esp]