我正在看一些汇编的练习代码,作业基本上是用另一个跳转点替换一个跳转点。原来的jmp是一个SHORT jmp,我需要接近的终点无法通过此指令到达。我现在有三个选项,要么删除'SHORT',要么插入'LONG',要么插入'FAR'。如果有任何文档表明它们之间的差异,我还没有找到。有人能帮忙吗?
o2g1uqev1#
我假设您的问题与x86架构有关;你没有在问题中具体说明。SHORT跳转是从当前指令指针地址跳转到特定偏移量。LONG跳转可以使用更大的偏移值,因此可以跳得更远离当前指令指针地址。这两种跳转类型通常都是 * 相对的 * -也就是说,操作数是当前指令指针的偏移量(尽管在汇编源代码中,您通常提供目标标签-汇编器或链接器然后计算偏移量)。它们都没有跳转到不同的代码段,所以它们都是“接近”跳转。FAR jump指定了一个段和偏移量,它们都是 * 绝对的 *,因为它们指定了所需的代码段和指令指针,而不是相对于当前代码段/指令指针的偏移量。总而言之,有三种类型的直接跳转:short 和 long,它们都是 near 跳转,能够用相同的代码段跳转不同的相对距离,以及 far,可以跳转到任何绝对地址(段和偏移)。(Note也可以执行 * 间接绝对 * 跳转,其中指定一个操作数,该操作数包含您希望跳转到的绝对地址。在这种情况下,跳跃可以是近的或远的,即它可以包括或不包括所需的代码段)。如果你没有指定跳转的“距离”,那么你得到的是短、长还是远的跳转就由汇编程序决定了。大多数现代汇编器都是“两遍”的,如果可能的话,会使用短跳转,否则会使用长跳转或远跳转-后者只在需要时使用。如果你需要帮助来理解我所说的“段”的意思,请参阅wikipedia's entry on x86 memory segmentation。有关可能的JMP指令寻址模式的完整详细信息,请参见this description of the x86 JMP instruction。
SHORT
LONG
FAR
5fjcxozz2#
一次SHORT跳跃:
一个LONG跳转,可以使用更大的偏移。一个FAR跳转,跳转到另一个代码段。
vhmi4jdf3#
TL:DR:short与long/near只是强制选择指令长度。far是一个完全不同的野兽。
short
long
near
far
jmp short foo
jmp rel8
jmp long foo
jmp near foo
jmp rel16/rel32
jmp far [rdi]
有关可用的机器码形式jmp/call、jcc(如jge的条件)和loop(其编码类似于jcc short),请参阅英特尔手册(如果您熟悉所用的符号,请参阅HTML scrape)。
jmp
call
jcc
jge
loop
jcc short
*远到新的CS:IP(或CS:EIP / CS:RIP,具体取决于模式)。
几乎从不用于正常的32位或64位代码(例如WOW 64 32位系统DLL调用使用syscall指令的64位代码,如果您无法将程序放入64 K,则仅使用16位代码,或者在MBR引导加载程序中将已知的CS设置为真实的模式,或者切换到32位。直接(64位模式除外)或内存间接,但始终是绝对的。有趣的是,x86的唯一绝对直接跳转。无条件远跳转,仅jmp或call/retf语法细节取决于汇编器,但通常像jmp 0x10:foo或jmp far [eax]这样的东西在NASM中工作,后者在32位模式下运行时从[DS:EAX]加载6个字节到CS:EIP中。
syscall
retf
jmp 0x10:foo
jmp far [eax]
*Near为正常跳转,不改变CS,只设置新的IP/EIP/RIP。以下表格可用:
jmp ax或jmp qword [rsi],或与call相同。(非条件间接jcc)。短或不短都没有意义,因为指令的机器码只对查找新[ER]IP的位置进行编码,而不直接编码如何到达它。ax与eax是操作数大小的问题,或者[esi]与[rsi]是地址大小的问题。和[rsi+0x1230]是一个问题的disp 8/disp 32用作寻址模式的一部分。
jmp ax
jmp qword [rsi]
ax
eax
[esi]
[rsi]
[rsi+0x1230]
jmp foo
jle .else
rel8
适用于jcc rel8和jmp rel8(以及loop),不适用于call
jcc rel8
rel16
rel32
适用于jmp rel16/32和call rel16/32,以及386和更高版本的jcc rel16/32。如果使用限制为286或更早的指令进行汇编,则汇编器将在到目标标签的距离超出rel 8的[-128,+127]范围时发出抱怨,或者使用类似jnle的回退。
jmp rel16/32
call rel16/32
jcc rel16/32
jnle
mov eax, 0x123456
jmp eax
EB 00
或者E8 00 00 00 00/5B是一个call next(或call $+5)/next: pop ebx,就像您在32位模式下读取EIP时可能使用的,而实际上在32位模式下,RIP相对莱亚不可用)
E8 00 00 00 00
5B
call next
call $+5
next: pop ebx
脚注2::从技术上讲,您可以在32位模式下使用jmp rel16,但它会将EIP截断为16位。(编码使用66h操作数大小前缀,因此它只比jmp rel32短1个字节)
jmp rel16
66h
jmp rel32
在16位和32位模式下,jmp rel16/rel32可以达到任何其他IP/EIP值,但在64位模式下,+-2GiB范围只是虚拟地址空间的一小部分。尽管如此,对于单个可执行文件的代码来说,假设它适合2GiB是正常的,因此任何代码都可以通过相对接近的跳转/调用到达同一库或主可执行文件中的任何其他代码。“大型”代码模型需要mov reg, imm64/jmp reg或类似的低效代码。或者更糟的是使其独立于位置。
mov reg, imm64
jmp reg
LONG是不常见的术语。在大多数汇编器中,编码覆盖是short(rel 8)或near(rel 16或rel 32,取决于模式),以强制长度(以及可以跳转多远)用于近跳转(cs不变,只是向IP/EIP/RIP添加偏移量)
cs
根据这里的其他答案,在long是一个东西的汇编器中,它是与NASM jmp near foo相同的rel 16或rel 32重写。NASM列表(nasm -felf32 foo.asm -l/dev/stdout)NASM进行多遍优化,以找到可用于每个分支的最短编码。这通常是最佳的,但请参阅Why is the "start small" algorithm for branch displacement not optimal?,了解手动强制一个分支的编码可以允许更小代码的极端情况。
nasm -felf32 foo.asm -l/dev/stdout
如果分支目标在另一个文件中,所以NASM在汇编时不知道它将有多远,它假定为near(非短)。如果知道要链接的文件很小(或者代码在特殊的部分),可以强制这样做。或者,如果你想留下一个完整的rel32,让其他东西修改这个机器码并写入一个新的偏移量,这将是near的一个用例。例如,在Linux上动态链接中使用的PLT曾经以这种方式工作(我认为),在jmp rel32中重写偏移量,而不是使用GOT条目进行间接jmp。从历史上看,一些汇编器并不像NASM那样智能,如果您想要跳转的短编码,则总是需要手动提示。特别是对于向前跳转,到一个汇编程序还没有看到的标签。(如果您使用那个时代的旧工具处理16位代码,您可能会遇到这种情况。)即使是NASM在旧版本中也会默认禁用优化,这会使它选择长编码。另外,jcc near只在386和更高版本上受支持,因此如果您希望汇编器实际发出它,则可能需要显式。
jcc near
3条答案
按热度按时间o2g1uqev1#
我假设您的问题与x86架构有关;你没有在问题中具体说明。
SHORT
跳转是从当前指令指针地址跳转到特定偏移量。LONG
跳转可以使用更大的偏移值,因此可以跳得更远离当前指令指针地址。这两种跳转类型通常都是 * 相对的 * -也就是说,操作数是当前指令指针的偏移量(尽管在汇编源代码中,您通常提供目标标签-汇编器或链接器然后计算偏移量)。它们都没有跳转到不同的代码段,所以它们都是“接近”跳转。FAR
jump指定了一个段和偏移量,它们都是 * 绝对的 *,因为它们指定了所需的代码段和指令指针,而不是相对于当前代码段/指令指针的偏移量。总而言之,有三种类型的直接跳转:short 和 long,它们都是 near 跳转,能够用相同的代码段跳转不同的相对距离,以及 far,可以跳转到任何绝对地址(段和偏移)。
(Note也可以执行 * 间接绝对 * 跳转,其中指定一个操作数,该操作数包含您希望跳转到的绝对地址。在这种情况下,跳跃可以是近的或远的,即它可以包括或不包括所需的代码段)。
如果你没有指定跳转的“距离”,那么你得到的是短、长还是远的跳转就由汇编程序决定了。大多数现代汇编器都是“两遍”的,如果可能的话,会使用短跳转,否则会使用长跳转或远跳转-后者只在需要时使用。
如果你需要帮助来理解我所说的“段”的意思,请参阅wikipedia's entry on x86 memory segmentation。
有关可能的JMP指令寻址模式的完整详细信息,请参见this description of the x86 JMP instruction。
5fjcxozz2#
一次
SHORT
跳跃:一个
LONG
跳转,可以使用更大的偏移。一个
FAR
跳转,跳转到另一个代码段。vhmi4jdf3#
TL:DR:
short
与long
/near
只是强制选择指令长度。far
是一个完全不同的野兽。jmp short foo
是jmp rel8
。jmp long foo
或jmp near foo
是jmp rel16/rel32
(取决于模式)jmp far [rdi]
是对新CS:[ER]IP的jmp /调用。你很少想要这个。有关可用的机器码形式
jmp
/call
、jcc
(如jge
的条件)和loop
(其编码类似于jcc short
),请参阅英特尔手册(如果您熟悉所用的符号,请参阅HTML scrape)。x86有两个主要的跳转/调用家族,每个家族都有一些变化:
*远到新的CS:IP(或CS:EIP / CS:RIP,具体取决于模式)。
几乎从不用于正常的32位或64位代码(例如WOW 64 32位系统DLL调用使用
syscall
指令的64位代码,如果您无法将程序放入64 K,则仅使用16位代码,或者在MBR引导加载程序中将已知的CS设置为真实的模式,或者切换到32位。直接(64位模式除外)或内存间接,但始终是绝对的。有趣的是,x86的唯一绝对直接跳转。
无条件远跳转,仅
jmp
或call
/retf
语法细节取决于汇编器,但通常像
jmp 0x10:foo
或jmp far [eax]
这样的东西在NASM中工作,后者在32位模式下运行时从[DS:EAX]加载6个字节到CS:EIP中。*Near为正常跳转,不改变CS,只设置新的IP/EIP/RIP。以下表格可用:
jmp ax
或jmp qword [rsi]
,或与call
相同。(非条件间接jcc
)。短或不短都没有意义,因为指令的机器码只对查找新[ER]IP的位置进行编码,而不直接编码如何到达它。ax
与eax
是操作数大小的问题,或者[esi]
与[rsi]
是地址大小的问题。和[rsi+0x1230]
是一个问题的disp 8/disp 32用作寻址模式的一部分。jmp foo
或jle .else
得到的,汇编程序通常会为你选择一个长度。*
short
表示使用8位(1字节)相对位移,也称为rel8
。适用于
jcc rel8
和jmp rel8
(以及loop
),不适用于call
rel16
(16位模式2)或rel32
(其他模式)。因此是2字节或4字节的相对位移。您可以在asm源中使用
near
或long
强制此编码。适用于
jmp rel16/32
和call rel16/32
,以及386和更高版本的jcc rel16/32
。如果使用限制为286或更早的指令进行汇编,则汇编器将在到目标标签的距离超出rel 8的[-128,+127]范围时发出抱怨,或者使用类似jnle
的回退。mov eax, 0x123456
/jmp eax
(近寄存器-间接)。**脚注1:**例如
EB 00
是使用短jmp
的慢NOP。或者
E8 00 00 00 00
/5B
是一个call next
(或call $+5
)/next: pop ebx
,就像您在32位模式下读取EIP时可能使用的,而实际上在32位模式下,RIP相对莱亚不可用)脚注2::从技术上讲,您可以在32位模式下使用
jmp rel16
,但它会将EIP截断为16位。(编码使用66h
操作数大小前缀,因此它只比jmp rel32
短1个字节)在16位和32位模式下,
jmp rel16/rel32
可以达到任何其他IP/EIP值,但在64位模式下,+-2GiB范围只是虚拟地址空间的一小部分。尽管如此,对于单个可执行文件的代码来说,假设它适合2GiB是正常的,因此任何代码都可以通过相对接近的跳转/调用到达同一库或主可执行文件中的任何其他代码。“大型”代码模型需要mov reg, imm64
/jmp reg
或类似的低效代码。或者更糟的是使其独立于位置。LONG是不常见的术语。在大多数汇编器中,编码覆盖是
short
(rel 8)或near
(rel 16或rel 32,取决于模式),以强制长度(以及可以跳转多远)用于近跳转(cs
不变,只是向IP/EIP/RIP添加偏移量)根据这里的其他答案,在
long
是一个东西的汇编器中,它是与NASMjmp near foo
相同的rel 16或rel 32重写。NASM列表(
nasm -felf32 foo.asm -l/dev/stdout
)NASM进行多遍优化,以找到可用于每个分支的最短编码。这通常是最佳的,但请参阅Why is the "start small" algorithm for branch displacement not optimal?,了解手动强制一个分支的编码可以允许更小代码的极端情况。
如果分支目标在另一个文件中,所以NASM在汇编时不知道它将有多远,它假定为
near
(非短)。如果知道要链接的文件很小(或者代码在特殊的部分),可以强制这样做。或者,如果你想留下一个完整的
rel32
,让其他东西修改这个机器码并写入一个新的偏移量,这将是near
的一个用例。例如,在Linux上动态链接中使用的PLT曾经以这种方式工作(我认为),在jmp rel32
中重写偏移量,而不是使用GOT条目进行间接jmp。从历史上看,一些汇编器并不像NASM那样智能,如果您想要跳转的短编码,则总是需要手动提示。特别是对于向前跳转,到一个汇编程序还没有看到的标签。(如果您使用那个时代的旧工具处理16位代码,您可能会遇到这种情况。)即使是NASM在旧版本中也会默认禁用优化,这会使它选择长编码。
另外,
jcc near
只在386和更高版本上受支持,因此如果您希望汇编器实际发出它,则可能需要显式。