在Assembly中LONG和FAR跳转之间有什么区别?

az31mfrm  于 2023-05-29  发布在  其他
关注(0)|答案(3)|浏览(150)

我正在看一些汇编的练习代码,作业基本上是用另一个跳转点替换一个跳转点。
原来的jmp是一个SHORT jmp,我需要接近的终点无法通过此指令到达。
我现在有三个选项,要么删除'SHORT',要么插入'LONG',要么插入'FAR'。
如果有任何文档表明它们之间的差异,我还没有找到。有人能帮忙吗?

o2g1uqev

o2g1uqev1#

我假设您的问题与x86架构有关;你没有在问题中具体说明。
SHORT跳转是从当前指令指针地址跳转到特定偏移量。LONG跳转可以使用更大的偏移值,因此可以跳得更远离当前指令指针地址。这两种跳转类型通常都是 * 相对的 * -也就是说,操作数是当前指令指针的偏移量(尽管在汇编源代码中,您通常提供目标标签-汇编器或链接器然后计算偏移量)。它们都没有跳转到不同的代码段,所以它们都是“接近”跳转
FAR jump指定了一个段和偏移量,它们都是 * 绝对的 *,因为它们指定了所需的代码段和指令指针,而不是相对于当前代码段/指令指针的偏移量。
总而言之,有三种类型的直接跳转:shortlong,它们都是 near 跳转,能够用相同的代码段跳转不同的相对距离,以及 far,可以跳转到任何绝对地址(段和偏移)。
(Note也可以执行 * 间接绝对 * 跳转,其中指定一个操作数,该操作数包含您希望跳转到的绝对地址。在这种情况下,跳跃可以是近的或远的,即它可以包括或不包括所需的代码段)。
如果你没有指定跳转的“距离”,那么你得到的是短、长还是远的跳转就由汇编程序决定了。大多数现代汇编器都是“两遍”的,如果可能的话,会使用短跳转,否则会使用长跳转或远跳转-后者只在需要时使用。
如果你需要帮助来理解我所说的“段”的意思,请参阅wikipedia's entry on x86 memory segmentation
有关可能的JMP指令寻址模式的完整详细信息,请参见this description of the x86 JMP instruction

5fjcxozz

5fjcxozz2#

一次SHORT跳跃:

  • 如果是向前跳转,则编码使用从00 h(+0)到7 Fh(+127)的相对偏移值,这使得程序执行能够跳转到另一条指令,其中最大值为127字节。
  • 如果是向后跳转,则编码使用从80 h(-128)到FFh(-1)的相对偏移值,这使得程序执行能够跳转到另一条指令,其中最大值为125字节。

一个LONG跳转,可以使用更大的偏移。
一个FAR跳转,跳转到另一个代码段。

vhmi4jdf

vhmi4jdf3#

TL:DR:shortlong/near只是强制选择指令长度。
far是一个完全不同的野兽。

  • jmp short foojmp rel8
  • jmp long foojmp near foojmp rel16/rel32(取决于模式)
  • jmp far [rdi]是对新CS:[ER]IP的jmp /调用。你很少想要这个。

有关可用的机器码形式jmp/calljcc(如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的唯一绝对直接跳转。
无条件远跳转,仅jmpcall/retf
语法细节取决于汇编器,但通常像jmp 0x10:foojmp far [eax]这样的东西在NASM中工作,后者在32位模式下运行时从[DS:EAX]加载6个字节到CS:EIP中。

*Near为正常跳转,不改变CS,只设置新的IP/EIP/RIP。以下表格可用:

    • 间接 *(使用绝对目标,例如函数指针或跳转表),如

jmp axjmp qword [rsi],或与call相同。(非条件间接jcc)。短或不短都没有意义,因为指令的机器码只对查找新[ER]IP的位置进行编码,而不直接编码如何到达它。axeax是操作数大小的问题,或者[esi][rsi]是地址大小的问题。和[rsi+0x1230]是一个问题的disp 8/disp 32用作寻址模式的一部分。

    • 直接 * 使用添加到IP/EIP/RIP的相对位移(编码到指令的机器码中)。所以它们是相对于指令1的结尾的。**这是你从普通的jmp foojle .else得到的,汇编程序通常会为你选择一个长度。
      *short表示使用8位(1字节)相对位移,也称为rel8

适用于jcc rel8jmp rel8(以及loop),不适用于call

  • 非短路,使用rel16(16位模式2)或rel32(其他模式)。因此是2字节或4字节的相对位移。
    您可以在asm源中使用nearlong强制此编码。

适用于jmp rel16/32call 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是一个东西的汇编器中,它是与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在汇编时不知道它将有多远,它假定为near(非短)。如果知道要链接的文件很小(或者代码在特殊的部分),可以强制这样做。
或者,如果你想留下一个完整的rel32,让其他东西修改这个机器码并写入一个新的偏移量,这将是near的一个用例。例如,在Linux上动态链接中使用的PLT曾经以这种方式工作(我认为),在jmp rel32中重写偏移量,而不是使用GOT条目进行间接jmp。
从历史上看,一些汇编器并不像NASM那样智能,如果您想要跳转的短编码,则总是需要手动提示。特别是对于向前跳转,到一个汇编程序还没有看到的标签。(如果您使用那个时代的旧工具处理16位代码,您可能会遇到这种情况。)即使是NASM在旧版本中也会默认禁用优化,这会使它选择长编码。
另外,jcc near只在386和更高版本上受支持,因此如果您希望汇编器实际发出它,则可能需要显式。

相关问题