`
8366
  • 浏览: 812849 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

全面复习linux C语言系列--第二讲--GCC编译器

阅读更多

 

 

 

1.编译过程简述:

 

  1.1 预编译

 

     1.1.1 宏定义指令

 

        #define #undef

 

     1.1.2 条件编译指令


例子: debug  , debug.c 内容如下

 

#include <stdio.h>
main()
{
        #ifdef DEBUG
        fprintf(stderr,"debug infomation \n");
        #endif
}

 

 

在测试环境上 我们使用下面的命令编译:

 

cc debug.c -DDEBUG -o debug

 

他的执行结果:

 

[root@xhu-vm 5]# ./debug
debug infomation
[root@xhu-vm 5]#

 

我们看到了debug 信息

 

上了生产环境,这样编译:

 

[root@xhu-vm 5]# cc debug.c -o debug

 

执行结果:

[root@xhu-vm 5]# ./debug
[root@xhu-vm 5]#

没有输出debug信息

 

  1.2 编译

 

  1.3 优化和汇编

 

 

2.链接过程

 

  2.1 在debug的例子上在加上一句 打印的测试代码,使用 编译-链接-执行 来完成这个例子

 

#include <stdio.h>
main()
{
        #ifdef DEBUG
        fprintf(stderr,"debug infomation \n");
        #endif
        fprintf(stderr,"just a test!\n");
}
 

 

 

a. 使用 [root@xhu-vm 5]# gcc -c debug.c   生成.o 目标文件 debug.o

 

b. 使用 [root@xhu-vm 5]# gcc debug.o -o debug 将目标文件链接成可执行文件

 

c. 查看结果

[root@xhu-vm 5]# ./debug
just a test!
[root@xhu-vm 5]#

 

d.使用dumpobj 反编译.o 目标文件

 

 

[root@xhu-vm 5]# objdump -d debug.o

debug.o:     file format elf32-i386

Disassembly of section .text:

00000000 <main>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   83 e4 f0                and    $0xfffffff0,%esp
   9:   b8 00 00 00 00          mov    $0x0,%eax
   e:   83 c0 0f                add    $0xf,%eax
  11:   83 c0 0f                add    $0xf,%eax
  14:   c1 e8 04                shr    $0x4,%eax
  17:   c1 e0 04                shl    $0x4,%eax
  1a:   29 c4                   sub    %eax,%esp
  1c:   83 ec 08                sub    $0x8,%esp
  1f:   68 00 00 00 00          push   $0x0
  24:   ff 35 00 00 00 00       pushl  0x0
  2a:   e8 fc ff ff ff          call   2b <main+0x2b>
  2f:   83 c4 10                add    $0x10,%esp
  32:   c9                      leave  
  33:   c3                      ret    
[root@xhu-vm 5]# 
 

2.2   动态链接和静态链接

 

静态链接:[root@xhu-vm 5]# gcc debug.o -o debug

动态链接:[root@xhu-vm 5]# gcc -s debug.c -o debug.s

运行:

[root@xhu-vm 5]# ./debug.s
just a test!
[root@xhu-vm 5]#

 

我们可以看到 静态链接 产生的文件体积要小

 

[root@xhu-vm 5]# ll
total 20
-rwxr-xr-x  1 root root 4818 Jun  7 06:41 debug
-rw-r--r--  1 root root  127 Jun  7 06:37 debug.c
-rw-r--r--  1 root root  920 Jun  7 06:37 debug.o
-rwxr-xr-x  1 root root 3000 Jun  7 07:04 debug.s
[root@xhu-vm 5]#
 

2.3  用GCC 生成可执行文件

 

例子目的: 按照c 项目的目录结构,建立inc,src,lib等目录,建立.h, .c,.lib文件 ,完成调用自己lib中的加法功能,其他程序可以静态链接这个.a 库文件 ,并使用其中的方法

 

a. 目录结构

 

 

[root@xhu-vm pro1]# pwd
/root/c_project/source code/5/pro1
[root@xhu-vm pro1]# ll
total 12
drwxr-xr-x  2 root root 4096 Jun  7 08:13 inc
drwxr-xr-x  2 root root 4096 Jun  7 08:13 lib
drwxr-xr-x  2 root root 4096 Jun  7 08:18 src

b. 在src 目录下写 tool.c ,并使用 gcc 将其编译成.o的目标文件

 

 

[root@xhu-vm src]# more tool.c 
#include <stdio.h>
int add(int x ,int y)
{
        return x+y;
}
[root@xhu-vm src]# 

 

[root@xhu-vm src]# cc -c tool.c 
[root@xhu-vm src]# ll
total 8
-rw-r--r--  1 root root  58 Jun  7 08:18 tool.c
-rw-r--r--  1 root root 684 Jun  7 08:21 tool.o
[root@xhu-vm src]# 

 

c. 将 tool.o形成 库文件,方便其他程序调用add方法,其中 lib tools.a  的命名必须规范,以lib开头,以.a结尾

[root@xhu-vm src]# ar rv libtools.a tool.o
ar: creating libtools.a
a - tool.o
[root@xhu-vm src]# 
 

d. 编写tool1.c 实现减法操作

 

[root@xhu-vm src]# vi tool1.c


#include <stdio.h>
int sub(int x,int y)
{
        return x-y;
}

 编译成.o 文件

 

[root@xhu-vm src]# ll
total 20
-rw-r--r--  1 root root 824 Jun  7 08:28 libtools.a
-rw-r--r--  1 root root  57 Jun  7 08:31 tool1.c
-rw-r--r--  1 root root 689 Jun  7 08:31 tool1.o
-rw-r--r--  1 root root  58 Jun  7 08:18 tool.c
-rw-r--r--  1 root root 684 Jun  7 08:21 tool.o
[root@xhu-vm src]# 

 

 

将tool1.o放到libtools.a库文件中

 

[root@xhu-vm src]# ar rv libtools.a tool1.o
a - tool1.o
 

查看 libtools.a包含哪些目标文件,可见加法和减法的目标文件都 已经放到库文件中去了

 

[root@xhu-vm src]# ar tv libtools.a
rw-r--r-- 0/0    684 Jun  7 08:21 2011 tool.o
rw-r--r-- 0/0    689 Jun  7 08:31 2011 tool1.o
[root@xhu-vm src]# 

 

将库文件 移动到lib下

 

[root@xhu-vm src]# mv libtools.a ../lib/

 

 

e.在inc下 编写 .h 头文件

 

[root@xhu-vm inc]# vi my.h

#define VALUE 100

 f. 编写a.c

 

[root@xhu-vm src]# vi a.c

#include <stdio.h>
#include "my.h"
main()
{
        fprintf(stderr,"VALUE + 10=%d\n",add(VALUE,10));
}
 

g. 编译 a.c 生成a.o , -I 指定头文件路径

 

[root@xhu-vm src]# gcc -c -I../inc a.c 
[root@xhu-vm src]# ll
total 24
-rw-r--r--  1 root root  96 Jun  7 08:44 a.c
-rw-r--r--  1 root root 964 Jun  7 08:46 a.o
-rw-r--r--  1 root root  57 Jun  7 08:31 tool1.c
-rw-r--r--  1 root root 689 Jun  7 08:31 tool1.o
-rw-r--r--  1 root root  58 Jun  7 08:18 tool.c
-rw-r--r--  1 root
 

 

h. 链接并执行,查看结果 ,-L 指定库文件路径 -l 指定库文件名称

 

[root@xhu-vm src]# gcc a.o -L../lib -ltools -o a
[root@xhu-vm src]# ./a 
VALUE + 10=110
[root@xhu-vm src]# 
 

 

2.4 用GCC 生成 动态链接库

 

 例子目的: 使用gcc生成一个.so的动态链接库

 

a. 建立 pro2目录,在下面建立一个 包含加法 运算的 a.c文件

 

[root@xhu-vm pro2]# vi a.c

{
main()
#include <stdio.h>
int add(int x,int y)
{
        return x+y;
}

 

b. 使用 -fPIC 编译源文件,生成可以重定位的目标代码,对于需要被动态链接的函数库,这点尤其重要。

 

[root@xhu-vm pro2]# gcc -fPIC -c a.c 
[root@xhu-vm pro2]# ll
total 8
-rw-r--r--  1 root root  57 Jun  8 13:14 a.c
-rw-r--r--  1 root root 681 Jun  8 13:15 a.o
[root@xhu-vm pro2]# 

 c.  使用gcc -shared -o libabc.so xxx.o 生成动态链接库 ,其中 lib abc.so 的命名必须规范,以lib开头,以.so结尾

 

[root@xhu-vm pro2]# gcc -shared -o libabc.so a.o
[root@xhu-vm pro2]# ll
total 16
-rw-r--r--  1 root root   57 Jun  8 13:14 a.c
-rw-r--r--  1 root root  681 Jun  8 13:15 a.o
-rwxr-xr-x  1 root root 4152 Jun  8 13:22 libabc.so

 d. 使用动态链接库

     2种方式:

 

1. 一是通过设置环境变量LD——LIBRARY_PATH的方式 (这种方式比较常用

2. 二是通过dlopen系列系统调用动态打开的方式

 

使用方式1, 先写b.c ,包含main

 

[root@xhu-vm pro2]# vi b.c

#include <stdio.h>
main()
{
        fprintf(stderr,"%d\n",add(1,1));
}

 

e. 编译b.c 文件

 

[root@xhu-vm pro2]# gcc -c -O b.c 
[root@xhu-vm pro2]# ll
total 24
-rw-r--r--  1 root root   57 Jun  8 13:14 a.c
-rw-r--r--  1 root root  681 Jun  8 13:15 a.o
-rw-r--r--  1 root root   64 Jun  8 13:33 b.c
-rw-r--r--  1 root root  936 Jun  8 13:34 b.o
-rwxr-xr-x  1 root root 4152 Jun  8 13:22 libabc.so
 

f. 生成可执行文件

[root@xhu-vm pro2]# gcc b.o -L./ -labc -s -o b
[root@xhu-vm pro2]# ll
total 28
-rw-r--r--  1 root root   57 Jun  8 13:14 a.c
-rw-r--r--  1 root root  681 Jun  8 13:15 a.o
-rwxr-xr-x  1 root root 3272 Jun  8 13:35 b
-rw-r--r--  1 root root   64 Jun  8 13:33 b.c
-rw-r--r--  1 root root  936 Jun  8 13:34 b.o
-rwxr-xr-x  1 root root 4152 Jun  8 13:22 libabc.so
[root@xhu-vm pro2]# 

 

-L 指定库文件所在的目录

-l  指定库文件的名字   ,系统会搜索 libabc.a 静态库文件 和  libabc.so 的动态库文件

-s 只搜索扩展名为.so的库文件

-o 指定输出文件名字

 

g. 运行可执行文件,报错了 找不到 共享的库文件,原因是我们没有设置环境变量

 

[root@xhu-vm pro2]# ./b 
./b: error while loading shared libraries: libabc.so: cannot open shared object file: No such file or directory
[root@xhu-vm pro2]# 

设置了  LD_LIBRARY_PATH 这个环境变量 指定动态库的位置 后 我们得到了 1+1=2 的结果

 

[root@xhu-vm pro2]# export LD_LIBRARY_PATH=./
[root@xhu-vm pro2]# ./b 
2
[root@xhu-vm pro2]# 
 

 

使用方式2 : 使用了系统的dlopen系列函数 (程序启动的时候并不加载,等需要使用的时候再加载)

 

[root@xhu-vm pro2]# vi c.c

int *pdata = NULL;



#include <stdio.h>
#include <dlfcn.h>

int (*fcn)(int,int) = NULL;

int load(const char *libname)

{

    void *handle;

    handle = dlopen(libname, RTLD_NOW);

    if (handle == NULL)

    {

        fprintf(stderr,"Failed to load %s: %s\n",libname, dlerror());

        return 1;

    }



    fcn = dlsym(handle,"add");


    if (fcn == NULL)

    {

        fprintf(stderr,"%s\n",dlerror());

        dlclose(handle);

        return 1;

    }



    /* 加载完不能dlclose,必须等到不再使用从handle中加载出的任何东西时才能dlclose*/

    return 0;

}



int main()

{

    if (load("./libabc.so"))

    {

        return 1;

    }

    int result = fcn(2,2);

    printf("%d\n",result);

    return 0;

}

 
[root@xhu-vm pro2]# gcc -o c -ldl c.c
[root@xhu-vm pro2]# ./c 
4
[root@xhu-vm pro2]# 

 

 

 

Note:  使用dlsym 函数不仅可以得到 动态库中 函数的指针,也可以得到 动态库中的全局变量的指针,从而得到全局变量的值,我们产品的agent framework 就是这样的,在so加载的时候用 dlsym 方式得到 一个全局变量aa, 这个aa 代表了 所有能操的op_list 集合,然后用 得到函数指针,最后调用方法, 下面有个例子

 

a.在 so 中定义2个全局变量,一个是float,一个是char[],在 caller中得到他们的地址

 

 

a.c

#include <stdio.h>  

float test=123.4;
char test2[]="xhu";

int add(int x,int y)  
{  
        return x+y;  
}

编译 : gcc -fPIC -c a.c  ->> 得到a.o

打包so  gcc -shared -o libabc.so a.o

 

测试程序 c.c

 

#include <stdio.h>  
#include <dlfcn.h>  
  
char* fcn=NULL;  
float* ff=NULL;   
int load(const char *libname)  
  
{  
  
    void *handle;  
  
    handle = dlopen(libname, RTLD_LAZY);  
  
    if (handle == NULL)  
  
    {  
  
        fprintf(stderr,"Failed to load %s: %s\n",libname, dlerror());  
  
        return 1;  
  
    }  
  
  
  
    fcn =(char*)dlsym(handle,"test2");  
    ff = (float*)dlsym(handle,"test");
  
    if (fcn == NULL)  
  
    {  
  
        fprintf(stderr,"%s\n",dlerror());  
  
        dlclose(handle);  
  
        return 1;  
  
    }  
  
  
  
    /* 加载完不能dlclose,必须等到不再使用从handle中加载出的任何东西时才能dlclose*/  
  
    return 0;  
  
}  
  
  
  
int main()  
  
{  
  
    if (load("../lib/libabc.so"))  
  
    {  
  
        return 1;  
  
    }   
    char* result = fcn;  
    printf("%s\n",result); 
 
    float* result2 = ff;
    printf("%f\n",*result2);
    return 0;  
  
} 
 

编译: gcc -o c -ldl c.c  >> 得到c

 

执行:

 

[root@egovmo03 src]# ./c
xhu
123.400002

 

打完收工

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    AKA Linux编程系列讲座第二期的所有资料

    在本资源"AKA Linux编程系列讲座第二期的所有资料"中,您将深入学习Linux操作系统下的编程技术。这个系列讲座的第二期是一个精心设计的教程,旨在为初学者提供一个全面且详细的指导,涵盖了大量的理论知识和实践案例...

    embedded Linux大纲

    #### 第2章 Shell编程基础 - **目标**: - 熟悉Linux系统下的编辑环境。 - 熟练进行shell编程。 - **主要知识点**: - **使用vi编辑文件**:包括基本模式切换、文本编辑操作等。 - **使用Emacs编辑文件**:介绍...

    linux操作系统+linux下编程+实验报告及代码和操作过程+期末复习

    在本实验报告中,我们将深入探讨Linux操作系统下的C语言编程,包括如何使用vi编辑器、GCC编译器、gdb调试器以及如何编写和管理多源文件项目。实验旨在提升学生对Linux环境下的编程能力,熟悉系统调用,以及学会使用...

    C-Primer-Plus(第6版)(中文版)1

    书中提到了UNIX系统、GNU编译器集合(GCC)和LLVM项目、Linux系统下的编译环境,以及PC上的命令行编译器和集成开发环境(IDE)等不同平台的编译工具。 5. 本书的组织结构按照学习C语言的步骤进行,从简单的程序示例...

    Linux 环境c程序设计源码+ppt(第二版)

    《Linux环境C程序设计》是徐诚先生在清华大学出版社出版的第二版教材,该书深入浅出地介绍了在Linux操作系统下进行C语言编程的基础知识和高级技巧。此资源包含源码和PPT,为读者提供了丰富的实践材料和教学辅助资料...

    嵌入式linux应用程序开发详解

    - **Gcc编译器**:GNU C Compiler,用于将C/C++源代码编译为可执行文件。 - **Gcc编译流程解析**:预处理、编译、汇编、链接四个阶段。 - **Gcc编译选项分析**:如`-Wall`显示所有警告信息,`-g`添加调试信息。 - ...

    嵌入式学习计划,绝对值10分

    - **第2、3周**:重点是学习在Linux环境下进行C语言编程。 - **周一**:介绍C程序设计的基本概念,包括流程图表示算法、数据类型等。 - **周二**:讲解循环控制语句、分支结构程序设计等。 - **周三**:深入理解...

    NOI笔试复习题

    从给定的文件信息中,我们可以总结出一系列与NOI(National Olympiad in Informatics,全国青少年信息学奥林匹克竞赛)笔试复习相关的IT知识点,主要集中在Linux操作系统的基本操作、编程语言概念以及程序调试等方面...

    Linux软件工程师模拟试卷与解析.pdf

    通过这些题目,读者可以复习和巩固Linux系统的基础知识,包括文件操作、进程管理、内存管理、GCC编译器的使用、GDB调试技巧以及C语言编程规范和逻辑判断等内容。这将有助于提高Linux软件工程师的技能水平和应对考试...

    Linux高级程序设计第三版杨宗德(源码+ppt)

    10. **编译与调试**:GCC编译器的使用、Makefile的编写,以及GDB调试器的运用,帮助开发者有效地构建和调试程序。 随书提供的源码和PPT资料进一步加强了学习体验。源码涵盖了书中示例,读者可以亲自运行和分析,...

    ICS2022-大作业

    \n\n1.2 **环境与工具**:报告中使用的操作系统为Ubuntu,这是一种广泛使用的Linux发行版,支持各种编程工具,如GCC(GNU Compiler Collection)作为编译器,GDB(GNU Debugger)用于调试,以及其它辅助工具如grep和...

    哈尔滨工业大学计统大作业

    开发工具包括Visual Studio Code、VI/VIM/GPEDIT以及GCC编译器。这些工具提供了编写、编辑、调试和构建C程序的平台。 通过对"hello.c"的生命周期的研究,张璐不仅复习了计算机系统的各个环节,还对进程管理有了更深...

Global site tag (gtag.js) - Google Analytics