首页 /  技术专区  /  C 屏幕太窄?试试伸展一下吧 >

gcc -v详细信息

gcc -v

(a)演示

gcc helloword.c -o helloworld -v

image.png

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=c:/mingw/bin/../libexec/gcc/mingw32/4.7.1/lto-wrapper.exe
Target: mingw32
Configured with: ../../src/gcc-4.7.1/configure --build=mingw32 --enable-languages=c,c++,ada,fortran,objc,obj-c++ --enable-threads=win32 --enable-libgomp --enable-lto --enable-fully-dynamic-string --enable-libstdcxx-debug --enable-version-specific-runtime-libs --with-gnu-ld --disable-nls --disable-win32-registry --disable-symvers --disable-build-poststage1-with-cxx --disable-werror --prefix=/mingw32tdm --with-local-prefix=/mingw32tdm --enable-cxx-flags='-fno-function-sections -fno-data-sections' --with-pkgversion=tdm-1 --enable-sjlj-exceptions --with-bugurl=http://tdm-gcc.tdragon.net/bugs
Thread model: win32
gcc version 4.7.1 (tdm-1)
COLLECT_GCC_OPTIONS='-o' 'helloworld.exe' '-v' '-mtune=i386' '-march=i386'
 c:/mingw/bin/../libexec/gcc/mingw32/4.7.1/cc1.exe -quiet -v -iprefix c:\mingw\bin\../lib/gcc/mingw32/4.7.1/ helloworld.c -quiet -dumpbase helloworld.c -mtune=i386 -march=i386 -auxbase helloworld -version -o C:\Users\ADMINI~1\AppData\Local\Temp\ccT7zCrx.s
GNU C (tdm-1) version 4.7.1 (mingw32)
        compiled by GNU C version 4.7.1, GMP version 4.3.2, MPFR version 2.4.2, MPC version 0.8.2
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "c:\mingw\bin\../lib/gcc/mingw32/4.7.1/../../../../mingw32/include"
ignoring duplicate directory "c:/mingw/lib/gcc/../../lib/gcc/mingw32/4.7.1/include"
ignoring duplicate directory "c:/mingw/lib/gcc/../../lib/gcc/mingw32/4.7.1/../../../../include"
ignoring duplicate directory "c:/mingw/lib/gcc/../../lib/gcc/mingw32/4.7.1/include-fixed"
ignoring nonexistent directory "c:/mingw/lib/gcc/../../lib/gcc/mingw32/4.7.1/../../../../mingw32/include"
#include "..." search starts here:
#include <...> search starts here:
 c:\mingw\bin\../lib/gcc/mingw32/4.7.1/include
 c:\mingw\bin\../lib/gcc/mingw32/4.7.1/../../../../include
 c:\mingw\bin\../lib/gcc/mingw32/4.7.1/include-fixed
End of search list.
GNU C (tdm-1) version 4.7.1 (mingw32)
        compiled by GNU C version 4.7.1, GMP version 4.3.2, MPFR version 2.4.2, MPC version 0.8.2
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 1223bc4d7eaab64b9220d8272e21f753
COLLECT_GCC_OPTIONS='-o' 'helloworld.exe' '-v' '-mtune=i386' '-march=i386'
 c:/mingw/bin/../lib/gcc/mingw32/4.7.1/../../../../mingw32/bin/as.exe -v -o C:\Users\ADMINI~1\AppData\Local\Temp\ccwSgkT4.o C:\Users\ADMINI~1\AppData\Local\Temp\ccT7zCrx.s
GNU assembler version 2.22 (mingw32) using BFD version (GNU Binutils) 2.22
COMPILER_PATH=c:/mingw/bin/../libexec/gcc/mingw32/4.7.1/;c:/mingw/bin/../libexec/gcc/;c:/mingw/bin/../lib/gcc/mingw32/4.7.1/../../../../mingw32/bin/
LIBRARY_PATH=c:/mingw/bin/../lib/gcc/mingw32/4.7.1/;c:/mingw/bin/../lib/gcc/;c:/mingw/bin/../lib/gcc/mingw32/4.7.1/../../../../mingw32/lib/;c:/mingw/bin/../lib/gcc/mingw32/4.7.1/../../../
COLLECT_GCC_OPTIONS='-o' 'helloworld.exe' '-v' '-mtune=i386' '-march=i386'
 c:/mingw/bin/../libexec/gcc/mingw32/4.7.1/collect2.exe -Bdynamic -o helloworld.exe c:/mingw/bin/../lib/gcc/mingw32/4.7.1/../../../crt2.o c:/mingw/bin/../lib/gcc/mingw32/4.7.1/crtbegin.o -Lc:/mingw/bin/../lib/gcc/mingw32/4.7.1 -Lc:/mingw/bin/../lib/gcc -Lc:/mingw/bin/../lib/gcc/mingw32/4.7.1/../../../../mingw32/lib -Lc:/mingw/bin/../lib/gcc/mingw32/4.7.1/../../.. C:\Users\ADMINI~1\AppData\Local\Temp\ccwSgkT4.o -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt c:/mingw/bin/../lib/gcc/mingw32/4.7.1/crtend.o

(b)分析gcc -v的详细信息的意文

首先我们需要清楚一点,我们并不能完全弄清楚gcc -v的所有信息,因为毕竞我们并不是gcc编译器集合的实现者,对于这些信息,他们才是最清楚的。

由于我们不能将所有的信息都搞清楚,所以我们只分析关键信息。

虽然我们不能将所有信息都全部弄清楚,但是分析里面的关键信息还是非常有意义的,我们可以通过这些信息弄清楚很多事情,比如:

·通过这些信息,我就知道gcc其实最终还是调用cc1/as/collect2等程序来实现编译的四个过程的。

·知道c的启动代码是怎么来的

·知道为什么在程序中调用printf、scanf、malloc等函数时,我们不需要主动链接这些函数的动态库,但是依然能够使用这些函数

(c) gcc helloword.c -o helloworld -v详细信息分析

Using built-in specs. //编译链接详细信息

COLLECT_GcC=gcc//编译时所调用的总调度程序

gcc编译得到的可执行文件的运行环境,

cpu: 64位x86,os: linux,gnu: gcc的开发组织Target: x86_64-linux-gnu


gcc配置信息:

1.什么是gcc配置信息

gcc也是一个程序,也是被别人开发出来的,应该是c/c++语言写的。

编写gcc这个编译器程序的人,在编译gcc时所给的信息就是配置信息,这些信息会决定编译gcc哪些代码,不编译哪些代码,最终得到针对某个环境(os/cpu)的gcc可执行程序。

2. gcc程序如何面对众多环境的

gnu开发的gcc可以面对很多的环境,比如windows x86环境,Linux x86环境,Linux arm环境。

编写gcc的人,为了让程序能够应对各种环境(os、cpu) , gcc程序里面会包含应对各个环境的代码,如果你想得到针对某个环境gcc可执行程序,就必须只编译针对该环境的代码,其它代码不编译。

3.如何选择只编译gcc程序针对某个环境的代码

通过条件编译来选择,通过条件编译这的东西,就可以在预编译阶段决定你要保留哪些代码,放弃哪些代码,编译时只编译你保留的代码。

举一个简单的例子

比如:

gcc.c
#define x86_LINUX
#ifdef x86_LINUx
针对x86、Linux不境的代码。
#endif
#ifdef ARM_LINUX
针对arm、Linux环境的代码。
#endif
#ifdef x86_wINDOws
针对x86、windows环境的代码。
#endif

通过条件编译所需的宏,就能让条件编译保留和编译只针对某个环境的代码。

但是由于c条件编译使用的宏实在是太多了,所以我们不可能自己一个一个的定义这些宏,所以就需要通过配置信息白动生成需要的宏。

配置信息保存在哪里呢?

配置信息保存在配置文件中,我们配置信息时,其实就是修改配置文件中的内容。

运行配置文件时,根据配置信息的要求,会自动生成需要的宏定义,并把这些宏定义保存到相应的.h(头文件)中。

再将.h给c/c++程序,预处理时,条件编译根据.h中定义的宏定义,就能决定保留和编译哪些代码。编译时就只编译保留的代码,最后就得到了针对某个环境的gcc可执行程序,不过这些配置信息会保留gcc中,gcc -v时会显示出来。

通过了解gcc的配置信息,可以大概的知道一些gcc的特性。

4.了解gcc条件编译和配置信息的意义

(1)有助于我们后面学习c语言"条件编译"相关的内容

(2)复杂的c/c++程序,都会涉及到条件编译和配置信息.

gcc也是一个复杂的c/c++程序,所以必然涉及条件编译和配置信息。

如果不理解条件编译和配置信息的话,我们很难实现复杂c程序和看懂别人写的复杂c程序。

我们后面讲uboot和linux内核时,里面会有大量的条件编译,编译时必须修改配置信息,通过配置信息去打开和关闭uboot/linux内核的条件编译,选择那些代码保留,那些代码忽略,然后编译保留的代码,得到针对某个环境(os/cpu)的可执行程序。


5. gcc配置信息

gcc的配置文件,这句话仅仅只是向我们表明,gcc的配置信息其实是来源于这个文件,这个配置文件并不在我的电脑上,而是在gcc开发者的电脑上,编译gcc时gcc开发者会去设置这个配置文件。

configured with: ../src/configure

gcc bug报告说明书:如果你发现了gcc的bug,需要按READNE,Bugs说明书的要求来提交gcc的bug

--with-bugurl=file: ///usr/share/doc/gcc-5/README.Bugs

gcc编译集所支持的语言,不过想要编译java等其它语言,需要下载相应的插件

--enable-languages=c,ada, c++,java, go,d,fortran,objc,obj-c++

路径固定前缀,也就是说gcc所使用到的路径都是/usr打头的,换句话说gcc所用到的文件,都在这个/usr目录下。

--prefix=/usr

GCC编译器集合"自带库"所在目录

--libdir=/usr/lib

使用gcc编译带界面的java程序,图形界面底层调用的是ubuntu的gtk基础图形库

--enable-java-awt=gtk

gcc可以编译java,但是java程序运行需要相应的运行环境(最起码要有个java虚拟机jvm)以下信息描述的就是java的运行环境

--with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre
--enable-java-home
--with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64
--with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64
--with-arch-directory=amd64
--with-ecj-jar=/usr/share/java/eclipse-ecj.jar
--enable-objc-gc

gcc所运行的cpu的cpu架构,对于cpu架构的详细信息,对于应用开发者来说,我们不需要进行详细的了解这些是做硬件、微电子、编译器开发者应该详细了解的内容

--enable-multiarch
--disable-werror
--with-arch-32=i686
--with-abi=m64
--with-multilib-list=m32,m64, mx32
--enable-multilib
--with-tune=generic
--enable-checking=release
--build=x86_64-linux-gnu

gcc本身的运行环境: cpu: 64位intel x86 cpu    OS: Linux

--host=x86_64-linux-gnu

gcc编译出的可执行程的运行环境

gcc helloworld.c -o helloworld,helloworld的运行环境为cpu: 64位intel x86 cpu OS:linux.

--target=x86_64-linux-gnu

gcc版本,与gcc所运行的ubuntu的版本(ubuntu是Linux发行版操作系统)

gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)

gcc基本选项,

-march=x86-64:intel 64位 x86 cpu
-mtune=generic:编译得到的机器指令,属于通用指令集(同款的不同型号的cpu都支持的指令集)

如果需要指定某型号cpu的特殊指令集时,就不能写成generic,而要写特殊指令集名称

COLLECT_GCC_OPTIONS='-o' 'helloworld' '-v' '-mtune=generic' '-march=x86-64'

预编译、编译

干货信息为:

cc1 helloworld.c -o /tmp/ccV9xVOr.s
/usr/lib/gcc/x86_64-linux-gnu/5/cc1 -quiet -v -imultiarch x86_64-linux-gnu 
helloworld.c -quiet -dumpbase helloworld.c -mtune=generic -march=x86-64 -auxbase 
helloworld -version -fstack-protector-strong -Wformat -Wformat-security -o 
/tmp/ccV9xVOr.s

包含""所指定的头文件:到程序员自己指定的路径下去搜索

#include "..." search starts here:

包含<>所指定的头文件:到系统指定的路径下去找

#include <...> search starts here: 
/usr/lib/gcc/x86_64-linux-gnu/5/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.

汇编:由于as的路径已经被加入到了环境变量中,因此调用as时,并不需要指定as的路径。

生成ccV9xVOr.s汇编文件是一个临时文件,/tmp目录专门用于存放Linux系统所生成的临时文件,

一旦编译得到了可执行文件,这个ccV9xVOr.s将会别删除

as -v --64 -o /tmp/ccyIcm4A.o /tmp/ccV9xVOr.s

链接

collect2为链接器,由于collect2的路径没有加入环境变量,因此需要我们自己指明collect2所在的路径。

/usr/lib/gcc/x86_64-linux-gnu/5/collect2

给程序指定动态链接器:程序运行起来后,用于加载动态库

-dynamic-linker /lib64/ld-linux-x86-64.so.2

最终生成的可执行文件

-o helloworld

crt1.o、crti.o、crtbegin.o:用于生成程序的“启动代码”,这三个.o是由GCC编译器集合提供的(由GCC开发者编写的),crt就是c/c++ run time的意思,翻译为中文就是“运行时环境”。

启动代码的作用:搭建c/c++的运行环境crt1.o:汇编写的。

· 里面的_start是整个程序的开始(入口)

· main函数是由crt1.o调用

· c/c++函数运行所需要的栈,是由crt1.o建立的

crti.o:在调用main之前,实现c的一些初始化工作,比如全局变量初始化

crtbegin.o:在调用main之前,实现c++的一些初始化,比如调用全局构造函数,创建全局对象

/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o 
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o 
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o

指定所有库所在路径,后面链接“库”时就到这些路径下寻找,后面讲c函数库时,会介绍-L选项

-L/usr/lib/gcc/x86_64-linux-gnu/5 
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu 
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib 
-L/lib/x86_64-linux-gnu -L/lib/../lib 
-L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib 
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../..

链接自己的.o:ccyIcm4A.o,是之前as汇编helloworld.c时,所得到的临时.o文件

我们这里自己的.o只有一个,如果源文件有很多.c的话,对应的这里就会有很多的.o

/tmp/ccyIcm4A.o

链接libc库

其实libc只是c标准函数库中的一个子库,libc只包含printf、scanf、strcpy、malloc、time等这些常用函数,正是因为gcc有自动链接libc这个常用函数库,所以我们才能在程序中使用这些常用函数,而不会报错说倒找不到这些函数。

但是像c标准函数库中的其它函数就不行了,比如使用数学函数,我们必须自己在gcc时自己加-lm去链接c标准函数库的子库——数学库(比如:gcc ***.c -o *** -lm), 否者无法使用数学库函数,数学库中的函数属于非常用函数,gcc是不会帮你自动链接数学库的。

-lc

crtend.o、crtn.o:用于生成扫尾代码,程序运行结束时,做一些扫尾工作,这两个.o也是由gcc开发者编写的,为了方便描述,我们往往将扫尾代码认为是启动代码的一部分。

crtend.o:扫尾做什么?比如调用c++析构函数,释放全局对象的空间

crtn.o:扫尾做什么?比如,接收main函数的返回值并处理

- 如果程序是裸机运行的,返回值到扫尾代码这里就结束了,裸机时返回值的意义不大

- 如果程序是基于OS运行的,扫尾代码会将返回值交给OS

/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o 
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

/* 简化后的链接过程,链接时,各个模块的顺序很重要 */

collect2    //链接程序
-dynamic-linker  /lib64/ld-linux-x86-64.so.2 //动态链接器
crt1.o  crti.o  crtbegin.o  //启动代码
ccyIcm4A.o  //自己程序的.o,这里目前只有一个
-lc //libc,常用c函数库——c标准库的子库
crtend.o  crtn.o //扫尾代码

6.总结gcc -v显示的详细信息

(1)如果四个过程都有的话

详细信息就是:gcc配置信息 + cc1信息(预处理+编译) + as信息 + collect2信息 

(2)如果只是某个过程的话

比如:gcc -S helloworld.i -o helloworld.s -v

详细信息就是:gcc配置信息 + 单个过程的信息

7. 再说说启动代码 

(1)启动代码是由“一个汇编文件+其它的.c文件”构成的,启动代码并不是我们程序员写的

· 广义上,“汇编文件+其它的.c文件”都是启动代码。

· 狭义上,“汇编文件”才是启动代码

(2)启动代码的作用

其中的核心作用就是,在内存中建立c程序运行所需的“堆和栈”,堆和栈其实就是程序运行时所需的运行环境之一,所谓运行环境,就是程序运行时必须要的支撑,就跟人一样,人活着也需要环境(生活环境/工作环境)的支撑,否则就麻烦了。

程序运行起来后就变成了动态运行的程序——进程,所以程序的运行环境,也被称为“进程环境”,进程环境不仅仅只有“堆和栈”,还需要其它的,我们在《Linux系统编程/网络编程》第4章,将会详细的介绍什么“进程环境”,所以需环境有哪些。

(3)我想看启动代码的源码,怎么办?

如果想看启动代码的话,也主要是汇编部分,因为.c部分,会根据程序运行时有无OS支持,会有很大的区别。

比如单片机没有OS,它的启动代码.c部分与有OS时启动代码的.c部分有很大区别,不仅.c,汇编部分也有区别,只不过区别没有那么大而已。

.c部分我们就不关心了,如果你想看启动代码汇编部分,完全可以看单片机c程序的启动代码汇编部分。

当有OS支持时,编译器提供的启动代码大都会被做成现成的.o,我们是看不到源码的,基本只有在裸机程序中才能看到启动代码汇编部分的源码,.c源码也能看见。

所谓裸机程序就是没有OS支持的程序,比如绝大多数单片机程序就是典型的裸机程序,为什么呢?

因为单片机很少运行OS,由于单片机开发的特殊性,有时候我们往往需要修改启动代码汇编部分,所以编译器会提供汇编启动代码的源码,所以完全可以通过看单片机的汇编启动代码,从而了解C的汇编启动代码干了什么事情。

在有OS支持时,启动代码是不需要修改的,所以人家才以.o形式提供。

讲单片机时,我们会分析单片机c程序的汇编启动代码,所以到时候你就知道它到底做了些什么事情,它是怎么去建立堆和栈的,建立堆和栈的代码长什么样子,那时你就不会再对启动代码感到神秘了。

大概看看单片机的汇编启动代码长什么样子

...
/* 设置堆栈指针,堆栈的大小 */
Stack_Size      EQU     0x00000400
AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp
; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size       EQU     0x00000200
AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit
PRESERVE8
THUMB
...

当然后续课程讲Uboot移植时,由于Uboot也是一个裸机程序,我们也能够看到Uboot的汇编启动代码的源码,你也能知道启动代码到底做了些什么事情,总之对于启动代码不要感到神秘,也没什么可神秘的。

不仅c程序,所有高级语言所写的程序,都是需要启动代码的,启动代码要做的事情也都是类似的。

当然就算你不了解启代码是咋回事,对于我们开发来说,也不会有什么太大的问题,但是如果你能了解什么是启动代码,大概做了些什么事情,当然是最好的了,因为这非常有助于你去理解与之相关的一些其它的深入问题。




0/200
图片验证码