11

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 another segment. From what I understand there are no segments in linux virtual memory? Also how do we know if my program's code is laid out in multiple segments?

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
ST-User
  • 395
  • 2
  • 3
  • 12

5 Answers5

11

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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Can you give me an example when i would have to change a selector for a FAR JMP? I am confused about real world usage – ST-User Feb 12 '13 at 08:26
  • @ST-User: sure. Let's say you have an OS where the bottom 3G is user code and the OS allows self-modifying code. That would mean the user-code selector would allow read/write access. However, the OS services sits in the top 1G and doesn't want you messing about with it, so the selector for that would be read-only (well, execute as well). That's one possibility. But keep in mind that's a contrived example - I know of no modern OS that does this simply because they prefer to keep things simple. – paxdiablo Feb 12 '13 at 08:35
  • Do you mean then that in modern OSs there is no Far Jmp required at all? It only comes into picture for the older 16 bit stuff where we used the segment registers for locating segments? – ST-User Feb 12 '13 at 10:24
  • @ST-User: yes, although it's not necessarily _quite_ that simple (since "flat memory" has meaning at multiple levels. For a layman, you could just consider that each process gets a 4G address space (in a "keep-it-simple" OS) and only needs _one_ code selector, one data selector and one stack selector within that 4G. In that case, no far jumps are ever required. In a much more complex situation, you may have code scattered amongst _many_ different code regions, each with their own selector - that would need far jumps. – paxdiablo Feb 12 '13 at 11:39
  • So as a assembly language programmer how can I know that my code is spread over multiple segments? and then use far jmp? – ST-User Feb 12 '13 at 14:28
  • As an assembly language programmer, _you_ control that. Assembly language is where you can muck about with selectors (OS permitting, of course). If you're not doing far jumps and/or setting up LDT/GDT entries,, then you're almost certainly within a single selector. – paxdiablo Feb 12 '13 at 15:44
6

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.

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • Can you gimme a real world example of a far jmp? I am confused as to when to prefix "far" say I write a very large program in assembly. – ST-User Feb 12 '13 at 08:27
  • If it's a very large 16-bit program and its code does not fit into one 64KB segment, you split it into multiple segments (each smaller than 64KB) and then pass control to the routines using either a far call or a far jump. Obviously, far calls and far returns must be matching, you shouldn't use a near call and then return with a far return or vice versa. – Alexey Frunze Feb 12 '13 at 08:30
  • No i meant example for a moder IA-32 powered Linux OS using protected mode + flat memory model. Not the older 16bits ones. – ST-User Feb 12 '13 at 10:23
  • You can still use segments in a similar fashion in protected mode, it's just that now you'll need to have segment descriptors in the GDT (or LDT) to define segments explicitly unlike in real mode, where the only segment attribute that is variable is the segment base and it's directly computable from the selector/segment. In protected mode more uses are possible. I'm sorry, but your question is still unclear. Want to find out more about the possible options? Read the Intel CPU manual. Restating it here is impractical. – Alexey Frunze Feb 12 '13 at 11:06
2

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).

Anton Kovalenko
  • 20,999
  • 2
  • 37
  • 69
  • The why do we even need far jmps? when we don't change segments? – ST-User Feb 12 '13 at 08:26
  • 2
    On modern systems with flat memory model you don't need far jumps except in *very* special circumstances, like *entering* protected mode. – Anton Kovalenko Feb 12 '13 at 09:44
  • But if the OS is Linux and already running in protected mode + flat memory model, then do we ever need Far JMPs? – ST-User Feb 12 '13 at 10:22
  • @ST-User: No, you don't ever need a far jmp in user-space or kernel mode on Linux. All user-space processes use the same CS segment selector (CPL=3 = ring 3 user mode), and the kernel uses a different one (CPL=0 = ring 0 kernel mode). You only switch between them 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 `sysret`. 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`. – Peter Cordes Apr 17 '19 at 23:30
1

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
1

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

vignesh
  • 11
  • 3