29

我正在查看学校项目中的一些旧代码,并尝试在我的笔记本电脑上编译它时遇到了一些问题。它最初是为旧的 32 位版本的 gcc 编写的。无论如何,我试图将一些程序集转换为 64 位兼容代码并遇到一些障碍。

这是原始代码:

pusha
pushl   %ds
pushl   %es
pushl   %fs
pushl   %gs
pushl   %ss

pusha在 64 位模式下无效。那么在 64 位模式下,在 x86_64 汇编中执行此操作的正确方法是什么?

pusha在 64 位模式下无效肯定是有原因的,所以我觉得手动推送所有寄存器可能不是一个好主意。

4

4 回答 4

22

AMDREX在开发 64 位 x86 扩展时需要一些空间来为前缀和其他一些新指令添加新的操作码。他们将一些操作码的含义更改为那些新指令。

其中一些指令只是现有指令的缩写形式,或者没有必要。PUSHA是受害者之一。目前尚不清楚他们为什么禁止PUSHA,但它似乎没有与任何新的指令操作码重叠。也许它们是保留的PUSHAPOPA操作码以供将来使用,因为它们是完全冗余的,不会更快,也不会在代码中出现足够频繁而无关紧要。

的顺序PUSHA是指令编码的顺序:eax, ecx, edx, ebx, esp, ebp, esi, edi. 请注意,它是多余的推esp!你需要知道esp才能找到它推送的数据!

如果您要从 64 位转换代码,那么PUSHA代码无论如何都不好,您需要更新它以将新寄存器推送到r8thru r15xmm8您还需要通过保存和恢复更大的 SSE 状态xmm15。假设你要破坏它们。

如果中断处理程序代码只是一个转发到 C 代码的存根,则不需要保存所有寄存器。您可以假设 C 编译器将生成将保留rbxrbprsirdir12thru的代码r15。您应该只需要保存和恢复rax, rcx,rdxr8thru r11(注意:在 Linux 或其他 System V ABI 平台上,编译器将保留rbx, rbp, r12- r15,您可以期待rsirdi破坏)

段寄存器在长模式下没有任何值(如果被中断的线程在 32 位兼容模式下运行,您必须保留段寄存器,感谢 ughoavgfhw)。实际上,它们摆脱了长模式下的大部分分段,但FS仍保留给操作系统用作线程本地数据的基地址。寄存器值本身无关紧要, 和 的基数是通过 MSR 和 设置FS的。假设您不会使用,则无需担心,只需记住 C 代码访问的任何线程本地数据都可能使用任何随机线程的 TLS。请注意这一点,因为 C 运行时库将 TLS 用于某些功能(例如:strtok 通常使用 TLS)。GS0xC00001000xC0000101FS

将值加载到FSor GS(即使在用户模式下)将覆盖FSBASEor GSBASEMSR。由于某些操作系统GS用作“处理器本地”存储(它们需要一种方法来为每个 CPU 提供指向结构的指针),因此它们需要将其保存在不会因GS在用户模式下加载而受到破坏的地方。为了解决这个问题,为GSBASE寄存器保留了两个 MSR:一个是活动的,一个是隐藏的。在内核模式下,内核GSBASE保存在通常的GSBASEMSR 中,而用户模式库保存在另一个(隐藏)中GSBASEMSR。当上下文从内核模式切换到用户模式上下文时,以及保存用户模式上下文并进入内核模式时,上下文切换代码必须执行 SWAPGS 指令,该指令交换可见和隐藏GSBASEMSR 的值。由于内核GSBASE在用户模式下安全地隐藏在另一个 MSR 中,因此用户模式代码无法GSBASE通过将值加载到GS. 当 CPU 重新进入内核模式时,上下文保存代码将执行SWAPGS并恢复内核的GSBASE.

于 2013-01-23T18:01:17.527 回答
9

pusha在 64 位模式下无效,因为它是冗余的。单独推送每个寄存器正是要做的事情。

于 2011-07-26T22:32:33.003 回答
9

从执行此类操作的现有代码中学习。例如:

事实上,“手动推送”regs 是 AMD64 上的唯一方法,因为PUSHA那里不存在。AMD64 在这方面并不是独一无二的——大多数非 x86 CPU 在某些时候也需要逐个寄存器保存/恢复。

但是,如果您仔细检查引用的源代码,您会发现并非所有中断处理程序都需要保存/恢复整个寄存器集,因此还有优化的空间。

于 2011-07-28T10:17:19.487 回答
5

嗨,这可能不是正确的方法,但可以创建像这样的宏

.macro pushaq
    push %rax
    push %rcx
    push %rdx
    push %rbx
    push %rbp
    push %rsi
    push %rdi
.endm # pushaq

.macro popaq
    pop %rdi    
    pop %rsi    
    pop %rbp    
    pop %rbx    
    pop %rdx    
    pop %rcx
    pop %rax
.endm # popaq

如果需要,最终添加其他 r8-15 寄存器

于 2017-08-23T20:40:56.420 回答