当涉及跳转时,汇编代码中的代码行之间究竟是如何相互关联的?
How exactly DO lines of code relate to each other in assembly code when jump is involved?
好了,我明白了mov是什么意思,我明白了寄存器是什么,我明白了操作命令是什么。我什至明白最左边的十六进制是指令的编号。例如,在第 7 行,十六进制的 7f 是指令 jg。美好的。
我不明白这些事实究竟是如何加起来的,这令人难以置信地沮丧。
目前我所知道的:
例如,在第 1 行上,0d 是否添加到第 804839c 行?不,它跳转到第 17 行,因为 0d 是第 1 行之后的指令。如果将 0d 添加到地址 804839e,则会得到 80483a7。好。
这是否意味着下一行的所有指令都是相对于第二个2位十六进制的?
意思是最左边的十六进制是当前行的指令吗?
我只需要多一点指导,我已经快要搞清楚了,我几乎可以尝到它的滋味了。
1 804839c: 7e 0d jle 80483ab <silly+0x17>
2 804839e: 89 d0 mov %edx,%eax
3 80483a0: d1 f8 sar %eax
4 80483a2: 29 c2 sub %eax,%edx
5 80483a4: 8d 14 52 lea (%edx,%edx,2),%edx
6 80483a7: 85 d2 test %edx,%edx
7 80483a9: 7f f3 jg 804839e <silly+0xa>
8 80483ab: 89 d0 mov %edx,%eax
跳转指令中使用的相对偏移量(1)最好理解如下:偏移量只是简单的添加(它是一个带符号的值,所以你可以向前跳转或向后,在有限范围内)到程序计数器以获得新程序计数器。
但这里要牢记的重要一点是,程序计数器(添加偏移量)是跳转后 指令的位置。我一直记得这一点,认为 CPU 已经将程序计数器推进到下一个位置,以期获得下一条指令 (2).
这很重要。根据您的示例代码(删除了不相关的内容):
1 804839c: 7e 0d jle 80483ab <silly+0x17>
2 804839e: 89 d0 mov %edx, %eax
3-7 blah blah, blah
8 80483ab: 89 d0 mov %edx, %eax
在第二行804839e
的位置加上偏移量0d
,得到第八行的跳转目标80483ab
.
(1)不是所有跳转指令都是相对的。只是您为您的问题选择了一个简短格式的操作码 7e
。您也可以选择近似形式 0f 8e
。我认为条件跳转的远形式变体不存在,您可以通过反转比较的意义来模拟这些变体,例如使用:
jle farPoint --> jg noJump
blah blah, blah jmp farPoint
noJump: blah blah, blah
(2) 因为那是我开始为 CPU 切割原始代码的日子里的做法。对于今天的流水线、推测执行等,我不太确定。
如果您对操作码感到困惑,那么您离理解这一点还有很长的路要走。您需要从指令集的文档开始。这对于 x86 来说很丰富,它不是很好的文档,但操作码仍然很清楚。有了这样的指令集,不难找到带有操作码图表的网页,然后单击它可以找到指令定义的其余部分。
相当典型的是相对地址是基于指令后的字节。如果您在一个全新处理器的团队中工作,那么您只需走到其中一个芯片人员立方体并询问(因为它还没有得到很好的记录)但由于这是一个旧设计,因此有可用的工具可以不问任何人,只给你答案。
试试这个
a0: jle a0
a1: jle a1
a2: jle a2
a3: jle a3
a4: jle a4
b0: jle b1
b1: jle b2
b2: jle b3
b3: jle b4
b4: jle b5
b5: nop
c0: jle c0
c1: jle c0
c2: jle c0
c3: jle c0
c4: jle c0
d0: jle d4
d1: jle d4
d2: jle d4
d3: jle d4
d4: jle d4
assemble 和 disassemble
0000000000000000 <a0>:
0: 7e fe jle 0 <a0>
0000000000000002 <a1>:
2: 7e fe jle 2 <a1>
0000000000000004 <a2>:
4: 7e fe jle 4 <a2>
0000000000000006 <a3>:
6: 7e fe jle 6 <a3>
0000000000000008 <a4>:
8: 7e fe jle 8 <a4>
000000000000000a <b0>:
a: 7e 00 jle c <b1>
000000000000000c <b1>:
c: 7e 00 jle e <b2>
000000000000000e <b2>:
e: 7e 00 jle 10 <b3>
0000000000000010 <b3>:
10: 7e 00 jle 12 <b4>
0000000000000012 <b4>:
12: 7e 00 jle 14 <b5>
0000000000000014 <b5>:
14: 90 nop
0000000000000015 <c0>:
15: 7e fe jle 15 <c0>
0000000000000017 <c1>:
17: 7e fc jle 15 <c0>
0000000000000019 <c2>:
19: 7e fa jle 15 <c0>
000000000000001b <c3>:
1b: 7e f8 jle 15 <c0>
000000000000001d <c4>:
1d: 7e f6 jle 15 <c0>
000000000000001f <d0>:
1f: 7e 06 jle 27 <d4>
0000000000000021 <d1>:
21: 7e 04 jle 27 <d4>
0000000000000023 <d2>:
23: 7e 02 jle 27 <d4>
0000000000000025 <d3>:
25: 7e 00 jle 27 <d4>
0000000000000027 <d4>:
27: 7e fe jle 27 <d4>
无需查看文档,它看起来很清楚 0x7E 是一个操作码,后面的字节是 pc 相对偏移量。第一项上的 0xFE 意味着它是一个带符号的偏移量,并且相对于指令后的字节。其余实验证实。
并不意味着您应该假设所有 jump/branch 指令都以这种方式用于此指令集,您可以使用已知可生成工作代码的工具进行类似实验。
这是一个缺少处理器文档的领域,您通常需要 1) 如果可以的话与硅工程师交谈 2) 查看芯片设计(源代码)3) 文档 4) 使用现有工具进行实验 5 ) 试验硬件
大多数人无法访问 1 和 2。如果您实际上拥有这些处理器之一,通常可以使用 3 和 4,通常要获得 5,您有 3,您可能可以访问 4,但有时不能。但是同样,文档通常会留下未知的相对地址,通常是指令后的字节,但就像在 ARM 中一样,它是相对于指令地址的固定偏移量,是特定管道的错觉。
804839c: 7e 0d jle 80483ab <silly+0x17>
804839c是jle指令yes的地址。 80483ab 是满足条件时跳转到的地址。 ab-9c = 0xf = 0xD + 2。2是指令的大小,0xD是指令中的offset/immediate。
我假设这种形式的其他条件分支(注意代码后面的 jg )是一个操作码字节和一个带符号的偏移字节。但是在制作自己的 assembler 或 disassembler 或模拟器之前,您应该始终进行检查。从文档开始,并使用您可以找到的已知适用于该平台的任何工具进行确认。
好了,我明白了mov是什么意思,我明白了寄存器是什么,我明白了操作命令是什么。我什至明白最左边的十六进制是指令的编号。例如,在第 7 行,十六进制的 7f 是指令 jg。美好的。
我不明白这些事实究竟是如何加起来的,这令人难以置信地沮丧。
目前我所知道的:
例如,在第 1 行上,0d 是否添加到第 804839c 行?不,它跳转到第 17 行,因为 0d 是第 1 行之后的指令。如果将 0d 添加到地址 804839e,则会得到 80483a7。好。
这是否意味着下一行的所有指令都是相对于第二个2位十六进制的?
意思是最左边的十六进制是当前行的指令吗?
我只需要多一点指导,我已经快要搞清楚了,我几乎可以尝到它的滋味了。
1 804839c: 7e 0d jle 80483ab <silly+0x17>
2 804839e: 89 d0 mov %edx,%eax
3 80483a0: d1 f8 sar %eax
4 80483a2: 29 c2 sub %eax,%edx
5 80483a4: 8d 14 52 lea (%edx,%edx,2),%edx
6 80483a7: 85 d2 test %edx,%edx
7 80483a9: 7f f3 jg 804839e <silly+0xa>
8 80483ab: 89 d0 mov %edx,%eax
跳转指令中使用的相对偏移量(1)最好理解如下:偏移量只是简单的添加(它是一个带符号的值,所以你可以向前跳转或向后,在有限范围内)到程序计数器以获得新程序计数器。
但这里要牢记的重要一点是,程序计数器(添加偏移量)是跳转后 指令的位置。我一直记得这一点,认为 CPU 已经将程序计数器推进到下一个位置,以期获得下一条指令 (2).
这很重要。根据您的示例代码(删除了不相关的内容):
1 804839c: 7e 0d jle 80483ab <silly+0x17>
2 804839e: 89 d0 mov %edx, %eax
3-7 blah blah, blah
8 80483ab: 89 d0 mov %edx, %eax
在第二行804839e
的位置加上偏移量0d
,得到第八行的跳转目标80483ab
.
(1)不是所有跳转指令都是相对的。只是您为您的问题选择了一个简短格式的操作码 7e
。您也可以选择近似形式 0f 8e
。我认为条件跳转的远形式变体不存在,您可以通过反转比较的意义来模拟这些变体,例如使用:
jle farPoint --> jg noJump
blah blah, blah jmp farPoint
noJump: blah blah, blah
(2) 因为那是我开始为 CPU 切割原始代码的日子里的做法。对于今天的流水线、推测执行等,我不太确定。
如果您对操作码感到困惑,那么您离理解这一点还有很长的路要走。您需要从指令集的文档开始。这对于 x86 来说很丰富,它不是很好的文档,但操作码仍然很清楚。有了这样的指令集,不难找到带有操作码图表的网页,然后单击它可以找到指令定义的其余部分。
相当典型的是相对地址是基于指令后的字节。如果您在一个全新处理器的团队中工作,那么您只需走到其中一个芯片人员立方体并询问(因为它还没有得到很好的记录)但由于这是一个旧设计,因此有可用的工具可以不问任何人,只给你答案。
试试这个
a0: jle a0
a1: jle a1
a2: jle a2
a3: jle a3
a4: jle a4
b0: jle b1
b1: jle b2
b2: jle b3
b3: jle b4
b4: jle b5
b5: nop
c0: jle c0
c1: jle c0
c2: jle c0
c3: jle c0
c4: jle c0
d0: jle d4
d1: jle d4
d2: jle d4
d3: jle d4
d4: jle d4
assemble 和 disassemble
0000000000000000 <a0>:
0: 7e fe jle 0 <a0>
0000000000000002 <a1>:
2: 7e fe jle 2 <a1>
0000000000000004 <a2>:
4: 7e fe jle 4 <a2>
0000000000000006 <a3>:
6: 7e fe jle 6 <a3>
0000000000000008 <a4>:
8: 7e fe jle 8 <a4>
000000000000000a <b0>:
a: 7e 00 jle c <b1>
000000000000000c <b1>:
c: 7e 00 jle e <b2>
000000000000000e <b2>:
e: 7e 00 jle 10 <b3>
0000000000000010 <b3>:
10: 7e 00 jle 12 <b4>
0000000000000012 <b4>:
12: 7e 00 jle 14 <b5>
0000000000000014 <b5>:
14: 90 nop
0000000000000015 <c0>:
15: 7e fe jle 15 <c0>
0000000000000017 <c1>:
17: 7e fc jle 15 <c0>
0000000000000019 <c2>:
19: 7e fa jle 15 <c0>
000000000000001b <c3>:
1b: 7e f8 jle 15 <c0>
000000000000001d <c4>:
1d: 7e f6 jle 15 <c0>
000000000000001f <d0>:
1f: 7e 06 jle 27 <d4>
0000000000000021 <d1>:
21: 7e 04 jle 27 <d4>
0000000000000023 <d2>:
23: 7e 02 jle 27 <d4>
0000000000000025 <d3>:
25: 7e 00 jle 27 <d4>
0000000000000027 <d4>:
27: 7e fe jle 27 <d4>
无需查看文档,它看起来很清楚 0x7E 是一个操作码,后面的字节是 pc 相对偏移量。第一项上的 0xFE 意味着它是一个带符号的偏移量,并且相对于指令后的字节。其余实验证实。
并不意味着您应该假设所有 jump/branch 指令都以这种方式用于此指令集,您可以使用已知可生成工作代码的工具进行类似实验。
这是一个缺少处理器文档的领域,您通常需要 1) 如果可以的话与硅工程师交谈 2) 查看芯片设计(源代码)3) 文档 4) 使用现有工具进行实验 5 ) 试验硬件
大多数人无法访问 1 和 2。如果您实际上拥有这些处理器之一,通常可以使用 3 和 4,通常要获得 5,您有 3,您可能可以访问 4,但有时不能。但是同样,文档通常会留下未知的相对地址,通常是指令后的字节,但就像在 ARM 中一样,它是相对于指令地址的固定偏移量,是特定管道的错觉。
804839c: 7e 0d jle 80483ab <silly+0x17>
804839c是jle指令yes的地址。 80483ab 是满足条件时跳转到的地址。 ab-9c = 0xf = 0xD + 2。2是指令的大小,0xD是指令中的offset/immediate。
我假设这种形式的其他条件分支(注意代码后面的 jg )是一个操作码字节和一个带符号的偏移字节。但是在制作自己的 assembler 或 disassembler 或模拟器之前,您应该始终进行检查。从文档开始,并使用您可以找到的已知适用于该平台的任何工具进行确认。