装配 - CMP 未按预期工作
Posted
技术标签:
【中文标题】装配 - CMP 未按预期工作【英文标题】:Assembly - CMP not working as expected 【发布时间】:2015-03-03 22:47:25 【问题描述】:我是汇编代码的新手,我正在使用 gdb 编写具有以下内容的代码:
=> 0x080485ee <+132>: cmp %eax,0x80498d4(,%ebx,4)
0x080485f5 <+139>: je 0x80485fc <main+146>
0x080485f7 <+141>: call 0x8048540 <bomb>
我在该行用断点停止了它并输入了这些 gdb 命令:
(gdb) print $eax
$10 = 134519000
(gdb) print 0x80498d8
$11 = 134519000
(gdb) print $ebx
$12 = 1
据我了解,cmp 指令应该比较 %eax 和 0x80498d4 + (%ebx * 4) 的值,它们是相等的,所以代码应该设置零标志并触发 je 跳转到 main+146 on下一行。但是当我单步执行代码时它不会跳转:
(gdb) stepi 2
0x080485ee <+132>: cmp %eax,0x80498d4(,%ebx,4)
0x080485f5 <+139>: je 0x80485fc <main+146>
=> 0x080485f7 <+141>: call 0x8048540 <bomb>
有人可以帮我理解为什么没有设置零标志并且 je 没有跳转到 main+146 吗?
【问题讨论】:
(我知道你可能不知道这一点,但其他人可能知道)Wot 语法!0x80498d4(,%ebx,4)
是否确实转换为更正确格式的 [0x80498d4+4*ebx]
?
这是内存引用,不是立即数,所以你需要使用x
命令而不是p
,或者自己取消引用指针。
【参考方案1】:
总结
您没有将内存访问转换为正确的 C 表达式以在 GDB 中进行评估。应该是*(int*)(0x80498d4 + $ebx*4)
。
说明
指令问题是cmp %eax,0x80498d4(,%ebx,4)
,我们分解一下将0x80498d4(,%ebx,4)
部分翻译成C的步骤:
为位移创建一个 GDB 便利变量以节省一些输入:
set variable $d = 0x80498d4
将Displacement + Index*Scale寻址方式转化为地址:
set variable $addr = $d + $ebx*4
取消引用地址。这就是你所缺少的:
set variable $v = *(int*)$addr
以上三个步骤相当于:set variable $v = *(int*)($d + $ebx*4)
。因此:
将内存中的值与 EAX 进行比较:
p $eax == $v
将 EAX 设置为内存中的值以实现跳转:
set variable $eax = $v
【讨论】:
【参考方案2】:如果我反编译这个汇编代码,我会得到这样的 C 代码:
if (p!=q[i])
bomb();
其中 p 是 EAX 中保存的值,q 似乎是一个数组的基地址,其元素每个有 4 个字节,即这个基地址 0x80498d4,而 i 是 EBX 中保存的值,用作所述的索引数组。
但您希望上述表达式的行为如下:
if (p != &q[i]) /* or (p != q+i)
bomb();
这对我来说更有意义:EAX 中的值显然是一个地址(一个指针),所以应该与另一个指针进行比较,它恰好是 q[i] 的地址(实际上,q[ 1] 因为 EBX=1) 具有相同的值,所以要么 q 是一个指针数组,要么原始的 C 代码应该像我写的第二个代码一样。
无论哪种方式,您编写的汇编代码都会将寄存器与内存值进行比较。事实上,唯一返回在这样的表达式(常量 + 寄存器 * 标度)中计算的值而不访问内存的汇编指令是 LEA 指令。所有其他人都使用这些表达式将有效地址计算为 LEA,然后使用该地址访问内存。
【讨论】:
以上是关于装配 - CMP 未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章
navigationController?.navigationBar.isUserInteractionEnabled 未按预期工作