汇编中的 LONG 和 FAR 跳转之间有什么区别(如果有的话)?
What is the difference, if any, between LONG and FAR jumps in Assembly?
我正在看一些汇编练习代码,作业基本上是用一个跳转点替换另一个。
原来的jmp是一个SHORT jmp,我需要接近的终点用这条指令无法到达
我现在有三个选择,我要么删除 'SHORT',要么插入 'LONG',要么插入 'FAR'。
如果有说明它们之间差异的文档,我还没有找到。有人可以帮忙吗?
我假设您的问题与 x86 架构有关;你没有在你的问题中指定。
A SHORT
跳转是从当前指令指针地址跳转到特定偏移量。 LONG
跳转可以使用更大的偏移值,因此可以跳离当前指令指针地址更远的地方。这两种跳转类型通常是 relative - 也就是说,操作数是当前指令指针的偏移量(尽管在汇编源代码中,您通常提供目标标签 - 然后是汇编器或链接器计算偏移量)。 都没有跳转到不同的代码段,所以都是'near'跳转.
A FAR
跳转 指定段和偏移量 ,它们都是 absolute 因为它们指定了需要代码段和指令指针,而不是相对于当前代码段/指令指针的偏移量。
总结一下,直接跳转分为三种:short和long,都是near跳转可以跳转相同代码段的不同相对距离,而far可以跳转到任意绝对地址(段和偏移量)。
(请注意,也可以执行 间接绝对 跳转,您可以在其中指定一个操作数来保存您希望跳转到的绝对地址。在这种情况下jump 可以是 near 也可以是 far - 即它可以包含或不包含所需的代码段)。
如果不指定跳转'distance',则由汇编程序决定是短跳转、长跳转还是远跳转。大多数现代汇编程序都是“两次通过”的,如果可能,将使用短跳转,否则将使用长跳转或远跳转 - 后者仅在需要时使用。
如果您需要帮助理解我所说的 'segment' 的意思,请参阅 wikipedia's entry on x86 memory segmentation。
有关可能的 JMP 指令寻址模式的完整详细信息,请参阅 this description of the x86 JMP instruction。
一个SHORT
跳:
- 如果是正向跳转,编码使用00h(+0)到7Fh(+127)的相对偏移值,使程序执行能够跳转到另一条指令,中间最大127字节.
- 如果是向后跳转,编码使用从80h(-128)到FFh(-1)的相对偏移值,这使得程序执行能够跳转到另一条指令,它们之间最多有125个字节.
一个LONG
跳转,可以使用较大的偏移量。
一个FAR
跳转,跳转到另一个代码段。
TL:DR: short
vs. long
/near
只是强制选择指令长度。
far
是完全不同的野兽。
jmp short foo
是 jmp rel8
.
jmp long foo
或 jmp near foo
是 jmp rel16/rel32
(取决于模式)
jmp far [rdi]
是对新 CS:[ER]IP 的跳转/调用。你很少想要这个。
请参阅 Intel 手册(如果您熟悉所使用的符号,请参阅 HTML scrape)以了解 jmp
/ call
, jcc
(conditionals like jge
), and loop
的可用 machine-code 形式(其编码类似于jcc short
).
x86 有两个主要的 jumps/calls 家族,每个家族都有一些变化:
Far 到新的 CS:IP(或 CS:EIP / CS:RIP,具体取决于模式)。
几乎从未在普通的 32 位或 64 位代码中使用过(例如调用使用 syscall
指令的 64 位代码的 WOW64 32 位系统 DLL),如果您不适合,则仅在 16 位代码中使用你的程序变成 64K,或者在 MBR 引导加载程序中设置一个已知的实模式 CS,或者切换到 32 位。
直接(64 位模式除外)或 memory-indirect,但始终是绝对的。有意思的是,x86唯一的绝对直接跳转。
没有条件远跳,只有 jmp
或 call
/ retf
语法细节取决于 assembler,但通常 jmp 0x10:foo
或 jmp far [eax]
在 NASM 中工作,后者从 [DS:EAX] 加载 6 个字节到 [=202] =] 如果 运行 在 32 位模式下。
Near是正常跳跃,不改变CS,只是设置了一个新的IP/EIP/RIP。可以使用以下表格:
- 间接(使用绝对目标,例如函数指针或跳转表)like
jmp ax
或jmp qword [rsi]
,或与call
相同。 (不是条件间接 jcc
)。 short 与否没有意义,因为该指令的机器代码仅对查找新 [ER]IP 的位置进行编码,而不直接编码如何到达它。 ax
vs. eax
是 operand-size 的问题,或者 [esi]
vs. [rsi]
是 address-size 的问题。 [rsi+0x1230]
是用作寻址模式一部分的 disp8 / disp32 的问题。
- Direct 使用添加到 IP/EIP/RIP 的相对位移(编码到指令的机器代码中)。所以它们是相对于指令的结尾1。 这是您从普通
jmp foo
或 jle .else
得到的结果,assembler 通常会为您选择一个长度。
short
表示使用 8 位(1 字节)相对位移,又名 rel8
.
适用于 jcc rel8
和 jmp rel8
(以及 loop
),不适用于 call
non-short,使用rel16
(16位模式2)或rel32
(其他模式) .所以一个2字节或4字节的相对位移。
您可以在 asm 源代码中使用 near
或 long
强制此编码。
适用于 jmp rel16/32
和 call rel16/32
,以及 386 和更高版本 jcc rel16/32
。如果使用限制为 286 或更早的指令进行组装,如果到目标标签的距离超出了 rel8 的 [-128, +127] 范围,则 assembler 会抱怨,或者使用 [=57 之类的回退=] 在 jmp
.
没有近乎绝对的直接。对于那个使用 mov eax, 0x123456
/ jmp eax
(在 register-indirect 附近)如果你不能保证(或者很容易让工具链在 link 时间计算)这段代码和绝对目标。
脚注 1: 例如 EB 00
是一个使用短 jmp
.
的慢速 NOP
或者 E8 00 00 00 00
/ 5B
是一个 call next
(或 call $+5
)/ next: pop ebx
就像你可能用来读取 EIP 而实际上在 32 位模式下没有去任何地方一样RIP-relative LEA 不可用)
脚注 2::从技术上讲,您 可以 在 32 位模式下使用 jmp rel16
,但它将 t运行将 EIP 转换为 16 位。 (并且编码使用 66h
operand-size 前缀,因此它仅比 jmp rel32
短 1 个字节)
在 16 位和 32 位模式下,jmp rel16/rel32
可以达到任何其他 IP/EIP 值,但在 64 位模式下,+-2GiB 范围只是虚拟地址的一小部分 space。尽管如此,单个可执行文件的代码假设它适合 2GiB 是正常的,因此任何代码都可以到达同一库中的任何其他代码 或主可执行文件 相对接近 jump/call. “大型”代码模型需要 mov reg, imm64
/ jmp reg
或类似低效的东西。或者更糟糕的是 position-independent.
LONG 是不常见的术语。在大多数 assemblers 中,编码覆盖是 short
(rel8) 或 near
(rel16 或 rel32,取决于模式)以强制长度(以及你可以跳多远)近jumps (cs
不变,只是在 IP/EIP/RIP 上增加一个偏移量)
根据此处的其他答案,在 assemblers 中 long
是一个东西,它与 NASM jmp near foo
相同的 rel16 或 rel32 覆盖.
NASM 列表 (nasm -felf32 foo.asm -l/dev/stdout
)
1 foo:
2 00000000 E9FBFFFFFF jmp near foo
3 00000005 EBF9 jmp foo ; optimizes to short by default
4 00000007 EBF7 jmp short foo
NASM 进行 multi-pass 优化以找到可用于每个分支的最短编码。这通常是最佳的,但 seWhy is the "start small" algorithm for branch displacement not optimal? 对于手动强制一个分支的编码可能允许更小代码的极端情况。
如果分支目标在另一个文件中,所以 NASM 在 assemble 时不知道它有多远,它假设 near
(non-short).如果你知道你将 link 在一起的文件很小(或者代码在一个特殊的部分),你可以强制这样做。
或者如果你想为其他东西留下完整的 rel32
来修改此机器代码并写入新的偏移量,那将为 near
的 use-case。例如,在 Linux 上动态 linking 中使用的 PLT 曾经以这种方式工作(我认为),重写 jmp rel32
中的偏移量而不是使用 GOT 条目进行间接 jmp .
从历史上看,一些 assemblers 不如 NASM 聪明,如果你想要跳转的短编码,总是需要手动提示。特别是对于向前跳转,到 assembler 还没有看到的标签。 (如果你正在使用那个时代的旧工具处理 16 位代码,你可能 运行 进入这个。)甚至 NASM 过去在旧版本中默认禁用优化,这将使它选择长编码。
此外,jcc near
仅在 386 及更高版本上受支持,因此如果您希望 assembler 实际发出该信号,则可能需要明确说明。
我正在看一些汇编练习代码,作业基本上是用一个跳转点替换另一个。
原来的jmp是一个SHORT jmp,我需要接近的终点用这条指令无法到达
我现在有三个选择,我要么删除 'SHORT',要么插入 'LONG',要么插入 'FAR'。
如果有说明它们之间差异的文档,我还没有找到。有人可以帮忙吗?
我假设您的问题与 x86 架构有关;你没有在你的问题中指定。
A SHORT
跳转是从当前指令指针地址跳转到特定偏移量。 LONG
跳转可以使用更大的偏移值,因此可以跳离当前指令指针地址更远的地方。这两种跳转类型通常是 relative - 也就是说,操作数是当前指令指针的偏移量(尽管在汇编源代码中,您通常提供目标标签 - 然后是汇编器或链接器计算偏移量)。 都没有跳转到不同的代码段,所以都是'near'跳转.
A FAR
跳转 指定段和偏移量 ,它们都是 absolute 因为它们指定了需要代码段和指令指针,而不是相对于当前代码段/指令指针的偏移量。
总结一下,直接跳转分为三种:short和long,都是near跳转可以跳转相同代码段的不同相对距离,而far可以跳转到任意绝对地址(段和偏移量)。
(请注意,也可以执行 间接绝对 跳转,您可以在其中指定一个操作数来保存您希望跳转到的绝对地址。在这种情况下jump 可以是 near 也可以是 far - 即它可以包含或不包含所需的代码段)。
如果不指定跳转'distance',则由汇编程序决定是短跳转、长跳转还是远跳转。大多数现代汇编程序都是“两次通过”的,如果可能,将使用短跳转,否则将使用长跳转或远跳转 - 后者仅在需要时使用。
如果您需要帮助理解我所说的 'segment' 的意思,请参阅 wikipedia's entry on x86 memory segmentation。
有关可能的 JMP 指令寻址模式的完整详细信息,请参阅 this description of the x86 JMP instruction。
一个SHORT
跳:
- 如果是正向跳转,编码使用00h(+0)到7Fh(+127)的相对偏移值,使程序执行能够跳转到另一条指令,中间最大127字节.
- 如果是向后跳转,编码使用从80h(-128)到FFh(-1)的相对偏移值,这使得程序执行能够跳转到另一条指令,它们之间最多有125个字节.
一个LONG
跳转,可以使用较大的偏移量。
一个FAR
跳转,跳转到另一个代码段。
TL:DR: short
vs. long
/near
只是强制选择指令长度。
far
是完全不同的野兽。
jmp short foo
是jmp rel8
.jmp long foo
或jmp near foo
是jmp rel16/rel32
(取决于模式)jmp far [rdi]
是对新 CS:[ER]IP 的跳转/调用。你很少想要这个。
请参阅 Intel 手册(如果您熟悉所使用的符号,请参阅 HTML scrape)以了解 jmp
/ call
, jcc
(conditionals like jge
), and loop
的可用 machine-code 形式(其编码类似于jcc short
).
x86 有两个主要的 jumps/calls 家族,每个家族都有一些变化:
Far 到新的 CS:IP(或 CS:EIP / CS:RIP,具体取决于模式)。
几乎从未在普通的 32 位或 64 位代码中使用过(例如调用使用syscall
指令的 64 位代码的 WOW64 32 位系统 DLL),如果您不适合,则仅在 16 位代码中使用你的程序变成 64K,或者在 MBR 引导加载程序中设置一个已知的实模式 CS,或者切换到 32 位。直接(64 位模式除外)或 memory-indirect,但始终是绝对的。有意思的是,x86唯一的绝对直接跳转。
没有条件远跳,只有jmp
或call
/retf
语法细节取决于 assembler,但通常jmp 0x10:foo
或jmp far [eax]
在 NASM 中工作,后者从 [DS:EAX] 加载 6 个字节到 [=202] =] 如果 运行 在 32 位模式下。Near是正常跳跃,不改变CS,只是设置了一个新的IP/EIP/RIP。可以使用以下表格:
- 间接(使用绝对目标,例如函数指针或跳转表)like
jmp ax
或jmp qword [rsi]
,或与call
相同。 (不是条件间接jcc
)。 short 与否没有意义,因为该指令的机器代码仅对查找新 [ER]IP 的位置进行编码,而不直接编码如何到达它。ax
vs.eax
是 operand-size 的问题,或者[esi]
vs.[rsi]
是 address-size 的问题。[rsi+0x1230]
是用作寻址模式一部分的 disp8 / disp32 的问题。 - Direct 使用添加到 IP/EIP/RIP 的相对位移(编码到指令的机器代码中)。所以它们是相对于指令的结尾1。 这是您从普通
jmp foo
或jle .else
得到的结果,assembler 通常会为您选择一个长度。short
表示使用 8 位(1 字节)相对位移,又名rel8
.
适用于jcc rel8
和jmp rel8
(以及loop
),不适用于call
non-short,使用
rel16
(16位模式2)或rel32
(其他模式) .所以一个2字节或4字节的相对位移。
您可以在 asm 源代码中使用near
或long
强制此编码。
适用于jmp rel16/32
和call rel16/32
,以及 386 和更高版本jcc rel16/32
。如果使用限制为 286 或更早的指令进行组装,如果到目标标签的距离超出了 rel8 的 [-128, +127] 范围,则 assembler 会抱怨,或者使用 [=57 之类的回退=] 在jmp
.没有近乎绝对的直接。对于那个使用
mov eax, 0x123456
/jmp eax
(在 register-indirect 附近)如果你不能保证(或者很容易让工具链在 link 时间计算)这段代码和绝对目标。
- 间接(使用绝对目标,例如函数指针或跳转表)like
脚注 1: 例如 EB 00
是一个使用短 jmp
.
的慢速 NOP
或者 E8 00 00 00 00
/ 5B
是一个 call next
(或 call $+5
)/ next: pop ebx
就像你可能用来读取 EIP 而实际上在 32 位模式下没有去任何地方一样RIP-relative LEA 不可用)
脚注 2::从技术上讲,您 可以 在 32 位模式下使用 jmp rel16
,但它将 t运行将 EIP 转换为 16 位。 (并且编码使用 66h
operand-size 前缀,因此它仅比 jmp rel32
短 1 个字节)
在 16 位和 32 位模式下,jmp rel16/rel32
可以达到任何其他 IP/EIP 值,但在 64 位模式下,+-2GiB 范围只是虚拟地址的一小部分 space。尽管如此,单个可执行文件的代码假设它适合 2GiB 是正常的,因此任何代码都可以到达同一库中的任何其他代码 或主可执行文件 相对接近 jump/call. “大型”代码模型需要 mov reg, imm64
/ jmp reg
或类似低效的东西。或者更糟糕的是 position-independent.
LONG 是不常见的术语。在大多数 assemblers 中,编码覆盖是 short
(rel8) 或 near
(rel16 或 rel32,取决于模式)以强制长度(以及你可以跳多远)近jumps (cs
不变,只是在 IP/EIP/RIP 上增加一个偏移量)
根据此处的其他答案,在 assemblers 中 long
是一个东西,它与 NASM jmp near foo
相同的 rel16 或 rel32 覆盖.
NASM 列表 (nasm -felf32 foo.asm -l/dev/stdout
)
1 foo:
2 00000000 E9FBFFFFFF jmp near foo
3 00000005 EBF9 jmp foo ; optimizes to short by default
4 00000007 EBF7 jmp short foo
NASM 进行 multi-pass 优化以找到可用于每个分支的最短编码。这通常是最佳的,但 seWhy is the "start small" algorithm for branch displacement not optimal? 对于手动强制一个分支的编码可能允许更小代码的极端情况。
如果分支目标在另一个文件中,所以 NASM 在 assemble 时不知道它有多远,它假设 near
(non-short).如果你知道你将 link 在一起的文件很小(或者代码在一个特殊的部分),你可以强制这样做。
或者如果你想为其他东西留下完整的 rel32
来修改此机器代码并写入新的偏移量,那将为 near
的 use-case。例如,在 Linux 上动态 linking 中使用的 PLT 曾经以这种方式工作(我认为),重写 jmp rel32
中的偏移量而不是使用 GOT 条目进行间接 jmp .
从历史上看,一些 assemblers 不如 NASM 聪明,如果你想要跳转的短编码,总是需要手动提示。特别是对于向前跳转,到 assembler 还没有看到的标签。 (如果你正在使用那个时代的旧工具处理 16 位代码,你可能 运行 进入这个。)甚至 NASM 过去在旧版本中默认禁用优化,这将使它选择长编码。
此外,jcc near
仅在 386 及更高版本上受支持,因此如果您希望 assembler 实际发出该信号,则可能需要明确说明。