Skompilować i uruchomić program bez main() w C

Próbuję skompilować i uruchomić następujący program bez main() funkcji w C. Skompilowałem mój program używając następującego polecenia.

gcc -nostartfiles nomain.c

I kompilator daje ostrzeżenie

/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340
Nie ma sprawy. następnie uruchomiłem plik wykonywalny (a. out), obie printf instrukcje druk pomyślnie, a następnie uzyskać segmentation fault.

Moje pytanie brzmi: Dlaczego błąd segmentacji po pomyślnym wykonaniu instrukcji print?

Mój kod:

#include <stdio.h>

void nomain()
{
        printf("Hello World...\n");
        printf("Successfully run without main...\n");
}

Wyjście:

Hello World...
Successfully run without main...
Segmentation fault (core dumped)

Uwaga:

Tutaj, -nostartfiles flaga gcc uniemożliwia kompilatorowi używanie standardowych plików startowych podczas łączenia

Author: rsp, 2017-02-19

2 answers

Spójrzmy na wygenerowany assembly Twojego programu:

.LC0:
        .string "Hello World..."
.LC1:
        .string "Successfully run without main..."
nomain:
        push    rbp
        mov     rbp, rsp
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        mov     edi, OFFSET FLAT:.LC1
        call    puts
        nop
        pop     rbp
        ret

Zwróć uwagę na ret oświadczenie. Punkt wejścia Twojego programu jest określony jako nomain, wszystko jest w porządku. Ale gdy funkcja powróci, próbuje wskoczyć na adres na stosie połączeń... to nie jest zaludnione. To jest nielegalny dostęp i wynika z tego błąd segmentacji.

Szybkim rozwiązaniem byłoby wywołanie exit() na końcu programu (i zakładając C11 możemy równie dobrze oznaczyć funkcja jako _Noreturn):

#include <stdio.h>
#include <stdlib.h>

_Noreturn void nomain(void)
{
    printf("Hello World...\n");
    printf("Successfully run without main...\n");
    exit(0);
}

W rzeczywistości, teraz twoja funkcja zachowuje się prawie jak zwykła funkcja main, ponieważ po powrocie z main, Funkcja exit jest wywoływana z wartością zwracaną przez main.

 120
Author: StoryTeller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-20 18:25:48

W C, gdy funkcje / podprogramy są wywoływane, stos jest wypełniany w kolejności:

  1. argumenty,
  2. adres zwrotny,
  3. zmienne lokalne, -- > top of the stack

Main () jako punkt startowy, Elf konstruuje program w taki sposób, że instrukcje, które pojawią się pierwsze, zostaną wypchnięte jako pierwsze, w tym przypadku printf.

Teraz program jest jakby okrojony bez return-address lub {[0] } i zakłada, że cokolwiek jest na stosie w tej lokalizacji (__end__) jest adresem zwrotnym, ale niestety nie jest i dlatego się zawiesza.

 20
Author: Milind Deore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-19 17:54:16