Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why isn't there a JMP immediate or absolute?

825 views
Skip to first unread message

spam...@crayne.org

unread,
Jun 12, 2006, 8:30:05 PM6/12/06
to

It has been a while, but I had to write some 32 bit x86 assembly. This
was a function
thunk in Windows / Win32.

I was looking for a simple 5 byte instruction to JMP to an absolute
(ie. non-relative)
or immediate (imm32) address. Essentially very simple code generation.

I could not find such an instruction. All the JMPs were relative,
indirect, or specifying some segment index/TSS switch. In fact, in all
the windows thunking code, they branch relative which requires a tiny
amount of extra work when generating the code. I ended up using the EAX
register and more than one instruction to accomplish this.

Why was this choice made by Intel??

thx in advance

Nicholas Sherlock

unread,
Jun 12, 2006, 10:25:40 PM6/12/06
to
spam...@crayne.org wrote:
> I was looking for a simple 5 byte instruction to JMP to an absolute
> (ie. non-relative)
> or immediate (imm32) address. Essentially very simple code generation.
>
> I could not find such an instruction. All the JMPs were relative,
> indirect, or specifying some segment index/TSS switch. In fact, in all
> the windows thunking code, they branch relative which requires a tiny
> amount of extra work when generating the code. I ended up using the EAX
> register and more than one instruction to accomplish this.
>
> Why was this choice made by Intel??

Relative jumps don't care where your code is located in memory, which is
a big bonus, particularly if you aren't running a virtual memory system.
It can be annoying sometimes when patching code up. Relative jumps work
just fine though, you just need to calculate the offset yourself (For E8
jmp):

newtargetaddress-callbyteaddress+5

Where callbyteaddress is the address of the E8 byte you inserted.

Or you can do an absolute jump through this far slower construct:

push newtargetaddress
ret

Cheers,
Nicholas Sherlock

--
http://www.sherlocksoftware.org

spam...@crayne.org

unread,
Jun 12, 2006, 10:10:55 PM6/12/06
to

JMP FAR does what you want. Of course, in flat-model OSes (like Windows
or Linux) you don't really get to use it. That's the real problem.

Then again, I would argue that a tiny bit of extra work at code
generation time is a whole lot better than executing two instructions
at run time. How hard is it to compute the distance between the JMP and
target if you already know the target address?
Cheers,
Randy Hyde

spam...@crayne.org

unread,
Jun 13, 2006, 5:16:24 AM6/13/06
to

spam...@crayne.org wrote:

> Why was this choice made by Intel??

Someone please correct me if I'm wrong, but I think it's to do with
relocation - if your code uses relative JMPs, it can be placed anywhere
in memory without breaking it, as opposed to if you'd used absoloute
addresses. Since, for example in Linux, your program may be being moved
about in memory, or swapped in and out to disk WHILE it's running (or
any other OS, probably Windows as well).

Can someone tell me what this JMP FAR mentioned above is? Will it take
absolute i.e. numerical address? "JUMP to address 10"?


Thanks.

spam...@crayne.org

unread,
Jun 13, 2006, 6:22:36 AM6/13/06
to
HI,
You can use absolute jumps like this:
mov eax,abs_address ; yes it can be zero or 10
JMP eax

or more directly

Call [my_jump_table]

or

Call eax

but then it is not a jumb anymore

I guess relocations did play a role in this choice. However CALL's are
absolute so they do hinder relocations.

A nother assumption is that the JMP's appear very offten in code
because of IF THEN ELSE like constructs in HLL languages and because of
this if the operand is smaller for "close" relative then the code might
be smaller and faster. Maybe adding to IP is also faster than simply
loading it (i doubt this)...

Regards,
Bogdan Ontanu

spam...@crayne.org a scris:

spam...@crayne.org

unread,
Jun 13, 2006, 10:23:16 AM6/13/06
to

spam...@crayne.org wrote:
> spam...@crayne.org wrote:
>
> > Why was this choice made by Intel??
>
> Someone please correct me if I'm wrong, but I think it's to do with
> relocation - if your code uses relative JMPs, it can be placed anywhere
> in memory without breaking it, as opposed to if you'd used absoloute
> addresses. Since, for example in Linux, your program may be being moved
> about in memory, or swapped in and out to disk WHILE it's running (or
> any other OS, probably Windows as well).

I have no problem with relative JMP, but why exclude a JMP immediate.
Given Intel's choice of instructions in the past, it would seem odd
that they
would exclude that.

spam...@crayne.org

unread,
Jun 13, 2006, 10:19:04 AM6/13/06
to

rand...@earthlink.net wrote:
>
> JMP FAR does what you want. Of course, in flat-model OSes (like Windows
> or Linux) you don't really get to use it. That's the real problem.

Interesting stuff. Is that instruction just 5 bytes? (which OS's don't
use a flat model?)

>
> Then again, I would argue that a tiny bit of extra work at code
> generation time is a whole lot better than executing two instructions
> at run time. How hard is it to compute the distance between the JMP and
> target if you already know the target address?
> Cheers,
> Randy Hyde

Yep, I don't mind... and in fact this question is kind of ironic for me
since I'm a big
fan of PIC. I just cannot think of a reason Intel would not put
something like that in.
It seems strange that something so simple would be missing.

Bjarni Juliusson

unread,
Jun 13, 2006, 2:42:38 PM6/13/06
to
spam...@crayne.org wrote:
> spam...@crayne.org wrote:
>
>> Why was this choice made by Intel??
>
> Someone please correct me if I'm wrong, but I think it's to do with
> relocation - if your code uses relative JMPs, it can be placed anywhere
> in memory without breaking it, as opposed to if you'd used absoloute
> addresses. Since, for example in Linux, your program may be being moved
> about in memory,

Only in physical memory. The linear addresses remain constant. Think
about it.

> or swapped in and out to disk WHILE it's running

This is completely transparent to the program.

> (or any other OS, probably Windows as well).

Insert pun on "as well".

> Can someone tell me what this JMP FAR mentioned above is? Will it take
> absolute i.e. numerical address? "JUMP to address 10"?

A far jump is a jump that takes a segment:offset pair as target. A near
jump only takes an offset inside the current segment. The IA-32 manual
lists all address modes for each instruction. Get it from Intel.


Bjarni
--

INFORMATION WANTS TO BE FREE

Frank Kotler

unread,
Jun 13, 2006, 6:48:42 PM6/13/06
to
spam...@crayne.org wrote:
> spam...@crayne.org wrote:
>
>>Why was this choice made by Intel??
>
> Someone please correct me if I'm wrong, but I think it's to do with
> relocation - if your code uses relative JMPs, it can be placed anywhere
> in memory without breaking it, as opposed to if you'd used absoloute
> addresses.

That sounds logical. Disassembling the object file looks like these
addresses *are* being relocated though, so... ???

> Since, for example in Linux, your program may be being moved
> about in memory, or swapped in and out to disk WHILE it's running (or
> any other OS, probably Windows as well).

I *think* we'd be at the same virtual address when we're swapped in and
running. I think a dynamic library can be moved to a different virtual
address, and so needs to be "position independent". I'm pretty fuzzy on
this...

> Can someone tell me what this JMP FAR mentioned above is? Will it take
> absolute i.e. numerical address? "JUMP to address 10"?

Well... "jmp far" and "call far" involve loading cs, as well as (e)ip.
In the case of "call far", cs is on the stack - altering the expected
position of parameters, relative to (e)sp/(e)bp (if you're doing that).
More useful in 16-bit code, where the segments are only 64k-1 (usually).
Not very useful in "flat model" OSen - not from "user code", anyway.
However, to my absolute astonishment, it *does* work (in Linux - can't
speak for Windows) if I use the same cs as I've already got (determined
by inspection - I suppose it's "standard"... ??? Different number for
Windows, probably).

You can *write* "jmp 10" or "call 10", in any case. What gets emitted
for code is not "10", but the distance (+/-) from "here" to "10". In the
case of "jmp", there's a signed-byte form, if the displacement fits in a
signed byte.

The "far" versions emit the address (and the segment/selector), not a
"relative" value. For example:

global _start

section .text
_start:

call far [target]
call 23h:subbie

;------------------------------------
; these next two lines will crash, if run
; included strictly to observe the disassembly

call subbie

jmp 10
;------------------------------------

jmp 23h:exit
jmp exit
jmp short exit

nop

exit:

mov eax, 1
int 80h

;------------------
subbie:
; say hi, just to prove we did something
mov eax, 4
mov ebx, 1
mov ecx, msg
mov edx, msg_len
int 80h
retf
;-----------------

section .data
target dd subbie
dw 23h

msg db 'tada!', 10
msg_len equ $ - msg
;--------------------------

Disassembles to:

08048080 FF1DC4900408 call far [0x80490c4]
08048086 9AAD8004082300 call 0x23:0x80480ad
0804808D E81B000000 call 0x80480ad
08048092 E9737FFBF7 jmp 0xa
08048097 EAA68004082300 jmp 0x23:0x80480a6
0804809E E903000000 jmp 0x80480a6
080480A3 EB01 jmp short 0x80480a6
080480A5 90 nop
080480A6 B801000000 mov eax,0x1
080480AB CD80 int 0x80
080480AD B804000000 mov eax,0x4
080480B2 BB01000000 mov ebx,0x1
080480B7 B9CA900408 mov ecx,0x80490ca
080480BC BA06000000 mov edx,0x6
080480C1 CD80 int 0x80
080480C3 CB retf

As you can see, the "far" forms do emit the actual address. I doubt if
it's what the O.P. was looking for...

Best,
Frank

f0dder

unread,
Jun 13, 2006, 7:42:01 PM6/13/06
to
Frank Kotler wrote:
> spam...@crayne.org wrote:
>> spam...@crayne.org wrote:
>>
>>> Why was this choice made by Intel??
>>
>> Someone please correct me if I'm wrong, but I think it's to do with
>> relocation - if your code uses relative JMPs, it can be placed
>> anywhere in memory without breaking it, as opposed to if you'd used
>> absoloute addresses.
>
> That sounds logical. Disassembling the object file looks like these
> addresses *are* being relocated though, so... ???


Depends on object format and the kind of call/jmp. Obviously, external
references will have to be fixed up by a linker, or at loadtime.

> I *think* we'd be at the same virtual address when we're swapped in
> and running. I think a dynamic library can be moved to a different
> virtual address, and so needs to be "position independent". I'm
> pretty fuzzy on this...

I can't think of why anybody would put you at a deifferent VA when swapped
in, unless perhaps using an OS that doesn't support hardware paging.
Segment-based swapping? Eek :)


> Well... "jmp far" and "call far" involve loading cs, as well as (e)ip.
> In the case of "call far", cs is on the stack - altering the expected
> position of parameters, relative to (e)sp/(e)bp (if you're doing
> that). More useful in 16-bit code, where the segments are only 64k-1
> (usually). Not very useful in "flat model" OSen - not from "user
> code", anyway. However, to my absolute astonishment, it *does* work
> (in Linux - can't speak for Windows) if I use the same cs as I've
> already got (determined by inspection - I suppose it's "standard"...
> ??? Different number for Windows, probably).

It works on windows too, but the CS value changes between operating systems
(at least between 9x/NT, haven't really bothered to look at the different NT
versions). The FAR call/jmp opcodes are longer than the relative calls
though, and might also be slower?

As for relocation, there's different ways to do it. For windows (in reality
only used for DLLs, and only if the preferred imagebase isn't available),
all absolute references are fixed up ("add [ref], delta").

Another way is to keep a global register that keeps the imagebase, and
reference everything from that - you can 100% avoid relocations that way. I
think that's what happens with GCC -fPIC.

Bjarni Juliusson

unread,
Jun 13, 2006, 8:19:12 PM6/13/06
to
Frank Kotler wrote:
> spam...@crayne.org wrote:
>> spam...@crayne.org wrote:
>
> Well... "jmp far" and "call far" involve loading cs, as well as (e)ip.
> In the case of "call far", cs is on the stack - altering the expected
> position of parameters, relative to (e)sp/(e)bp (if you're doing that).

What Frank means here is that a call far instruction pushes cs:ip on the
stack, as opposed to just ip in the case of a near call. Thus, the
parameters, which were pushed before the call, end up further away from
the stack pointer after the call instruction if it is a far call, as the
pushed cs takes up an additional word of stack space.

> More useful in 16-bit code, where the segments are only 64k-1 (usually).

They're 64K exactly, and that is in real mode - 16 bit code in protected
mode allows other lengths of segments, if I'm not entirely mistaken.

spam...@crayne.org

unread,
Jun 13, 2006, 10:33:11 PM6/13/06
to

Bjarni Juliusson wrote:
>
> > Can someone tell me what this JMP FAR mentioned above is? Will it take
> > absolute i.e. numerical address? "JUMP to address 10"?
>
> A far jump is a jump that takes a segment:offset pair as target. A near
> jump only takes an offset inside the current segment. The IA-32 manual
> lists all address modes for each instruction. Get it from Intel.
>

The near jumps are all relative or indirect.

Frank Kotler

unread,
Jun 14, 2006, 1:57:32 AM6/14/06
to
Bjarni Juliusson wrote:

....


>> More useful in 16-bit code, where the segments are only 64k-1 (usually).
>
> They're 64K exactly,

Right you are. The "limit", or maximum offset, is "-1".

> and that is in real mode - 16 bit code in protected
> mode allows other lengths of segments, if I'm not entirely mistaken.

Right. Although addresses may be limited to 24 bits on an actual 286(?).
We can even hop over into pmode, set "big" limits, and switch back to
rmode - the so-called "Flat Real Mode", "Big Real Mode" (etc.). But
that's not what I'm calling "usually". :)

Best,
Frank

Frank Kotler

unread,
Jun 14, 2006, 2:17:56 AM6/14/06
to
f0dder wrote:

....


> It works on windows too, but the CS value changes between operating systems
> (at least between 9x/NT, haven't really bothered to look at the different NT
> versions).

May be like that in Linux, too - I haven't looked into it. Makes it
somewhat less useful. (even more less useful...)

> The FAR call/jmp opcodes are longer than the relative calls
> though, and might also be slower?

I wouldn't be surprised. I think *any* load of a segreg is slow. I'm not
advocating a far jmp - the O.P. wanted to know if there was a "jmp imm"
that *emitted* the actual address, not a displacement (if I understood
the question). He may have been writing an assembler/compiler... the
rest of us don't care much. :)

The real answer to "why" is probably "marketing". :)

Best,
Frank

spam...@crayne.org

unread,
Jun 14, 2006, 12:56:37 AM6/14/06
to

Although, I'm beating a dead horse here...

I'm sure that the microops translate a:

move imm32, eax
jmp eax

or similar sequence into a straight
cs:mem -> eip operation.

I should do some tests to confirm this.

spam...@crayne.org

unread,
Jun 14, 2006, 9:41:19 AM6/14/06
to
Frank Kotler wrote:
> O.P. wanted to know if there was a "jmp imm"
> that *emitted* the actual address, not a displacement (if I understood
> the question). He may have been writing an assembler/compiler... the
> rest of us don't care much. :)
>
> The real answer to "why" is probably "marketing". :)

Hi Frank,

Yeah, the reason was that I was writing some thunking code in windows.
For some strange reason I thought that this existed. Usually, whenever
I have
an assumption like that, and I discover that something isn't there red
flags
go up and I start researching like mad. (For example, a while back, I
read
about innodb for MySQL and assumed that they would have the same
log rotation system as Oracle. Surprisingly, and unfortunately, they
didn't)

I'm not writing an assembler/compiler.

0 new messages