Wie wird in C die Methode main() anfänglich aufgerufen?

Lesezeit: 7 Minuten

Benutzeravatar von user418627
Benutzer418627

Wie wird ein C-Programm gestartet?

  • Sie machen! Du rufst die main()! Ja, das ist richtig! Ja, das tun Sie!

    – rekursiv

    12. August 2010 um 16:41 Uhr

  • Wer beobachtet die Wächter?

    – Kaskabel

    12. August 2010 um 16:41 Uhr

  • Das Subjekt ist eine Aussage, aber der Körper ist eine Frage.

    – Brian R. Bondy

    12. August 2010 um 16:43 Uhr

  • Warum, Justin, warum? Es war Poesie!

    – jball

    12. August 2010 um 17:21 Uhr

  • Wow, die Art und Weise, wie sich diese Frage entwickelt, erinnert mich an “Wie bewege ich die Schildkröte in LOGO?”

    – Brian

    12. August 2010 um 17:53 Uhr

Benutzeravatar von Zan Lynx
Zan Luchs

Das Betriebssystem ruft die main() Funktion. Letztlich.

Das Ausführbares und verlinkbares Format (ELF) die viele Unix-Betriebssysteme verwenden, definiert eine Einstiegspunktadresse und eine INIT-Adresse. Dort beginnt das Programm zu laufen, nachdem das Betriebssystem seine Arbeit beendet hat exec() Anruf. Auf einem Linux-System ist dies der Fall _init in dem .init Sektion. Danach springt es zur Einstiegspunktadresse, die ist _start in dem .text Sektion.

Der C-Compiler verknüpft eine Standardbibliothek mit jeder Anwendung, die diese vom Betriebssystem definierten Initialisierungs- und Einstiegspunkte bereitstellt. Diese Bibliothek ruft dann an main().

Hier ist mein C-Quellcode für das Beispiel:

#include <stdio.h>

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

Aus 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 <[email protected]_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>

Aus readelf -h Sie können die übereinstimmende Einstiegspunktadresse sehen _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

Aus 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

Sie können sehen, dass INIT gleich der Adresse von ist _init.

Es gibt auch eine ganze Reihe von Funktionszeigern in INIT_ARRAY. Sehen objdump -s -j .init_array c-test:

c-test:     file format elf64-x86-64

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

Sie können sehen, dass die Adresse 0x3db8 mit INIT_ARRAY im ELF-Header identisch ist.

Die Adresse 0x1140 (erinnern Sie sich an das Little-Endian-Byte-Layout von 40110000) ist die Funktion frame_dummy Sie können in der Demontage sehen. Was dann anruft register_tm_clones und wer weiß was noch.

Der Code für die Initialisierung befindet sich in einer Reihe von Dateien mit den Namen crtbegin.o und crtend.o (und Varianten dieser Namen). Das __libc_start_main Funktion ist in libc.so.6 definiert. Diese Bibliotheken sind Teil von GCC. Dieser Code erledigt verschiedene Dinge, die für ein C-Programm erforderlich sind, wie das Einrichten von stdin, stdout, globalen und statischen Variablen und anderen Dingen.

Der folgende Artikel beschreibt ziemlich gut, was es unter Linux tut (aus einer Antwort unten mit weniger Stimmen entnommen): http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html

Ich glaube, die Antwort eines anderen hat bereits beschrieben, was Windows tut.

  • es ruft nicht _init oder irgend ein anderer. es ruft die Einstiegspunktadresse auf. es kann überall sein.

    – Andrej

    12. August 2010 um 16:44 Uhr

  • BENÖTIGT libstdc++.so.6? Dies ist nicht aus einem C-Programm!

    – Jens

    3. Dezember 2012 um 15:54 Uhr

  • @Jens: In diesem Fall macht es keinen Unterschied.

    – Zan Luchs

    3. Dezember 2012 um 17:51 Uhr

  • @ZanLynx Woher weißt du das? Wie kann ich sagen? Warum nicht einfach ein C-Programm anschauen? C und C++ sind sehr unterschiedliche Sprachen, und der Programmstart ist einer der Bereiche, in denen dies sichtbar ist (z. B. beim Aufrufen von Konstruktoren).

    – Jens

    4. Dezember 2012 um 9:19 Uhr

  • @Jens: Wie kann Sie Sag es, da du mir nicht vertraust? Verwenden Sie den objdump -d Disassembler, um sich den Maschinencode anzusehen, und verwenden Sie readelf -d, um das ELF-Zeug anzuzeigen. Oder schnappen Sie sich einen Hex-Editor und eine Kopie der ELF-Spezifikation.

    – Zan Luchs

    5. Dezember 2012 um 7:24 Uhr

Benutzeravatar von Andrey
Andrej

Schließlich ist es Betriebssystem. Normalerweise gibt es zwischen realem Einstiegspunkt und Hauptfunktion ein Medium, das eingefügt wird Compiler Linker.

Einige Details (in Bezug auf Windows): Es gibt einen Header in der PE-Datei mit dem Namen IMAGE_OPTIONAL_HEADER die das Feld hat AddressOfEntryPointdie wiederum die Adresse des ersten Codebytes in der auszuführenden Datei ist.

  • Es wird nicht vom Compiler eingefügt, sondern vom Linker, normalerweise durch Verlinken auf etwas wie crt.a(crt.o) oder crt.lib(crt.obj)was normalerweise Teil von so etwas ist libc.a oder c.lib.

    – Christian Hüjer

    11. August 2015 um 16:11 Uhr

  • @ChristianHujer du hast recht, mit Compiler meinte ich gut die Toolchain.

    – Andrej

    11. August 2015 um 16:15 Uhr

  • Sie sicher Linkerich denke mal was Lader tut dann?

    – Wurzelreisender

    11. September 2017 um 11:23 Uhr


  • @roottraveller Linker-Links. Die ausführbare Datei wird möglicherweise sogar nie geladen.

    – Andrej

    11. September 2017 um 14:23 Uhr

http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2008-04/msg04617.html

  • Ich weiß nicht, warum dies abgelehnt wurde, der Link enthält gute Informationen

    – zol

    12. August 2010 um 18:04 Uhr

  • @Zack Im Allgemeinen ist es verpönt, hier einen Link ohne irgendeine Zusammenfassung zu posten

    – Michael Mrozek

    13. August 2010 um 15:10 Uhr

Das Betriebssystem ruft main auf. Es wird eine Adresse in der verschiebbaren ausführbaren Datei geben, die auf den Speicherort von main zeigt (weitere Informationen finden Sie in der Unix-ABI).

Aberwer ruft das Betriebssystem?

Die zentrale Verarbeitungseinheit beginnt bei dem “RESET”-Signal (das auch beim Einschalten geltend gemacht wird) damit, in irgendeinem ROM an einer gegebenen Adresse (z. B. 0xffff) nach seinen Anweisungen zu suchen.

Normalerweise gibt es eine Art Sprungbefehl zum BIOS, der die Speicherchips konfiguriert, die grundlegenden Festplattentreiber lädt usw. usw. Dann wird der Boot-Sektor der Festplatte gelesen und die nächste Bootloader wird gestartet, der die Datei lädt, die die grundlegenden Informationen enthält, wie man beispielsweise eine NTFS-Partition liest und wie man die Kernel-Datei selbst liest. Die Kernel-Umgebung wird eingerichtet, der Kernel geladen und dann – und dann! – Der Kernel wird zur Ausführung angesprungen.

Nachdem all diese harte Arbeit erledigt ist, kann der Kernel dann damit fortfahren, unsere Software zu laden.

Das Betriebssystem ruft eine Funktion auf, die in der C-Laufzeit (CRT) enthalten und mit Ihrer ausführbaren Datei verknüpft ist. Nennen Sie dies “CRT-Hauptleitung”.

CRT main macht ein paar Dinge, von denen die beiden wichtigsten, zumindest in C++, darin bestehen, ein Array globaler C++-Klassen zu durchlaufen und ihre Konstruktoren aufzurufen und Ihre main()-Funktion aufzurufen und ihren Rückgabewert an die Shell zu übergeben .

Der CRT-Hauptbildschirm von Visual C++ macht noch ein paar Dinge, wenn der Speicher reicht. Es konfiguriert den Speicherzuordner, was wichtig ist, wenn die Debug-CRT verwendet wird, um Speicherlecks oder fehlerhafte Zugriffe zu finden. Es ruft auch main innerhalb von a auf strukturierte Ausnahme Handler, der fehlerhaften Speicherzugriff und andere Abstürze abfängt und anzeigt.

  • Auch wenn Sie von C++ und nicht von C sprechen, ist Ihr Beitrag aufschlussreich!

    – Gab是好人

    9. Dezember 2016 um 18:25 Uhr

Brians Benutzeravatar
Brian

Beachten Sie, dass Sie zusätzlich zu den bereits geposteten Antworten auch anrufen können main dich selbst. Im Allgemeinen ist dies eine schlechte Idee, die für verschleierten Code reserviert ist.

  • Auch wenn Sie von C++ und nicht von C sprechen, ist Ihr Beitrag aufschlussreich!

    – Gab是好人

    9. Dezember 2016 um 18:25 Uhr

Benutzeravatar von Nandan Bharadwaj
Nandan Bharadwaj

Die wahrscheinlich besten Informationen zu Ihrer Frage finden Sie unter dem unten genannten Link
http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.htmldas Beste, was mir bis jetzt untergekommen ist.

  • Das ist großartig! Danke vielmals!

    – Gab是好人

    9. Dezember 2016 um 18:26 Uhr

1412910cookie-checkWie wird in C die Methode main() anfänglich aufgerufen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy