6.1.4、 long mode 下的 call gate
6.1.4.1、 64 位的 call gate descriptor 中已经不支持 parameter count 域,offset 域被扩展至 64 位 base 值。
情景提示: 64 位的 call gate descriptor 的中 selector 域必须是一个 64 位代码的 selector,也就是说:这个 selector 指向的 code segment descriptor 中的 L 属性必须是 1,并且 default operand size 为 32 位,即:D 属性为 0。否则将产生 #GP 异常。(L = 1 && D = 0) 若要转到 32 位代码,须使用 32 位的 call gate descriptor。 |
由于 64 位的 call gate descriptor 中不支持 parameter count 域,所以在 long mode 下,processor 不支持自动复制参数的行为(从 caller 的 stack 复制到 callee 的 stack 中)。
那么,在 long mode 下如何使用 call gate 调用例程时,如果确实需要传递参数,又是如何传递参数呢? 在 stack 不进行切换的情况下,和一般的例程调用别无两样。在要进行 stack 切换的情形下,原来的 stack 的 ss 和 rsp 值被保存至新的 stack 中(也就是 caller 的 ss 和 rsp 会保存至 callee 的 stack 中)。那么,例程 callee 将直将使用原来的 rsp 去获取参数。
这样做的根本原因是,在 x64 的 long mode 下在硬件级下使用的是平坦内存管理模式,忽略了 segmentation 管理,callee 将可以直接使用 rsp 来获取参数。
看看下面的指令的情形:
caller:
... ... push param1 push param2 call [call_gate] /* call gate */ ... ...
|
caller 中用 call gate 进行调用例程 callee, 假设这里需要进行 stack 的切换。
callee:
callee 中仅使用 ret 进行返回,无需进行清栈处理。
那么:
caller 的 ss 和 rsp 将被保存至 callee 的 stack 中,如下:
------------------
caller's SS + 24
------------------
caller's RSP + 16
------------------
caller's CS + 8
------------------
caller's RIP <------------ callee's rsp
------------------
... ...
------------------
在例程 callee 中 [rsp+16] 处获取 caller's rsp 值,callee 中如下处理:
push rbp
mov rbp, rsp
mov rax, [rbp + 24] /* get caller's rsp */
mov rbx, [rax] /* get param1 */
... ...
和一般的调用例程,使用参数无异,不像 x86 下使用 call gate 调用例程,processor 会自动产生复制参数行为。
6.1.4.2、 前面提过,call gate 只能存放在 GDT / LDT 中,不能放在 IDT 中 这造成 64 位的 call gate descriptors 的高 8 字节的 type 属性必须为 0000,以避免 32 位的 descriptor 与 64 位的 descriptors 重叠在一起。为 0 的 type 是属于无效的 descriptors 类型,processor 会检测这个 type 是否为 0,为 0 则是无效的 descriptors。
这样的话,想提取 64 位的 descriptors 的高半部分作为 32 位 descriptors 使用就会产生一个 #GP 异常。
阅读(1018) | 评论(0) | 转发(0) |