C语言函数中的中间数,汇编调用到C语言mian函数--中间究竟发生了什么过程

本文详细解析了main函数在编译流程中的位置,指出它并非C语言首个调用函数,而是通过__libc_start_main间接调用。通过实例和反汇编,展示了从汇编到链接main函数的步骤,揭示了C程序启动的底层机制。
摘要由CSDN通过智能技术生成

最近面试,技术经理问我汇编到main函数步骤是什么,当时我是一脸疑惑,what?main函数就不是C语言调用第一个函数吗?后面我自己查了相关资料,才发现自己too young too navie.

很多人只知道main函数是c语言的第一个调用函数,其实不是,下面用个例子说明

在开始讲解之前,我先科普一下知识,平时我们编译.c文件是,一般分为三个步骤:

第一步生成汇编代码

第二部生成目标文件

第三步生成可执行文件

[test@localhost Assembler]$ gcc -S main.c

[test@localhost Assembler]$ gcc -c main.s

[test@localhost Assembler]$ gcc main.o

-S生成汇编代码,-c生成目标文件,gcc生成可执行文件。

80dc00d25b2dec81cfdd32dad38086c5.png

好了,下面我们来写个程序!

创建一个test.c文件,里面写以下代码

#include

#include

嘻嘻,没错,我故意没写main函数,看看会编译后会发生什么结果!

9c6ee2d7315aae3328945bb5e9c28bca.png

这个编译结果有两个意思,第一个是在Scrt1.o文件里调用了_start函数。第二个意思是在_start函数中没调用main函数。这说明main函数并不是我们所说的C语言第一个调用的函数,下面用更具体的证据证明。

下面我们查看Scrt1.o里面调用了什么:

d6389e57262775a31635b01b314a162a.pngD是初始化data段,U是undefined的含义,说明main函数在该文件中未定义,但是T _start代表存在代码区 _start,并且在_start里面调用了main函数,进一步证实了我们的猜测。

如果小伙伴想深究下去main函数怎么调用,请继续往下看!

我们先修改main.c文件代码,加上main函数

#include

#include

int main(){

}

然后我们编译是没有报错的,然后我们反编译该代码

yates@yates-HLY-WX9XX:~/software/c$ objdump -d test

test: file format elf64-x86-64

Disassembly of section .init:

0000000000001000 <_init>:

1000:f3 0f 1e fa endbr64

1004:48 83 ec 08 sub $0x8,%rsp

1008:48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8

100f:48 85 c0 test %rax,%rax

1012:74 02 je 1016 <_init>

1014:ff d0 callq *%rax

1016:48 83 c4 08 add $0x8,%rsp

101a:c3 retq

Disassembly of section .plt:

0000000000001020 <.plt>:

1020:ff 35 a2 2f 00 00 pushq 0x2fa2(%rip) # 3fc8

1026:f2 ff 25 a3 2f 00 00 bnd jmpq *0x2fa3(%rip) # 3fd0

102d:0f 1f 00 nopl (%rax)

Disassembly of section .plt.got:

0000000000001030 <__cxa_finalize>:

1030:f3 0f 1e fa endbr64

1034:f2 ff 25 bd 2f 00 00 bnd jmpq *0x2fbd(%rip) # 3ff8

103b:0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

Disassembly of section .text:

0000000000001040 <_start>:

1040:f3 0f 1e fa endbr64

1044:31 ed xor %ebp,%ebp

1046:49 89 d1 mov %rdx,%r9

1049:5e pop %rsi

104a:48 89 e2 mov %rsp,%rdx

104d:48 83 e4 f0 and $0xfffffffffffffff0,%rsp

1051:50 push %rax

1052:54 push %rsp

1053:4c 8d 05 56 01 00 00 lea 0x156(%rip),%r8 # 11b0

105a:48 8d 0d df 00 00 00 lea 0xdf(%rip),%rcx # 1140

1061:48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1129

1068:ff 15 72 2f 00 00 callq *0x2f72(%rip) # 3fe0

106e:f4 hlt

106f:90 nop

0000000000001070 :

1070:48 8d 3d 99 2f 00 00 lea 0x2f99(%rip),%rdi # 4010

1077:48 8d 05 92 2f 00 00 lea 0x2f92(%rip),%rax # 4010

107e:48 39 f8 cmp %rdi,%rax

1081:74 15 je 1098

1083:48 8b 05 4e 2f 00 00 mov 0x2f4e(%rip),%rax # 3fd8

108a:48 85 c0 test %rax,%rax

108d:74 09 je 1098

108f:ff e0 jmpq *%rax

1091:0f 1f 80 00 00 00 00 nopl 0x0(%rax)

1098:c3 retq

1099:0f 1f 80 00 00 00 00 nopl 0x0(%rax)

00000000000010a0 :

10a0:48 8d 3d 69 2f 00 00 lea 0x2f69(%rip),%rdi # 4010

10a7:48 8d 35 62 2f 00 00 lea 0x2f62(%rip),%rsi # 4010

10ae:48 29 fe sub %rdi,%rsi

10b1:48 89 f0 mov %rsi,%rax

10b4:48 c1 ee 3f shr $0x3f,%rsi

10b8:48 c1 f8 03 sar $0x3,%rax

10bc:48 01 c6 add %rax,%rsi

10bf:48 d1 fe sar %rsi

10c2:74 14 je 10d8

10c4:48 8b 05 25 2f 00 00 mov 0x2f25(%rip),%rax # 3ff0

10cb:48 85 c0 test %rax,%rax

10ce:74 08 je 10d8

10d0:ff e0 jmpq *%rax

10d2:66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)

10d8:c3 retq

10d9:0f 1f 80 00 00 00 00 nopl 0x0(%rax)

00000000000010e0 <__do_global_dtors_aux>:

10e0:f3 0f 1e fa endbr64

10e4:80 3d 25 2f 00 00 00 cmpb $0x0,0x2f25(%rip) # 4010

10eb:75 2b jne 1118 <__do_global_dtors_aux>

10ed:55 push %rbp

10ee:48 83 3d 02 2f 00 00 cmpq $0x0,0x2f02(%rip) # 3ff8

10f5:00

10f6:48 89 e5 mov %rsp,%rbp

10f9:74 0c je 1107 <__do_global_dtors_aux>

10fb:48 8b 3d 06 2f 00 00 mov 0x2f06(%rip),%rdi # 4008

1102:e8 29 ff ff ff callq 1030 <__cxa_finalize>

1107:e8 64 ff ff ff callq 1070

110c:c6 05 fd 2e 00 00 01 movb $0x1,0x2efd(%rip) # 4010

1113:5d pop %rbp

1114:c3 retq

1115:0f 1f 00 nopl (%rax)

1118:c3 retq

1119:0f 1f 80 00 00 00 00 nopl 0x0(%rax)

0000000000001120 :

1120:f3 0f 1e fa endbr64

1124:e9 77 ff ff ff jmpq 10a0

0000000000001129 :

1129:f3 0f 1e fa endbr64

112d:55 push %rbp

112e:48 89 e5 mov %rsp,%rbp

1131:b8 00 00 00 00 mov $0x0,%eax

1136:5d pop %rbp

1137:c3 retq

1138:0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)

113f:00

0000000000001140 <__libc_csu_init>:

1140:f3 0f 1e fa endbr64

1144:41 57 push %r15

1146:4c 8d 3d a3 2c 00 00 lea 0x2ca3(%rip),%r15 # 3df0

114d:41 56 push %r14

114f:49 89 d6 mov %rdx,%r14

1152:41 55 push %r13

1154:49 89 f5 mov %rsi,%r13

1157:41 54 push %r12

1159:41 89 fc mov %edi,%r12d

115c:55 push %rbp

115d:48 8d 2d 94 2c 00 00 lea 0x2c94(%rip),%rbp # 3df8

1164:53 push %rbx

1165:4c 29 fd sub %r15,%rbp

1168:48 83 ec 08 sub $0x8,%rsp

116c:e8 8f fe ff ff callq 1000 <_init>

1171:48 c1 fd 03 sar $0x3,%rbp

1175:74 1f je 1196 <__libc_csu_init>

1177:31 db xor %ebx,%ebx

1179:0f 1f 80 00 00 00 00 nopl 0x0(%rax)

1180:4c 89 f2 mov %r14,%rdx

1183:4c 89 ee mov %r13,%rsi

1186:44 89 e7 mov %r12d,%edi

1189:41 ff 14 df callq *(%r15,%rbx,8)

118d:48 83 c3 01 add $0x1,%rbx

1191:48 39 dd cmp %rbx,%rbp

1194:75 ea jne 1180 <__libc_csu_init>

1196:48 83 c4 08 add $0x8,%rsp

119a:5b pop %rbx

119b:5d pop %rbp

119c:41 5c pop %r12

119e:41 5d pop %r13

11a0:41 5e pop %r14

11a2:41 5f pop %r15

11a4:c3 retq

11a5:66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)

11ac:00 00 00 00

00000000000011b0 <__libc_csu_fini>:

11b0:f3 0f 1e fa endbr64

11b4:c3 retq

Disassembly of section .fini:

00000000000011b8 <_fini>:

11b8:f3 0f 1e fa endbr64

11bc:48 83 ec 08 sub $0x8,%rsp

11c0:48 83 c4 08 add $0x8,%rsp

11c4:c3 retq

我们发现这里面调用了__libc_start_main函数,但是该函数并没有在Scrt1.o里面调用,其实我们根据名字可以猜测到,该函数调用在libc库中调用。

首先将一系列参数压栈,然后调用 libc 的库函数 __libc_start_main 做初始化工作,其中最后一个压栈的参数 push $129是 main 函数的地址,__libc_start_main 在完成初始化工作之后会调用 main 函数。由于 __libc_start_main 需要动态链接,所以这个库函数的指令在可执行文件 main 的反汇编中肯定是找不到的,然而我们找到了这个:

0000000000001040 <_start>:

1040:f3 0f 1e fa endbr64

1044:31 ed xor %ebp,%ebp

1046:49 89 d1 mov %rdx,%r9

1049:5e pop %rsi

104a:48 89 e2 mov %rsp,%rdx

104d:48 83 e4 f0 and $0xfffffffffffffff0,%rsp

1051:50 push %rax

1052:54 push %rsp

1053:4c 8d 05 56 01 00 00 lea 0x156(%rip),%r8 # 11b0

105a:48 8d 0d df 00 00 00 lea 0xdf(%rip),%rcx # 1140

1061:48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1129

1068:ff 15 72 2f 00 00 callq *0x2f72(%rip) # 3fe0

106e:f4 hlt

106f:90 nop

从上面可以说明,编译一个.c文件,除了要链接Scrt1.o文件和libc库。

以上是我在Ubuntu 20系统上debug 出来的结果,但是实际每个系统出现现象会有不一样,但是具体原理基本不便,所以小伙伴要多动手,多动脑。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值