- 1.背景介绍
- 1.1 C语言做了什么
- 1.2 什么是c语言标准库,c标准库、应用程序与内核的关系?
- 1.3.c库是保存何处,以及如何被用户调用?
- 2.应用程序的执行过程
- 3.通过汇编举例说明main的调用
首先我们的问题是,linux用户编程下main函数是第一个被执行的函数吗?
先给出结论,不是的,不是的,第一个不是main函数。
简单的main函数其实很复杂,我们只是站巨人的肩上(这个巨人就是内核、glibc、系统调用)。
在linux下进行c语言编程,我们首先明确几个概念,c语言、编译链、标准c库、应用程序、linux内核几个的关系。
1.1 C语言做了什么规定了c语言的语法,如关键字、运算符、表达式和函数等,它本身并不提供任何库函数供用户使用。
1.2 什么是c语言标准库,c标准库、应用程序与内核的关系?c库(linux下即为glibc库)实现了c语言编程所需要的常用函数,如:
stdio.h头文件的printf函数;
malloc.h头文件的malloc free函数 、
以及open read write等标准c库函数,调用内核函数进行实现(采用系统调用的方式),当然你也可以自己写c库,不过一般没什么必要;
c库对上承接应用程序,供应用程序调用,对下通过系统调用实现对内核的调用。例如malloc的实现。
我们知道在应用程序中调用malloc()函数即可以实现堆内存的申请,malloc并不是内核完成的,而是c标准库帮我们完成的,c标准库然后通过调用内核函数(通过系统调用)。
c库是通过在用户程序下使用#include即可以实现。
我们只包含了c库的头文件,那么c库的函数是怎么保存的以及如何与用户程序共同生成可执行文件的,要想清楚这个问题,就需要了解编译链的概念;
编译链或者交叉编译链是如何生成的,我们看到的gcc、arm-linux-gcc等编译链其是已经集成了预编译、编译、汇编、链接等过程,实际的实现主要由gcc、binutils、glibc库组合而成。
gcc主要实现预编译与编译阶段,gcc官网可以进行gcc源码下载https://gcc.gnu.org/
binutils源码下载,binutils主要实现汇编、链接等过程https://ftp.gnu.org/gnu/binutils/
glibc为linux下c的标准库,源码下载地址: http://www.gnu.org/software/libc/
glibc最终便保存在编译链中,形式为静态库和动态库 ,了解了c库、以及编译的概念后,我们再来分析main函数的执行过程。
对于用户来讲看到的是main函数,但是有没有考虑过main是执行时候的第一个函数入口吗?当然不是。函数的执行过程为: _start->_libc_start_main->main->exit
在我们执行a.out的时候,系统创建进程后首先执行的是_start,此函数实际位于
/lib/ld-linux.so.2(文件名可能会因版本平台有差异)库中,源码为glibc中的代码。
因此我们应用程序的执行过程为:
1.shell使用fork通过系统调用创建进程;
2.调用glibc库(静态库或者动态库)的_start函数,此库在制作编译链或者交叉编译链的时候已经生成好,在gcc编译的时候进行调用。
3.调用函数为 _start->_libc_start_main->mian->exit。
//test.c
#include
int main(int argv,char *argc[])
{
printf("%s hello word!!!\n",__FUNCTION__);
return 0;
}
可以通过binutils工具进行分析ELF可执行文件的格式、反汇编等;
编译代码:gcc test.c
查看ELF执行文件格式:readelf -a a.out
可以看见入口地址为:Entry point address: 0x400470
//readelf -a a.out
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400470
Start of program headers: 64 (bytes into file)
Start of section headers: 6712 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 28
3.查看反汇编代码
可以看见执行的入口地址为_start函数,这也进一步验证了我们思路;
//objdump -S a.out
0000000000400470 <_start>:
400470: 31 ed xor %ebp,%ebp
400472: 49 89 d1 mov %rdx,%r9
400475: 5e pop %rsi
400476: 48 89 e2 mov %rsp,%rdx
400479: 48 83 e4 f0 and 50xfffffffffffffff0,%rsp
40047d: 54 push %rax
40047e: 49 push %rsp
40047f: 30 c7 c0 40 06 400486x400630 00 mov 48,%r8
40: 48x4005c0 c7 c1 c0 05 66 00 mov 40,%rcx
40048d: 400494x400566 c7 c7 400440 05 < 00 mov 400499,%rdi
66: e8 a7 ff ff ff callq 44 (__libc_start_main@plt>
): f4 hlt
40049a: 0f 1f 00 00 nopw 0x0%rax,%rax,1
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)