Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

64-bit GCC mixing 32-bit and 64-bit pointers

Although the code works, I'm baffled by the compiler's decision to seemingly mix 32 and 64 bit parameters of the same type. Specifically, I have a function which receives three char pointers. Looking at the assembly code, two of the three are passed as 64-bit pointers (as expected), while the third, a local constant, but character string nonetheless, is being passed as a 32-bit pointer. I don't see how my function could ever know when the 3rd parameter isn't a fully loaded 64-bit pointer. Obviously it doesn't matter as long as the higher side is 0, but I don't see it making an effort to ensure that. Anything could be in the high side of RDX in this example. What am I missing? BTW, the receiving function assumes it's a full 64-bit pointer and includes this code on entry:

     movq    %rdx, -24(%rbp)

This is the code in question:

.LC4
    .string "My Silly String"

    .text
    .globl funky_funk
    .type  funky_funk, @function
    funky_funk:
        pushq     %rbp
            movq      %rsp, %rbp
            pushq     %rbx
            subq      $16, %rsp
            movq      %rdi, -16(%rbp)          ;char *dst 64-bit
            movl      %esi, -20(%rbp)          ;int len, 32 bits OK

            movl      $.LC4, %edx              ;<<<<---- why is it not RDX?

            movl      -20(%rbp), %ecx          ;int len 32-bits OK
            movq      -16(%rbp), %rbx          ;char *dst 64-bit
            movq      -16(%rbp), %rax          ;char *dst 64-bit
            movq      %rbx, %rsi               ;char *dst 64-bit
            movq      %rax, %rdi               ;char *dst 64-bit
            call      edc_function


    void funky_funk(char *dst, int len)
    {                                             //how will function know when 
         edc_function(dst, dst, STRING_LC4, len); //a str passed in 3rd parm
    }                                             //is 32-bit ptr vs 64-bit ptr?

    void edc_function(char *dst, char *src, char *key, int len)
    {
         //so, is key a 32-bit ptr? or is key a 64-bit ptr?
    }
like image 707
Gary Avatar asked Jan 27 '13 18:01

Gary


2 Answers

When loading a 32 bits value in a register, the value is zero extended. You are probably working in a mode where the compiler knows the code is in the lower 32 bit addressable memory.

GCC has several memory models for x64, two of which have that property. From GCC documentation:

`-mcmodel=small'
     Generate code for the small code model: the program and its
     symbols must be linked in the lower 2 GB of the address space.
     Pointers are 64 bits.  Programs can be statically or dynamically
     linked.  This is the default code model.
`-mcmodel=medium'
     Generate code for the medium model: The program is linked in the
     lower 2 GB of the address space.  Small symbols are also placed
     there.  Symbols with sizes larger than `-mlarge-data-threshold'
     are put into large data or bss sections and can be located above
     2GB.  Programs can be statically or dynamically linked.

(the other ones are kernel, which is similar to small but in the upper/negative 2GB of address space and large with no restriction).

like image 186
AProgrammer Avatar answered Oct 15 '22 05:10

AProgrammer


Adding this as an answer, as it contains "part of the puzzle" for the original question:

As long as the compiler can determine [by for example specifying a memorymodel that satisfies this] that .LC4 is within the first 4GB, it can do this. %edx will be loaded with 32 bits of the address of LC4, and upper bits set to zero, so when the edc_function() is called, it can use the full 64-bits of %rdx, and as long as the address is within the lower 4GB, it will work out fine.

like image 4
Mats Petersson Avatar answered Oct 15 '22 05:10

Mats Petersson