这里提出的答案是正确的,但是缺少一个部分:所提出的语法不会产生长时间跳跃。我的建议是Margaret Bloom ,但它没有奏效。我的代码一定有问题,因为我知道她给了我正确的答案,因为其他人也提出了同样的问题。综观GDB,当我应用上述的语法:
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]
显然:
asm("pushw 0xa0");
asm("pushd 0x0");
asm("jmp far [esp]");
(以上语法内联组件,GCC样式)
看着GDB,jmp far
作为生成,[esp + 0xff06]
对我来说不是很远。这是一个接近跳跃,从esp
偏移。更明显地,从objdump
输出:
000030a8 <task1_start>:
30a8: 55 push %ebp
30a9: 89 e5 mov %esp,%ebp
30ab: 66 68 a0 00 pushw $0xa0
30af: 6a 00 push $0x0
30b1: ff a4 24 06 ff 00 00 jmp *0xff06(%esp)
30b8: 90 nop
30b9: 5d pop %ebp
30ba: c3 ret
通知操作码在0x30ab
,对应于jmp
指令。纵观英特尔手册中,操作码的近跳转:
0xff
代表jmp
指令。
0xa4
是[--][--] + disp32
有效地址为esp
的ModR/M字节。这意味着,需要一个SiB字节,这是偏移量。 (参考:表2-2使用ModR/M字节的32位寻址格式)
0x24
是SiB
字节代表ESP,但没有任何缩放(值为none
),实际上保持不变。 (参考:表2-3。带有SIB字节的32位寻址表单)。
上述生成jmp
对应于FF /4
操作码(参考:jmp instruction),这意味着一个近跳,由于所产生的MODR/M字节是0xa4
。远跳的正确操作码是FF /5
。
显然,我必须为汇编程序产生一个跳远。所以,事实证明,它很容易使用,而不是像这样jmp far
语法ljmp
指令来解决:
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
在上面,ljmp
是生成:
0xff
是jmp
的操作码,相同。 ljmp
只是GAS(GNU汇编程序)用来生成FF /5
操作码的特定语法。
0x2c
是[--][--]
(无位移)的ModR/M字节,但位于表2-2中的列5
。这意味着,这个操作码真的是FF /5
。
0x24
是近乎跳跃相同,这意味着没有缩放。
这是由GDB看到的实际代码:
0x308b <task1_start+3> pushw 0xa0
0x308f <task1_start+7> push 0x0
0x3091 <task1_start+9> jmp FWORD PTR [esp]
现在,FWORD
是新的东西,但至少现在不加随机位移。实际上,该任务正确切换到0xa0
。
感谢您的建议,每个人。没有它,我可能从来没有调查过。
但'ltr'不会导致任务切换,即当前状态会自动保存。但我想我会尝试切换到一个新任务(非中断任务;中断处理程序,因为任务已经工作),然后解决此限制。 – Amumu
我误解了这个问题。 –
我不确定为什么你不能使用JMP作为操作数的内存引用。内存操作数将指向包含选择器/偏移量的内存地址。该内存地址可以在运行时修改 –