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

目录

文章目录

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;
}
  • 主函数
// 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-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

可见,链接(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
……

在链接器把程序转换为 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)
……

使用 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:   

猜你喜欢

转载自blog.csdn.net/Jmilk/article/details/106912598
今日推荐