只是好奇在一个程序中是否所有的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
和一个popq
。exampleMOV AH,DL
-使用push/pop
和临时缓冲区模拟它。example
Gotchas
需要mov
- 控制寄存器(CR 0、CR2、CR 3、CR 4等)
- 寄存器(DR 0、DR 1、DR 2等)
1条答案
按热度按时间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
也不可模拟。