gcc 4.8.4默认是静态链接数学库吗?

时间:2015-08-28 05:30:32

标签: gcc linker nm

我无法解释gcc 4.8.4展示的以下行为。我们将以下代码段命名为pow_main.c

#include <stdio.h>
#include <math.h>

int main()
{
    printf("%lf\n", pow(2, 10));
    return 0;
}

我正在使用以下命令行编译程序:

gcc -c -O0 -Wall pow_main.c -o pow_main.o 

请注意,我只是编译(-c)程序,而不是链接它。此外,禁用所有编译器优化(-O0)。如果我查看目标文件中包含的符号,我会发现:

$ nm pow_main.o
0000000000000000 T main
                 U printf

对我而言,似乎编译器已经从pow库中静态地解析了对方法math的引用,或者使用一些内联代码优化了pow调用。

  1. 为什么符号pow在目标文件中不存在?
  2. 负责解析外部引用的链接器是静态的还是动态的?
  3. U目标文件中是否存在未定义的(pow)符号pow_main.o
  4. 以下是平台详细信息:

    gcc -v
    Using built-in specs.
    COLLECT_GCC=gcc
    COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
    Target: x86_64-linux-gnu
    Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
    Thread model: posix
    gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04) 
    
    编辑:似乎我的两个怀疑之一是正确的。 gcc预先计算了表达式并用常量替换它。

    00000000004006dd <main>:
      4006dd:   55                      push   %rbp
      4006de:   48 89 e5                mov    %rsp,%rbp
      4006e1:   48 83 ec 10             sub    $0x10,%rsp
      4006e5:   48 b8 00 00 00 00 00    movabs $0x4090000000000000,%rax
      4006ec:   00 90 40 
      4006ef:   48 89 45 f8             mov    %rax,-0x8(%rbp)
      4006f3:   f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
      4006f8:   bf e4 07 40 00          mov    $0x4007e4,%edi
      4006fd:   b8 01 00 00 00          mov    $0x1,%eax
      400702:   e8 b9 fe ff ff          callq  4005c0 <printf@plt>
      400707:   48 ba 00 00 00 00 00    movabs $0x4024000000000000,%rdx
      40070e:   00 24 40 
      400711:   48 b8 00 00 00 00 00    movabs $0x4000000000000000,%rax
      400718:   00 00 40 
      40071b:   48 89 55 f8             mov    %rdx,-0x8(%rbp)
      40071f:   f2 0f 10 4d f8          movsd  -0x8(%rbp),%xmm1
      400724:   48 89 45 f8             mov    %rax,-0x8(%rbp)
      400728:   f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
      40072d:   e8 7e fe ff ff          callq  4005b0 <power@plt>
      400732:   f2 0f 11 45 f8          movsd  %xmm0,-0x8(%rbp)
      400737:   48 8b 45 f8             mov    -0x8(%rbp),%rax
      40073b:   48 89 45 f8             mov    %rax,-0x8(%rbp)
      40073f:   f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
      400744:   bf e4 07 40 00          mov    $0x4007e4,%edi
      400749:   b8 01 00 00 00          mov    $0x1,%eax
      40074e:   e8 6d fe ff ff          callq  4005c0 <printf@plt>
      400753:   b8 00 00 00 00          mov    $0x0,%eax
      400758:   c9                      leaveq 
      400759:   c3                      retq   
      40075a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
    

    现在的问题是,如何关闭这样的优化,即使在O0

2 个答案:

答案 0 :(得分:1)

编译器可以在编译时计算出pow(2, 10)的结果为1024,所以就这样做了。并且,显然,即使在优化级别0,一些计算也在编译时完成。因此,不需要将数学库与程序链接。

当然,在某些系统上,例如Mac OS X,您不需要指定数学库-lm;数学函数在主C库中。

答案 1 :(得分:0)

  1. 可以使用专用CPU指令实现,也可以完全优化并替换为常量1024。
  2. 是的,如果你有一个符号。
  3. 不一定如此。
  4. 如果您静态链接库,通常会在T的输出中看到标有nm的已解析符号。