Есть ли какой-нибудь способ, которым программа C / C ++ может аварийно завершить работу перед main ()? - PullRequest
63 голосов
/ 25 марта 2010

Есть ли какой-нибудь способ, которым программа может аварийно завершить работу перед main ()?

Ответы [ 17 ]

36 голосов
/ 25 марта 2010

С помощью gcc вы можете пометить функцию атрибутом конструктора (что приводит к запуску функции до main). В следующей функции premain будет вызываться до main:

#include <stdio.h>

void premain() __attribute__ ((constructor));

void premain()
{
    fputs("premain\n", stdout);
}

int main()
{
    fputs("main\n", stdout);
    return 0;
}

Итак, если в premain есть ошибка сбоя, вы будете падать до main.

33 голосов
/ 25 марта 2010

Да, по крайней мере, под Windows. Если программа использует библиотеки DLL, их можно загрузить до запуска main(). Функции DllMain этих DLL будут выполняться до main(). Если они обнаружат ошибку, это может привести к остановке или сбоям всего процесса.

20 голосов
/ 25 марта 2010

Если у вас есть программа на C ++, она может инициализировать переменные и объекты через функции и конструкторы перед вводом main. Ошибка в любом из них может вызвать сбой программы.

18 голосов
/ 25 марта 2010

конечно в с ++; статические объекты с конструкторами будут вызываться раньше, чем main - они могут умереть

не уверен насчет c

вот образец

class X
{
public:
X()
{
  char *x = 0;
  *x = 1;
}
};

X x;
int main()
{
return 0;
}

произойдет сбой перед основным

18 голосов
/ 04 апреля 2010

Простой ответ: Да .

Более конкретно, мы можем различить две причины для этого. Я назову их зависящими от реализации и независимыми от реализации .

Единственный случай, который вообще не зависит от вашей среды , - это случай статических объектов в C ++, который был упомянут здесь. Следующий код умирает до main():

#include <iostream>

class Useless {
public:
    Useless() { throw "You can't construct me!"; }

};

static Useless object;

int main() {
    std::cout << "This will never be printed" << std::endl;

    return 0;
}

Более интересными являются зависимые от платформы причины . Некоторые были упомянуты здесь. Пару раз упоминалось здесь использование динамически связанных библиотек (DLL в Windows, SO в Linux и т. Д.) - если загрузчик вашей ОС загружает их до main(), они могут привести к тому, что ваше приложение умрет до main().

Более общая версия этой причины говорит о всех вещах, которые точка входа вашего двоичного файла делает до вызова вашей точки входа (main()). Обычно, когда вы создаете свой двоичный файл, возникает довольно серьезный блок кода, который вызывается, когда загрузчик вашей операционной системы начинает запускать ваш двоичный файл, и когда он это делает, он вызывает ваш main(). Одна из распространенных функций этого кода - инициализация стандартной библиотеки C / C ++. Этот код может завершиться с ошибкой по ряду причин (нехватка любого вида системного ресурса, который он пытается выделить для одной).

Один интересный способ выполнения двоичного кода перед main() в Windows - это использование обратных вызовов TLS (Google расскажет вам больше о них). Этот метод обычно используется в вредоносных программах как основной прием противодействия отладке (этот прием использовался для того, чтобы обмануть ollydbg в то время, даже не знаю, так ли это до сих пор).

Суть в том, что ваш вопрос на самом деле эквивалентен "есть ли способ, при котором загрузка двоичного кода приведет к выполнению пользовательского кода перед кодом в main()?", И ответ - черт, да!

10 голосов
/ 25 марта 2010

Любая программа, которая полагается на общие объекты (DLL), загружаемые до того, как main может завершиться сбоем перед main.

Под кодом Linux в библиотеке динамического компоновщика (ld - *. So) запускается для предоставления любых зависимостей библиотеки задолго до main. Если какие-либо необходимые библиотеки не могут быть найдены, имеют разрешения, которые не позволяют вам получить к ним доступ, не являются обычными файлами или не имеют какого-либо символа, который, по мнению динамического компоновщика, связывающего вашу программу, должен иметь это связало вашу программу, тогда это может вызвать сбой.

Кроме того, каждая библиотека может запускать некоторый код, когда она связана. Это происходит главным образом потому, что библиотека может нуждаться в связывании большего количества библиотек или может нуждаться в запуске некоторых конструкторов (даже в программе на C библиотеки могут иметь некоторый C ++ или что-то еще, использующее конструкторы). Кроме того, стандартные программы на Си уже создали файлы stdio FILE stdin, stdout и stderr. Во многих системах они также могут быть закрыты. Это означает, что они также свободны () ed, что означает, что они (и их буферы) были malloc () ed, что может привести к сбою. Это также предполагает, что они, возможно, сделали некоторые другие вещи с файловыми дескрипторами, которые представляют эти структуры FILE, что может привести к сбою.

Другие вещи, которые могут произойти, могут возникнуть, если операционная система испортит установку переменных среды и / или аргументов командной строки, которые были переданы программе. Код перед main, вероятно, должен был что-то использовать с этими данными перед вызовом main.

Многое происходит до того, как главное. Любой из них может привести к фатальному исходу.

6 голосов
/ 25 марта 2010

Я не уверен, но если у вас есть глобальная переменная, такая как:

static SomeClass object;

int main(){
   return 0;
}

Конструктор SomeClass может вызвать сбой программы перед выполнением основного.

5 голосов
/ 26 марта 2010

Есть много возможностей.

Во-первых, нам нужно понять, что на самом деле происходит перед выполнением main:

  • Загрузка динамических библиотек
  • Инициализация глобалов
  • Один несколько компиляторов, некоторые функции могут выполняться явно

Теперь, все это может вызвать сбой несколькими способами:

  • обычное неопределенное поведение (разыменование нулевого указателя, доступ к памяти не следует ...)
  • сгенерировано исключение> поскольку catch нет, вызывается terminate и завершается работа программы

Конечно, это действительно раздражает и, возможно, трудно отладить, поэтому вам следует как можно больше воздерживаться от выполнения кода до main и предпочитать отложенную инициализацию, если можете, или явную инициализацию в main.

Конечно, когда происходит сбой DLL, и вы не можете ее изменить, вас ждет боль.

4 голосов
/ 25 марта 2010

Сортировка: http://blog.ksplice.com/2010/03/libc-free-world/

Если вы компилируете без стандартной библиотеки, вот так: gcc -nostdlib -o hello hello.c

он не будет знать, как запустить main (), и вылетит.

3 голосов
/ 25 марта 2010

Глобальные и статические объекты в программе на C ++ будут вызывать свои конструкторы до того, как будет выполнен первый оператор в main (), поэтому ошибка в одном из конструкторов может вызвать сбой.

Это не может произойти в программах на C, однако.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...