Re: 64-bit write() system call

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Bob Plantz wrote:

> The default address size is 64 bits in 64-bit mode. But when I look at
> the assembly language for the write() syscall, gcc uses only 32 bits of
> the address:

The design of the x86_64 instruction set made some compromises for speed
and for keeping the size of the instructions down, namely that there are
very few instructions that take a full 64 bit operand... in fact
'movabs' is the only one.  All the rest all take 32 bit operands and the
address is calculated through either sign extension or as
%rip-relative.  This is actually not a big deal because these days most
linking is done with shared libraries, and so you already have a level
of indirection there with the PLT/GOT so it doesn't matter that much
that it's awkward to refer to an absolute 64 bit address -- turns out
this isn't a very common thing to need to do.

If you read the x86_64 ELF document there are a number of different code
models defined: small, medium, large, and kernel, corresponding to the
gcc switch "-mcmodel=".  The small model is the default and to quote the
ELF document:

> The virtual address of code executed is known at link time. Additionally 
> all symbols are known to be located in the virtual addresses in the 
> range from 0 to 2**31 - 2**24 - 1 or from 0x00000000 to 0x7effffff. 
> This allows the compiler to encode symbolic references with offsets in 
> the range from -(2**31) to 2**24 or from 0x80000000 to 0x01000000 
> directly in the sign extended immediate operands, with offsets in the 
> range from 0 to 2**31 - 2**24 or from 0x00000000 to 0x7f000000 in the 
> zero extended immediate operands and use instruction pointer relative 
> addressing for the symbols with offsets in the range -(2**24) to 2**24 
> or 0xff000000 to 0x01000000. This is the fastest code model and we 
> expect it to be suitable for the vast majority of programs.

Because of these assumptions, the reference to your aLetter symbol can
be handled with a simple 32 bit reloc (R_X86_64_32) which is defined as
truncating the address into 32 bits.  This combines with the fact that
the processor zero extends 32 bit operations with register destinations,
so when the value of that reloc is loaded into %esi the upper 32 bits of
%rsi are implicitly set to zero.  This also has an advantage for library
calls like your call to write(), since %rip-relative addressing mode can
cover the whole available address space -- the call will be be resolved
with a R_X86_64_PLT32 reloc.

The end result is 64 bit addressing without paying the price of wider
opcodes or more complicated relocs, it's really a win-win.  Of course
for those apps that really need more arbitrary addressing, it's
available in the other models, but this way the common case doesn't have
to pay the price.

Brian

[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux