2017-02-19 8 views
69

Я пытаюсь скомпилировать и запустить следующую программу без main() функции в C. Я скомпилировал свою программу, используя следующую команду.Скомпилировать и запустить программу без main() в C

gcc -nostartfiles nomain.c 

И компилятор дает предупреждение

/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340 

Хорошо, нет проблем. то я запускаю исполняемый файл (a.out), и операторы printf печатают успешно, а затем получают ошибка сегментации.

Итак, мой вопрос: Почему ошибка сегментации после успешного выполнения заявлений печати?

мой код:

#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 НКУ флаг, компилятор с использованием стандартных файлов запуска при связывании

+32

Я удивлен, что это работает вообще. Честно говоря, я считаю, что это обращение компоновщика ошибочно (или, по крайней мере, Bad Thing): не было точки входа, поэтому линкер просто галлюцинировал его из любой функции. Blech. – imallett

+4

@imallett, по крайней мере, компоновщик был достаточно любезен, чтобы привлечь внимание к нему с предупреждением и объяснить, какое противодействующее действие он принял! Вы правы, что это может быть лучше, чем ошибка, а не просто предупреждение. –

+0

Зачем вам не использовать главный? –

ответ

116

Давайте посмотрим на сгенерированный assembly вашей программы:

.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.

+6

Я думаю, что есть некоторые комбинации архитектуры/ОС, где вы можете просто «вернуться» из программы; MS-DOS .COM исполняемые файлы? В любом случае, мы глубоко вписываемся в поведение, специфичное для реализации. – pjc50

+4

@ pjc50 - Мы действительно. Хотя путь в OP предполагал вариант Unix. Это в сочетании с популярностью некоторых архитектур и наборов инструкций было единственной причиной, по которой мне было удобно представить сгенерированную сборку в ответе. – StoryTeller

+1

Просто наблюдение. '-nostartfiles' также может сделать C-библиотеку непригодной. Без запуска _C_ запуска последующие вызовы функций библиотеки _C_ могут неожиданно завершиться. В Linux, если вы собираетесь компилировать с помощью '-nostartupfiles' и' -static', вы можете обнаружить, что программа будет виновата. Существуют библиотеки _C_, такие как MUSL, которые не требуют начальной инициализации, которые предназначены для работы в этой среде. –

20

В C, когда функции/подпрограммы называются стек заполняется, как (в произвольном порядке):

  1. аргументы,
  2. Обратный адрес,
  3. Локальные переменные ->верх стек

основные() является начальной точкой, ELF структуры программы таким образом, что все инструкции приходят первым получили бы толкнули первые, в этом случае printfs есть.

Теперь программа является своего рода усеченный без возврата-адреса или __end__ и Infact это предполагает, что все, что есть в стеке в том, что (__end__) расположение возвращение-адрес, но, к сожалению, его нет, и, следовательно, он выходит из строя.

+4

Является ли порядок данных стека определенным стандартом C? Я думал, что это зависит от архитектуры системы. –

+1

Вот почему я упомянул ELF (исполняемый и связанный формат файла), это генерируется кросс-компиляцией для определенного типа ARCH на требуемой ОС. –

+0

Чтобы быть разборчивым, вы можете использовать формат ELF даже в системах без стека. Одним из примеров такой системы является [Freescale RS08] (https://en.wikipedia.org/wiki/Freescale_RS08) с компилятором Codewarrior, который генерирует файлы линкера ELF. – Lundin