我正在尝试使用 gcc 在 Linux中将 RR0D Rasta Ring 0 调试器从 32 位模式转换为 64 位模式(长模式)。我熟悉 x86 32 位汇编(在 MS-DOS 环境中),但我是 x86 64 位汇编和一般 Linux 汇编编程的初学者。
该项目用于生产(我需要一个工作的非源调试器),但我也尝试学习如何进行 32 位到 64 位的转换。如果可能的话,我试图找到一种通用的方法来进行 32 位到 64 位的转换,这种方法可以在任何 32 位程序上使用正则表达式完成(以便它可以自动化)。我知道不存在通用解决方案(64 位代码可能比 32 位代码占用更多空间等并消耗更多堆栈等),但即使在这种情况下,自动转换的代码也可以作为起点。
这个想法是保持 8 位和 16 位操作数不变,并用 64 位操作数替换 32 位操作数。pushw %ax; pushw %bx; popl %ecx
如果替换为,这种方法自然会失败pushw %ax; pushw %bx; popq %rcx
,但表现良好的程序通常不会push
两个 16 位操作数,然后pop
是一个 32 位操作数,或者是吗?
这些是迄今为止的转换:
编辑:修复:pusha
/pushad
可以替换为连续push
的 'es,因为pusha
/pushad
命令在实际推送之前sp
推送/的值,并且在 286+ 中的工作方式相同,但在 8088/8086 The Assembly Language database中的工作方式不同。这种差异在这里不是问题(对于 386+ 代码)。因此可以用连续的命令代替。esp
sp
push sp
pusha
pushad
push
另一种方法与OpenSolaris 的privregs.h
代码类似。
编辑:修复:对所有命令使用 64 位内存寻址。
pusha
->push %ax; push %cx; push %dx; push %bx; push %sp; push %bp; push %si; push %di
。编辑:修复:一个有效的替代方案(使用
lea
),请注意 x86 处理器是 little-endian:pusha
->movq %rax, -8(%rsp); lea -8(%rsp), %rax; mov %ax, -10(%rsp); movq -8(%rsp), %rax; movw %cx, -4(%rsp); movw %dx, -6(%rsp); movw %bx, -8(%rsp); movw %bp, -12(%rsp); movw %si, -14(%rsp); movw %di, -16(%rsp); lea -16(%rsp), %rsp
。pushad
->push %rax; push %rcx; push %rdx; push %rbx; push %rsp; push %rbp; push %rsi; push %rdi
。编辑:修复:一个有效的替代方案(使用
lea
):pushad
->movq %rax, -8(%rsp); movq %rcx, -16(%rsp); movq %rdx, -24(%rsp); movq %rbx, -32(%rsp); lea -32(%rsp), %rax; movq %rax, -40(%rsp); movq -8(%rsp), %rax; movq %rbp, -48(%rsp); movq %rsi, -56(%rsp); movq %rdi, -64(%rsp); lea -64(%rsp), %rsp
。编辑:修复:
popa
并popad
弹出sp
/的值esp
但将其丢弃(英特尔指令集 - popa/popad)。让我们pop
进入bx
/rbx
。popa
->popw %di; popw %si; popw %bp; popw %bx; popw %bx; popw %dx; popw %cx; popw %ax
。popad
->popq %rdi; popq %rsi; popq %rbp; popq %rbx; popq %rbx; popq %rdx; popq %rcx; popq %rax
。pushfd
->pushfq
。popfd
->popfq
。编辑:
push
段寄存器,例如。pushw %ds
->pushw %ax; pushw %ax; movw %ds, %ax; movw %ax, 2(%rsp); popw %ax
。编辑:
pop
段寄存器,例如。popw %ds
->pushw %ax; movw 2(%rsp), %ax; movw %ax, %ds; popw %ax
。编辑:
inc %reg16
->add $1, %reg16
,例如。inc %ax
->add $1, %ax
。编辑:
dec %reg16
->sub $1, %reg16
,例如。dec %ax
->sub $1, %ax
。编辑:
inc %reg32
->add $1, %reg64
,例如。inc %eax
->add $1, %rax
。编辑:
dec %reg32
->sub $1, %reg64
,例如。dec %eax
->sub $1, %rax
。编辑:
aaa
->?编辑:
aad
->?编辑:
aam
->?编辑:
aas
->?编辑:
arpl
->?编辑:
bound
->?编辑:
daa
->?编辑:
das
->?编辑:
lahf
->?编辑:
sahf
->?编辑修复:任何带有直接操作数的命令在 64 位模式下不适合 32 位操作数大小,例如。
pushl $0xDEADBEEF
->pushq %rax; pushq %rax; movq $0xDEADBEEF, %rax; movq %rax, 8(%rsp); popq %rax
。ret
使用立即操作数:我认为在这种情况下,必须回溯源代码以查看 lastpush
ed 操作数的大小,并采取相应的行动,例如。pushl %eax; ret 4
->pushq %rax; ret 8
。编辑::系统调用:
int $0x80
->pushq %rdi; movq %rbp, %r9; movq %rdi, %r8; movq %rbx, %rdi; xchgq %rcx, %rsi; -- replace %rax value using a substitution list --; syscall; popq %rdi; xchgq %rcx, %rsi
(注意:32 位系统调用可能有超过 6 个参数,6 个在寄存器中,其余在堆栈中,64 位系统调用可能永远不会有超过 6 个参数)。
编辑:还应该考虑什么?将 32 位代码转换为 64 位代码(以长模式运行)还需要哪些其他转换?