C 语言编程 — 程序的装载与运行
目录
文章目录
- 目录
- 文章目录
- C 程序在操作系统中的装载与运行
- ELF 文件
- 反汇编 ELF 文件
文章目录
《C 语言编程 — GCC 工具链》
《C 语言编程 — 程序的编译流程》
《C 语言编程 — 静态库、动态库和共享库》
《C 语言编程 — 程序的装载与运行》
《计算机组成原理 — 指令系统》
《C 语言编程 — 结构化程序流的汇编代码与 CPU 指令集》
C 程序在操作系统中的装载与运行
一个程序在操作系统上运行需要经历以下阶段:
第一阶段:得到可执行文件
- 编译(Compile)
- 汇编(Assemble)
- 链接(Link)
第二阶段:装载运行
- 装载器(Loader)将可执行文件载入到内存
- CPU 从内存中可执行文件的程序入口开始读取指令和数据,开始真正执行程序。
编译和汇编的过程在上文中已经提到了,下面再继续介绍链接的过程。
- 子程序
// add_lib.cint add(int a, int b)
{return a+b;
}
- 主函数
// link_example.c#include <stdio.h>
int main()
{int a = 10;int b = 5;int c = add(a, b);printf("c = %d\n", c);
}
- 编译 C 程序得到 Object 文件
$ gcc -g -c add_lib.c link_example.c
- 链接上述两个 Object 文件得到一个可执行文件
$ gcc -o link-example add_lib.o link_example.o$ ./link-example
c = 15
区别于 Object 文件,真正的可执行文件的内容如下:
$ objdump -d -M intel -S link-examplelink-example: file format elf64-x86-64Disassembly of section .init:00000000004003c8 <_init>:4003c8: 48 83 ec 08 sub rsp,0x84003cc: 48 8b 05 25 0c 20 00 mov rax,QWORD PTR [rip+0x200c25] # 600ff8 <__gmon_start__>4003d3: 48 85 c0 test rax,rax4003d6: 74 05 je 4003dd <_init+0x15>4003d8: e8 43 00 00 00 call 400420 <.plt.got>4003dd: 48 83 c4 08 add rsp,0x84003e1: c3 retDisassembly 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 0x040040b: 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 0x140041b: 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,axDisassembly of section .text:0000000000400430 <_start>:400430: 31 ed xor ebp,ebp400432: 49 89 d1 mov r9,rdx400435: 5e pop rsi400436: 48 89 e2 mov rdx,rsp400439: 48 83 e4 f0 and rsp,0xfffffffffffffff040043d: 50 push rax40043e: 54 push rsp40043f: 49 c7 c0 f0 05 40 00 mov r8,0x4005f0400446: 48 c7 c1 80 05 40 00 mov rcx,0x40058040044d: 48 c7 c7 31 05 40 00 mov rdi,0x400531400454: e8 b7 ff ff ff call 400410 <__libc_start_main@plt>400459: f4 hlt40045a: 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,0x601037400465: 55 push rbp400466: 48 2d 30 10 60 00 sub rax,0x60103040046c: 48 83 f8 0e cmp rax,0xe400470: 48 89 e5 mov rbp,rsp400473: 77 02 ja 400477 <deregister_tm_clones+0x17>400475: 5d pop rbp400476: c3 ret400477: b8 00 00 00 00 mov eax,0x040047c: 48 85 c0 test rax,rax40047f: 74 f4 je 400475 <deregister_tm_clones+0x15>400481: 5d pop rbp400482: bf 30 10 60 00 mov edi,0x601030400487: ff e0 jmp rax400489: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0]0000000000400490 <register_tm_clones>:400490: b8 30 10 60 00 mov eax,0x601030400495: 55 push rbp400496: 48 2d 30 10 60 00 sub rax,0x60103040049c: 48 c1 f8 03 sar rax,0x34004a0: 48 89 e5 mov rbp,rsp4004a3: 48 89 c2 mov rdx,rax4004a6: 48 c1 ea 3f shr rdx,0x3f4004aa: 48 01 d0 add rax,rdx4004ad: 48 d1 f8 sar rax,14004b0: 75 02 jne 4004b4 <register_tm_clones+0x24>4004b2: 5d pop rbp4004b3: c3 ret4004b4: ba 00 00 00 00 mov edx,0x04004b9: 48 85 d2 test rdx,rdx4004bc: 74 f4 je 4004b2 <register_tm_clones+0x22>4004be: 5d pop rbp4004bf: 48 89 c6 mov rsi,rax4004c2: bf 30 10 60 00 mov edi,0x6010304004c7: ff e2 jmp rdx4004c9: 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 rbp4004da: 48 89 e5 mov rbp,rsp4004dd: e8 7e ff ff ff call 400460 <deregister_tm_clones>4004e2: 5d pop rbp4004e3: c6 05 42 0b 20 00 01 mov BYTE PTR [rip+0x200b42],0x1 # 60102c <_edata>4004ea: f3 c3 repz ret4004ec: 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: 004004f8: 74 1e je 400518 <frame_dummy+0x28>4004fa: b8 00 00 00 00 mov eax,0x04004ff: 48 85 c0 test rax,rax400502: 74 14 je 400518 <frame_dummy+0x28>400504: 55 push rbp400505: bf 20 0e 60 00 mov edi,0x600e2040050a: 48 89 e5 mov rbp,rsp40050d: ff d0 call rax40050f: 5d pop rbp400510: 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.cint add(int a, int b)
{40051d: 55 push rbp40051e: 48 89 e5 mov rbp,rsp400521: 89 7d fc mov DWORD PTR [rbp-0x4],edi400524: 89 75 f8 mov DWORD PTR [rbp-0x8],esireturn 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 rbp400530: c3 ret0000000000400531 <main>:
// link_example.c#include <stdio.h>
int main()
{400531: 55 push rbp400532: 48 89 e5 mov rbp,rsp400535: 48 83 ec 10 sub rsp,0x10int a = 10;400539: c7 45 fc 0a 00 00 00 mov DWORD PTR [rbp-0x4],0xaint b = 5;400540: c7 45 f8 05 00 00 00 mov DWORD PTR [rbp-0x8],0x5int 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,edx40054f: 89 c7 mov edi,eax400551: b8 00 00 00 00 mov eax,0x0400556: e8 c2 ff ff ff call 40051d <add>40055b: 89 45 f4 mov DWORD PTR [rbp-0xc],eaxprintf("c = %d\n", c);40055e: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]400561: 89 c6 mov esi,eax400563: bf 10 06 40 00 mov edi,0x400610400568: b8 00 00 00 00 mov eax,0x040056d: e8 8e fe ff ff call 400400 <printf@plt>
}400572: c9 leave400573: c3 ret400574: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]40057b: 00 00 0040057e: 66 90 xchg ax,ax0000000000400580 <__libc_csu_init>:400580: 41 57 push r15400582: 41 89 ff mov r15d,edi400585: 41 56 push r14400587: 49 89 f6 mov r14,rsi40058a: 41 55 push r1340058c: 49 89 d5 mov r13,rdx40058f: 41 54 push r12400591: 4c 8d 25 78 08 20 00 lea r12,[rip+0x200878] # 600e10 <__frame_dummy_init_array_entry>400598: 55 push rbp400599: 48 8d 2d 78 08 20 00 lea rbp,[rip+0x200878] # 600e18 <__init_array_end>4005a0: 53 push rbx4005a1: 4c 29 e5 sub rbp,r124005a4: 31 db xor ebx,ebx4005a6: 48 c1 fd 03 sar rbp,0x34005aa: 48 83 ec 08 sub rsp,0x84005ae: e8 15 fe ff ff call 4003c8 <_init>4005b3: 48 85 ed test rbp,rbp4005b6: 74 1e je 4005d6 <__libc_csu_init+0x56>4005b8: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0]4005bf: 004005c0: 4c 89 ea mov rdx,r134005c3: 4c 89 f6 mov rsi,r144005c6: 44 89 ff mov edi,r15d4005c9: 41 ff 14 dc call QWORD PTR [r12+rbx*8]4005cd: 48 83 c3 01 add rbx,0x14005d1: 48 39 eb cmp rbx,rbp4005d4: 75 ea jne 4005c0 <__libc_csu_init+0x40>4005d6: 48 83 c4 08 add rsp,0x84005da: 5b pop rbx4005db: 5d pop rbp4005dc: 41 5c pop r124005de: 41 5d pop r134005e0: 41 5e pop r144005e2: 41 5f pop r154005e4: c3 ret4005e5: 90 nop4005e6: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]4005ed: 00 00 0000000000004005f0 <__libc_csu_fini>:4005f0: f3 c3 repz retDisassembly of section .fini:00000000004005f4 <_fini>:4005f4: 48 83 ec 08 sub rsp,0x84005f8: 48 83 c4 08 add rsp,0x84005fc: c3 ret
可见,链接(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 OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0
……[11] .init PROGBITS 00000000004003c8 000003c8000000000000001a 0000000000000000 AX 0 0 4
……[14] .text PROGBITS 0000000000400430 000004300000000000000182 0000000000000000 AX 0 0 16[15] .fini PROGBITS 00000000004005b4 000005b4
……
在链接器把程序转换为 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 %rbp400527: 48 89 e5 mov %rsp,%rbp40052a: bf c4 05 40 00 mov $0x4005c4,%edi40052f: e8 cc fe ff ff callq 400400 <puts@plt>400534: b8 00 00 00 00 mov $0x0,%eax400539: 5d pop %rbp40053a: c3 retq 40053b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
……
使用 objdump -S 将其反汇编并且将其 C 语言源代码混合显示出来:
# 要加上 -g 选项$ gcc -o hello -g hello.c
$ objdump -S hello
……
0000000000400526 <main>:
#include <stdio.h>int
main(void)
{400526: 55 push %rbp400527: 48 89 e5 mov %rsp,%rbpprintf("Hello World!" "\n");40052a: bf c4 05 40 00 mov $0x4005c4,%edi40052f: e8 cc fe ff ff callq 400400 <puts@plt>return 0;400534: b8 00 00 00 00 mov $0x0,%eax
}400539: 5d pop %rbp40053a: c3 retq 40053b:
C 语言编程 — 程序的装载与运行相关推荐
- C 语言编程 — 程序的编译流程
目录 文章目录 目录 文章目录 C 程序的编译流程 预处理 编译 汇编 链接 编译多个源文件 文章目录 <C 语言编程 - GCC 工具链> <C 语言编程 - 程序的编译流程> ...
- C 语言编程 — 程序编译原理
目录 文章目录 目录 语言的本质 编译器的工作原理 词法分析 语法分析 语义分析 GCC 编译器套件 常用的指令选项 常见的文件类型 C 程序的编译流程 1.预处理(Preprocessing) 2. ...
- 零基础学习PHP编程——程序的编写和运行过程
零基础学习PHP编程--程序的编写和运行过程 注意: 本文主要写给零基础的同学,作为编程的入门引导, 如有不当之处,还请指正. 访问源站 欢迎交流QQ群: 640765823 回顾上一节,我们已经基本 ...
- 2n 用c语言编程程序,用C语言编写程序.ppt
<用C语言编写程序.ppt>由会员分享,可在线阅读,更多相关<用C语言编写程序.ppt(64页珍藏版)>请在人人文库网上搜索. 1.第2章 用C语言编写程序,2.1 在屏幕上显 ...
- Go 语言编程 — 程序运行环境
目录 文章目录 目录 安装 Golang 下载地址 CentOS 环境 MAC pro 环境 Go proxy GOPATH 环境变量 安装 Golang 下载地址 https://golang.or ...
- 数据结构c语言编程程序,图书管理程序(数据结构c语言实现增删改查)
本人用C语言编写的第一个完整小程序实现图书的借阅管理,不完美之处欢迎交流! PS:我很菜.QQ:997459445 #include #include #include #include #inclu ...
- Go 语言编程 — 程序结构
目录 文章目录 目录 Hello World 程序结构 包声明 导入包 函数 标识符 关键字 语句 表达式 注释 Hello World package mainimport "fmt&qu ...
- casio pb-700简单使用basic语言编程并编译执行(运行)(希望能给你提供便利)
闲鱼上淘宝casio pb-700(1983年的计算机),铁路系统版本,果断收藏. 花时间,清除了该清除的,恢复了casio pb-700原来的样子. 是否可以写个basic程序,运行一下? 程序敲完 ...
- 求[100,9999]区间的超级素数,c语言编程,程序中含数组,2010计算机等级考试二级C语言预测题...
99 编程序求出1到5000之间的能被7整除的前若干个数之和,当和大于1500时退出并输出结果. 1617 100 "水仙花数"是指这样的数,其各位数字的立方和等于该数本身,如: ...
最新文章
- 以Java的视角来聊聊BIO、NIO与AIO的区别
- 为你的程序添加监听器
- 从思维导图学习操作系统(三)
- 三件套都有什么_床上用品三件套、四件套、21件套都各指什么啊?
- 容联雷辉:视频系统由标清进入到移动高清时代
- Python多线程3:queue
- C、C++和C#区别概述
- 蚂蚁式管理(Style of Ant Management)
- c#split方法拆分为数据_【转载】C#使用Split函数根据特定分隔符分割字符串
- c++右值引用以及使用
- Macaron的注入struct
- 【转】fatal error C1010: unexpected end of file解决方案
- JSP基础--J2EE赢在起跑线
- 【生信进阶练习1000days】day5-TxDb等注释包的使用
- 2021-07-22-第一次实训(HTML+CSS+JS)
- cad快捷栏怎么调出来_cad左边工具栏不见了怎么办|cad工具栏怎么调出来_PC6教学...
- python基础第二章:流程控制
- 扫一扫就可一键叫车 杭州暖心车站让老人去往生活里的远处
- 计算机教室 设备负荷,设备建设标准和规范教室线缆布线.ppt
- oracle fnd global,Oracle EBS fnd_request.submit_request 与 Fnd_concurrent.wait_for_ruqest
热门文章
- 编译包含Google Play服务App的SDK版本问题
- anki 新的卡片类型_梁宝川:这一类型Anki卡片,你做了吗?
- html css 表格自动高度,html – 表格单元格(IE)中的Textarea CSS {height:100%}
- VR/AR标准委员会成立,宣布全新的标准OpenXR
- java对象头_浅谈java对象结构 对象头 Markword
- 韦布望远镜睁眼看到第一缕星光,镜面校准进行时
- Go语言11岁了,网友:他喵的,终于确定出「泛型」了
- 137% YOLOv3加速、10倍搜索性能提升!这样的惊喜,最新版PaddleSlim有10个
- 想让论文能发表,应该星期几投稿?丨SCI研究
- 谷歌二季度净利同比增211%,英特尔降17%,两个公司盘后股价都大涨