近和远JMP

时间:2013-02-11 12:42:25

标签: assembly x86 nasm

我正在进行Linux程序集,我知道它有一个平坦的内存模型。令我困惑的是NEAR和FAR JMP。

NEAR属于同一部分,而FAR是另一部分。据我所知,linux虚拟内存中没有任何段?另外我们如何知道我的程序代码是否分布在多个部分中?

5 个答案:

答案 0 :(得分:8)

现在很久没有细分了。保护模式x86中的正确术语是选择器。

话虽如此,近跳和远跳之间的区别在于前者保持相同的代码选择器cs,而后者(通常)更改它。

在平面记忆模型中,前一种情况几乎总是如此。

可以拥有一个操作系统,其中平面内存模型由多个选择器提供服务,但我看不到它的有用用例,而且它不是Linux的工作方式,至少在86。

答案 1 :(得分:5)

  

NEAR属于同一部分,而FAR是另一部分。

近跳转到当前代码段内的某个位置(由cs指向)。远跳通常用于跳转到不同代码段内的位置,但如果远地​​址中的段选择器与cs中的值一致,它也可以跳转到当前段内的位置。

  

根据我的理解,linux虚拟内存中没有段?

使用某种分段内存找到CPU的Linux端口我不会感到惊讶。所以,我认为这取决于。但是,您不太可能在x86平台上看到Linux使用片段。但同样,你或其他人可以让一个小型Linux以实模式运行并使用段。

  

另外,我们如何知道我的程序代码是否分布在多个部分中?

检查CPU和操作系统。当然,如果您编写可移植的C代码,这对您来说应该不用担心。

答案 2 :(得分:2)

  

根据我的理解,linux虚拟内存中没有段?

这很准确。有一个特定于线程的数据,其位置由%fs base 指向,但没有适合远距离跳跃的段。

  

另外,我们如何知道我的程序代码是否以多个方式排列   段?

如果您的目标平台是Linux,那么您已经知道它不是。 (如果任何现代操作系统仍以某种方式使用段,使jump far有意义,我会感到惊讶。)

答案 3 :(得分:0)

在现代主流操作系统(如Linux)中使用的平面内存模型使分段变得过时了,(幸运的是)您无需担心。

在页表支持NX位以将页标记为不可执行之前,有一些使用段限制来避免执行可写内存(尤其是堆栈)的工作,这使得缓冲区溢出利用比仅返回到缓冲区更困难。 Shellcode。例如Exec Shield (lwn article)从2003年开始。

我忘记了它是如何工作的,我认为这主要是对不包括堆栈的CS设置一个段限制,而不是对每个代码块(主可执行文件和每个动态库)使用带有新段描述符的远jmp。

但是幸运的是,现代的x86可以使用带有NX位(PAE或x86-64)的现代页表,这意味着用户空间可以具有正常的每页执行权限,其设置方式与读写权限相同({ 1}},mmap和程序初始部分的ELF元数据,例如堆栈,读/写数据和文本+只读数据)。或者,对于非Linux,它们的等效系统调用和元数据当然也是如此。

  

但是,如果操作系统是Linux,并且已经在保护模式+平面内存模型下运行,那么我们是否需要Far JMP?

不,您不需要在Linux的用户空间或内核模式下使用mprotect,这不是一个好主意。

您可能会想用远jmp ptr16:32来编码直接跳转到绝对地址的代码(新的CS值被硬编码为Linux已知用于32位的CS值)用户空间)。但这比far jmp附近的正常速度慢很多,后者可以从任何其他32位地址访问任何32位地址。 (直接近跳仅适用于相对位移,而不适用于绝对目标。如果您不知道自己的自己地址来计算相对位移,则需要间接跳转才能近距离跳转到绝对地址。 )

在64位模式下,甚至没有jmp rel32 ptr16:64编码,而仅是间接内存的,这甚至不是一个选项。因此,如果跳转目​​标距离jmp far 80-bit immediate编码太远,您将像普通人一样使用mov rax, imm64 / jmp rax


Linux上的所有用户空间进程都使用相同的32位或64位CS段选择器(当前特权级别CPL = 3 =环形3用户模式),而内核使用不同的选择器(CPL = 0 =环0内核模式)。

CS在现代x86操作系统上的唯一目的是选择32位和64位模式(GDT条目中的rel32位)和特权级别。 >

您只能通过中断/异常以及诸如.Lintsysenter之类的指令在用户和内核CS之间切换,以进入内核模式,并通过syscall来还原{{ 1}}或iret从内核堆栈,或cs:eip(32位内核)或cs:rip用于优化从系统调用返回到用户空间。首先进入保护模式(使用sysexit)后,内核将不会sysret更改CS。


除非您想执行一些不稳定的愚蠢的计算机技巧,例如在以64位开始的进程中更改为32位模式,否则在Linux下使用jmp far的理由是零。 >

这可能是 ,但我不知道它是否真的稳定。例如内核可能会记住您的进程应该是64位的,并以64位模式从中断返回。 (即,将CS异步设置为硬编码的jmp far常量,而不是还原旧值。)IIRC在使用jmp far的{​​{1}}返回路径中执行此操作,请参见{{ 3}}

您要这样做吗?你不可以。除了使用USER32_CSsyscall伪指令的汇编程序外,任何工具链都对此提供零支持,基本上是零收益,而且崩溃风险很大(您的过程,而不是机器)。您可以在32位模式下的手写asm中执行任何操作,也可以使用通过sysret分配的32位指针或使用x32 ABI在64位模式中执行同样的操作。

我想也许是在原始Core 2上(其中cmp / jcc宏融合仅在32位模式下工作),在32位模式下运行循环并且仅将64位模式用于占用大量内存,但是切换基本上会浪费管道刷新的时间,因此通常只需花费一点时间就便宜,而不是切换到32位模式并针对特定的长时间运行循环切换回64位。

答案 4 :(得分:0)

FAR和NEAR控制转移指令基本上是一种控制转移协议通常,我们有时会看到程序从上到下逐行执行,有时需要将控制从一个位置转移到另一个位置 NEAR-如果要将控制转移到当前代码段内的存储位置,则称为NEAR(内部段) 如果控件转移到当前代码段之外,则称为FAR跳转 在FAR中,因为控件传递到当前代码段之外,因此CS(代码段)和IP(指令指针)都必须更新为新值