Скомпилировать и запустить программу без main() в C
Я пытаюсь скомпилировать и запустить следующую программу без функции main()
в C
. Я скомпилировал свою программу, используя следующую команду.
gcc -nostartfiles nomain.c
И компилятор дает предупреждение
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340
Хорошо, проблем нет. то я запускаю исполняемый файл (a.out), оба оператора printf
печатаются успешно, а затем получают segmentation fault.
Итак, мой вопрос: Почему ошибка сегментации после успешного выполнения операторов печати?
мой код:
#include <stdio.h>
void nomain()
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
}
выход:
Hello World...
Successfully run without main...
Segmentation fault (core dumped)
Примечание:
Здесь флаг -nostartfiles
gcc запрещает компилятору использовать стандартные файлы запуска при связывании
Ответы
Ответ 1
Посмотрим на созданную сборку вашей программы:
.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
Обратите внимание на инструкцию ret
. Ваша точка входа в программу определяется как nomain
, все в порядке. Но как только функция возвращается, она пытается перейти в адрес стека вызовов... который не заполняется. Что следует за незаконным доступом и ошибкой сегментации.
Быстрое решение заключалось бы в вызове exit()
в конце вашей программы (и, предположив C11, мы могли бы также отметить функцию как _Noreturn
):
#include <stdio.h>
#include <stdlib.h>
_Noreturn void nomain(void)
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
exit(0);
}
Фактически теперь ваша функция ведет себя как обычная функция main
, так как после возврата из main
функция exit
вызывается с возвращаемым значением main
.
Ответ 2
В C, когда вызываются функции/подпрограммы, стек заполняется как (в порядке):
- Аргументы
- Обратный адрес,
- Локальные переменные, → верхняя часть стека
main() является начальной точкой, ELF структурирует программу таким образом, что все инструкции сначала будут вставлены сначала, в этом случае printfs.
Теперь программа является усеченной без обратного адреса OR __end__
и infact, она предполагает, что все, что есть в стеке в этом (__end__
) месте, является обратным адресом, но, к сожалению, его нет и, следовательно, это аварии.