汇编:cmp指令和寻址
Posted
技术标签:
【中文标题】汇编:cmp指令和寻址【英文标题】:Assembly : cmp instruction and addressing 【发布时间】:2021-05-09 13:01:52 【问题描述】:我是 asm 新手,很困惑...
这是我在nasm
程序集中工作的strlen
函数的循环:
.loop: inc rax
inc rbx
cmp [rbx], byte 0
jne .loop
ret
但是我在教程中看到过 cmp bl, 0
这样的东西,但是当我尝试时,我得到了错误的长度。
我尝试过cmp [bl],0
,但在组装时出现invalid effective address
错误。
我也试过cmp rbx, 0
。但是程序永远不会停止所以我最好的猜测是我
将一个非常大的地址与永远不会发生的 0 进行比较。
如何理解这种行为?为什么我不能使用cmp rbx, 0
但有些
人们可以使用cmp bl, 0
吗?
如果您找到更具描述性的标题,请随时编辑标题。
【问题讨论】:
您能否展示您创建的完整strlen
过程,以及您看到的使用cmp bl, 0
的示例?
如果将字符加载到寄存器中,则可以将其与 0 进行比较。如果不是,则需要 cmp
的内存操作数。顺便说一句,cmp rbx,0
将指针与 NULL 进行比较是正确的;在将指针环绕到 0 之前,您会点击一个未映射的页面。
你明白cmp byte [rbx], 0
是什么意思吗?应该有很多教程来解释寻址模式和内存与寄存器,例如找到链接自***.com/tags/x86/info 的教程或指南并阅读。这也应该清楚为什么cmp [bl], 0
甚至不能汇编(x86 不允许 8 位地址),以及为什么即使它可以运行它也不会做你想做的事情。
还有另一种搜索 NUL 终止符的方法:REPNE SCAS。
你不用inc rax
,把字符串的开头移到rax中,然后减去你刚刚找到的0-Byte
的地址,就可以得到长度了。
【参考方案1】:
要比较地址处的字节,我建议将字节移动到正确大小的寄存器中,然后进行比较。根据您使用的调用约定,但假设字符串的地址在 rdi 中传递,我会建议以下内容。
.loop: inc rax
mov bl, [rdi + rax]
cmp bl, byte 0
jne .loop
ret
编辑:意识到您正在尝试确定给定地址处字符串的长度
【讨论】:
rax 在 .loop 上方被初始化为零。 请注意,您没有修复 OP 不检查第一个字节的错误(可能是[rdi+rax-1]
,或在循环之前将 RAX 初始化为 -1
)。那么这是合理的,但通常您希望使用movzx ecx, byte [rdi + rax]
进行字节加载,而不是创建错误的依赖关系并通过合并到RBX 的低字节来获取ALU uop。 (或 RCX,这是 strlen 的更好选择,因为调用约定允许您在不保存的情况下修改它)。当然,与 SIMD 相比,一次读取 1 个字节非常慢,所以这只是您作为初学者练习要做的事情。
@PeterCordes:我通常会写 rep scasb 但是我再也不会写 asm 来提高性能了。
@Joshua:是的,这对于代码大小和简单性来说没问题,但除此之外就没什么了。它一次只走 1 个字节;只有 rep movs 和 rep stos 有快速字符串微码;不幸的是,条件重复/重复指令在当前的 CPU 中没有。 repne scasb
本身不会分支错误预测,但我认为往往会使前端停滞不前。虽然可能不适用于最初爆发的 uops 覆盖长度的短字符串。以上是关于汇编:cmp指令和寻址的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向x86 汇编 ( cmp 比较指令 | test 比较指令 )