基于变址方式转为间接寻址方式(x86汇编)
Transformation of based indexed mode into indirect addressing mode (x86 assembly)
我目前正在努力将示例从复杂的间接寻址模式更改为简单的间接寻址模式。
但是,我遇到了基于模式的示例,我无法 "transform"。
代码:
move %eax, 28(%esp)
我试过了
addl , %esp
movl (%eax), %esp
这会造成分段错误;而且我不知道我还应该怎么写。
再举个例子,我“转换失败”是
compl , 28(%esp)
->进入
addl , %esp
cmpl , %esp
然而这是有效的,但它稍微改变了我的输出,所以它也可能不正确。
示例 1:
这个:
addl , %esp
movl (%eax), %esp
与 move %eax, 28(%esp)
做的事情不同。您正在寻找的是这样的东西:
addl , %esp
movl %eax, (%esp)
但请注意,您的版本修改了 esp
的值,而原始指令没有。
示例 2:
同样,这个:
addl , %esp
cmpl , %esp
和cmpl , 28(%esp)
做的事情不一样。原始指令将内存中地址 esp+28
的 32 位值与值 4 进行比较。您的版本将值 esp+28
与值 4 进行比较(即没有内存访问)。
免责声明:我不喜欢 AT&T 语法,所以如果下面的描述似乎与你想做的完全相反,我把它搞砸了,你需要切换参数(到,从 my 的观点,与说明的意思相反,这与我 的意思 相反,但似乎是这样做的其他事情......等等,我想我在这里的一个角落里扭曲了自己)。
(编者注:是的,这是倒退的,例如加载而不是存储,以及至少一个除此之外的倒退操作数。现在已修复。)
mov %eax, 28(%esp) # store EAX to memory at ESP+28
你需要一个临时寄存器来计算存储地址。如果你正在加载,你可以在目标寄存器中计算,但对于存储我们需要原始数据和地址。这是寻址模式如此方便的原因之一。
mov %esp, %ecx # copy ESP to ECX
add , %ecx # modify the tmp copy, not the stack pointer
mov %eax, (%ecx) # store EAX into memory dword pointed to by ECX
正如@nrz 评论的那样,修改 ESP 本身可能会使您的函数稍后在尝试 return 时崩溃,方法是使用 ret
从堆栈中弹出其 return 地址。
类似地,
的小步变体
cmpl , 28(%esp)
是
mov %esp, %eax
add , %eax
cmpl , (%eax) # compare immediate with dword pointed to by EAX
或者,如果最后一行太复杂(因为也是间接的),
mov (%eax), %eax
cmp , %eax # dword operand size (cmpl) implied by the register
请注意,此指令序列会改变 %eax
,而原始的单个指令不会。这是有原因的:提供间接指针操作因为它们不需要中间寄存器。 (而且因为它们节省了指令,增加了每条指令和每字节代码可以完成的工作量。)
在 Z80 等较旧的 CPU 中,确实 不得不 通过 "manually" 加载一个寄存器作为基址,添加偏移量,然后加载目标来进行间接寻址值。
我目前正在努力将示例从复杂的间接寻址模式更改为简单的间接寻址模式。 但是,我遇到了基于模式的示例,我无法 "transform"。
代码:
move %eax, 28(%esp)
我试过了
addl , %esp
movl (%eax), %esp
这会造成分段错误;而且我不知道我还应该怎么写。
再举个例子,我“转换失败”是
compl , 28(%esp)
->进入
addl , %esp
cmpl , %esp
然而这是有效的,但它稍微改变了我的输出,所以它也可能不正确。
示例 1:
这个:
addl , %esp
movl (%eax), %esp
与 move %eax, 28(%esp)
做的事情不同。您正在寻找的是这样的东西:
addl , %esp
movl %eax, (%esp)
但请注意,您的版本修改了 esp
的值,而原始指令没有。
示例 2:
同样,这个:
addl , %esp
cmpl , %esp
和cmpl , 28(%esp)
做的事情不一样。原始指令将内存中地址 esp+28
的 32 位值与值 4 进行比较。您的版本将值 esp+28
与值 4 进行比较(即没有内存访问)。
免责声明:我不喜欢 AT&T 语法,所以如果下面的描述似乎与你想做的完全相反,我把它搞砸了,你需要切换参数(到,从 my 的观点,与说明的意思相反,这与我 的意思 相反,但似乎是这样做的其他事情......等等,我想我在这里的一个角落里扭曲了自己)。
(编者注:是的,这是倒退的,例如加载而不是存储,以及至少一个除此之外的倒退操作数。现在已修复。)
mov %eax, 28(%esp) # store EAX to memory at ESP+28
你需要一个临时寄存器来计算存储地址。如果你正在加载,你可以在目标寄存器中计算,但对于存储我们需要原始数据和地址。这是寻址模式如此方便的原因之一。
mov %esp, %ecx # copy ESP to ECX
add , %ecx # modify the tmp copy, not the stack pointer
mov %eax, (%ecx) # store EAX into memory dword pointed to by ECX
正如@nrz 评论的那样,修改 ESP 本身可能会使您的函数稍后在尝试 return 时崩溃,方法是使用 ret
从堆栈中弹出其 return 地址。
类似地,
的小步变体cmpl , 28(%esp)
是
mov %esp, %eax
add , %eax
cmpl , (%eax) # compare immediate with dword pointed to by EAX
或者,如果最后一行太复杂(因为也是间接的),
mov (%eax), %eax
cmp , %eax # dword operand size (cmpl) implied by the register
请注意,此指令序列会改变 %eax
,而原始的单个指令不会。这是有原因的:提供间接指针操作因为它们不需要中间寄存器。 (而且因为它们节省了指令,增加了每条指令和每字节代码可以完成的工作量。)
在 Z80 等较旧的 CPU 中,确实 不得不 通过 "manually" 加载一个寄存器作为基址,添加偏移量,然后加载目标来进行间接寻址值。