GCC 编译过程和链接库

GCC 加工程序的过程

在Linux下进行C语言编程,必然要采用GNU GCC来编译C源代码生成可执行程序。

一、GCC使用

Gcc指令的一般格式为:Gcc [选项] 要编译的文件 [选项] [目标文件]

其中,目标文件可缺省,Gcc默认生成可执行的文件名为:a.out

我们来看一下经典入门程序"Hello World!"

# vi  main.c

#include <stdio.h>

void main ()

{
   printf("hello world!\n");

}

用gcc编译成执行程序。

#gcc main.c

该命令将main .c直接生成最终二进制可执行程序a.out

这条命令隐含执行了(1)预处理、(2)汇编、(3)编译并(4)链接形成最终的二进制可执行程序。这里未指定输出文件,默认输出为a.out。

如果要指定最终二进制可执行程序名,那么用-o选项来指定名称。比如需要生成执行程序main ,那么

#gcc main.c -o  main

二、GCC的执行过程

从上面我们知道GCC编译源代码生成最终可执行的二进制程序,GCC后台隐含执行了四个阶段步骤。

GCC编译C源码有四个步骤:

预处理-----> 编译 ----> 汇编 ----> 链接

1.预处理,生成.i的文件[预处理器cpp]

2.将预处理后的文件不转换成汇编语言,生成文件.s[编译器egcs]

3.有汇编变为目标代码(机器代码)生成.o的文件[汇编器as]

4.连接目标代码,生成可执行程序[链接器ld]

现在我们就用GCC的命令选项来逐个剖析GCC过程。

1)预处理(Pre-processing)

在该阶段,编译器将C源代码中的包含的头文件如stdio.h编译进来,用户可以使用gcc的选项”-E”进行查看。

用法:#gcc -E main.c -o main.i

作用:将main .c预处理输出main .i文件。

[root]# gcc -E main.c -o main.i

[root]# ls

main.c  main.i

[root]# cat main.i

# 906 "/usr/include/stdio.h" 3 4

# 936 "/usr/include/stdio.h" 3 4

# 2 "main.c" 2

main()

{

printf ("Hello world\n");

}

2)编译阶段(Compiling)

第二步进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。

选项 -S

用法:# gcc –S main.i –o main.s

作用:将预处理输出文件main .i汇编成main .s文件。
[root]# gcc –S main.i –o main.s

[[email protected] hello-gcc]# ls

main.c  main.i  main.s

如下为main.s汇编代码

[[email protected] hello-gcc]# cat main.s

.file      "main.c"

.section      .rodata

.LC0:

.string      "Hello world"

.text

.globl      main

.type      main, @function

main:

.LFB0:

.cfi_startproc

pushq      %rbp

.cfi_def_cfa_offset 16

.cfi_offset 6, -16

movq      %rsp, %rbp

.cfi_def_cfa_register 6

movl      $.LC0, %edi

call      puts

popq      %rbp

.cfi_def_cfa 7, 8

ret

.cfi_endproc

.LFE0:

.size      main, .-main

.ident      "GCC: (Debian 4.7.2-5) 4.7.2"

.section      .note.GNU-stack,"",@progbits

3)汇编阶段(Assembling)

汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码.

选项 -c

用法:# gcc -c main.s -o main.o

作用:将汇编输出文件main.s编译输出main.o文件。

[root]# gcc -c main.s -o main.o

[root]# ls

main.c main.i  main.o  main.s

4)链接阶段(Link)

在成功编译之后,就进入了链接阶段。

无选项链接

用法:# gcc main.o –o main

作用:将编译输出文件main .o链接成最终可执行文件 main

[root]# gcc main.o –o main

[root]# ls

main.c  main  main.i  main.o  main.s

运行该可执行文件,出现正确的结果如下。

[[email protected] Gcc]# ./main

Hello World!

在这里涉及到一个重要的概念:函数库

在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,gcc会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf” 了,而这也就是链接的作用。

可以用ldd命令查看动态库加载情况:

[root]# ldd main

linux-vdso.so.1 =>  (0x00007fffb9eee000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f452c209000)

/lib64/ld-linux-x86-64.so.2 (0x00007f452c5b8000)

函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为”.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为”.so”,如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。

静态链接库和动态链接库

静态链接:

链接是在编译器完成的,所有相关对象在编译的时候被整合到一个可执行文件。若程序要用到的函数很多的话,编译的可执行文件会很大。但是编译好的可执行文件可以独立运行,不需要依赖外部。

动态链接:

与前面相对应,在程序运行的时候需要相应的函数的时候才会载入相应的动态库。
动态链接的优点:
             除了静态链接库所有的模块化和代码复用外,动态链接库还有如下优点。
            可以实现进程之间的库共享:

当多个进程共享一个库时(如stl库和一些系统库是基本上大多数程序都用的),动态链接方式可以只在内存中保留一份副本,

节约内 存。

升级变得简单:

用户只需要升级动态链接库,而无需重新编译链接其他原有的代码就可以完成  整个程序的升级(很多Windows的补丁就是这种方式发布的)。

可以动态载入:
                    当软件比较大的时候,可以根据需要动态载入/卸载相应的链接库,而无需像静态链接的方式那样一次性全部载入。

这里先解释一下编译的选项:

-static 使用静态链接库编译

-shared 编译成动态链接库

-Lpath  指定查找链接库的路径,-L.中的-L选项表示指定路径,后面的 . 表示路径是当前目录

-lxxx      xxx是链接库的名称,例如libstack.so中的stack

静态链接库的创建(linux中静态链接库名必须是libxxx.a)

#gcc -c  stack.c

#ar -cqs libstack.a stack.o

使用静态链接库编译程序

#gcc main.c -o ss -static -L. -lstack

若编译的静态链接库不是标准的libxxx.a形式,则可以这样

#gcc main.c -o ss -static xxx.a

(xxx.a必须在当前目录或指定绝对路径,注意若编译后的静态库名为xxx.a,即使手动修改为标准的libxxx.a形式,一样不能用 -Lpath -lxxx的方式,要这样使用则必须重新编译成标准名称的静态库,即libxxx.a形式)

动态链接库的创建(linux中动态链接库名必须是libxxx.so)

#gcc -fPIC -shared stack.c -o libstack.so

使用动态链接库编译程序

#gcc main.c -L. -lstack -o ss

运行一下

#./ss

这时会出错,因为动态链接库没有在系统默认的指定路径,找不到。

让可执行程序找到动态链接库的方法:

1、设置LD_LIBRARY_PATH环境变量,把动态库所在的路径加到这个环境变量中

#export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/YOUR/PATH

2、把动态链接库文件拷贝到/lib或/usr/lib

3、修改/etc/ld.so.conf文件,把自己的路径作为单独一行写在末尾, 不过不建议直接改这个文件,

因为这个文件包含了/etc/ld.so.conf.d/*.conf,因此只需在/etc/ld.so.conf.d/目录下新建一个xxx.conf,里面写上自己的 动态库的路径即可

查看可执行文件依赖的动态库命令ldd:

#ldd ss      ==>查看可执行文件ss依赖的动态库

我的QQ空间原文:http://user.qzone.qq.com/1475032202/blog/1418879205

时间: 2024-10-15 11:35:54

GCC 编译过程和链接库的相关文章

GCC编译过程与动态链接库和静态链接库

1. 库的介绍 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行.库有两种:静态库(.a..lib)和动态库(.so..dll). windows上对应的是.lib .dll linux上对应的是.a .so 在这里先介绍下Linux下的gcc编译的几个选项 g++ -c hellospeak.cpp 会将hellospeak.cpp 选

C/C++学习 - gcc编译过程查看汇编代码

GCC GCC参数 举例 GCC gcc大家都很熟悉,是个编译器,功能强大,全称是:GUN GCC. 平时大家都简单的称为:GCC, 功能强大,能够编译很多的语言.包括:c, c++, java, ada等- GCC参数 gcc平时有很多参数,在平时我编译小程序的时候,都直接gcc -o object source.c就可以了. 但是有一次我想查看我同样目的但是不同写法的两个代码是执行效率上是否有差别.所以我就查了下gcc编译过程中的汇编代码的参数. 参数 说明 -c 只编译不链接,生成*.o文

gcc 编译过程

gcc 编译过程从 hello.c 到 hello(或 a.out)文件, 必须历经 hello.i. hello.s. hello.o,最后才得到 hello(或a.out)文件,分别对应着预处理.编译.汇编和链接 4 个步骤,整个过程如图 10.5 所示. 这 4 步大致的工作内容如下:(1) 预处理, C 编译器对各种预处理命令进行处理,包括头文件包含.宏定义的扩展.条件编译的选择等:(2) 编译,将预处理得到的源代码文件,进行"翻译转换",产生出机器语言的目标程序,得到机器语言

libcurl的源码,编译为静态链接库出现的问题

libcurl的源码,编译为静态链接库出现的问题如下图所示: 解决方案: 具体步骤就是: 1.给工程添加依赖的库:项目->属性->链接器->输入->附加依赖项,把libcurl.lib ws2_32.lib winmm.lib wldap32.lib添加进去 注意,debug配置用libcurld.lib 2.加入预编译选项:项目->属性->c/c++ ->预处理器->预处理器,把  ;BUILDING_LIBCURL;HTTP_ONLY复制进去(注意不要丢

gcc编译过程简述

在linux系统上,从源文件到目标文件的转化是由编译器完成的.以hello.c程序的编译为例,如下: [email protected]: gcc -o hello hello.c 在这里,gcc编译器读取源文件hello.c,并把它翻译成一个可执行文件 hello. 这个翻译过程可分为四个阶段逐步完成:预处理,编译,汇编,链接,如下图所示. 逐步做下简单分析: 在未编译前,hello.c 的源代码如下 #include <stdio.h> int main() { printf("

GCC编译过程

以下是C程序一般的编译过程: gcc的编译流程分为四个步骤,分别为:· 预处理(Pre-Processing) 对C语言进行预处理,生成*.i文件.· 编译(Compiling) 将上一步生成的*.i文件编译生成汇编语言文件,后缀名为*.s· 汇编(Assembling) 将汇编语言文件*.s经过汇编,生成目标文件,后缀名为*.o· 链接(Linking) 将各个模块的*.o文件链接起来,生成最终的可执行文件 示例代码: 1 //hello.c源代码 2 3 #include<stdio.h>

gcc 编译过程及有关命令详解

用gcc编译.c文件流程 1 预处理阶段,输入c的源文件,编译器分析处理源代码文件中的各种宏指令,如"#ifdef,#endif,#include"等,进行去注释,头文件展开,宏替换等操作 .用户可以用 -E 参数让GCC在预处理结束后停止编译过程(gcc -E [source file(.c)] -o [output file(.i)]. 2 编译阶段,从高级语言转换为汇编语言,GCC在预处理后要检查代码规范,是否有语法错误.无误后翻译为汇编语言. GCC 的 -S 选项让GCC在进

Linux系统GCC常用命令和GCC编译过程描述

前言: GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言.GCC 很快地扩展,变得可处理 C++.后来又 扩展能够支持更多编程语言,如Fortran.Pascal.Objective-C.Java.Ada.Go以及各类处理器架构上的汇编语言等,所以改 名GNU编译器套件(GNU Compiler Collection).(百度百科,想了解自己查询) 一.GCC程序的编译过程大概分四个阶段 预处理(Pre-Processing) 编译(Compil

system 系统调用、gcc编译过程

system 库函数的功能是执行操作系统的命令或者运行指定的程序 #include <stdio.h> #include <stdlib.h>//引入库 int main() { //system("notepad");//打开记事本 system("dir");//查看目录 system("ipconfig");查看ip return 0; } 多线程 pthread_create thread_create POSIX