在 C 中,main() 方法最初是如何调用的? [英] In C, how is the main() method initially called?

查看:5
本文介绍了在 C 中,main() 方法最初是如何调用的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何开始 C 程序?

推荐答案

操作系统调用main()函数.最终.

The operating system calls the main() function. Eventually.

可执行和可链接格式 (ELF) 许多 Unix 操作系统的使用定义了一个入口点地址和一个 INIT 地址.这是操作系统完成其 exec() 调用后程序开始运行的地方.在 Linux 系统上,这是 .init 部分中的 _init.之后返回它跳转到入口点地址,即 .text 部分中的 _start.

The Executable and Linkable Format (ELF) which many Unix OS's use defines an entry point address and an INIT address. That is where the program begins to run after the OS finishes its exec() call. On a Linux system this is _init in the .init section. After that returns it jumps to the entry point address which is _start in the .text section.

C 编译器将标准库链接到每个提供这些操作系统定义的初始化和入口点的应用程序.然后该库调用 main().

The C compiler links a standard library to every application which provides these operating system defined initialization and entry points. That library then calls main().

这是我的示例的 C 源代码:

Here is my C source code for the example:

#include <stdio.h>

int main() {
  puts("Hello world!");
  return 0;
}

来自objdump -d:

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 <__gmon_start__>
    100f:   48 85 c0                test   %rax,%rax
    1012:   74 02                   je     1016 <_init+0x16>
    1014:   ff d0                   callq  *%rax
    1016:   48 83 c4 08             add    $0x8,%rsp
    101a:   c3                      retq   

Disassembly of section .text:

0000000000001060 <_start>:
    1060:   f3 0f 1e fa             endbr64 
    1064:   31 ed                   xor    %ebp,%ebp
    1066:   49 89 d1                mov    %rdx,%r9
    1069:   5e                      pop    %rsi
    106a:   48 89 e2                mov    %rsp,%rdx
    106d:   48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
    1071:   50                      push   %rax
    1072:   54                      push   %rsp
    1073:   4c 8d 05 66 01 00 00    lea    0x166(%rip),%r8        # 11e0 <__libc_csu_fini>
    107a:   48 8d 0d ef 00 00 00    lea    0xef(%rip),%rcx        # 1170 <__libc_csu_init>
    1081:   48 8d 3d c1 00 00 00    lea    0xc1(%rip),%rdi        # 1149 <main>
    1088:   ff 15 52 2f 00 00       callq  *0x2f52(%rip)          # 3fe0 <__libc_start_main@GLIBC_2.2.5>
    108e:   f4                      hlt    
    108f:   90                      nop

0000000000001140 <frame_dummy>:
    1140:   f3 0f 1e fa             endbr64 
    1144:   e9 77 ff ff ff          jmpq   10c0 <register_tm_clones>

readelf -h可以看到匹配_start的Entry point地址:

From readelf -h you can see the Entry point address that matches _start:

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:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1060
  Start of program headers:          64 (bytes into file)
  Start of section headers:          17416 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         36
  Section header string table index: 35

来自readelf -d:

Dynamic section at offset 0x2dc8 contains 27 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x1000
 0x000000000000000d (FINI)               0x11e8
 0x0000000000000019 (INIT_ARRAY)         0x3db8
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x3dc0
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x3a0
 0x0000000000000005 (STRTAB)             0x470
 0x0000000000000006 (SYMTAB)             0x3c8
 0x000000000000000a (STRSZ)              130 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x3fb8
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x5e0
 0x0000000000000007 (RELA)               0x520
 0x0000000000000008 (RELASZ)             192 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000000000001e (FLAGS)              BIND_NOW
 0x000000006ffffffb (FLAGS_1)            Flags: NOW PIE
 0x000000006ffffffe (VERNEED)            0x500
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x4f2
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0

可以看到INIT等于_init的地址.

You can see that INIT is equal to the address of _init.

在 INIT_ARRAY 中还有一个完整的函数指针数组.见objdump -s -j .init_array c-test:

There is a whole array of function pointers in INIT_ARRAY also. See objdump -s -j .init_array c-test:

c-test:     file format elf64-x86-64

Contents of section .init_array:
 3db8 40110000 00000000                    @.......        

可以看到地址 0x3db8 与 ELF 头中的 INIT_ARRAY 相同.

You can see that address 0x3db8 is the same as INIT_ARRAY in the ELF header.

地址 0x1140(记住 40110000 中的小端字节布局)是您可以在反汇编中看到的函数 frame_dummy.然后调用 register_tm_clones 并且谁知道还有什么.

The address 0x1140 (remember little-endian byte layout from 40110000) is the function frame_dummy you can see in the disassembly. Which then calls register_tm_clones and who knows what else.

初始化代码位于一组名为 crtbegin.o 和 crtend.o(以及这些名称的变体)的文件中.__libc_start_main 函数在 libc.so.6 中定义.这些库是 GCC 的一部分.该代码执行 C 程序所需的各种操作,例如设置标准输入、标准输出、全局和静态变量等.

The code for initialization is in a set of files named crtbegin.o and crtend.o (and variants of those names). The __libc_start_main function is defined in libc.so.6. These libraries are part of GCC. That code does various things necessary for a C program like setting up stdin, stdout, global and static variables and other things.

以下文章很好地描述了它在 Linux 中的作用(取自以下投票较少的答案):http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html

The following article describes quite well what it does in Linux (taken from an answer below with less votes): http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html

我相信其他人的回答已经描述了 Windows 的功能.

I believe someone else's answer already described what Windows does.

这篇关于在 C 中,main() 方法最初是如何调用的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆