C 语言编程 — 程序的装载与运行

举报
云物互联 发表于 2021/08/06 00:54:30 2021/08/06
【摘要】 目录 文章目录 目录文章目录C 程序在操作系统中的装载与运行ELF 文件反汇编 ELF 文件 文章目录 《C 语言编程 — GCC 工具链》 《C 语言编程 — 程序的编译流程》 《C 语言编程 — 静态库、动态库和共享库》 《C 语言编程 — 程序的装载与运行》 《计算机组成原理 — 指令系统》 《C 语言编程 — 结构化程序流的汇编代码与 CPU ...

目录

文章目录

C 语言编程 — GCC 工具链
C 语言编程 — 程序的编译流程
C 语言编程 — 静态库、动态库和共享库
C 语言编程 — 程序的装载与运行
计算机组成原理 — 指令系统
C 语言编程 — 结构化程序流的汇编代码与 CPU 指令集

C 程序在操作系统中的装载与运行

一个程序在操作系统上运行需要经历以下阶段:

第一阶段:得到可执行文件

  1. 编译(Compile)
  2. 汇编(Assemble)
  3. 链接(Link)

第二阶段:装载运行

  1. 装载器(Loader)将可执行文件载入到内存
  2. CPU 从内存中可执行文件的程序入口开始读取指令和数据,开始真正执行程序。

在这里插入图片描述
编译和汇编的过程在上文中已经提到了,下面再继续介绍链接的过程。

  • 子程序
// add_lib.c

int add(int a, int b)
{ return a+b;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 主函数
// link_example.c

#include <stdio.h>
int main()
{ int a = 10; int b = 5; int c = add(a, b); printf("c = %d\n", c);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 编译 C 程序得到 Object 文件
$ gcc -g -c add_lib.c link_example.c

  
 
  • 1
  • 链接上述两个 Object 文件得到一个可执行文件
$ gcc -o link-example add_lib.o link_example.o

$ ./link-example
c = 15

  
 
  • 1
  • 2
  • 3
  • 4

区别于 Object 文件,真正的可执行文件的内容如下:

$ objdump -d -M intel -S link-example

link-example: file format elf64-x86-64


Disassembly of section .init:

00000000004003c8 <_init>:
  4003c8: 48 83 ec 08 sub rsp,0x8
  4003cc: 48 8b 05 25 0c 20 00 mov rax,QWORD PTR [rip+0x200c25] # 600ff8 <__gmon_start__>
  4003d3: 48 85 c0 test   rax,rax
  4003d6: 74 05 je 4003dd <_init+0x15>
  4003d8: e8 43 00 00 00 call   400420 <.plt.got>
  4003dd: 48 83 c4 08 add rsp,0x8
  4003e1: c3 ret

Disassembly of section .plt:

00000000004003f0 <.plt>:
  4003f0: ff 35 12 0c 20 00 push   QWORD PTR [rip+0x200c12] # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  4003f6: ff 25 14 0c 20 00 jmp QWORD PTR [rip+0x200c14] # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  4003fc: 0f 1f 40 00 nop DWORD PTR [rax+0x0]

0000000000400400 <printf@plt>:
  400400: ff 25 12 0c 20 00 jmp QWORD PTR [rip+0x200c12] # 601018 <printf@GLIBC_2.2.5>
  400406: 68 00 00 00 00 push   0x0
  40040b: e9 e0 ff ff ff jmp 4003f0 <.plt>

0000000000400410 <__libc_start_main@plt>:
  400410: ff 25 0a 0c 20 00 jmp QWORD PTR [rip+0x200c0a] # 601020 <__libc_start_main@GLIBC_2.2.5>
  400416: 68 01 00 00 00 push   0x1
  40041b: e9 d0 ff ff ff jmp 4003f0 <.plt>

Disassembly of section .plt.got:

0000000000400420 <.plt.got>:
  400420: ff 25 d2 0b 20 00 jmp QWORD PTR [rip+0x200bd2] # 600ff8 <__gmon_start__>
  400426: 66 90 xchg   ax,ax

Disassembly of section .text:

0000000000400430 <_start>:
  400430: 31 ed xor ebp,ebp
  400432: 49 89 d1 mov r9,rdx
  400435: 5e pop rsi
  400436: 48 89 e2 mov rdx,rsp
  400439: 48 83 e4 f0 and rsp,0xfffffffffffffff0
  40043d: 50 push   rax
  40043e: 54 push   rsp
  40043f: 49 c7 c0 f0 05 40 00 mov r8,0x4005f0
  400446: 48 c7 c1 80 05 40 00 mov rcx,0x400580
  40044d: 48 c7 c7 31 05 40 00 mov rdi,0x400531
  400454: e8 b7 ff ff ff call   400410 <__libc_start_main@plt>
  400459: f4 hlt
  40045a: 66 0f 1f 44 00 00 nop WORD PTR [rax+rax*1+0x0]

0000000000400460 <deregister_tm_clones>:
  400460: b8 37 10 60 00 mov eax,0x601037
  400465: 55 push   rbp
  400466: 48 2d 30 10 60 00 sub rax,0x601030
  40046c: 48 83 f8 0e cmp rax,0xe
  400470: 48 89 e5 mov rbp,rsp
  400473: 77 02 ja 400477 <deregister_tm_clones+0x17>
  400475: 5d pop rbp
  400476: c3 ret
  400477: b8 00 00 00 00 mov eax,0x0
  40047c: 48 85 c0 test   rax,rax
  40047f: 74 f4 je 400475 <deregister_tm_clones+0x15>
  400481: 5d pop rbp
  400482: bf 30 10 60 00 mov edi,0x601030
  400487: ff e0 jmp rax
  400489: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0]

0000000000400490 <register_tm_clones>:
  400490: b8 30 10 60 00 mov eax,0x601030
  400495: 55 push   rbp
  400496: 48 2d 30 10 60 00 sub rax,0x601030
  40049c: 48 c1 f8 03 sar rax,0x3
  4004a0: 48 89 e5 mov rbp,rsp
  4004a3: 48 89 c2 mov rdx,rax
  4004a6: 48 c1 ea 3f shr rdx,0x3f
  4004aa: 48 01 d0 add rax,rdx
  4004ad: 48 d1 f8 sar rax,1
  4004b0: 75 02 jne 4004b4 <register_tm_clones+0x24>
  4004b2: 5d pop rbp
  4004b3: c3 ret
  4004b4: ba 00 00 00 00 mov edx,0x0
  4004b9: 48 85 d2 test   rdx,rdx
  4004bc: 74 f4 je 4004b2 <register_tm_clones+0x22>
  4004be: 5d pop rbp
  4004bf: 48 89 c6 mov rsi,rax
  4004c2: bf 30 10 60 00 mov edi,0x601030
  4004c7: ff e2 jmp rdx
  4004c9: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0]

00000000004004d0 <__do_global_dtors_aux>:
  4004d0: 80 3d 55 0b 20 00 00 cmp BYTE PTR [rip+0x200b55],0x0 # 60102c <_edata>
  4004d7: 75 11 jne 4004ea <__do_global_dtors_aux+0x1a>
  4004d9: 55 push   rbp
  4004da: 48 89 e5 mov rbp,rsp
  4004dd: e8 7e ff ff ff call   400460 <deregister_tm_clones>
  4004e2: 5d pop rbp
  4004e3: c6 05 42 0b 20 00 01 mov BYTE PTR [rip+0x200b42],0x1 # 60102c <_edata>
  4004ea: f3 c3 repz ret
  4004ec: 0f 1f 40 00 nop DWORD PTR [rax+0x0]

00000000004004f0 <frame_dummy>:
  4004f0: 48 83 3d 28 09 20 00 cmp QWORD PTR [rip+0x200928],0x0 # 600e20 <__JCR_END__>
  4004f7: 00
  4004f8: 74 1e je 400518 <frame_dummy+0x28>
  4004fa: b8 00 00 00 00 mov eax,0x0
  4004ff: 48 85 c0 test   rax,rax
  400502: 74 14 je 400518 <frame_dummy+0x28>
  400504: 55 push   rbp
  400505: bf 20 0e 60 00 mov edi,0x600e20
  40050a: 48 89 e5 mov rbp,rsp
  40050d: ff d0 call   rax
  40050f: 5d pop rbp
  400510: e9 7b ff ff ff jmp 400490 <register_tm_clones>
  400515: 0f 1f 00 nop DWORD PTR [rax]
  400518: e9 73 ff ff ff jmp 400490 <register_tm_clones>

000000000040051d <add>:
// add_lib.c

int add(int a, int b)
{
  40051d: 55 push   rbp
  40051e: 48 89 e5 mov rbp,rsp
  400521: 89 7d fc mov DWORD PTR [rbp-0x4],edi
  400524: 89 75 f8 mov DWORD PTR [rbp-0x8],esi return a+b;
  400527: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
  40052a: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
  40052d: 01 d0 add eax,edx
}
  40052f: 5d pop rbp
  400530: c3 ret

0000000000400531 <main>:
// link_example.c

#include <stdio.h>
int main()
{
  400531: 55 push   rbp
  400532: 48 89 e5 mov rbp,rsp
  400535: 48 83 ec 10 sub rsp,0x10 int a = 10;
  400539: c7 45 fc 0a 00 00 00 mov DWORD PTR [rbp-0x4],0xa int b = 5;
  400540: c7 45 f8 05 00 00 00 mov DWORD PTR [rbp-0x8],0x5 int c = add(a, b);
  400547: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8]
  40054a: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
  40054d: 89 d6 mov esi,edx
  40054f: 89 c7 mov edi,eax
  400551: b8 00 00 00 00 mov eax,0x0
  400556: e8 c2 ff ff ff call   40051d <add>
  40055b: 89 45 f4 mov DWORD PTR [rbp-0xc],eax printf("c = %d\n", c);
  40055e: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]
  400561: 89 c6 mov esi,eax
  400563: bf 10 06 40 00 mov edi,0x400610
  400568: b8 00 00 00 00 mov eax,0x0
  40056d: e8 8e fe ff ff call   400400 <printf@plt>
}
  400572: c9 leave
  400573: c3 ret
  400574: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
  40057b: 00 00 00
  40057e: 66 90 xchg   ax,ax

0000000000400580 <__libc_csu_init>:
  400580: 41 57 push   r15
  400582: 41 89 ff mov r15d,edi
  400585: 41 56 push   r14
  400587: 49 89 f6 mov r14,rsi
  40058a: 41 55 push   r13
  40058c: 49 89 d5 mov r13,rdx
  40058f: 41 54 push   r12
  400591: 4c 8d 25 78 08 20 00 lea r12,[rip+0x200878] # 600e10 <__frame_dummy_init_array_entry>
  400598: 55 push   rbp
  400599: 48 8d 2d 78 08 20 00 lea rbp,[rip+0x200878] # 600e18 <__init_array_end>
  4005a0: 53 push   rbx
  4005a1: 4c 29 e5 sub rbp,r12
  4005a4: 31 db xor ebx,ebx
  4005a6: 48 c1 fd 03 sar rbp,0x3
  4005aa: 48 83 ec 08 sub rsp,0x8
  4005ae: e8 15 fe ff ff call   4003c8 <_init>
  4005b3: 48 85 ed test   rbp,rbp
  4005b6: 74 1e je 4005d6 <__libc_csu_init+0x56>
  4005b8: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0]
  4005bf: 00
  4005c0: 4c 89 ea mov rdx,r13
  4005c3: 4c 89 f6 mov rsi,r14
  4005c6: 44 89 ff mov edi,r15d
  4005c9: 41 ff 14 dc call   QWORD PTR [r12+rbx*8]
  4005cd: 48 83 c3 01 add rbx,0x1
  4005d1: 48 39 eb cmp rbx,rbp
  4005d4: 75 ea jne 4005c0 <__libc_csu_init+0x40>
  4005d6: 48 83 c4 08 add rsp,0x8
  4005da: 5b pop rbx
  4005db: 5d pop rbp
  4005dc: 41 5c pop r12
  4005de: 41 5d pop r13
  4005e0: 41 5e pop r14
  4005e2: 41 5f pop r15
  4005e4: c3 ret
  4005e5: 90 nop
  4005e6: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
  4005ed: 00 00 00

00000000004005f0 <__libc_csu_fini>:
  4005f0: f3 c3 repz ret

Disassembly of section .fini:

00000000004005f4 <_fini>:
  4005f4: 48 83 ec 08 sub rsp,0x8
  4005f8: 48 83 c4 08 add rsp,0x8
  4005fc: c3 ret

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222

可见,链接(Link) 不仅仅是单纯的将多个 Object 文件拼凑起来而已,而是将程序真正的转换为一个可以在操作系统上执行的文件格式,且这个文件中还包含了整个程序所有 Object 文件的内容。在 Linux 上,这个文件格式就是 ELF(Execuatable and Linkable File Format,可执行与可链接文件格式)。

ELF 文件

ELF 文件格式:是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。ELF 文件由 4 部分组成,分别是 ELF header、程序头表(Program Header Table)、节(Section)和节头表(Section Header Table)。
在这里插入图片描述

位于 ELF Header 和 Section Header Table 之间的都是段(Section)。一个典型的 ELF 文件包含下面几个段:

  • .text:已编译程序的指令代码段。
  • .rodata:即只读数据(譬如常数 const)。
  • .data:已初始化的C程序全局变量和静态局部变量。
  • .bss:未初始化的C程序全局变量和静态局部变量。
  • .debug:调试符号表,调试器用此段的信息帮助调试。

在这里插入图片描述
可以使用 readelf -S 查看其各个 section 的信息如下:

$ readelf -S hello
There are 31 section headers, starting at offset 0x19d8:

Section Headers:
  [Nr] Name Type Address Offset Size EntSize Flags  Link  Info  Align
  [ 0] NULL 0000000000000000  00000000 0000000000000000  0000000000000000 0 0 0
……
  [11] .init PROGBITS 00000000004003c8  000003c8 000000000000001a  0000000000000000  AX 0 0 4
……
  [14] .text PROGBITS 0000000000400430  00000430 0000000000000182  0000000000000000  AX 0 0 16
  [15] .fini PROGBITS 00000000004005b4  000005b4
……

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在链接器把程序转换为 ELF 格式的可执行文件之后,装载器再去处理就会容易得多。因为装载器不再需要考虑地址跳转的问题,只需要解析 ELF 文件,把对应的指令和数据加载到内存里面供 CPU 执行就可以了。

同样,Windows 也有自己的可执行文件格式 PE(Portable Executable Format)。因为 Linux 和 Windows 的可执行文件格式不同,所以也就不能够 “一次编译,跨平台执行” 了。那么换句话说:是不是只要在 Linux 上运行可以解析 PE 文件的装载器就可以解决这个问题呢?答案是肯定的,Linux 著名的开源软件 Wine 正是此类装载器,国内很多 Linux Desktop 发行版都是基于 Wine 实现了 Windows 常用软件的移植。

反汇编 ELF 文件

由于 ELF 文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF 文件包含的指令和数据,需要使用反汇编的方法。

使用 objdump -D 对其进行反汇编如下:

$ objdump -D hello
……
0000000000400526 <main>:  // main标签的PC地址
//PC地址:指令编码 指令的汇编格式
  400526: 55 push   %rbp
  400527: 48 89 e5 mov %rsp,%rbp
  40052a: bf c4 05 40 00 mov $0x4005c4,%edi
  40052f: e8 cc fe ff ff callq  400400 <puts@plt>
  400534: b8 00 00 00 00 mov $0x0,%eax
  400539: 5d pop %rbp
  40053a: c3 retq 40053b: 0f 1f 44 00 00 nopl   0x0(%rax,%rax,1)
……

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

使用 objdump -S 将其反汇编并且将其 C 语言源代码混合显示出来:

# 要加上 -g 选项

$ gcc -o hello -g hello.c 
$ objdump -S hello
……
0000000000400526 <main>:
#include <stdio.h>

int
main(void)
{
  400526: 55 push   %rbp
  400527: 48 89 e5 mov %rsp,%rbp
  printf("Hello World!" "\n");
  40052a: bf c4 05 40 00 mov $0x4005c4,%edi
  40052f: e8 cc fe ff ff callq  400400 <puts@plt>
  return 0;
  400534: b8 00 00 00 00 mov $0x0,%eax
}
  400539: 5d pop %rbp
  40053a: c3 retq 40053b: 
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

文章来源: is-cloud.blog.csdn.net,作者:范桂飓,版权归原作者所有,如需转载,请联系作者。

原文链接:is-cloud.blog.csdn.net/article/details/106912598

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。