Near and Far JMPs

前端 未结 5 1164
梦谈多话
梦谈多话 2021-02-05 23:56

I am doing Linux assembly and I understand that is has a flat memory model. What I am confused about is NEAR and FAR JMPs.

NEAR is in the same segment while FAR is anot

相关标签:
5条回答
  • 2021-02-06 00:29

    FAR and NEAR control transfer instructions basically is a control transfer protocol Usually, we see program execute line by line from top to bottom in a sequence some times it is necessary to transfer control from one location to other NEAR - if you want to transfer control to a memory location within the current code segment then it is called NEAR (Intra segment) If a control is transferred outside the current code segment then it is called FAR jump in FAR because the control is passing outside the current code segment both CS(code segment) and IP(Instruction Pointer) have to be updated to new values

    0 讨论(0)
  • 2021-02-06 00:30

    It hasn't been segments for a long time now. The correct term in protected mode x86 is selector.

    Having said that, the difference between a near jump and a far one is that the former maintains the same code selector cs while the latter (usually) changes it.

    In a flat memory model, the former case is almost always how it's done.

    You could have an operating system where the flat memory model is served by multiple selectors but I can't see a useful use case for it, and it's not the way Linux works, at least on x86.

    0 讨论(0)
  • 2021-02-06 00:33

    NEAR is in the same segment while FAR is another segment.

    A near jump jumps to a location within the current code segment (pointed to by cs). A far jump is normally used to jump to a location within a different code segment, but it can jump to a location within the current segment as well, if the segment selector in the far address coincides with the value in cs.

    From what I understand there are no segments in linux virtual memory?

    I wouldn't be surprised to find Linux ports to CPUs using some kind of segmented memory. So, I'd say it depends. You're unlikely to see Linux use segments on the x86 platform, though. But again, you or someone else could make a small Linux running in real mode and using segments.

    Also how do we know if my program's code is laid out in multiple segments?

    You check the CPU and OS. Naturally, if you write portable C code, this should be of no concern to you.

    0 讨论(0)
  • 2021-02-06 00:36

    From what I understand there are no segments in linux virtual memory?

    It's accurate enough. There are thread-specific data with a location pointed by %fs segment base, but there is no segments suitable for far jumps.

    Also how do we know if my program's code is laid out in multiple segments?

    If your target platform is Linux, you already know it is not. (I would be surprised if any modern OS still uses segments in a way which makes jump far make sense).

    0 讨论(0)
  • 2021-02-06 00:36

    Flat memory models used in modern mainstream OSes like Linux make segmentation mostly obsolete, and (fortunately) not something you ever need to worry about.

    Before page tables supported a NX bit to mark pages as non-executable, there was some work on using segment limits to avoid execution of writeable memory (especially the stack), making buffer overflow exploits harder than just returning into a buffer of shellcode. e.g. Exec Shield (lwn article) from 2003.

    I forget how this actually worked, I think it was mostly just setting a segment limit on CS that excluded the stack, not using far jmp with a new segment descriptor for each block of code (main executable + each dynamic library).

    But fortunately modern x86 can use modern page tables with a NX bit (PAE or x86-64), meaning user-space can have normal per-page execute permission set up the same way as read and write permissions (with mmap, mprotect, and ELF metadata for the initial parts of the program like the stack, r/w data, and text + read-only data). Or for non-Linux, their equivalent system calls and metadata of course.

    But if the OS is Linux and already running in protected mode + flat memory model, then do we ever need Far JMPs?

    No, you don't ever need a far jmp in user-space or kernel mode on Linux, and it would be a bad idea to make one.

    You might be tempted to use a far jmp ptr16:32 to encode a direct jump to an absolute address (with the new CS value being hard-coded as the same CS value that Linux is known to use for 32-bit user-space). But that's a lot slower than a normal near jmp rel32, which can reach any 32-bit address from any other 32-bit address. (Direct near jumps are only available with relative displacement, not absolute targets. You need an indirect jump for a near jump to an absolute address if you don't know your own address to calculate a relative displacement.)

    That's not even an option in 64-bit mode, where there's no jmp far 80-bit immediate ptr16:64 encoding, only memory-indirect. So you'd use mov rax, imm64 / jmp rax like a normal person if the jump target is too far away for a rel32 encoding.


    All user-space processes on Linux use the same 32-bit or 64-bit CS segment selector (with Current Privilege Level CPL = 3 = ring 3 user mode), and the kernel uses a different one (CPL=0 = ring 0 kernel mode).

    The only purpose of CS on modern x86 OSes is to select 32 vs. 64-bit mode (the .L bit in the GDT entry), and the privilege level.

    You only switch between user and kernel CS via interrupts / exceptions and instructions like int, sysenter or syscall to enter kernel mode, and iret to restore cs:eip or cs:rip from the kernel stack, or sysexit (32-bit kernels) or sysret for optimized return to user-space from system calls. After entering protected mode in the first place (with a jmp far), the kernel won't jmp far to change CS.


    Unless you want to do unstable silly computer tricks like changing to 32-bit mode in a process that started as 64-bit, there's zero reason to jmp far under Linux.

    That is possible, but I don't know if it's actually stable. e.g. the kernel might remember that your process is supposed to be 64-bit and return from an interrupt in 64-bit mode. (i.e. asynchronously set CS to the hard-coded USER32_CS constant, instead of restoring the old value.) IIRC, it does this in the syscall return path that uses sysret, see What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?

    Do you want to do that? No, you do not. There's zero support for doing so from any toolchain except for assemblers with BITS 32 vs. BITS 64 directives, basically zero benefit, and big risk of crashing (your process, not the machine). Anything you could do in hand-written asm in 32-bit mode, you could do just as well in 64-bit mode using 32-bit pointers allocated with mmap(MAP_32BIT), or using the x32 ABI.

    I guess maybe on original Core 2 (where cmp/jcc macro-fusion only works in 32-bit mode), there could be a perf advantage to running a loop in 32-bit mode and only using 64-bit mode for touching lots of memory, but switching basically costs a pipeline flush so it would normally be cheaper to just unroll a bit, instead of switching to 32-bit mode and back to 64 for a specific long-running loop.

    0 讨论(0)
提交回复
热议问题