assembly 每个MOV指令都可以用PUSH/POP替换吗?

6l7fqoea  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(112)

只是好奇在一个程序中是否所有的MOV都可以用PUSH/POP替换?
我明白这样的替换既不实际,效率也不高。
这个godbolt示例显示了一个使用MOV的标准printf调用和另一个使用PUSH/POP的printf调用,以进行比较。我的直觉说这是可能的,但可能有一些陷阱沿沿着?

#include <stdio.h>

char format_string[] asm("format_string") = "%d %d %d %d %d\n";

void MOV_printf() {
    __asm__ (
        "subq $128, %%rsp\n\t"
        "lea format_string(%%rip), %%rdi\n\t"
        "movq $1, %%rsi\n\t"
        "movq $2, %%rdx\n\t"
        "movq $3, %%rcx\n\t"
        "movq $4, %%r8\n\t"
        "movq $5, %%r9\n\t"
        "call printf\n\t"
        "addq $136, %%rsp\n"
        ::: "rdi", "rsi", "rdx", "rcx", "r8", "r9", "rsp"
    );
}

void PUSH_POP_printf() {
    __asm__ (
        "subq $128, %%rsp\n\t"
        "lea format_string(%%rip), %%rdi\n\t"
        "pushq $1\n\t" 
        "popq %%rsi\n\t"
        "pushq $2\n\t" 
        "popq %%rdx\n\t"
        "pushq $3\n\t" 
        "popq %%rcx\n\t"
        "pushq $4\n\t" 
        "popq %%r8\n\t"
        "pushq $5\n\t" 
        "popq %%r9\n\t"
        "call printf\n\t"
        "addq $136, %%rsp\n"
        ::: "rdi", "rsi", "rdx", "rcx", "r8", "r9", "rsp"
    );        
}

int main() {
    MOV_printf();
    PUSH_POP_printf();
    return 0;
}

解决方案

MOV r64, imm64-替换为4个pushw和一个popqexample
MOV AH,DL-使用push/pop和临时缓冲区模拟它。example
Gotchas
需要mov

  • 控制寄存器(CR 0、CR2、CR 3、CR 4等)
  • 寄存器(DR 0、DR 1、DR 2等)
h5qlskok

h5qlskok1#

mov %al, (%rdi)这样的字节存储不可能使用push。任何加载/存储包含单词或qword并将其存储回的模拟都不是线程安全的;包含字的非原子RMW可以通过另一个线程来逐步存储到另一个字节。(* Can modern x86 hardware not store a single byte to memory? * -它可以,大多数ISA也可以,尽管存在误解。)

如果您愿意接受非线程安全的模拟,那么部分重叠的pop m16操作可能会用您在静态缓冲区中寻找的值构造一个字,您可以pop m16/push m16复制原始字节。
但是你不知道(%rdi)的字节是包含它的16位字的低字节还是高字节,所以你不知道你可以访问-1(%rdi)0(%rdi)中的哪一个,而不会可能通过进入下一页来分段。只有一个 aligned 16位的加载/存储保证不跨越任何更宽的边界(如4k页),因此如果字包含任何你知道有效的字节,就不能出现页面错误。(Is it safe to read past the end of a buffer within the same page on x86 and x64?
push/pop不能单独检查%rdi的低位和相应的分支。
(x86-64使得不可能具有段限制,在32位模式中,在不假定平面存储器模型的一般情况下,段限制可能是奇数个字节。但实际上,x86-64(仍然?)使得奇数段基对于FS和GS成为可能,我认为,所以mov %al, %fs:(%rdi)更加未知;即使你可以test %1, %dil; jnz,这仍然不能告诉你线性地址是奇数还是偶数。
此外,除了调试和控制寄存器之外,x86-64还删除了FS/GS以外的段寄存器的push/pop操作码。https://www.felixcloutier.com/x86/push。因此mov ds, eax也不可模拟。

相关问题