在GCC的链接阶段更改了x86_64汇编指令

在GCC的链接阶段更改了x86_64汇编指令,gcc,assembly,optimization,linker,ld,Gcc,Assembly,Optimization,Linker,Ld,我正在Linux(centos7_64)中使用sqlite3库编译一个程序。由于用户有一个旧的CPU,我在GCC中设置了-march=nehalem标志(-march=nehalem-mtune=nehalem-m64-O3)。我发现我不能将汇编指令限制为nehalem,一些BMI操作仍然存在于最终二进制文件中 按照输出一步一步,我发现问题来自链接器(ld) libsqlite3.a: 632c2: 66 41 83 4f 26 01 orw $0x1,0x2

我正在Linux(centos7_64)中使用sqlite3库编译一个程序。由于用户有一个旧的CPU,我在GCC中设置了-march=nehalem标志(-march=nehalem-mtune=nehalem-m64-O3)。我发现我不能将汇编指令限制为nehalem,一些BMI操作仍然存在于最终二进制文件中

按照输出一步一步,我发现问题来自链接器(ld)

libsqlite3.a:

   632c2:       66 41 83 4f 26 01       orw    $0x1,0x26(%r15)
   632c8:       0f b6 84 24 80 00 00    movzbl 0x80(%rsp),%eax
   632cf:       00
   632d0:       c1 e0 08                shl    $0x8,%eax
   632d3:       89 c2                   mov    %eax,%edx
   632d5:       0f b6 84 24 81 00 00    movzbl 0x81(%rsp),%eax
   632dc:       00
   632dd:       c1 e0 10                shl    $0x10,%eax
   632e0:       09 d0                   or     %edx,%eax
   632e2:       8d 90 00 fe ff ff       lea    -0x200(%rax),%edx
   632e8:       41 89 47 30             mov    %eax,0x30(%r15)
   632ec:       81 fa 00 fe 00 00       cmp    $0xfe00,%edx
   632f2:       0f 87 d1 05 00 00       ja     638c9 <sqlite3BtreeOpen+0xb29>
   632f8:       8d 50 ff                lea    -0x1(%rax),%edx
   632fb:       85 c2                   test   %eax,%edx
   632fd:       0f 85 c6 05 00 00       jne    638c9 <sqlite3BtreeOpen+0xb29>
632c2:66 41 83 4f 26 01或$0x1,0x26(%r15)
632c8:0f b6 84 24 80 00 movzbl 0x80(%rsp),%eax
632cf:00
632d0:c1 e0 08 shl$0x8,%eax
632d3:89 c2 mov%eax,%edx
632d5:0f b6 84 24 81 00 movzbl 0x81(%rsp),%eax
632dc:00
632dd:c1 e0 10 shl$0x10,%eax
632e0:09 d0或%edx,%eax
632e2:8d 90 00 fe ff ff lea-0x200(%rax),%edx
632e8:41894730MOV%eax,0x30(%r15)
632ec:81 fa 00 fe 00 cmp$0xfe00,%edx
632f2:0f 87 d1 05 00 ja 638c9
632f8:8d 50 ff lea-0x1(%rax),%edx
632fb:85 c2测试%eax,%edx
632fd:0f 85 c6 05 00 jne 638c9
但是,在最终二进制文件中:

  9499f2:       66 41 83 4f 26 01       orw    $0x1,0x26(%r15)
  9499f8:       0f b6 84 24 80 00 00    movzbl 0x80(%rsp),%eax
  9499ff:       00
  949a00:       0f b6 94 24 81 00 00    movzbl 0x81(%rsp),%edx
  949a07:       00
  949a08:       c1 e0 08                shl    $0x8,%eax
  949a0b:       89 c1                   mov    %eax,%ecx
  949a0d:       89 d0                   mov    %edx,%eax
  949a0f:       c1 e0 10                shl    $0x10,%eax
  949a12:       09 c8                   or     %ecx,%eax
  949a14:       8d 90 00 fe ff ff       lea    -0x200(%rax),%edx
  949a1a:       41 89 47 30             mov    %eax,0x30(%r15)
  949a1e:       81 fa 00 fe 00 00       cmp    $0xfe00,%edx
  949a24:       0f 87 cf 05 00 00       ja     949ff9 <sqlite3BtreeOpen+0xb09>
  949a2a:       c4 e2 78 f3 c8          blsr   %eax,%eax
  949a2f:       85 c0                   test   %eax,%eax
  949a31:       0f 85 c2 05 00 00       jne    949ff9 <sqlite3BtreeOpen+0xb09>
9499f2:66 41 83 4f 26 01或$0x1,0x26(%r15)
9499f8:0f b6 84 24 80 00 movzbl 0x80(%rsp),%eax
9499ff:00
949a00:0f b6 94 24 81 00 movzbl 0x81(%rsp),%edx
949a07:00
949a08:c1 e0 08 shl$0x8,%eax
949a0b:89 c1 mov%eax,%ecx
949a0d:89 d0 mov%edx,%eax
949a0f:c1 e0 10 shl$0x10,%eax
949a12:09 c8或%ecx,%eax
949a14:8d 90 00 fe ff ff lea-0x200(%rax),%edx
949a1a:41894730MOV%eax,0x30(%r15)
949a1e:81 fa 00 fe 00 cmp$0xfe00,%edx
949a24:0f 87 cf 05 00 00 ja 949ff9
949a2a:c4 e2 78 f3 c8 blsr%eax,%eax
949a2f:85 c0测试%eax,%eax
949a31:0f 85 c2 05 00 jne 949ff9
注意最后几行,链接器将lea更改为blsr,这是出乎意料的


因此,为什么会发生这种情况。链接器(ld)会进一步优化代码吗?如何限制链接器使用的说明

非常感谢您的评论。我发现了问题,正如PeterCordes在评论中所说,我链接到了另一组sqlite库。我安装了太多套GCC编译器环境,每个编译器在其默认库路径中都有自己的sqlite。我的项目由cmake管理,它记住了以前所有的GCC设置

查找的步骤:

  • 将-v标志添加到gcc命令

  • 复制ld命令,并添加标志“-print map-map=demo.map”,再次运行完整的ld命令

  • 在demo.map中搜索库名(此处为sqlite),我清楚地发现另一组sqlite库已链接。意识到我是多么愚蠢


  • 更新:我有一个新问题:如果库.a是用高级CPU指令编译的,如何在链接阶段将其降级,似乎这些指令将被复制成二进制,而不检查GCC中的-march标志。

    非常感谢您的评论。我发现了问题,正如PeterCordes在评论中所说,我链接到了另一组sqlite库。我安装了太多套GCC编译器环境,每个编译器在其默认库路径中都有自己的sqlite。我的项目由cmake管理,它记住了以前所有的GCC设置

    查找的步骤:

  • 将-v标志添加到gcc命令

  • 复制ld命令,并添加标志“-print map-map=demo.map”,再次运行完整的ld命令

  • 在demo.map中搜索库名(此处为sqlite),我清楚地发现另一组sqlite库已链接。意识到我是多么愚蠢


  • 更新:我有一个新问题:如果库.a是用高级CPU指令编译的,如何在链接阶段将其降级,似乎这些指令将被复制成二进制,而不检查GCC中的-march标志。

    您确定没有使用
    -flto
    启用链接时间优化/code gen吗?允许跨文件内联通常是一件好事,但您也需要为此设置正确的arch和优化选项。很明显,对于生成最终代码的内容,除了基线设置之外,您还有其他设置。(或者你链接了一些不同的对象文件…)而且,我认为GCC对于这两个字节的加载来说是非常低效的代码。它看起来像是
    movzwl0x80(%rsp),%eax
    /
    shl$8,%eax
    可以工作。您是否有可以重现错过的优化的来源?或者这只发生在一些旧的GCC版本中?我真的不确定,但可能是因为一些放松。我的意思是想象一个架构,它有一个
    跳转
    指令,只能跳转到
    256个字节之外。你调用一个
    foo
    函数,它在这个范围内,所以会生成类似
    跳转foo
    的东西。现在,这在编译单元中可能有效,但当您将它与其他代码链接时,foo
    可能会超出
    256
    字节范围,因此必须用类似的指令替换它,该指令能够进行更长的跳转。因此,无需进行优化,可能只需使代码有效即可。请制作一个包含编译器确切版本和生成错误二进制文件所键入的确切命令的示例。可能存在多个问题,如果不知道编译器和链接器到底是如何涉及的,很难说问题出在哪里。感谢所有有用的评论。我发现