GCC 预处理 编译 汇编 链接全过程及其含义 程序的开始不是main函数

来源:互联网 发布:日上免税店 mac 编辑:程序博客网 时间:2024/06/08 05:11

下面对c语言程序的实现过程做简单的总结:

首先咱写一个简单的程序 test.c  test.h main.c

tset.h:

 #ifndef _TEST_H_ #define _TEST_H_ int add(int,int); #endif
test.c
   #include"test.h"   int add(int a,int b){       return a+b;   }
main.c

   #include<stdio.h>   #include"test.h"   int main(){       int a=6,b=4;       printf("%d+%d=%d\n",a,b,add(a,b));       return 0;   }

首先对test.c main.c 进行预处理

采用gcc -E test.c  -o test.i

       gcc -E main.c -o main.i

将程序中用到的printf函数及头文件翻译成c语言(-o的意思是将翻译后的文件命名为test.i/main.i)

再使用gcc -S test.i -o test.s

           gcc -S main. -o main.s

把翻译后的文件翻译成汇编文件(汇编语言),这个过程叫做编译

在使用gcc -c main.s

           gcc -c test.s

把编译后的汇编文件(汇编语言)汇编成机器语言(计算机识别的语言)会生成 main.o  test.o文件

以上是从你编写的程序到计算机能识别的语言的一个过程,只有从汇编语言到机器语言的过程才叫汇编。


下面演示连接的过程:

先介绍一个命令:nm(被用于显示二进制目标文件的符号表)       什么事符号表:表里的内容包括函数的名字 全局变量的名字  静态局部变量的名字等

先运行 nm main.o 结果如下:

                 U add0000000000000000 T main                 U printf

nm test.o 结果如下

0000000000000000 T add
T的意思是 该函数已在文件中被定义,有这个函数的二进制代码

U的意思是 该函数在文件中被调用,没有这个函数的二进制代码

可以看出在main.o中只有main函数的二进制代码,add 函数和printf函数都只是被调用

在add函数中只有add函数的二进制代码

下面运行gcc main.o test.o

会生成a.out 文件

我们在nm a.out 结果如下:

0000000000600e50 d _DYNAMIC0000000000600fe8 d _GLOBAL_OFFSET_TABLE_0000000000400638 R _IO_stdin_used                 w _Jv_RegisterClasses0000000000600e30 d __CTOR_END__0000000000600e28 d __CTOR_LIST__0000000000600e40 D __DTOR_END__0000000000600e38 d __DTOR_LIST__0000000000400740 r __FRAME_END__0000000000600e48 d __JCR_END__0000000000600e48 d __JCR_LIST__0000000000601020 A __bss_start0000000000601010 D __data_start00000000004005f0 t __do_global_ctors_aux0000000000400460 t __do_global_dtors_aux0000000000601018 D __dso_handle                 w __gmon_start__0000000000600e24 d __init_array_end0000000000600e24 d __init_array_start00000000004005e0 T __libc_csu_fini0000000000400550 T __libc_csu_init                 U __libc_start_main@@GLIBC_2.2.50000000000601020 A _edata0000000000601030 A _end0000000000400628 T _fini00000000004003c8 T _init0000000000400410 T _start000000000040053c T add000000000040043c t call_gmon_start0000000000601020 b completed.65310000000000601010 W data_start0000000000601028 b dtor_idx.653300000000004004d0 t frame_dummy00000000004004f4 T main                 U printf@@GLIBC_2.2.5
红色背景的部分是我需要观察的部分:

可以看出 在a.out文件中add 函数 main 函数为T ,表示在阿。a.out中有两个函数的二进制代码了。这个过程叫做静态连接!

printf 前面的U表示:printf在代码执行时,代码装进内存的时候会再次进行连接,这个连接过程在代码执行的时候完成。这个过程叫做动态连接。


根据咱上面说的 我来推一下程序的开始是不是main函数!

首先使用 gcc main.o -v(加-v 显示编译或连接整个过程的信息)

部分结果如下:

gcc 版本 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../:/lib/:/usr/lib/COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64' /usr/lib/gcc/x86_64-linux-gnu/4.6/collect2 --sysroot=/ --build-id --no-add-needed --as-needed --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro /usr/lib/gcc/x86_64-linux-gnu/ 4.6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crti.o /usr/li b/gcc/x86_64-linux-gnu/4.6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.6 -L/usr/lib/gcc/x86_64-linux-gnu/4.6/. ./../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../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/4.6/../../.. main.o -lgcc --a s-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.6/ crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crtn.omain.o: In function `main':main.c:(.text+0x21): undefined reference to `add'collect2: ld 返回 1

我们在nm  /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crti.o   (只要是.o结尾的文件就可以)

得到结果如下:

0000000000000000 R _IO_stdin_used0000000000000000 D __data_start                 U __libc_csu_fini                 U __libc_csu_init                 U __libc_start_main0000000000000000 T _start0000000000000000 W data_start                 U maine
可以看出 main函数前是U    main函数被调用了!!

而 _start 前是T!

在我们nm a.out 时第一个红色背景也是 T _start

所以我认为main函数仅仅是一个框架!程序的开始因该是 _start函数 


疑问

最后说一个学到的隐藏main.c 的方法:

alias函数可以给命令起别名 类似于 typedef

输入 alisa haha="gcc main.c"   haha为别名

然后 haha tset.c 就可以生成a.out文件了。


最后谢谢查阅!有问题欢迎指出,咱们共同探讨!大笑



阅读全文
1 0