【问题标题】:64-bit GCC mixing 32-bit and 64-bit pointers64 位 GCC 混合 32 位和 64 位指针
【发布时间】:2013-01-11 02:50:00
【问题描述】:

虽然代码有效,但编译器决定混合相同类型的 32 位和 64 位参数,这让我感到困惑。具体来说,我有一个接收三个字符指针的函数。查看汇编代码,三个中的两个作为 64 位指针传递(如预期的那样),而第三个是局部常量,但仍然是字符串,作为 32 位指针传递。我看不出我的函数如何知道第三个参数何时不是完全加载的 64 位指针。显然,只要较高的一侧为 0,这并不重要,但我认为它没有努力确保这一点。在这个例子中,任何东西都可能在 RDX 的高端。我错过了什么?顺便说一句,接收函数假定它是一个完整的 64 位指针,并在入口处包含以下代码:

     movq    %rdx, -24(%rbp)

这是有问题的代码:

.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?
    }

【问题讨论】:

  • 编译器只要能确定.LC4在前4GB以内,就可以做到这一点。它看起来确实像一个错误。 %edx 会加载 LC4 的 32 位地址,高位设置为零,所以调用 edc_function() 时,可以使用完整的 64 位,只要地址在低 4GB 以内,它会正常工作。但这对我来说确实很奇怪。
  • 好的,我不知道 CPU 对负载进行了零扩展。哇!这让我睡得很好......
  • 仅供参考,如果无法进行此优化,下一个最佳选择是lea .LC4(%rip), %rdx。不是具有 64 位绝对地址的 10 字节 movHow to load address of function or label into register in GNU Assembler

标签: c gcc x86-64 gnu-assembler


【解决方案1】:

在寄存器中加载 32 位值时,该值是零扩展的。您可能在编译器知道代码位于低 32 位可寻址内存中的模式下工作。

GCC 有几个用于 x64 的内存模型,其中两个具有该属性。来自 GCC 文档:

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

(其他都是kernel,类似small但是在upper/negative 2GB of 地址空间大,无限制)。

【讨论】:

  • AFAIK,我使用 mcmodel=small 因为它是默认值(除了 -O2,我没有编译器选项。“-mcmodel=small 为小代码模型生成代码:程序及其符号必须在地址空间的低 2 GB 中链接。指针是 64 位。程序可以静态或动态链接。这是默认代码模型。”但是你对零扩展和编译器知道这一点的事实是正确的特定字符串的下落。
  • @GarysTampaOfficeOfficeTampa,已修复。我正在搜索一些不存在的东西,并忽略了刚好在我的起点之上的证明;)我(回想起来愚蠢地)假设 small 与 -mx32 类似,长度为 64 位。
  • 嗯,你的主要答案仍然是正确的:32 位 reg 在 64 位模式下是零扩展的。
【解决方案2】:

将此添加为答案,因为它包含原始问题的“部分难题”:

只要编译器可以确定 [例如通过指定满足此要求的内存模型] .LC4 在前 4GB 内,它就可以执行此操作。 %edx 会加载 LC4 的 32 位地址,并且高位设置为零,所以当调用 edc_function() 时,它可以使用 %rdx 的全部 64 位,并且只要地址在较低的4GB,它会工作得很好。

【讨论】:

    猜你喜欢
    • 2013-10-23
    • 2011-05-22
    • 1970-01-01
    • 2017-02-02
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 1970-01-01
    • 2013-12-19
    相关资源
    最近更新 更多