如何理解 "cmpl [=10=]x0, -0x30(%rbp)" / "je ..."
How to understand "cmpl $0x0, -0x30(%rbp)" / "je ..."
这是我的bomb lap问题的汇编代码,我卡在了phase2;
炸弹实验室要求我们根据汇编代码找出正确的输入,否则它会爆炸。
从 <+20> 我知道 %rbp -0x30(48) == 0 否则它会调用 <+32> 并引爆炸弹;所以 %rbp = 48(DEC)
After that(+26) %rbp - 0x2c(44) 必须等于 1 否则它会引爆炸弹...
但是因为 %rbp = 48,炸弹会在任何地方爆炸所以我现在很困惑...
我想我误解了 compl ,je/jne 或者如何计算这些东西...
-0x30(%ebp)
并不意味着使用值 %ebp - 0x30
。这是一个要读取的内存地址。指令 (cmpl
) 有一个 l
后缀,所以它处理的是一个 4 字节的数量。所以实际发生的是它从地址 %ebp - 0x30
读取一个 4 字节的数字并检查它是否为零。
($
前缀表示它是立即数,而不是地址。这就是为什么 0x0
是按字面意思而不是取消引用的原因。)
你不需要知道 rbp
的值来拆除炸弹,它总是以相对的方式用于寻址内存的特定部分。它在开始时按以下顺序设置:
pushq %rbp
movq %rsp, %rbp
pushq %r12
pushq %rbx
subq [=10=]x20, %rsp
确实将旧的 rbp
值压入堆栈(以保存它),然后将 rbp
设置为当前堆栈指针值 (rsp
)。所以 (%rbp)
现在包含旧的 rbp
。然后另外两个寄存器 r12
和 rbx
通过将它们压入堆栈来保留(这使得现在 rsp == rbp - 16
)。然后 rsp
再减去 32 调整一次,即 rsp == rbp - 48
.
这是如何为局部变量分配内存 space 的常见模式,即任何进一步的 push
指令将使用低于 rbp - 48
的内存。从 rbp - 48
到 rbp - 17
(包括)的内存未定义,可自由用作 "local variables" 内存。然后在 rbp - 16
存储 8 个字节原始 rbx
值,在 rbp - 8
存储原始 r12
值,在 rbp
存储旧 rbp
]. (我的意思是所有"rbp - x"都被用作内存地址来寻址内存中的值)
然后你的代码调用输入 6 个数字,我猜这意味着 32 位整数(根据以下代码判断),所以它提供地址 rbp - 48
。 6*4 = 24 => 输入的值将存储在内存中从地址 rbp - 48
到(包括)rbp - 25
.
请注意 "unused memory" 从 rbp - 24
到 rbp - 17
的差距,这是另外 8 字节的备用本地存储,您发布的代码未使用,很可能添加了填充由编译器使 rsp
在 callq read_six_numbers
.
之前正确对齐
所以基本上你不需要知道 rsp/rbp
指向的确切位置,它指向一些有效的堆栈内存(否则代码会崩溃,炸弹会 NOT explode ...有点奇怪的设计:))))。您可以选择任意值,例如 0x8000
开始时 rsp
,并以此模拟 运行。 (即 <+11> leaq -0x30,(%rbp), %rsi
然后是 rsi = 0x7FC8;
(0x8000 - 8 - 0x30)。
其余-x(%rbp)
在各种指令中的含义(注意"memory operand"用法的lea
与<any other instruction>
语义差异,lea
确实仅内存地址计算,而其他指令仅以此开头,使用计算出的地址访问存储在内存中的实际值)在其他答案+评论中进行了描述。再加上使用 x86 指令参考指南,并多读几遍一些教程,直到理解为止。
这是我的bomb lap问题的汇编代码,我卡在了phase2; 炸弹实验室要求我们根据汇编代码找出正确的输入,否则它会爆炸。
从 <+20> 我知道 %rbp -0x30(48) == 0 否则它会调用 <+32> 并引爆炸弹;所以 %rbp = 48(DEC)
After that(+26) %rbp - 0x2c(44) 必须等于 1 否则它会引爆炸弹... 但是因为 %rbp = 48,炸弹会在任何地方爆炸所以我现在很困惑...
我想我误解了 compl ,je/jne 或者如何计算这些东西...
-0x30(%ebp)
并不意味着使用值 %ebp - 0x30
。这是一个要读取的内存地址。指令 (cmpl
) 有一个 l
后缀,所以它处理的是一个 4 字节的数量。所以实际发生的是它从地址 %ebp - 0x30
读取一个 4 字节的数字并检查它是否为零。
($
前缀表示它是立即数,而不是地址。这就是为什么 0x0
是按字面意思而不是取消引用的原因。)
你不需要知道 rbp
的值来拆除炸弹,它总是以相对的方式用于寻址内存的特定部分。它在开始时按以下顺序设置:
pushq %rbp
movq %rsp, %rbp
pushq %r12
pushq %rbx
subq [=10=]x20, %rsp
确实将旧的 rbp
值压入堆栈(以保存它),然后将 rbp
设置为当前堆栈指针值 (rsp
)。所以 (%rbp)
现在包含旧的 rbp
。然后另外两个寄存器 r12
和 rbx
通过将它们压入堆栈来保留(这使得现在 rsp == rbp - 16
)。然后 rsp
再减去 32 调整一次,即 rsp == rbp - 48
.
这是如何为局部变量分配内存 space 的常见模式,即任何进一步的 push
指令将使用低于 rbp - 48
的内存。从 rbp - 48
到 rbp - 17
(包括)的内存未定义,可自由用作 "local variables" 内存。然后在 rbp - 16
存储 8 个字节原始 rbx
值,在 rbp - 8
存储原始 r12
值,在 rbp
存储旧 rbp
]. (我的意思是所有"rbp - x"都被用作内存地址来寻址内存中的值)
然后你的代码调用输入 6 个数字,我猜这意味着 32 位整数(根据以下代码判断),所以它提供地址 rbp - 48
。 6*4 = 24 => 输入的值将存储在内存中从地址 rbp - 48
到(包括)rbp - 25
.
请注意 "unused memory" 从 rbp - 24
到 rbp - 17
的差距,这是另外 8 字节的备用本地存储,您发布的代码未使用,很可能添加了填充由编译器使 rsp
在 callq read_six_numbers
.
所以基本上你不需要知道 rsp/rbp
指向的确切位置,它指向一些有效的堆栈内存(否则代码会崩溃,炸弹会 NOT explode ...有点奇怪的设计:))))。您可以选择任意值,例如 0x8000
开始时 rsp
,并以此模拟 运行。 (即 <+11> leaq -0x30,(%rbp), %rsi
然后是 rsi = 0x7FC8;
(0x8000 - 8 - 0x30)。
其余-x(%rbp)
在各种指令中的含义(注意"memory operand"用法的lea
与<any other instruction>
语义差异,lea
确实仅内存地址计算,而其他指令仅以此开头,使用计算出的地址访问存储在内存中的实际值)在其他答案+评论中进行了描述。再加上使用 x86 指令参考指南,并多读几遍一些教程,直到理解为止。